diff options
author | 2003-06-26 18:30:05 +0000 | |
---|---|---|
committer | 2003-06-26 18:30:05 +0000 | |
commit | b27d04fb8e04e7e970b5c287ca2f510173d6ba87 (patch) | |
tree | 568b8fb902e6d4c56934d983dbc97e5d753a5133 | |
parent | correct an example, and fix BUGS; (diff) | |
download | wireguard-openbsd-b27d04fb8e04e7e970b5c287ca2f510173d6ba87.tar.xz wireguard-openbsd-b27d04fb8e04e7e970b5c287ca2f510173d6ba87.zip |
Introduce a simple static checker for making sure that the bounds
length passed to common functions such as strlcpy/strlcat match the
real length of the buffer. It also checks to make sure that the bound
length was not incorrectly derived from a sizeof(pointer) operation.
Functions must be marked with the new attribute __bounded__, and warnings
are turned on by -Wbounded. Specifying -Wformat also enables bounds
checking for scanf(3) bounds to '%s' format variables. -Wall now turns
on -Wbounded also.
The checking is pretty limited right now to constant parameters, and the
buffers must be statically declared, and not inside a record type. This
simple checking still found hundreds of bugs around the ports tree though,
and there have been no false positive warnings.
10x to niklas@, Richard Sharp and David Scott {rich,dave}@recoil.org for
compiler advice. deraadt@ ok, miod@ tested on his collection of hardware
You need to recompile gcc now if source upgrading in -current before
doing a make world.
-rw-r--r-- | gnu/egcs/gcc/c-common.c | 422 | ||||
-rw-r--r-- | gnu/egcs/gcc/c-decl.c | 9 | ||||
-rw-r--r-- | gnu/egcs/gcc/c-tree.h | 5 | ||||
-rw-r--r-- | gnu/egcs/gcc/c-typeck.c | 7 | ||||
-rw-r--r-- | gnu/egcs/gcc/cp/call.c | 4 | ||||
-rw-r--r-- | gnu/egcs/gcc/cp/cp-tree.h | 10 | ||||
-rw-r--r-- | gnu/egcs/gcc/cp/decl2.c | 7 | ||||
-rw-r--r-- | gnu/egcs/gcc/cp/typeck.c | 13 | ||||
-rw-r--r-- | gnu/egcs/gcc/fold-const.c | 14 | ||||
-rw-r--r-- | gnu/egcs/gcc/toplev.c | 2 | ||||
-rw-r--r-- | gnu/egcs/gcc/tree.h | 14 |
11 files changed, 504 insertions, 3 deletions
diff --git a/gnu/egcs/gcc/c-common.c b/gnu/egcs/gcc/c-common.c index 38bf4927916..167781113bc 100644 --- a/gnu/egcs/gcc/c-common.c +++ b/gnu/egcs/gcc/c-common.c @@ -57,11 +57,14 @@ enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION, A_NO_CHECK_MEMORY_USAGE, A_NO_INSTRUMENT_FUNCTION, A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED, A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS, A_NONNULL, - A_SENTINEL }; + A_SENTINEL, A_BOUNDED}; enum format_type { printf_format_type, scanf_format_type, strftime_format_type, syslog_format_type }; +enum bounded_type { buffer_bound_type, string_bound_type, + minbytes_bound_type, size_bound_type }; + static void declare_hidden_char_array PROTO((const char *, const char *)); static void add_attribute PROTO((enum attrs, const char *, int, int, int)); @@ -393,6 +396,7 @@ init_attributes () add_attribute (A_FORMAT_ARG, "format_arg", 1, 1, 1); add_attribute (A_NONNULL, "nonnull", 0, -1, 1); add_attribute (A_SENTINEL, "sentinel", 0, 0, 1); + add_attribute (A_BOUNDED, "bounded", 3, 4, 1); add_attribute (A_WEAK, "weak", 0, 0, 1); add_attribute (A_ALIAS, "alias", 1, 1, 1); add_attribute (A_NO_INSTRUMENT_FUNCTION, "no_instrument_function", 0, 0, 1); @@ -438,6 +442,8 @@ static function_attribute_info *new_function_format static function_attribute_info *new_international_format PROTO((int)); static function_attribute_info *new_nonnull_info PROTO((int)); static function_attribute_info *new_sentinel_info PROTO((int)); +static function_attribute_info *new_bound_check_info + PROTO((enum bounded_type, int, int, int)); static function_attributes_info *insert_function_attribute PROTO((function_attributes_info *, tree, tree, function_attribute_info *)); @@ -849,6 +855,183 @@ decl_attributes (node, attributes, prefix_attributes) break; } + case A_BOUNDED: + { + 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))); + int bounded_num, bounded_buf, bounded_size, arg_num; + enum bounded_type bounded_type = string_bound_type; + tree argument, arg_iterate; + + if (TREE_CODE (decl) != FUNCTION_DECL) + { + error_with_decl (decl, + "attribute bounded specified for non-function `%s'"); + continue; + } + + if (TREE_CODE (bounded_type_id) != IDENTIFIER_NODE) + { + error ("unrecognized bounded type specifier"); + continue; + } + else + { + const char *p = IDENTIFIER_POINTER (bounded_type_id); + + if (!strcmp (p, "string") || !strcmp (p, "__string__")) + bounded_type = string_bound_type; + else if (!strcmp (p, "buffer") || !strcmp (p, "__buffer__")) + bounded_type = buffer_bound_type; + else if (!strcmp (p, "minbytes") || !strcmp (p, "__minbytes__")) + bounded_type = minbytes_bound_type; + else if (!strcmp (p, "size") || !strcmp (p, "__size__")) + bounded_type = size_bound_type; + else + { + warning ("`%s' is an unrecognized bounded function type", p); + continue; + } + } + + /* Extract the third argument if its appropriate */ + switch (bounded_type) + { + case string_bound_type: + if (bounded_size_expr) + warning ("`string' bound type only takes 2 parameters"); + bounded_size_expr = size_int (0); + break; + case buffer_bound_type: + if (bounded_size_expr) + warning ("`buffer' bound type only takes 2 parameters"); + bounded_size_expr = size_int (0); + break; + case minbytes_bound_type: + if (bounded_size_expr) + warning("`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"); + continue; + } + 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) + { + error ("bound length operand number is not an integer constant"); + continue; + } + + if (TREE_CODE (bounded_buf_expr) != INTEGER_CST) + { + error ("bound buffer operand number is not an integer constant"); + continue; + } + + if (TREE_CODE (bounded_size_expr) != INTEGER_CST) + { + error ("bound element size operand number is not an integer constant"); + continue; + } + + bounded_num = TREE_INT_CST_LOW (bounded_num_expr); + bounded_buf = TREE_INT_CST_LOW (bounded_buf_expr); + bounded_size = TREE_INT_CST_LOW (bounded_size_expr); + argument = TYPE_ARG_TYPES (type); + + /* `min_size' directly specifies the minimum buffer length */ + if (bounded_type == minbytes_bound_type && bounded_num <= 0) + { + error ("`minbytes' bound size must be a positive integer value"); + continue; + } + + /* Check the function arguments for correct types */ + if (argument) + { + arg_iterate = argument; + for (arg_num = 1; ; ++arg_num) + { + if (arg_iterate == 0 || arg_num == 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"); + continue; + } + + if (bounded_type == size_bound_type + || bounded_type == string_bound_type + || bounded_type == buffer_bound_type) + { + arg_iterate = argument; + for (arg_num = 1; ; ++arg_num) + { + if (arg_iterate == 0 || arg_num == 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"); + continue; + } + } + + if (bounded_type == size_bound_type) + { + arg_iterate = argument; + for (arg_num = 1; ; ++arg_num) + { + if (arg_iterate == 0 || arg_num == 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"); + continue; + } + } + } + + list = insert_function_attribute (list, DECL_NAME (decl), + DECL_ASSEMBLER_NAME (decl), + new_bound_check_info (bounded_type, bounded_buf, bounded_num, bounded_size)); + break; + } case A_FORMAT_ARG: { tree format_num_expr = TREE_VALUE (args); @@ -1331,12 +1514,23 @@ typedef struct sentinel_info int argument_num; /* number of non-null argument */ } sentinel_info; +typedef struct bound_check_info +{ + struct function_attribute_info *next; + enum attrs type; + enum bounded_type bounded_type; /* type of bound (string, minsize, etc) */ + int argument_buf; /* number of buffer pointer arg */ + int argument_num; /* number of buffer length arg || min size */ + int argument_size; /* number of buffer element size arg */ +} bound_check_info; + static function_attribute_info *find_function_attribute PROTO((tree, tree, enum attrs)); static void check_format_info PROTO((function_format_info *, tree)); static void check_nonnull_info PROTO((nonnull_info *, tree)); static void check_sentinel_info PROTO((sentinel_info *, tree)); +static void check_bound_info PROTO((bound_check_info *, tree)); /* Helper function for setting up initial attribute for printf-like functions, since the format argument is also non-null checked for @@ -1467,6 +1661,27 @@ new_sentinel_info (argument_num) return (function_attribute_info *) (info); } +/* Create information record for functions with bounded parameters + ARGUMENT_BUF is the number of the buffer pointer argument. + ARGUMENT_NUM is the number of the buffer length argument. */ + +static function_attribute_info * +new_bound_check_info (argument_type, argument_buf, argument_num, argument_size) + enum bounded_type argument_type; + int argument_buf, argument_num, argument_size; +{ + bound_check_info *info; + info = (bound_check_info *) + xmalloc (sizeof (bound_check_info)); + info->next = NULL; + info->type = A_BOUNDED; + info->bounded_type = argument_type; + info->argument_buf = argument_buf; + info->argument_num = argument_num; + info->argument_size = argument_size; + return (function_attribute_info *) (info); +} + /* Record attribute information for the names of function. Used as: * newlist = insert_function_attribute (old, name, asm, new_xxx (...) ); * In reality, newlist == old if old != NULL, but clients don't need to @@ -1568,6 +1783,39 @@ check_function_format (name, assembler_name, params) } } +/* Check the argument list of a call to strlcpy, strcat, etc. + NAME is the function identifier. + ASSEMBLER_NAME is the function's assembler identifier. + (Either NAME or ASSEMBLER_NAME, but not both, may be NULL_TREE.) + PARAMS is the list of argument values. */ + +void +check_function_bounds (name, assembler_name, params) + tree name; + tree assembler_name; + tree params; +{ + function_attributes_info *info; + function_attribute_info *ck; + + /* See if this function has attributes. */ + for (info = function_attributes_list; info; info = info->next) + { + if (info->assembler_name + ? (info->assembler_name == assembler_name) + : (info->name == name)) + { + /* Yup; check those attributes. */ + for (ck = info->first; ck; ck = ck->next) + { + if (ck->type == A_BOUNDED) + check_bound_info ((bound_check_info *)ck, params); + } + break; + } + } +} + /* Find a function attribute corresponding to given function names, and a given attribute TYPE. */ @@ -1677,6 +1925,144 @@ check_nonnull_info (info, params) } } +/* Given two arguments (a buffer and buffer length), check + that the buffer length was not derived from the size of + a pointer, and that the length arg is not greater than + the static buffer size. */ + +static void +check_bound_info (info, params) + bound_check_info *info; + tree params; +{ + tree buf_expr, length_expr, record_expr, size_expr; + 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->argument_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->argument_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->argument_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->argument_size) + break; + size_expr = TREE_CHAIN (size_expr); + } + size_expr = TREE_VALUE (size_expr); + } + else + size_expr = size_int (0); + + STRIP_NOPS (buf_expr); + + /* Check for a possible sizeof(pointer) error in string functions */ + if (info->bounded_type == string_bound_type + && SIZEOF_PTR_DERIVED (length_expr)) + warning("sizeof(pointer) possibly incorrect in argument %d", + info->argument_num); + + /* 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)); + + /* Get the size of the type of the array and sanity check it */ + type_size = TREE_INT_CST_LOW (TYPE_SIZE (array_type)); + 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 (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 */ + array_size = (TREE_INT_CST_LOW (array_size_expr) + 1) * type_size; + length = TREE_INT_CST_LOW (length_expr); + + /* XXX - warn about a too-small buffer? */ + if (array_size < 1) + return; + + switch (info->bounded_type) + { + case string_bound_type: + case buffer_bound_type: + /* warn about illegal bounds value */ + if (length < 1) + warning ("non-positive bounds length (%d) detected", length); + /* check if the static buffer is smaller than bound length */ + if (array_size < length) + warning("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) + warning ("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) + warning ("non-positive bounds length (%d) detected", length); + /* check if the static buffer is smaller than bound length */ + if (array_size < (length * elem_size)) + warning("array size (%d) smaller than required length (%d * %d)", + array_size, length, elem_size); + break; + } + } +} + /* Check the argument list for sentinel values. INFO points to the sentinel_info structure. PARAMS is the list of argument values. */ @@ -1793,6 +2179,7 @@ check_format_info (info, params) while (1) { int aflag; + int format_num = 0; if (*format_chars == 0) { if (format_chars - TREE_STRING_POINTER (format_tree) != format_length) @@ -1817,11 +2204,17 @@ check_format_info (info, params) suppressed = wide = precise = FALSE; if (info->format_type == scanf_format_type) { + char format_num_str[33]; suppressed = *format_chars == '*'; if (suppressed) ++format_chars; - while (ISDIGIT (*format_chars)) + while (ISDIGIT (*format_chars)) { + if (format_num < sizeof(format_num_str)-1) + format_num_str[format_num++] = *format_chars; ++format_chars; + } + format_num_str[format_num] = '\0'; + format_num = atoi(format_num_str); } else if (info->format_type == strftime_format_type) { @@ -2196,6 +2589,31 @@ check_format_info (info, params) break; } + /* Test static string bounds for scanf if -Wbounded is on as well */ + if (warn_bounded + && info->format_type == scanf_format_type + && format_char == 's' + && i == fci->pointer_count + aflag + && cur_param != 0 + && TREE_CODE (cur_type) != ERROR_MARK + && 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) { + int array_size = TREE_INT_CST_LOW (array_size_expr) + 1; + +#if 0 /* Gives false positives at the moment */ + if (format_num == 0) + warning("Unbounded string written into a static array[%d]", + array_size); +#endif + /* Need extra slot for the '\0' */ + if (array_size < (format_num + 1)) + warning("Array size (%d) smaller than format string size (%d)", + array_size, format_num + 1); + } + } + /* See if this is an attempt to write into a const type with scanf or with printf "%n". */ if ((info->format_type == scanf_format_type diff --git a/gnu/egcs/gcc/c-decl.c b/gnu/egcs/gcc/c-decl.c index 1f9aad01ae2..80dd78a6868 100644 --- a/gnu/egcs/gcc/c-decl.c +++ b/gnu/egcs/gcc/c-decl.c @@ -559,6 +559,10 @@ int warn_nested_externs = 0; int warn_format; +/* Warn about potential overruns in static buffers. */ + +int warn_bounded; + /* Warn about a subscript that has type char. */ int warn_char_subscripts = 0; @@ -828,6 +832,10 @@ c_decode_option (argc, argv) warn_format = 1; else if (!strcmp (p, "-Wno-format")) warn_format = 0; + else if (!strcmp (p, "-Wbounded")) + warn_bounded = 1; + else if (!strcmp (p, "-Wno-bounded")) + warn_bounded = 0; else if (!strcmp (p, "-Wchar-subscripts")) warn_char_subscripts = 1; else if (!strcmp (p, "-Wno-char-subscripts")) @@ -899,6 +907,7 @@ c_decode_option (argc, argv) warn_unused = 1; warn_switch = 1; warn_format = 1; + warn_bounded = 1; warn_char_subscripts = 1; warn_parentheses = 1; warn_missing_braces = 1; diff --git a/gnu/egcs/gcc/c-tree.h b/gnu/egcs/gcc/c-tree.h index bcf325b68fd..05943e5d2f0 100644 --- a/gnu/egcs/gcc/c-tree.h +++ b/gnu/egcs/gcc/c-tree.h @@ -180,6 +180,7 @@ extern void declare_function_name PROTO((void)); extern void decl_attributes PROTO((tree, tree, tree)); extern void init_function_format_info PROTO((void)); extern void check_function_format PROTO((tree, tree, tree)); +extern void check_function_bounds PROTO((tree, tree, tree)); extern int c_get_alias_set PROTO((tree)); extern void c_apply_type_quals_to_decl PROTO((int, tree)); /* Print an error message for invalid operands to arith operation CODE. @@ -445,6 +446,10 @@ extern int flag_hosted; extern int warn_implicit; +/* Nonzero means warn about static buffer abuse. */ + +extern int warn_bounded; + /* Nonzero means give string constants the type `const char *' to get extra warnings from them. These warnings will be too numerous to be useful, except in thoroughly ANSIfied programs. */ diff --git a/gnu/egcs/gcc/c-typeck.c b/gnu/egcs/gcc/c-typeck.c index a24cc38eaff..c1b16f2ca8a 100644 --- a/gnu/egcs/gcc/c-typeck.c +++ b/gnu/egcs/gcc/c-typeck.c @@ -840,6 +840,8 @@ c_sizeof (type) t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type), size_int (TYPE_PRECISION (char_type_node))); t = convert (sizetype, t); + if (code == POINTER_TYPE) + SIZEOF_PTR_DERIVED (t) = 1; /* size_binop does not put the constant in range, so do it now. */ if (TREE_CODE (t) == INTEGER_CST && force_fit_type (t, 0)) TREE_CONSTANT_OVERFLOW (t) = TREE_OVERFLOW (t) = 1; @@ -864,6 +866,8 @@ c_sizeof_nowarn (type) t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type), size_int (TYPE_PRECISION (char_type_node))); t = convert (sizetype, t); + if (code == POINTER_TYPE) + SIZEOF_PTR_DERIVED (t) = 1; force_fit_type (t, 0); return t; } @@ -1580,6 +1584,9 @@ build_function_call (function, params) if (warn_format && (name || assembler_name)) check_function_format (name, assembler_name, coerced_params); + if (warn_bounded && (name || assembler_name)) + check_function_bounds (name, assembler_name, coerced_params); + /* Recognize certain built-in functions so we can make tree-codes other than CALL_EXPR. We do this when it enables fold-const.c to do something useful. */ diff --git a/gnu/egcs/gcc/cp/call.c b/gnu/egcs/gcc/cp/call.c index a931500d2d8..b0b73ca2cbb 100644 --- a/gnu/egcs/gcc/cp/call.c +++ b/gnu/egcs/gcc/cp/call.c @@ -3391,6 +3391,10 @@ build_over_call (cand, args, flags) check_function_format (DECL_NAME (fn), DECL_ASSEMBLER_NAME (fn), converted_args); + if (warn_bounded && (DECL_NAME (fn) || DECL_ASSEMBLER_NAME (fn))) + check_function_bounds (DECL_NAME (fn), DECL_ASSEMBLER_NAME (fn), + converted_args); + /* Avoid actually calling copy constructors and copy assignment operators, if possible. */ diff --git a/gnu/egcs/gcc/cp/cp-tree.h b/gnu/egcs/gcc/cp/cp-tree.h index 8c1e7dfd7c1..c7c501cbefe 100644 --- a/gnu/egcs/gcc/cp/cp-tree.h +++ b/gnu/egcs/gcc/cp/cp-tree.h @@ -53,7 +53,7 @@ Boston, MA 02111-1307, USA. */ TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR, or FIELD_DECL). 5: TYPE_USES_PVBASES (in a class TYPE). - 6: Not used. + 6: SIZEOF_PTR_DERIVED (for the bounds checker, string types) Usage of TYPE_LANG_FLAG_?: 0: C_TYPE_FIELDS_READONLY (in RECORD_TYPE or UNION_TYPE). @@ -458,6 +458,10 @@ extern int warn_cast_qual; extern int warn_format; +/* Nonzero means warn about static buffer abuse. */ + +extern int warn_bounded; + /* Nonzero means warn about non virtual destructors in classes that have virtual functions. */ @@ -894,6 +898,9 @@ struct lang_type This flag is set only when we use vtable thunks. */ #define TYPE_USES_PVBASES(NODE) (TREE_LANG_FLAG_5(NODE)) +/* Used to track constants derived from sizeof(pointer) operations */ +#define SIZEOF_PTR_DERIVED(NODE) (TREE_LANG_FLAG_6((NODE))) + /* Vector member functions defined in this class. Each element is either a FUNCTION_DECL, a TEMPLATE_DECL, or an OVERLOAD. All functions with the same name end up in the same slot. The first @@ -2137,6 +2144,7 @@ extern void decl_attributes PROTO((tree, tree, tree)); extern void init_function_format_info PROTO((void)); extern void record_function_format PROTO((tree, tree, int, int, int)); extern void check_function_format PROTO((tree, tree, tree)); +extern void check_function_bounds PROTO((tree, tree, tree)); /* Print an error message for invalid operands to arith operation CODE. NOP_EXPR is used as a special case (see truthvalue_conversion). */ extern void binary_op_error PROTO((enum tree_code)); diff --git a/gnu/egcs/gcc/cp/decl2.c b/gnu/egcs/gcc/cp/decl2.c index d7f58bfbfd2..6d0d8ec9fe9 100644 --- a/gnu/egcs/gcc/cp/decl2.c +++ b/gnu/egcs/gcc/cp/decl2.c @@ -289,6 +289,10 @@ int warn_sign_compare; int warn_format; +/* Warn about potential overruns in static buffers. */ + +int warn_bounded; + /* Warn about a subscript that has type char. */ int warn_char_subscripts; @@ -750,6 +754,8 @@ lang_decode_option (argc, argv) warn_sign_compare = setting; else if (!strcmp (p, "format")) warn_format = setting; + else if (!strcmp (p, "bounded")) + warn_bounded = setting; else if (!strcmp (p, "conversion")) warn_conversion = setting; else if (!strcmp (p, "parentheses")) @@ -797,6 +803,7 @@ lang_decode_option (argc, argv) warn_implicit = setting; warn_switch = setting; warn_format = setting; + warn_bounded = setting; warn_parentheses = setting; warn_missing_braces = setting; warn_sign_compare = setting; diff --git a/gnu/egcs/gcc/cp/typeck.c b/gnu/egcs/gcc/cp/typeck.c index d0743e950f4..def2615ccee 100644 --- a/gnu/egcs/gcc/cp/typeck.c +++ b/gnu/egcs/gcc/cp/typeck.c @@ -1614,6 +1614,11 @@ c_sizeof (type) t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type), size_int (TYPE_PRECISION (char_type_node))); t = convert (sizetype, t); + + /* Keep track if the sizeof is of a pointer */ + if (code == POINTER_TYPE) + SIZEOF_PTR_DERIVED (t) = 1; + /* size_binop does not put the constant in range, so do it now. */ if (TREE_CODE (t) == INTEGER_CST && force_fit_type (t, 0)) TREE_CONSTANT_OVERFLOW (t) = TREE_OVERFLOW (t) = 1; @@ -1674,6 +1679,11 @@ c_sizeof_nowarn (type) t = size_binop (CEIL_DIV_EXPR, TYPE_SIZE (type), size_int (TYPE_PRECISION (char_type_node))); t = convert (sizetype, t); + + /* Keep track if the sizeof is of a pointer */ + if (code == POINTER_TYPE) + SIZEOF_PTR_DERIVED (t) = 1; + force_fit_type (t, 0); return t; } @@ -3026,6 +3036,9 @@ build_function_call_real (function, params, require_complete, flags) if (warn_format && (name || assembler_name)) check_function_format (name, assembler_name, coerced_params); + if (warn_bounded && (name || assembler_name)) + check_function_bounds (name, assembler_name, coerced_params); + /* Recognize certain built-in functions so we can make tree-codes other than CALL_EXPR. We do this when it enables fold-const.c to do something useful. */ diff --git a/gnu/egcs/gcc/fold-const.c b/gnu/egcs/gcc/fold-const.c index a1901a86caa..52d3d68331b 100644 --- a/gnu/egcs/gcc/fold-const.c +++ b/gnu/egcs/gcc/fold-const.c @@ -1305,6 +1305,10 @@ int_const_binop (code, arg1, arg2, notrunc, forsize) int uns = TREE_UNSIGNED (TREE_TYPE (arg1)); int overflow = 0; int no_overflow = 0; + int sizeof_flag = 0; + + if (SIZEOF_PTR_DERIVED (arg1) == 1 || SIZEOF_PTR_DERIVED (arg2) == 1) + sizeof_flag = 1; int1l = TREE_INT_CST_LOW (arg1); int1h = TREE_INT_CST_HIGH (arg1); @@ -1476,6 +1480,10 @@ int_const_binop (code, arg1, arg2, notrunc, forsize) TREE_CONSTANT_OVERFLOW (t) = (TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg1) | TREE_CONSTANT_OVERFLOW (arg2)); + + if (sizeof_flag == 1) + SIZEOF_PTR_DERIVED (t) = 1; + return t; } @@ -4664,6 +4672,9 @@ fold (expr) | force_fit_type (t, overflow && !TREE_UNSIGNED (type))); TREE_CONSTANT_OVERFLOW (t) = TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg0); + /* If arg0 was calculated from sizeof(ptr), record this */ + if (SIZEOF_PTR_DERIVED (arg0)) + SIZEOF_PTR_DERIVED (t) = 1; } else if (TREE_CODE (arg0) == REAL_CST) t = build_real (type, REAL_VALUE_NEGATE (TREE_REAL_CST (arg0))); @@ -4697,6 +4708,9 @@ fold (expr) | force_fit_type (t, overflow)); TREE_CONSTANT_OVERFLOW (t) = TREE_OVERFLOW (t) | TREE_CONSTANT_OVERFLOW (arg0); + /* If arg0 was calculated from sizeof(ptr), record this */ + if (SIZEOF_PTR_DERIVED (arg0)) + SIZEOF_PTR_DERIVED (t) = 1; } } else if (TREE_CODE (arg0) == REAL_CST) diff --git a/gnu/egcs/gcc/toplev.c b/gnu/egcs/gcc/toplev.c index e98b4751a25..77abd5f6f3a 100644 --- a/gnu/egcs/gcc/toplev.c +++ b/gnu/egcs/gcc/toplev.c @@ -1064,6 +1064,8 @@ documented_lang_options[] = { "-Wno-conversion", "" }, { "-Wformat", "Warn about printf format anomalies" }, { "-Wno-format", "" }, + { "-Wbounded", "Warn about potential overruns in static buffers" }, + { "-Wno-bounded", "" }, { "-Wimplicit-function-declaration", "Warn about implicit function declarations" }, { "-Wno-implicit-function-declaration", "" }, diff --git a/gnu/egcs/gcc/tree.h b/gnu/egcs/gcc/tree.h index 29fe800835e..b25c1cf8816 100644 --- a/gnu/egcs/gcc/tree.h +++ b/gnu/egcs/gcc/tree.h @@ -25,6 +25,16 @@ Boston, MA 02111-1307, USA. */ struct rtx_def; #endif +/* Usage of TREE_LANG_FLAG_?: + 0: Not Used. + 1: Not Used. + 2: Not Used. + 3: Not Used. + 4: Not Used. + 5: Not Used. + 6: SIZEOF_PTR_DERIVED +*/ + /* Codes of tree nodes */ #define DEFTREECODE(SYM, STRING, TYPE, NARGS) SYM, @@ -547,6 +557,10 @@ struct tree_common #define TREE_LANG_FLAG_4(NODE) ((NODE)->common.lang_flag_4) #define TREE_LANG_FLAG_5(NODE) ((NODE)->common.lang_flag_5) #define TREE_LANG_FLAG_6(NODE) ((NODE)->common.lang_flag_6) + +/* Used to track constants derived from sizeof(pointer) operations */ +#define SIZEOF_PTR_DERIVED(NODE) (TREE_LANG_FLAG_6((NODE))) + /* Define additional fields and accessors for nodes representing constants. */ |