diff mbox

[C/C++] Implement -Wsizeof-array-argument (PR c/6940)

Message ID 20140626222238.GF489@redhat.com
State New
Headers show

Commit Message

Marek Polacek June 26, 2014, 10:22 p.m. UTC
The following is a revamped patch for -Wsizeof-array-argument.  Almost two
months back there was an initial attempt by Prathamesh:
<https://gcc.gnu.org/ml/gcc-patches/2014-05/msg00142.html>, but that patch
never made it in.  This version implements the warning for both C and C++
FEs, adds more testing, enables the warning by default (I can move it to
-Wall, of course), makes the warning work properly even for multidimensional
arrays, etc.
Its purpose is to detect suspicious usage of the sizeof operator on an array
function parameter.  A few years back I fixed exactly this kind of bug in
elfutils - so -Wsizeof-array-argument might be indeed useful.  (The warning didn't
trigger during GCC bootstrap though.)

Jason/Joseph, could you please look at the C++, resp. C FE parts?

Tested x86_64-unknown-linux-gnu, ok for trunk?

2014-06-26  Marek Polacek  <polacek@redhat.com>

	PR c/6940
	* doc/invoke.texi: Document -Wsizeof-array-argument.
c-family/
	* c.opt (Wsizeof-array-argument): New option.
c/
	* c-decl.c (grokdeclarator): Set C_ARRAY_PARAMETER.
	* c-tree.h (C_ARRAY_PARAMETER): Define.
	* c-typeck.c (c_expr_sizeof_expr): Warn when using sizeof on an array
	function parameter.
cp/
	* cp-tree.h (DECL_ARRAY_PARAMETER_P): Define.
	* decl.c (grokdeclarator): Set DECL_ARRAY_PARAMETER_P.
	* typeck.c (cxx_sizeof_expr): Warn when using sizeof on an array
	function parameter.
testsuite/
	* c-c++-common/Wsizeof-pointer-memaccess1.c: Use
	-Wno-sizeof-array-argument.
	* c-c++-common/Wsizeof-pointer-memaccess2.c: Likewise.
	* g++.dg/warn/Wsizeof-pointer-memaccess-1.C: Likewise.
	* gcc.dg/Wsizeof-pointer-memaccess1.c: Likewise.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Likewise.
	* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Likewise.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Likewise.
	* c-c++-common/sizeof-array-argument.c: New test.
	* gcc.dg/vla-5.c: Add dg-warnings.
../libgomp/
	* testsuite/libgomp.c/appendix-a/a.29.1.c (f): Add dg-warnings.


	Marek

Comments

Joseph Myers June 27, 2014, 5:15 p.m. UTC | #1
On Fri, 27 Jun 2014, Marek Polacek wrote:

> The following is a revamped patch for -Wsizeof-array-argument.  Almost two
> months back there was an initial attempt by Prathamesh:
> <https://gcc.gnu.org/ml/gcc-patches/2014-05/msg00142.html>, but that patch
> never made it in.  This version implements the warning for both C and C++
> FEs, adds more testing, enables the warning by default (I can move it to
> -Wall, of course), makes the warning work properly even for multidimensional
> arrays, etc.
> Its purpose is to detect suspicious usage of the sizeof operator on an array
> function parameter.  A few years back I fixed exactly this kind of bug in
> elfutils - so -Wsizeof-array-argument might be indeed useful.  (The warning didn't
> trigger during GCC bootstrap though.)
> 
> Jason/Joseph, could you please look at the C++, resp. C FE parts?

The C changes are OK.
Jason Merrill July 3, 2014, 2:27 a.m. UTC | #2
On 06/26/2014 03:22 PM, Marek Polacek wrote:
> The following is a revamped patch for -Wsizeof-array-argument.
> Its purpose is to detect suspicious usage of the sizeof operator on an array
> function parameter.

Then the name should be -Wsizeof-array-parm, not -argument.

> @@ -9550,6 +9551,8 @@ grokdeclarator (const cp_declarator *declarator,
>  	       array.  */
>  	    returned_attrs = chainon (returned_attrs,
>  				      declarator->std_attributes);
> +	  if (decl_context == PARM)
> +	    array_parameter_p = true;
>  	  break;

Setting this here means that you'll treat a parameter with 
pointer-to-array type as an array parm.  I think you want to set it 
here, instead:

>       /* A parameter declared as an array of T is really a pointer to T.
>          One declared as a function is really a pointer to a function.
>          One declared as a member is really a pointer to member.  */
>
>       if (TREE_CODE (type) == ARRAY_TYPE)
>         {
>           /* Transfer const-ness of array into that of type pointed to.  */
>           type = build_pointer_type (TREE_TYPE (type));
>           type_quals = TYPE_UNQUALIFIED;
>         }

Jason
diff mbox

Patch

diff --git gcc/gcc/c-family/c.opt gcc/gcc/c-family/c.opt
index 1d02bae..3d3cf14 100644
--- gcc/gcc/c-family/c.opt
+++ gcc/gcc/c-family/c.opt
@@ -526,6 +526,10 @@  Wsizeof-pointer-memaccess
 C ObjC C++ ObjC++ Var(warn_sizeof_pointer_memaccess) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
 Warn about suspicious length parameters to certain string functions if the argument uses sizeof
 
+Wsizeof-array-argument
+C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1)
+Warn when sizeof is applied on a parameter declared as an array
+
 Wsuggest-attribute=format
 C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning
 Warn about functions which might be candidates for format attributes
diff --git gcc/gcc/c/c-decl.c gcc/gcc/c/c-decl.c
index def10a2..a1bc114 100644
--- gcc/gcc/c/c-decl.c
+++ gcc/gcc/c/c-decl.c
@@ -6092,6 +6092,7 @@  grokdeclarator (const struct c_declarator *declarator,
     if (decl_context == PARM)
       {
 	tree promoted_type;
+	bool array_parameter_p = false;
 
 	/* A parameter declared as an array of T is really a pointer to T.
 	   One declared as a function is really a pointer to a function.  */
@@ -6113,6 +6114,7 @@  grokdeclarator (const struct c_declarator *declarator,
 			  "attributes in parameter array declarator ignored");
 
 	    size_varies = false;
+	    array_parameter_p = true;
 	  }
 	else if (TREE_CODE (type) == FUNCTION_TYPE)
 	  {
@@ -6137,6 +6139,7 @@  grokdeclarator (const struct c_declarator *declarator,
 			   PARM_DECL, declarator->u.id, type);
 	if (size_varies)
 	  C_DECL_VARIABLE_SIZE (decl) = 1;
+	C_ARRAY_PARAMETER (decl) = array_parameter_p;
 
 	/* Compute the type actually passed in the parmlist,
 	   for the case where there is no prototype.
diff --git gcc/gcc/c/c-tree.h gcc/gcc/c/c-tree.h
index 133930f..f97d0d5 100644
--- gcc/gcc/c/c-tree.h
+++ gcc/gcc/c/c-tree.h
@@ -66,6 +66,9 @@  along with GCC; see the file COPYING3.  If not see
 /* For a FUNCTION_DECL, nonzero if it was an implicit declaration.  */
 #define C_DECL_IMPLICIT(EXP) DECL_LANG_FLAG_2 (EXP)
 
+/* For a PARM_DECL, nonzero if it was declared as an array.  */
+#define C_ARRAY_PARAMETER(NODE) DECL_LANG_FLAG_0 (NODE)
+
 /* For FUNCTION_DECLs, evaluates true if the decl is built-in but has
    been declared.  */
 #define C_DECL_DECLARED_BUILTIN(EXP)		\
diff --git gcc/gcc/c/c-typeck.c gcc/gcc/c/c-typeck.c
index b62e830..0b63e98 100644
--- gcc/gcc/c/c-typeck.c
+++ gcc/gcc/c/c-typeck.c
@@ -2731,6 +2731,16 @@  c_expr_sizeof_expr (location_t loc, struct c_expr expr)
   else
     {
       bool expr_const_operands = true;
+
+      if (TREE_CODE (expr.value) == PARM_DECL
+	  && C_ARRAY_PARAMETER (expr.value))
+	{
+	  if (warning_at (loc, OPT_Wsizeof_array_argument,
+			  "%<sizeof%> on array function parameter %qE will "
+			  "return size of %qT", expr.value,
+			  expr.original_type))
+	    inform (DECL_SOURCE_LOCATION (expr.value), "declared here");
+	}
       tree folded_expr = c_fully_fold (expr.value, require_constant_value,
 				       &expr_const_operands);
       ret.value = c_sizeof (loc, TREE_TYPE (folded_expr));
diff --git gcc/gcc/cp/cp-tree.h gcc/gcc/cp/cp-tree.h
index c1bd7cf..3aac677 100644
--- gcc/gcc/cp/cp-tree.h
+++ gcc/gcc/cp/cp-tree.h
@@ -145,6 +145,7 @@  c-common.h, not after.
       DECL_MEMBER_TEMPLATE_P (in TEMPLATE_DECL)
       USING_DECL_TYPENAME_P (in USING_DECL)
       DECL_VLA_CAPTURE_P (in FIELD_DECL)
+      DECL_ARRAY_PARAMETER_P (in PARM_DECL)
    2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL).
       DECL_IMPLICIT_TYPEDEF_P (in a TYPE_DECL)
    3: DECL_IN_AGGR_P.
@@ -3681,6 +3682,11 @@  more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_VLA_CAPTURE_P(NODE) \
   DECL_LANG_FLAG_1 (FIELD_DECL_CHECK (NODE))
 
+/* Nonzero for PARM_DECL node means that this is an array function
+   parameter, i.e, a[] rather than *a.  */
+#define DECL_ARRAY_PARAMETER_P(NODE) \
+  DECL_LANG_FLAG_1 (PARM_DECL_CHECK (NODE))
+
 /* Nonzero for FIELD_DECL node means that this field is a base class
    of the parent object, as opposed to a member field.  */
 #define DECL_FIELD_IS_BASE(NODE) \
diff --git gcc/gcc/cp/decl.c gcc/gcc/cp/decl.c
index d548f61..0c2187e 100644
--- gcc/gcc/cp/decl.c
+++ gcc/gcc/cp/decl.c
@@ -8816,6 +8816,7 @@  grokdeclarator (const cp_declarator *declarator,
   bool typedef_p = decl_spec_seq_has_spec_p (declspecs, ds_typedef);
   bool constexpr_p = decl_spec_seq_has_spec_p (declspecs, ds_constexpr);
   bool late_return_type_p = false;
+  bool array_parameter_p = false;
   source_location saved_loc = input_location;
   const char *errmsg;
 
@@ -9550,6 +9551,8 @@  grokdeclarator (const cp_declarator *declarator,
 	       array.  */
 	    returned_attrs = chainon (returned_attrs,
 				      declarator->std_attributes);
+	  if (decl_context == PARM)
+	    array_parameter_p = true;
 	  break;
 
 	case cdk_function:
@@ -10474,6 +10477,7 @@  grokdeclarator (const cp_declarator *declarator,
     if (decl_context == PARM)
       {
 	decl = cp_build_parm_decl (unqualified_id, type);
+	DECL_ARRAY_PARAMETER_P (decl) = array_parameter_p;
 
 	bad_specifiers (decl, BSP_PARM, virtualp,
 			memfn_quals != TYPE_UNQUALIFIED,
diff --git gcc/gcc/cp/typeck.c gcc/gcc/cp/typeck.c
index 65dccf7..ed6a59b 100644
--- gcc/gcc/cp/typeck.c
+++ gcc/gcc/cp/typeck.c
@@ -1614,6 +1614,15 @@  cxx_sizeof_expr (tree e, tsubst_flags_t complain)
       && DECL_TEMPLATE_INSTANTIATION (e))
     instantiate_decl (e, /*defer_ok*/true, /*expl_inst_mem*/false);
 
+  if (TREE_CODE (e) == PARM_DECL
+      && DECL_ARRAY_PARAMETER_P (e)
+      && (complain & tf_warning))
+    {
+      if (warning (OPT_Wsizeof_array_argument, "%<sizeof%> on array function "
+		   "parameter %qE will return size of %qT", e, TREE_TYPE (e)))
+	inform (DECL_SOURCE_LOCATION (e), "declared here");
+    }
+
   e = mark_type_use (e);
 
   if (TREE_CODE (e) == COMPONENT_REF
diff --git gcc/gcc/doc/invoke.texi gcc/gcc/doc/invoke.texi
index 4df5f81..c8685d9 100644
--- gcc/gcc/doc/invoke.texi
+++ gcc/gcc/doc/invoke.texi
@@ -266,7 +266,7 @@  Objective-C and Objective-C++ Dialects}.
 -Wredundant-decls  -Wno-return-local-addr @gol
 -Wreturn-type  -Wsequence-point  -Wshadow  -Wno-shadow-ivar @gol
 -Wsign-compare  -Wsign-conversion -Wfloat-conversion @gol
--Wsizeof-pointer-memaccess @gol
+-Wsizeof-pointer-memaccess @gol  -Wsizeof-array-argument @gol
 -Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol
 -Wstrict-aliasing=n @gol -Wstrict-overflow -Wstrict-overflow=@var{n} @gol
 -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol
@@ -4662,6 +4662,13 @@  but a pointer, and suggests a possible fix, or about
 @code{memcpy (&foo, ptr, sizeof (&foo));}.  This warning is enabled by
 @option{-Wall}.
 
+@item -Wsizeof-array-argument
+@opindex Wsizeof-array-argument
+@opindex Wno-sizeof-array-argument
+Warn when the @code{sizeof} operator is applied to a parameter that is
+declared as an array in a function definition.  This warning is enabled by
+default for C and C++ programs.
+
 @item -Waddress
 @opindex Waddress
 @opindex Wno-address
diff --git gcc/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c gcc/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
index 2a5f419..8e829d6 100644
--- gcc/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
+++ gcc/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@ 
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall" } */
+/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
 
 typedef __SIZE_TYPE__ size_t;
 #ifdef __cplusplus
diff --git gcc/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c gcc/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
index 73cdf0e..fe17a70 100644
--- gcc/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
+++ gcc/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
@@ -1,6 +1,6 @@ 
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -O2" } */
+/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument" } */
 
 #define bos(ptr) __builtin_object_size (ptr, 1)
 #define bos0(ptr) __builtin_object_size (ptr, 0)
diff --git gcc/gcc/testsuite/c-c++-common/sizeof-array-argument.c gcc/gcc/testsuite/c-c++-common/sizeof-array-argument.c
index e69de29..d22212e 100644
--- gcc/gcc/testsuite/c-c++-common/sizeof-array-argument.c
+++ gcc/gcc/testsuite/c-c++-common/sizeof-array-argument.c
@@ -0,0 +1,86 @@ 
+/* PR c/6940 */
+/* { dg-do compile } */
+
+/* Test -Wsizeof-array-argument warning.  */
+
+int
+fn1 (int a[])
+{
+  return sizeof a; /* { dg-warning "on array function parameter" } */
+}
+
+int
+fn2 (int x, int b[3])
+{
+  return x + sizeof b; /* { dg-warning "on array function parameter" } */
+}
+
+int
+fn3 (int *p)
+{
+  return sizeof p;
+}
+
+int fn4 (int *p);
+int
+fn4 (int p[])
+{
+  return sizeof p; /* { dg-warning "on array function parameter" } */
+}
+
+int fn5 (int x[]);
+int
+fn5 (int *x)
+{
+  return sizeof x;
+}
+
+#ifndef __cplusplus
+/* C++ doesn't know VLA unspec.  */
+int fn6 (int x[*]);
+int
+fn6 (int x[])
+{
+  return sizeof x; /* { dg-warning "on array function parameter" "" { target c } } */
+}
+#endif
+
+int
+fn7 (int x[][2])
+{
+  return sizeof x; /* { dg-warning "on array function parameter" } */
+}
+
+int
+fn8 (char *x[])
+{
+  return sizeof x; /* { dg-warning "on array function parameter" } */
+}
+
+int
+fn9 (char **x)
+{
+  return sizeof x;
+}
+
+#ifndef __cplusplus
+int
+fn10 (int a, char x[static sizeof a])
+{
+  return sizeof x; /* { dg-warning "on array function parameter" "" { target c } } */
+}
+
+int
+fn11 (a)
+  char a[];
+{
+  return sizeof a; /* { dg-warning "on array function parameter" "" { target c } } */
+}
+
+int
+fn12 (a)
+  char *a;
+{
+  return sizeof a;
+}
+#endif
diff --git gcc/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C gcc/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
index 6cb3980..8b5c33e 100644
--- gcc/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
+++ gcc/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
@@ -1,6 +1,6 @@ 
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall" }
+// { dg-options "-Wall -Wno-sizeof-array-argument" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
 // { dg-skip-if "" { *-*-* }  { "-flto" } { "" } }
diff --git gcc/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C gcc/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
index 9e2805d..0e99568 100644
--- gcc/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
+++ gcc/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
@@ -1,6 +1,6 @@ 
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall" }
+// { dg-options "-Wall -Wno-sizeof-array-argument" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
 // { dg-skip-if "" { *-*-* }  { "-flto" } { "" } }
diff --git gcc/gcc/testsuite/g++.dg/warn/Wsizeof-pointer-memaccess-1.C gcc/gcc/testsuite/g++.dg/warn/Wsizeof-pointer-memaccess-1.C
index e2ba876..798cb6d 100644
--- gcc/gcc/testsuite/g++.dg/warn/Wsizeof-pointer-memaccess-1.C
+++ gcc/gcc/testsuite/g++.dg/warn/Wsizeof-pointer-memaccess-1.C
@@ -1,6 +1,6 @@ 
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall" }
+// { dg-options "-Wall -Wno-sizeof-array-argument" }
 
 typedef __SIZE_TYPE__ size_t;
 extern "C" void *memset (void *, int, size_t);
diff --git gcc/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c gcc/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
index b683be7..66be5a5 100644
--- gcc/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
+++ gcc/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@ 
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall" } */
+/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
 
 typedef __SIZE_TYPE__ size_t;
 extern void bzero (void *, size_t);
diff --git gcc/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c gcc/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
index 8d01bc6..a82f4ef 100644
--- gcc/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
+++ gcc/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@ 
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall" } */
+/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
 /* Test just twice, once with -O0 non-fortified, once with -O2 fortified.  */
 /* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } } */
 /* { dg-skip-if "" { *-*-* }  { "-flto" } { "" } } */
diff --git gcc/gcc/testsuite/gcc.dg/vla-5.c gcc/gcc/testsuite/gcc.dg/vla-5.c
index f5256c4..2c253b5 100644
--- gcc/gcc/testsuite/gcc.dg/vla-5.c
+++ gcc/gcc/testsuite/gcc.dg/vla-5.c
@@ -13,12 +13,12 @@  void foo4(int j, int a[j]) {
 
 int foo5(int a, int b[*][*], int c[static sizeof(*b)]);
 int foo5(int a, int b[10][10], int c[400]) {
-  return sizeof (c);
+  return sizeof (c); /* { dg-warning "on array function parameter" } */
 }
 
 int foo6(int a, int b[*][*], int c[static sizeof(*b)]);
 int foo6(int a, int b[a][a], int c[sizeof(*b)]) {
-  return sizeof (c);
+  return sizeof (c); /* { dg-warning "on array function parameter" } */
 }
 
 void foo7(__typeof__ (int (*)(int o[*])) i);
diff --git gcc/libgomp/testsuite/libgomp.c/appendix-a/a.29.1.c gcc/libgomp/testsuite/libgomp.c/appendix-a/a.29.1.c
index 6f0f65f..4843212 100644
--- gcc/libgomp/testsuite/libgomp.c/appendix-a/a.29.1.c
+++ gcc/libgomp/testsuite/libgomp.c/appendix-a/a.29.1.c
@@ -11,8 +11,8 @@  f (int n, int B[n][n], int C[])
   E[1][1] = 4;
 #pragma omp parallel firstprivate(B, C, D, E)
   {
-    assert (sizeof (B) == sizeof (int (*)[n]));
-    assert (sizeof (C) == sizeof (int *));
+    assert (sizeof (B) == sizeof (int (*)[n])); /* { dg-warning "on array function parameter" } */
+    assert (sizeof (C) == sizeof (int *)); /* { dg-warning "on array function parameter" } */
     assert (sizeof (D) == 4 * sizeof (int));
     assert (sizeof (E) == n * n * sizeof (int));
     /* Private B and C have values of original B and C. */