This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] add alloc_size attribute
- From: Dirk Mueller <dmueller at suse dot de>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Marcus Meissner <meissner at suse dot de>
- Date: Wed, 25 Apr 2007 01:31:02 +0200
- Subject: [PATCH] add alloc_size attribute
Hi,
the patch below adds an alloc_size attribute that can be used to annotate
custom memory allocators (and in a followup patch the C++ new operators) to
give further input for __builtin_object_size. this can improve warnings about
possible buffer overflows or can reduce number of checking calls being
emitted in fortify mode and thereby reducing overhead.
bootstrapped, regtested on x86_64_suse-linux, no further regressions.
Dirk
2007-04-25 Dirk Mueller <dmueller@suse.de>
Marcus Meissner <meissner@suse.de>
* doc/extend.texi (alloc_size): New attribute.
* c-common.c (handle_alloc_size_attribute): New.
* tree-object-size.c (alloc_object_size): Use alloc_size
attribute, if available.
* testsuite/gcc.dg/attr-alloc_size.c: New.
--- doc/extend.texi (revision 123941)
+++ doc/extend.texi (working copy)
@@ -1572,13 +1572,14 @@ The keyword @code{__attribute__} allows
attributes when making a declaration. This keyword is followed by an
attribute specification inside double parentheses. The following
attributes are currently defined for functions on all targets:
-@code{noreturn}, @code{returns_twice}, @code{noinline}, @code{always_inline},
-@code{flatten}, @code{pure}, @code{const}, @code{nothrow}, @code{sentinel},
-@code{format}, @code{format_arg}, @code{no_instrument_function},
-@code{section}, @code{constructor}, @code{destructor}, @code{used},
-@code{unused}, @code{deprecated}, @code{weak}, @code{malloc},
-@code{alias}, @code{warn_unused_result}, @code{nonnull},
-@code{gnu_inline} and @code{externally_visible}, @code{hot}, @code{cold}.
+@code{alloc_size}, @code{noreturn}, @code{returns_twice}, @code{noinline},
+@code{always_inline}, @code{flatten}, @code{pure}, @code{const},
+@code{nothrow}, @code{sentinel}, @code{format}, @code{format_arg},
+@code{no_instrument_function}, @code{section}, @code{constructor},
+@code{destructor}, @code{used}, @code{unused}, @code{deprecated},
+@code{weak}, @code{malloc}, @code{alias}, @code{warn_unused_result},
+@code{nonnull}, @code{gnu_inline} and @code{externally_visible},
+@code{hot}, @code{cold}.
Several other attributes are defined for functions on particular target
systems. Other attributes, including @code{section} are supported for
variables declarations (@pxref{Variable Attributes}) and for types
(@pxref{Type
@@ -1611,6 +1612,22 @@ is not defined in the same translation u
Not all target machines support this attribute.
+@item alloc_size
+@cindex @code{alloc_size} attribute
+The @code{alloc_size} attribute is used to tell the compiler that the
+function return value points to memory, where the size is given by
+one or two of the functions parameters. GCC uses this
+information to improve the correctness of @code{__builtin_object_size}.
+
+For instance,
+
+@smallexample
+void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)))
+@end smallexample
+
+declares that my_calloc will return memory of the size given by
+the product of parameter 1 and 2.
+
@item always_inline
@cindex @code{always_inline} function attribute
Generally, functions are not inlined unless optimization is specified.
--- c-common.c
+++ c-common.c
@@ -556,6 +556,7 @@ static tree handle_cleanup_attribute (tr
static tree handle_warn_unused_result_attribute (tree *, tree, tree, int,
bool *);
static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
+static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
static void check_function_nonnull (tree, int, tree *);
static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
@@ -650,6 +651,8 @@ const struct attribute_spec c_common_att
handle_warn_unused_result_attribute },
{ "sentinel", 0, 1, false, true, true,
handle_sentinel_attribute },
+ { "alloc_size", 1, 2, false, true, true,
+ handle_alloc_size_attribute },
{ "cold", 0, 0, true, false, false,
handle_cold_attribute },
{ "hot", 0, 0, true, false, false,
@@ -5543,6 +5546,37 @@ handle_malloc_attribute (tree *node, tre
return NULL_TREE;
}
+/* Handle a "alloc_size" attribute; arguments as in
+ struct attribute_spec.handler. */
+
+static tree
+handle_alloc_size_attribute (tree *node, tree ARG_UNUSED (name), tree args,
+ int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+ tree params = TYPE_ARG_TYPES (*node);
+ unsigned arg_count = 0;
+
+ for (; TREE_CHAIN (params); params = TREE_CHAIN (params))
+ arg_count ++;
+
+ for (; args; args = TREE_CHAIN (args))
+ {
+ tree position = TREE_VALUE (args);
+
+ if (TREE_CODE (position) != INTEGER_CST
+ || TREE_INT_CST_HIGH (position)
+ || TREE_INT_CST_LOW (position) < 1
+ || TREE_INT_CST_LOW (position) > arg_count )
+ {
+ warning (OPT_Wattributes,
+ "alloc_size parameter outside range");
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+ }
+ return NULL_TREE;
+}
+
/* Handle a "returns_twice" attribute; arguments as in
struct attribute_spec.handler. */
--- tree-object-size.c
+++ tree-object-size.c
@@ -24,6 +24,7 @@ Boston, MA 02110-1301, USA. */
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
+#include "toplev.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "tree-pass.h"
@@ -229,39 +230,48 @@ static unsigned HOST_WIDE_INT
alloc_object_size (tree call, int object_size_type)
{
tree callee, bytes = NULL_TREE;
+ tree alloc_size;
+ int arg1 = 0, arg2 = 0;
gcc_assert (TREE_CODE (call) == CALL_EXPR);
callee = get_callee_fndecl (call);
- if (callee
- && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
+ if (!callee)
+ return unknown[object_size_type];
+
+ alloc_size = lookup_attribute ("alloc_size", TYPE_ATTRIBUTES
(TREE_TYPE(callee)));
+ if (alloc_size && TREE_VALUE (alloc_size))
+ {
+ tree p = TREE_VALUE (alloc_size);
+
+ arg1 = TREE_INT_CST_LOW (TREE_VALUE (p));
+ if (TREE_CHAIN (p))
+ arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)));
+ }
+
+ if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (callee))
{
+ case BUILT_IN_CALLOC:
+ arg2 = 2;
+ /* fall through */
case BUILT_IN_MALLOC:
case BUILT_IN_ALLOCA:
- if (call_expr_nargs (call) == 1
- && TREE_CODE (CALL_EXPR_ARG (call, 0)) == INTEGER_CST)
- bytes = fold_convert (sizetype, CALL_EXPR_ARG (call, 0));
- break;
- /*
- case BUILT_IN_REALLOC:
- if (call_expr_nargs (call) == 2
- && TREE_CODE (CALL_EXPR_ARG (call, 1)) == INTEGER_CST)
- bytes = fold_convert (sizetype, CALL_EXPR_ARG (call, 1));
- break;
- */
- case BUILT_IN_CALLOC:
- if (call_expr_nargs (call) == 2
- && TREE_CODE (CALL_EXPR_ARG (call, 0)) == INTEGER_CST
- && TREE_CODE (CALL_EXPR_ARG (call, 1)) == INTEGER_CST)
- bytes = size_binop (MULT_EXPR,
- fold_convert (sizetype, CALL_EXPR_ARG (call, 0)),
- fold_convert (sizetype, CALL_EXPR_ARG (call, 1)));
- break;
+ arg1 = 1;
default:
break;
}
+ if (arg2 && call_expr_nargs (call) >= arg2
+ && TREE_CODE (CALL_EXPR_ARG (call, arg1-1)) == INTEGER_CST
+ && TREE_CODE (CALL_EXPR_ARG (call, arg2-1)) == INTEGER_CST)
+ bytes = size_binop (MULT_EXPR,
+ fold_convert (sizetype, CALL_EXPR_ARG (call, arg1-1)),
+ fold_convert (sizetype, CALL_EXPR_ARG (call, arg2-1)));
+ else if (arg1 && call_expr_nargs (call) >= arg1
+ && TREE_CODE (CALL_EXPR_ARG (call, arg1-1)) == INTEGER_CST)
+ bytes = fold_convert (sizetype, CALL_EXPR_ARG (call, arg1-1));
+
if (bytes && host_integerp (bytes, 1))
return tree_low_cst (bytes, 1);
--- testsuite/gcc.dg/attr-alloc_size.c
+++ testsuite/gcc.dg/attr-alloc_size.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wall" } */
+
+extern void abort (void);
+
+#include "../gcc.c-torture/execute/builtins/chk.h"
+
+extern char *mallocminus1(int size) __attribute__((alloc_size(-1))); /* {
dg-warning "parameter outside range" } */
+extern char *malloc0(int size) __attribute__((alloc_size(0))); /* {
dg-warning "parameter outside range" } */
+extern char *malloc1(int size) __attribute__((alloc_size(1)));
+extern char *malloc2(int empty, int size) __attribute__((alloc_size(2)));
+extern char *calloc1(int size, int elements)
__attribute__((alloc_size(1,2)));
+extern char *calloc2(int size, int empty, int elements)
__attribute__((alloc_size(1,3)));
+extern char *balloc1(void *size) __attribute__((alloc_size(1)));
+
+void
+test (void)
+{
+ char *p;
+
+ p = malloc0 (6);
+ strcpy (p, "Hello");
+ p = malloc1 (6);
+ strcpy (p, "Hello");
+ strcpy (p, "Hello World"); /* { dg-warning "will always
overflow" "strcpy" } */
+ my_read(p, 7);
+ p = malloc2 (424242, 6);
+ strcpy (p, "World");
+ strcpy (p, "Hello World"); /* { dg-warning "will always
overflow" "strcpy" } */
+ p = calloc1 (2, 5);
+ strcpy (p, "World");
+ strcpy (p, "Hello World"); /* { dg-warning "will always
overflow" "strcpy" } */
+ p = calloc2 (2, 424242, 5);
+ strcpy (p, "World");
+ strcpy (p, "Hello World"); /* { dg-warning "will always
overflow" "strcpy" } */
+}
+