diff options
author | Nicholas Marriott <nicm@cvs.openbsd.org> | 2011-09-15 12:19:13 +0000 |
---|---|---|
committer | Nicholas Marriott <nicm@cvs.openbsd.org> | 2011-09-15 12:19:13 +0000 |
commit | 6599d05679056c4927894ec9aac978c151de4eff (patch) | |
tree | a5bb0d457724eee40d1049a473f601f2b03e182d | |
parent | 27333e86313ada2e6da66ffbe3d171a417c53fcf (diff) |
Port the -Wbounded extension from gcc3 to gcc4. Based on work started by
jasper@.
This omits the sizeof(pointer) tracking which is very difficult to
implement for gcc4 due to internal changes.
Thanks to landry@ for running through a ports bulk build and both
landry@ and jasper@ for fixing a few issues.
ok deraadt millert jasper
-rw-r--r-- | gnu/gcc/gcc/builtin-attrs.def | 75 | ||||
-rw-r--r-- | gnu/gcc/gcc/builtins.def | 16 | ||||
-rw-r--r-- | gnu/gcc/gcc/c-bounded.c | 490 | ||||
-rw-r--r-- | gnu/gcc/gcc/c-common.c | 13 | ||||
-rw-r--r-- | gnu/gcc/gcc/c-common.h | 2 | ||||
-rw-r--r-- | gnu/gcc/gcc/c-format.c | 63 | ||||
-rw-r--r-- | gnu/gcc/gcc/c-opts.c | 1 | ||||
-rw-r--r-- | gnu/gcc/gcc/c.opt | 2 | ||||
-rw-r--r-- | gnu/usr.bin/cc/cc_int/Makefile | 4 |
9 files changed, 637 insertions, 29 deletions
diff --git a/gnu/gcc/gcc/builtin-attrs.def b/gnu/gcc/gcc/builtin-attrs.def index 46ac70d4c35..d72530fe7b4 100644 --- a/gnu/gcc/gcc/builtin-attrs.def +++ b/gnu/gcc/gcc/builtin-attrs.def @@ -65,10 +65,13 @@ DEF_ATTR_FOR_INT (6) ATTR_##VALUE1, ATTR_LIST_##VALUE2) DEF_LIST_INT_INT (1,0) DEF_LIST_INT_INT (1,2) +DEF_LIST_INT_INT (1,3) DEF_LIST_INT_INT (2,0) DEF_LIST_INT_INT (2,3) DEF_LIST_INT_INT (3,0) +DEF_LIST_INT_INT (3,2) DEF_LIST_INT_INT (3,4) +DEF_LIST_INT_INT (1,3_2) DEF_LIST_INT_INT (4,0) DEF_LIST_INT_INT (4,5) DEF_LIST_INT_INT (5,0) @@ -76,6 +79,8 @@ DEF_LIST_INT_INT (5,6) #undef DEF_LIST_INT_INT /* Construct trees for identifiers. */ +DEF_ATTR_IDENT (ATTR_BOUNDED, "bounded") +DEF_ATTR_IDENT (ATTR_BUFFER, "buffer") DEF_ATTR_IDENT (ATTR_CONST, "const") DEF_ATTR_IDENT (ATTR_FORMAT, "format") DEF_ATTR_IDENT (ATTR_FORMAT_ARG, "format_arg") @@ -91,8 +96,10 @@ DEF_ATTR_IDENT (ATTR_GCC_CXXDIAG, "gcc_cxxdiag") DEF_ATTR_IDENT (ATTR_PURE, "pure") DEF_ATTR_IDENT (ATTR_NOVOPS, "no vops") DEF_ATTR_IDENT (ATTR_SCANF, "scanf") +DEF_ATTR_IDENT (ATTR_SIZE, "size") DEF_ATTR_IDENT (ATTR_SENTINEL, "sentinel") DEF_ATTR_IDENT (ATTR_STRFMON, "strfmon") +DEF_ATTR_IDENT (ATTR_STRING, "string") DEF_ATTR_IDENT (ATTR_STRFTIME, "strftime") DEF_ATTR_TREE_LIST (ATTR_NOVOPS_LIST, ATTR_NOVOPS, ATTR_NULL, ATTR_NULL) @@ -181,3 +188,71 @@ DEF_FORMAT_ARG_ATTRIBUTE(1) DEF_FORMAT_ARG_ATTRIBUTE(2) #undef DEF_FORMAT_ARG_ATTRIBUTE +/* Construct a tree for a bounded attribute. */ + +/* Generate a fragment for future use in a bounded attr, e.g. ATTR_BUFFER_2_3 */ +#define DEF_ATTR_BOUNDED_FRAG(TYPE, VALUES) \ + DEF_ATTR_TREE_LIST (ATTR_##TYPE##_##VALUES, \ + ATTR_NULL, \ + ATTR_##TYPE, \ + ATTR_LIST_##VALUES) + +/* Create bounded attribute chained to nothrow, e.g. of the + form ATTR_NOTHROW_BOUNDED_BUFFER_2_3 */ +#define DEF_NOTHROW_BOUNDED_ATTRIBUTE(TYPE, VALUES) \ + DEF_ATTR_TREE_LIST (ATTR_NOTHROW_BOUNDED_##TYPE##_##VALUES, \ + ATTR_BOUNDED, \ + ATTR_##TYPE##_##VALUES, \ + ATTR_NOTHROW_LIST) + +/* Create bounded attribute chained to nothrow nonnull, e.g. of the + form ATTR_NOTHROW_NONNULL_BOUNDED_BUFFER_2_3 */ +#define DEF_NOTHROW_NONNULL_BOUNDED_ATTRIBUTE(TYPE, VALUES) \ + DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_BOUNDED_##TYPE##_##VALUES, \ + ATTR_BOUNDED, \ + ATTR_##TYPE##_##VALUES, \ + ATTR_NOTHROW_NONNULL) + +/* Chain multiple nothrow bounded buffer attributes together */ +#define DEF_MULTIPLE_NOTHROW_BOUNDED_BUFFER_ATTRIBUTE(VAL1,VAL2) \ + DEF_ATTR_TREE_LIST (ATTR_NOTHROW_BOUNDED_BUFFER_##VAL1##_##VAL2, \ + ATTR_BOUNDED, \ + ATTR_BUFFER_##VAL1, \ + ATTR_NOTHROW_BOUNDED_BUFFER_##VAL2) + +/* Chain multiple nothrow nonnull bounded buffer attributes together */ +#define DEF_MULTIPLE_NOTHROW_NONNULL_BOUNDED_BUFFER_ATTRIBUTE(VAL1,VAL2) \ + DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_BOUNDED_BUFFER_##VAL1##_##VAL2, \ + ATTR_BOUNDED, \ + ATTR_BUFFER_##VAL1, \ + ATTR_NOTHROW_NONNULL_BOUNDED_BUFFER_##VAL2) + +/* Chain a bounded attribute to a format printf attribute */ +#define DEF_FORMAT_PRINTF_BOUNDED_ATTRIBUTE(TYPE, VALUES, PREPEND, PRINTVALS) \ + DEF_ATTR_TREE_LIST (PREPEND##TYPE##_##VALUES, \ + ATTR_BOUNDED, \ + ATTR_##TYPE##_##VALUES, \ + ATTR_FORMAT_PRINTF_##PRINTVALS) + +DEF_ATTR_BOUNDED_FRAG(BUFFER,1_2) +DEF_ATTR_BOUNDED_FRAG(BUFFER,1_3) +DEF_ATTR_BOUNDED_FRAG(BUFFER,2_3) +DEF_ATTR_BOUNDED_FRAG(SIZE,1_3_2) +DEF_ATTR_BOUNDED_FRAG(STRING,1_2) + +DEF_NOTHROW_BOUNDED_ATTRIBUTE(BUFFER,1_2) +DEF_NOTHROW_NONNULL_BOUNDED_ATTRIBUTE(BUFFER,1_3) +DEF_NOTHROW_BOUNDED_ATTRIBUTE(BUFFER,2_3) +DEF_NOTHROW_NONNULL_BOUNDED_ATTRIBUTE(SIZE,1_3_2) + +DEF_MULTIPLE_NOTHROW_NONNULL_BOUNDED_BUFFER_ATTRIBUTE(2_3,1_3) + +DEF_FORMAT_PRINTF_BOUNDED_ATTRIBUTE(STRING,1_2,ATTR_PRINTF_3_4_BOUNDED_,3_4) +DEF_FORMAT_PRINTF_BOUNDED_ATTRIBUTE(STRING,1_2,ATTR_PRINTF_3_0_BOUNDED_,3_0) + +#undef DEF_ATTR_BOUNDED_FRAG +#undef DEF_NOTHROW_NONNULL_BOUNDED_ATTRIBUTE +#undef DEF_NOTHROW_BOUNDED_ATTRIBUTE +#undef DEF_MULTIPLE_NOTHROW_BOUNDED_ATTRIBUTE +#undef DEF_MULTIPLE_NOTHROW_NONNULL_BOUNDED_ATTRIBUTE +#undef DEF_FORMAT_PRINTF_BOUNDED_ATTRIBUTE diff --git a/gnu/gcc/gcc/builtins.def b/gnu/gcc/gcc/builtins.def index 37ea28c0960..fd8713c4fa3 100644 --- a/gnu/gcc/gcc/builtins.def +++ b/gnu/gcc/gcc/builtins.def @@ -494,13 +494,13 @@ DEF_C99_BUILTIN (BUILT_IN_CTANL, "ctanl", BT_FN_COMPLEX_LONGDOUBLE_COMPLE when the length parameter is zero, so don't apply attribute "nonnull". */ DEF_EXT_LIB_BUILTIN (BUILT_IN_BCMP, "bcmp", BT_FN_INT_CONST_PTR_CONST_PTR_SIZE, ATTR_PURE_NOTHROW_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_BCOPY, "bcopy", BT_FN_VOID_CONST_PTR_PTR_SIZE, ATTR_NOTHROW_LIST) -DEF_EXT_LIB_BUILTIN (BUILT_IN_BZERO, "bzero", BT_FN_VOID_PTR_SIZE, ATTR_NOTHROW_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_BZERO, "bzero", BT_FN_VOID_PTR_SIZE, ATTR_NOTHROW_BOUNDED_BUFFER_1_2) DEF_EXT_LIB_BUILTIN (BUILT_IN_INDEX, "index", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL) DEF_LIB_BUILTIN (BUILT_IN_MEMCMP, "memcmp", BT_FN_INT_CONST_PTR_CONST_PTR_SIZE, ATTR_PURE_NOTHROW_NONNULL) -DEF_LIB_BUILTIN (BUILT_IN_MEMCPY, "memcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL) +DEF_LIB_BUILTIN (BUILT_IN_MEMCPY, "memcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL_BOUNDED_BUFFER_2_3_1_3) DEF_LIB_BUILTIN (BUILT_IN_MEMMOVE, "memmove", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL) DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMPCPY, "mempcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL) -DEF_LIB_BUILTIN (BUILT_IN_MEMSET, "memset", BT_FN_PTR_PTR_INT_SIZE, ATTR_NOTHROW_NONNULL) +DEF_LIB_BUILTIN (BUILT_IN_MEMSET, "memset", BT_FN_PTR_PTR_INT_SIZE, ATTR_NOTHROW_NONNULL_BOUNDED_BUFFER_1_3) DEF_EXT_LIB_BUILTIN (BUILT_IN_RINDEX, "rindex", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL) DEF_EXT_LIB_BUILTIN (BUILT_IN_STPCPY, "stpcpy", BT_FN_STRING_STRING_CONST_STRING, ATTR_NOTHROW_NONNULL) DEF_EXT_LIB_BUILTIN (BUILT_IN_STPNCPY, "stpncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL) @@ -514,9 +514,9 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_STRDUP, "strdup", BT_FN_STRING_CONST_STRING, AT DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNDUP, "strndup", BT_FN_STRING_CONST_STRING_SIZE, ATTR_MALLOC_NOTHROW_NONNULL) DEF_LIB_BUILTIN (BUILT_IN_STRLEN, "strlen", BT_FN_SIZE_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL) DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCASECMP, "strncasecmp", BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL) -DEF_LIB_BUILTIN (BUILT_IN_STRNCAT, "strncat", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL) +DEF_LIB_BUILTIN (BUILT_IN_STRNCAT, "strncat", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_BOUNDED_BUFFER_1_3) DEF_LIB_BUILTIN (BUILT_IN_STRNCMP, "strncmp", BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL) -DEF_LIB_BUILTIN (BUILT_IN_STRNCPY, "strncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL) +DEF_LIB_BUILTIN (BUILT_IN_STRNCPY, "strncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_BOUNDED_BUFFER_1_3) DEF_LIB_BUILTIN (BUILT_IN_STRPBRK, "strpbrk", BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL) DEF_LIB_BUILTIN (BUILT_IN_STRRCHR, "strrchr", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL) DEF_LIB_BUILTIN (BUILT_IN_STRSPN, "strspn", BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL) @@ -532,7 +532,7 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_FPUTC_UNLOCKED, "fputc_unlocked", BT_FN_INT_INT DEF_LIB_BUILTIN (BUILT_IN_FPUTS, "fputs", BT_FN_INT_CONST_STRING_FILEPTR, ATTR_NONNULL_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_FPUTS_UNLOCKED, "fputs_unlocked", BT_FN_INT_CONST_STRING_FILEPTR, ATTR_NONNULL_LIST) DEF_LIB_BUILTIN (BUILT_IN_FSCANF, "fscanf", BT_FN_INT_FILEPTR_CONST_STRING_VAR, ATTR_FORMAT_SCANF_2_3) -DEF_LIB_BUILTIN (BUILT_IN_FWRITE, "fwrite", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_LIST) +DEF_LIB_BUILTIN (BUILT_IN_FWRITE, "fwrite", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NOTHROW_NONNULL_BOUNDED_SIZE_1_3_2) DEF_EXT_LIB_BUILTIN (BUILT_IN_FWRITE_UNLOCKED, "fwrite_unlocked", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_FILEPTR, ATTR_NONNULL_LIST) DEF_LIB_BUILTIN (BUILT_IN_PRINTF, "printf", BT_FN_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_1_2) DEF_EXT_LIB_BUILTIN (BUILT_IN_PRINTF_UNLOCKED, "printf_unlocked", BT_FN_INT_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_1_2) @@ -541,14 +541,14 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_PUTCHAR_UNLOCKED, "putchar_unlocked", BT_FN_INT DEF_LIB_BUILTIN (BUILT_IN_PUTS, "puts", BT_FN_INT_CONST_STRING, ATTR_NONNULL_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_PUTS_UNLOCKED, "puts_unlocked", BT_FN_INT_CONST_STRING, ATTR_NONNULL_LIST) DEF_LIB_BUILTIN (BUILT_IN_SCANF, "scanf", BT_FN_INT_CONST_STRING_VAR, ATTR_FORMAT_SCANF_1_2) -DEF_C99_BUILTIN (BUILT_IN_SNPRINTF, "snprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_3_4) +DEF_C99_BUILTIN (BUILT_IN_SNPRINTF, "snprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VAR, ATTR_PRINTF_3_4_BOUNDED_STRING_1_2) DEF_LIB_BUILTIN (BUILT_IN_SPRINTF, "sprintf", BT_FN_INT_STRING_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_2_3) DEF_LIB_BUILTIN (BUILT_IN_SSCANF, "sscanf", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_FORMAT_SCANF_2_3) DEF_LIB_BUILTIN (BUILT_IN_VFPRINTF, "vfprintf", BT_FN_INT_FILEPTR_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_2_0) DEF_C99_BUILTIN (BUILT_IN_VFSCANF, "vfscanf", BT_FN_INT_FILEPTR_CONST_STRING_VALIST_ARG, ATTR_FORMAT_SCANF_2_0) DEF_LIB_BUILTIN (BUILT_IN_VPRINTF, "vprintf", BT_FN_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_1_0) DEF_C99_BUILTIN (BUILT_IN_VSCANF, "vscanf", BT_FN_INT_CONST_STRING_VALIST_ARG, ATTR_FORMAT_SCANF_1_0) -DEF_C99_BUILTIN (BUILT_IN_VSNPRINTF, "vsnprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_3_0) +DEF_C99_BUILTIN (BUILT_IN_VSNPRINTF, "vsnprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG, ATTR_PRINTF_3_0_BOUNDED_STRING_1_2) DEF_LIB_BUILTIN (BUILT_IN_VSPRINTF, "vsprintf", BT_FN_INT_STRING_CONST_STRING_VALIST_ARG, ATTR_FORMAT_PRINTF_2_0) DEF_C99_BUILTIN (BUILT_IN_VSSCANF, "vsscanf", BT_FN_INT_CONST_STRING_CONST_STRING_VALIST_ARG, ATTR_FORMAT_SCANF_2_0) diff --git a/gnu/gcc/gcc/c-bounded.c b/gnu/gcc/gcc/c-bounded.c new file mode 100644 index 00000000000..eab953fd906 --- /dev/null +++ b/gnu/gcc/gcc/c-bounded.c @@ -0,0 +1,490 @@ +/* Bounds checker for library functions with buffers and sizes. + * + * Copyright (c) 2004 Anil Madhavapeddy <anil@recoil.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "flags.h" +#include "tree.h" +#include "c-tree.h" +#include "flags.h" +#include "toplev.h" +#include "c-common.h" +#include "intl.h" +#include "diagnostic.h" +#include "langhooks.h" + +/* Handle attributes associated with bounds checking. */ + +/* Bounded attribute types */ +enum bounded_type { buffer_bound_type, string_bound_type, + minbytes_bound_type, size_bound_type, + bounded_type_error }; + +typedef struct bound_check_info +{ + enum bounded_type bounded_type; /* type of bound (string, minsize, etc) */ + unsigned HOST_WIDE_INT bounded_buf; /* number of buffer pointer arg */ + unsigned HOST_WIDE_INT bounded_num; /* number of buffer length arg || min size */ + unsigned HOST_WIDE_INT bounded_size; /* number of buffer element size arg */ +} function_bounded_info; + +tree handle_bounded_attribute(tree *, tree, tree, int, bool *); +void check_function_bounded (int *, tree, tree); + +static bool decode_bounded_attr PARAMS ((tree, function_bounded_info *, int)); +static enum bounded_type decode_bounded_type PARAMS ((const char *)); + +/* Handle a "bounded" attribute; arguments as in + struct attribute_spec.handler. */ +tree +handle_bounded_attribute (node, name, args, flags, no_add_attrs) + tree *node; + tree name ATTRIBUTE_UNUSED; + tree args; + int flags ATTRIBUTE_UNUSED; + bool *no_add_attrs; +{ + tree type = *node; + function_bounded_info info; + tree argument, arg_iterate; + unsigned HOST_WIDE_INT arg_num; + + if (!decode_bounded_attr (args, &info, 0)) + { + *no_add_attrs = true; + return NULL_TREE; + } + + /* `min_size' directly specifies the minimum buffer length */ + if (info.bounded_type == minbytes_bound_type + && info.bounded_num <= 0) + { + error ("`minbytes' bound size must be a positive integer value"); + *no_add_attrs = true; + return NULL_TREE; + } + + argument = TYPE_ARG_TYPES (type); + if (argument) + { + arg_iterate = argument; + for (arg_num = 1; ; ++arg_num) + { + if (arg_iterate == 0 || arg_num == info.bounded_buf) + break; + arg_iterate = TREE_CHAIN (arg_iterate); + } + if (! arg_iterate + || (TREE_CODE (TREE_VALUE (arg_iterate)) != POINTER_TYPE + && TREE_CODE (TREE_VALUE (arg_iterate)) != ARRAY_TYPE)) + { + error ("bound buffer argument not an array or pointer type"); + *no_add_attrs = true; + return NULL_TREE; + } + + if (info.bounded_type == size_bound_type + || info.bounded_type == string_bound_type + || info.bounded_type == buffer_bound_type) + { + arg_iterate = argument; + for (arg_num = 1; ; ++arg_num) + { + if (arg_iterate == 0 || arg_num == info.bounded_num) + break; + arg_iterate = TREE_CHAIN (arg_iterate); + } + if (! arg_iterate + || TREE_CODE (TREE_VALUE (arg_iterate)) != INTEGER_TYPE) + { + error ("bound length argument not an integer type"); + *no_add_attrs = true; + return NULL_TREE; + } + } + if (info.bounded_type == size_bound_type) + { + arg_iterate = argument; + for (arg_num = 1; ; ++arg_num) + { + if (arg_iterate == 0 || arg_num == info.bounded_size) + break; + arg_iterate = TREE_CHAIN (arg_iterate); + } + if (! arg_iterate + || TREE_CODE (TREE_VALUE (arg_iterate)) != INTEGER_TYPE) + { + error ("bound element size argument not an integer type"); + *no_add_attrs = true; + return NULL_TREE; + } + } + } + + return NULL_TREE; +} + +/* Decode the arguments to a "bounded" attribute into a function_bounded_info + structure. It is already known that the list is of the right length. + If VALIDATED_P is true, then these attributes have already been validated + and this function will abort if they are erroneous; if false, it + will give an error message. Returns true if the attributes are + successfully decoded, false otherwise. */ + +static bool +decode_bounded_attr (args, info, validated_p) + tree args; + function_bounded_info *info; + int validated_p; +{ + int bounded_num; + tree bounded_type_id = TREE_VALUE (args); + tree bounded_buf_expr = TREE_VALUE (TREE_CHAIN (args)); + tree bounded_num_expr = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); + tree bounded_size_expr = TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (args))); + + if (TREE_CODE (bounded_type_id) != IDENTIFIER_NODE) + { + if (validated_p) + abort (); + error ("unrecognized bounded type specifier"); + return false; + } + else + { + const char *p = IDENTIFIER_POINTER (bounded_type_id); + + info->bounded_type = decode_bounded_type (p); + + if (info->bounded_type == bounded_type_error) + { + if (validated_p) + abort (); + warning (OPT_Wbounded, + "`%s' is an unrecognized bounded function type", p); + return false; + } + } + + /* Extract the third argument if its appropriate */ + switch (info->bounded_type) + { + case bounded_type_error: + /* should never happen */ + internal_error ("unexpected bounded_type_error in decode_bounded_attr"); + break; + case string_bound_type: + if (bounded_size_expr) + warning (OPT_Wbounded, "`string' bound type only takes 2 parameters"); + bounded_size_expr = size_int (0); + break; + case buffer_bound_type: + if (bounded_size_expr) + warning (OPT_Wbounded, "`buffer' bound type only takes 2 parameters"); + bounded_size_expr = size_int (0); + break; + case minbytes_bound_type: + if (bounded_size_expr) + warning (OPT_Wbounded, "`minbytes' bound type only takes 2 parameters"); + bounded_size_expr = size_int (0); + break; + case size_bound_type: + if (bounded_size_expr) + bounded_size_expr = TREE_VALUE (bounded_size_expr); + else + { + error ("parameter 3 not specified for `size' bounded function"); + return false; + } + break; + } + + /* Strip any conversions from the buffer parameters and verify they + are constants */ + while (TREE_CODE (bounded_num_expr) == NOP_EXPR + || TREE_CODE (bounded_num_expr) == CONVERT_EXPR + || TREE_CODE (bounded_num_expr) == NON_LVALUE_EXPR) + bounded_num_expr = TREE_OPERAND (bounded_num_expr, 0); + + while (TREE_CODE (bounded_buf_expr) == NOP_EXPR + || TREE_CODE (bounded_buf_expr) == CONVERT_EXPR + || TREE_CODE (bounded_buf_expr) == NON_LVALUE_EXPR) + bounded_buf_expr = TREE_OPERAND (bounded_buf_expr, 0); + + while (TREE_CODE (bounded_size_expr) == NOP_EXPR + || TREE_CODE (bounded_size_expr) == CONVERT_EXPR + || TREE_CODE (bounded_size_expr) == NON_LVALUE_EXPR) + bounded_size_expr = TREE_OPERAND (bounded_size_expr, 0); + + if (TREE_CODE (bounded_num_expr) != INTEGER_CST) + { + if (validated_p) + abort (); + error ("bound length operand number is not an integer constant"); + return false; + } + + if (TREE_CODE (bounded_buf_expr) != INTEGER_CST) + { + if (validated_p) + abort (); + error ("bound buffer operand number is not an integer constant"); + return false; + } + + if (TREE_CODE (bounded_size_expr) != INTEGER_CST) + { + if (validated_p) + abort (); + error ("bound element size operand number is not an integer constant"); + return false; + } + + info->bounded_buf = TREE_INT_CST_LOW (bounded_buf_expr); + info->bounded_size = TREE_INT_CST_LOW (bounded_size_expr); + bounded_num = TREE_INT_CST_LOW (bounded_num_expr); + + /* `minbytes' directly specifies the minimum buffer length */ + if (info->bounded_type == minbytes_bound_type + && bounded_num <= 0) + { + if (validated_p) + abort (); + error ("`minbytes' bound size must be a positive integer value"); + return false; + } + + info->bounded_num = (unsigned HOST_WIDE_INT) bounded_num; + return true; +} + +static void check_bounded_info PARAMS ((int *, function_bounded_info *, tree)); + +/* Decode a bounded type from a string, returning the type, or + bounded_type_error if not valid, in which case the caller should print an + error message. */ +static enum bounded_type +decode_bounded_type (s) + const char *s; +{ + if (!strcmp (s, "string") || !strcmp (s, "__string__")) + return string_bound_type; + else if (!strcmp (s, "buffer") || !strcmp (s, "__buffer__")) + return buffer_bound_type; + else if (!strcmp (s, "minbytes") || !strcmp (s, "__minbytes__")) + return minbytes_bound_type; + else if (!strcmp (s, "size") || !strcmp (s, "__size__")) + return size_bound_type; + else + return bounded_type_error; +} + +/* Check the argument list of a call to memcpy, bzero, etc. + ATTRS are the attributes on the function type. + PARAMS is the list of argument values. */ + +void +check_function_bounded (status, attrs, params) + int *status; + tree attrs; + tree params; +{ + tree a; + /* See if this function has any bounded attributes. */ + for (a = attrs; a; a = TREE_CHAIN (a)) + { + if (is_attribute_p ("bounded", TREE_PURPOSE (a))) + { + /* Yup; check it. */ + function_bounded_info info; + decode_bounded_attr (TREE_VALUE (a), &info, 1); + check_bounded_info (status, &info, params); + } + } +} + +/* This function replaces `warning' inside the bounds checking + functions. If the `status' parameter is non-NULL, then it is + dereferenced and set to 1 whenever a warning is caught. Otherwise + it warns as usual by replicating the innards of the warning + function from diagnostic.c. */ +static void +status_warning (int *status, const char *msgid, ...) +{ + diagnostic_info diagnostic; + va_list ap; + + va_start (ap, msgid); + + if (status) + *status = 1; + else + { + /* This duplicates the warning function behavior. */ + diagnostic_set_info (&diagnostic, _(msgid), &ap, input_location, + DK_WARNING); + report_diagnostic (&diagnostic); + } + + va_end (ap); +} + +/* Check the argument list of a call to memcpy, bzero, etc. + INFO points to the function_bounded_info structure. + PARAMS is the list of argument values. */ + +static void +check_bounded_info (status, info, params) + int *status; + function_bounded_info *info; + tree params; +{ + tree buf_expr, length_expr, size_expr; + unsigned HOST_WIDE_INT arg_num; + + /* Extract the buffer expression from the arguments */ + buf_expr = params; + for (arg_num = 1; ; ++arg_num) + { + if (buf_expr == 0) + return; + if (arg_num == info->bounded_buf) + break; + buf_expr = TREE_CHAIN (buf_expr); + } + buf_expr = TREE_VALUE (buf_expr); + + /* Get the buffer length, either directly from the function attribute + info, or from the parameter pointed to */ + if (info->bounded_type == minbytes_bound_type) + length_expr = size_int (info->bounded_num); + else + { + /* Extract the buffer length expression from the arguments */ + length_expr = params; + for (arg_num = 1; ; ++arg_num) + { + if (length_expr == 0) + return; + if (arg_num == info->bounded_num) + break; + length_expr = TREE_CHAIN (length_expr); + } + length_expr = TREE_VALUE (length_expr); + } + + /* If the bound type is `size', resolve the third parameter */ + if (info->bounded_type == size_bound_type) + { + size_expr = params; + for (arg_num = 1; ; ++arg_num) + { + if (size_expr == 0) + return; + if (arg_num == info->bounded_size) + break; + size_expr = TREE_CHAIN (size_expr); + } + size_expr = TREE_VALUE (size_expr); + } + else + size_expr = size_int (0); + + STRIP_NOPS (buf_expr); + + /* We only need to check if the buffer expression is a static + * array (which is inside an ADDR_EXPR) */ + if (TREE_CODE (buf_expr) != ADDR_EXPR) + return; + buf_expr = TREE_OPERAND (buf_expr, 0); + + if (TREE_CODE (TREE_TYPE (buf_expr)) == ARRAY_TYPE + && TYPE_DOMAIN (TREE_TYPE (buf_expr))) + { + int array_size, length, elem_size, type_size; + tree array_size_expr = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (buf_expr))); + tree array_type = TREE_TYPE (TREE_TYPE (buf_expr)); + tree array_type_size_expr = TYPE_SIZE (array_type); + + /* Can't deal with variable-sized arrays yet */ + if (TREE_CODE (array_type_size_expr) != INTEGER_CST) + return; + + /* Get the size of the type of the array and sanity check it */ + type_size = TREE_INT_CST_LOW (array_type_size_expr); + if ((type_size % 8) != 0) + { + error ("found non-byte aligned type while checking bounds"); + return; + } + type_size /= 8; + + /* Both the size of the static buffer and the length should be + * integer constants by now */ + if ((array_size_expr && TREE_CODE (array_size_expr) != INTEGER_CST) + || TREE_CODE (length_expr) != INTEGER_CST + || TREE_CODE (size_expr) != INTEGER_CST) + return; + + /* array_size_expr contains maximum array index, so add one for size */ + if (array_size_expr) + array_size = (TREE_INT_CST_LOW (array_size_expr) + 1) * type_size; + else + array_size_expr = 0; + length = TREE_INT_CST_LOW (length_expr); + + /* XXX - warn about a too-small buffer? */ + if (array_size < 1) + return; + + switch (info->bounded_type) + { + case bounded_type_error: + /* should never happen */ + internal_error ("unexpected bounded_type_error"); + break; + case string_bound_type: + case buffer_bound_type: + /* warn about illegal bounds value */ + if (length < 0) + status_warning (status, "non-positive bounds length (%d) detected", length); + /* check if the static buffer is smaller than bound length */ + if (array_size < length) + status_warning(status, "array size (%d) smaller than bound length (%d)", + array_size, length); + break; + case minbytes_bound_type: + /* check if array is smaller than the minimum allowed */ + if (array_size < length) + status_warning (status, "array size (%d) is smaller than minimum required (%d)", + array_size, length); + break; + case size_bound_type: + elem_size = TREE_INT_CST_LOW (size_expr); + /* warn about illegal bounds value */ + if (length < 1) + status_warning (status, "non-positive bounds length (%d) detected", length); + /* check if the static buffer is smaller than bound length */ + if (array_size < (length * elem_size)) + status_warning(status, "array size (%d) smaller than required length (%d * %d)", + array_size, length, elem_size); + break; + } + } +} diff --git a/gnu/gcc/gcc/c-common.c b/gnu/gcc/gcc/c-common.c index 5a79fa632b3..68db97e8c05 100644 --- a/gnu/gcc/gcc/c-common.c +++ b/gnu/gcc/gcc/c-common.c @@ -545,7 +545,6 @@ static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *); 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_bounded_attribute (tree *, tree, tree, int, bool *); static void check_function_nonnull (tree, tree); static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT); @@ -4648,15 +4647,6 @@ handle_mode_attribute (tree *node, tree name, tree args, return NULL_TREE; } -static tree -handle_bounded_attribute (tree *ARG_UNUSED (node), tree ARG_UNUSED (name), - tree ARG_UNUSED (args), - int ARG_UNUSED (flags), bool *no_add_attrs) -{ - *no_add_attrs = true; - return NULL_TREE; -} - /* Handle a "section" attribute; arguments as in struct attribute_spec.handler. */ @@ -5715,6 +5705,9 @@ check_function_arguments (tree attrs, tree params, tree typelist) if (warn_format) check_function_sentinel (attrs, params, typelist); + + if (warn_bounded) + check_function_bounded (NULL, attrs, params); } /* Generic argument checking recursion routine. PARAM is the argument to diff --git a/gnu/gcc/gcc/c-common.h b/gnu/gcc/gcc/c-common.h index dd3b240a6c0..60b8a82f339 100644 --- a/gnu/gcc/gcc/c-common.h +++ b/gnu/gcc/gcc/c-common.h @@ -632,7 +632,9 @@ extern void check_function_arguments_recurse (void (*) void *, tree, unsigned HOST_WIDE_INT); extern void check_function_format (tree, tree); +extern void check_function_bounded (int *, tree, tree); extern void set_Wformat (int); +extern tree handle_bounded_attribute (tree *, tree, tree, int, bool *); extern tree handle_format_attribute (tree *, tree, tree, int, bool *); extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *); extern int c_common_handle_option (size_t code, const char *arg, int value); diff --git a/gnu/gcc/gcc/c-format.c b/gnu/gcc/gcc/c-format.c index a81559ff0e7..2f3449541ca 100644 --- a/gnu/gcc/gcc/c-format.c +++ b/gnu/gcc/gcc/c-format.c @@ -265,6 +265,10 @@ typedef struct format_wanted_type /* Whether the argument, dereferenced once, is written into and so the argument must not be a pointer to a const-qualified type. */ int writing_in_flag; + /* If the argument is to be written into and is an array, should the + width specifier be equal to the size of the array, or one less + (to accommodate a NULL being placed at the end) */ + int size_equals_width; /* Whether the argument, dereferenced once, is read from and so must not be a NULL pointer. */ int reading_from_flag; @@ -277,6 +281,8 @@ typedef struct format_wanted_type tree param; /* The argument number of that parameter. */ int arg_num; + /* Field width of type */ + int field_width; /* The next type to check for this format conversion, or NULL if none. */ struct format_wanted_type *next; } format_wanted_type; @@ -884,7 +890,7 @@ static void finish_dollar_format_checking (format_check_results *, int); static const format_flag_spec *get_flag_spec (const format_flag_spec *, int, const char *); -static void check_format_types (format_wanted_type *, const char *, int); +static void check_format_types (format_wanted_type *, const char *, int, bool); static void format_type_warning (const char *, const char *, int, tree, int, const char *, tree, int); @@ -1529,6 +1535,7 @@ check_format_info_main (format_check_results *res, const format_length_info *fli = NULL; const format_char_info *fci = NULL; char flag_chars[256]; + int field_width = 0; int aflag = 0; const char *format_start = format_chars; if (*format_chars == 0) @@ -1681,19 +1688,27 @@ check_format_info_main (format_check_results *res, /* Possibly read a numeric width. If the width is zero, we complain if appropriate. */ int non_zero_width_char = FALSE; - int found_width = FALSE; + unsigned int found_width = 0; + char format_num_str[32]; + + format_num_str[0] = '\0'; while (ISDIGIT (*format_chars)) { - found_width = TRUE; + if (found_width < (sizeof(format_num_str)-2)) + { + format_num_str[found_width++] = *format_chars; + format_num_str[found_width] = '\0'; + } if (*format_chars != '0') non_zero_width_char = TRUE; ++format_chars; } - if (found_width && !non_zero_width_char && + if (found_width > 0 && !non_zero_width_char && (fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD)) warning (OPT_Wformat, "zero width in %s format", fki->name); - if (found_width) + if (found_width > 0) { + field_width = atoi(format_num_str); i = strlen (flag_chars); flag_chars[i++] = fki->width_char; flag_chars[i] = 0; @@ -2113,10 +2128,15 @@ check_format_info_main (format_check_results *res, wanted_type_ptr->wanted_type_name = wanted_type_name; wanted_type_ptr->pointer_count = fci->pointer_count + aflag; wanted_type_ptr->char_lenient_flag = 0; + wanted_type_ptr->field_width = field_width; if (strchr (fci->flags2, 'c') != 0) wanted_type_ptr->char_lenient_flag = 1; wanted_type_ptr->writing_in_flag = 0; wanted_type_ptr->reading_from_flag = 0; + if (strchr (fci->format_chars, 'c') != 0) + wanted_type_ptr->size_equals_width = 1; + else + wanted_type_ptr->size_equals_width = 0; if (aflag) wanted_type_ptr->writing_in_flag = 1; else @@ -2148,8 +2168,13 @@ check_format_info_main (format_check_results *res, } if (first_wanted_type != 0) - check_format_types (first_wanted_type, format_start, - format_chars - format_start); + { + bool apply_bounded = format_chars > format_start + && (format_chars[-1] == 'c' || format_chars[-1] == 's'); + + check_format_types (first_wanted_type, format_start, + format_chars - format_start, apply_bounded); + } if (main_wanted_type.next != NULL) { @@ -2169,7 +2194,7 @@ check_format_info_main (format_check_results *res, including width and precision arguments). */ static void check_format_types (format_wanted_type *types, const char *format_start, - int format_length) + int format_length, bool apply_bounded) { for (; types != 0; types = types->next) { @@ -2231,6 +2256,28 @@ check_format_types (format_wanted_type *types, const char *format_start, else cur_param = 0; + /* Test static string bounds for sscan if -Wbounded is on as well */ + if (warn_bounded && apply_bounded + && types->writing_in_flag + && i == 0 + && cur_param != 0 + && COMPLETE_TYPE_P (TREE_TYPE (cur_param)) + && TREE_CODE (TREE_TYPE (cur_param)) == ARRAY_TYPE + && TREE_CODE (TREE_TYPE (TREE_TYPE (cur_param))) == INTEGER_TYPE) + { + tree array_size_expr = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (cur_param))); + if (array_size_expr != 0 && types->field_width > 0) + { + int f = types->size_equals_width ? 0 : 1; + int array_size = TREE_INT_CST_LOW (array_size_expr) + 1; + if (array_size < (types->field_width + f)) + warning (OPT_Wbounded, + "Array size (%d) smaller than format " + "string size (%d) (arg %d)", array_size, + types->field_width + f, arg_num); + } + } + /* See if this is an attempt to write into a const type with scanf or with printf "%n". Note: the writing in happens at the first indirection only, if for example diff --git a/gnu/gcc/gcc/c-opts.c b/gnu/gcc/gcc/c-opts.c index 25a9b38b3b8..0494b9c2c37 100644 --- a/gnu/gcc/gcc/c-opts.c +++ b/gnu/gcc/gcc/c-opts.c @@ -385,6 +385,7 @@ c_common_handle_option (size_t scode, const char *arg, int value) set_Wunused (value); set_Wformat (value); set_Wimplicit (value); + warn_bounded = value; warn_char_subscripts = value; warn_missing_braces = value; warn_parentheses = value; diff --git a/gnu/gcc/gcc/c.opt b/gnu/gcc/gcc/c.opt index d76cefb280d..7d1bd41b10e 100644 --- a/gnu/gcc/gcc/c.opt +++ b/gnu/gcc/gcc/c.opt @@ -133,7 +133,7 @@ C ObjC Var(warn_bad_function_cast) Warn about casting functions to incompatible types Wbounded -C ObjC Var(warn_bounded) +C ObjC C++ Var(warn_bounded) Warn about basic buffer size problems Wc++-compat diff --git a/gnu/usr.bin/cc/cc_int/Makefile b/gnu/usr.bin/cc/cc_int/Makefile index af57bb55d7b..34d73d86788 100644 --- a/gnu/usr.bin/cc/cc_int/Makefile +++ b/gnu/usr.bin/cc/cc_int/Makefile @@ -1,5 +1,5 @@ # $FreeBSD: src/gnu/usr.bin/cc/cc_int/Makefile,v 1.52.8.1 2009/04/15 03:14:26 kensmith Exp $ -# $OpenBSD: Makefile,v 1.2 2009/10/18 02:47:27 kurt Exp $ +# $OpenBSD: Makefile,v 1.3 2011/09/15 12:19:12 nicm Exp $ .include <bsd.own.mk> @@ -88,7 +88,7 @@ C_AND_OBJC_OBJS = attribs.o c-errors.o c-lex.o c-pragma.o c-decl.o c-typeck.o \ c-convert.o c-aux-info.o c-common.o c-opts.o c-format.o c-semantics.o \ c-incpath.o cppdefault.o c-ppoutput.o c-cppbuiltin.o prefix.o \ c-objc-common.o c-dump.o c-pch.o c-parser.o $(C_TARGET_OBJS) \ - c-gimplify.o tree-mudflap.o c-pretty-print.o c-omp.o + c-gimplify.o tree-mudflap.o c-pretty-print.o c-omp.o c-bounded.o # Language-specific object files for C. C_OBJS = c-lang.o stub-objc.o $(C_AND_OBJC_OBJS) |