summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoravsm <avsm@openbsd.org>2003-06-26 18:30:05 +0000
committeravsm <avsm@openbsd.org>2003-06-26 18:30:05 +0000
commitb27d04fb8e04e7e970b5c287ca2f510173d6ba87 (patch)
tree568b8fb902e6d4c56934d983dbc97e5d753a5133
parentcorrect an example, and fix BUGS; (diff)
downloadwireguard-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.c422
-rw-r--r--gnu/egcs/gcc/c-decl.c9
-rw-r--r--gnu/egcs/gcc/c-tree.h5
-rw-r--r--gnu/egcs/gcc/c-typeck.c7
-rw-r--r--gnu/egcs/gcc/cp/call.c4
-rw-r--r--gnu/egcs/gcc/cp/cp-tree.h10
-rw-r--r--gnu/egcs/gcc/cp/decl2.c7
-rw-r--r--gnu/egcs/gcc/cp/typeck.c13
-rw-r--r--gnu/egcs/gcc/fold-const.c14
-rw-r--r--gnu/egcs/gcc/toplev.c2
-rw-r--r--gnu/egcs/gcc/tree.h14
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. */