diff options
author | Anil Madhavapeddy <avsm@cvs.openbsd.org> | 2004-04-23 21:19:12 +0000 |
---|---|---|
committer | Anil Madhavapeddy <avsm@cvs.openbsd.org> | 2004-04-23 21:19:12 +0000 |
commit | ae834b19461bc0784018ab2c201fffe866af2094 (patch) | |
tree | 33394cc3d0d88dd77fe14bfe71f2c4b7f48db89c | |
parent | e17ad16e253a789d440f5c4c0a963591f643dd35 (diff) |
Port the gcc2 bounds checking support to gcc3, activated with -Wbounded
and documented in gcc-local(1).
tested by marc@ brad@ and lots of bulk ports builds
-rw-r--r-- | gnu/usr.bin/gcc/gcc/Makefile.in | 7 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/builtin-attrs.def | 46 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/builtins.def | 16 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/c-bounded.c | 490 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/c-common.c | 44 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/c-common.h | 6 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/c-format.c | 50 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/c-opts.c | 6 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/cp/Make-lang.in | 4 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/cp/call.c | 4 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/cp/typeck.c | 3 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/fold-const.c | 14 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/gcc/tree.h | 4 |
13 files changed, 654 insertions, 40 deletions
diff --git a/gnu/usr.bin/gcc/gcc/Makefile.in b/gnu/usr.bin/gcc/gcc/Makefile.in index e1f277be3b3..dbb8ecbf64b 100644 --- a/gnu/usr.bin/gcc/gcc/Makefile.in +++ b/gnu/usr.bin/gcc/gcc/Makefile.in @@ -739,8 +739,8 @@ CXX_TARGET_OBJS=@cxx_target_objs@ # Language-specific object files for C and Objective C. 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-objc-common.o c-dump.o libcpp.a $(C_TARGET_OBJS) + c-convert.o c-aux-info.o c-common.o c-opts.o c-format.o c-bounded.o \ + c-semantics.o c-objc-common.o c-dump.o libcpp.a $(C_TARGET_OBJS) # Language-specific object files for C. C_OBJS = c-parse.o c-lang.o c-pretty-print.o $(C_AND_OBJC_OBJS) @@ -1267,6 +1267,9 @@ attribs.o : attribs.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) flags.h \ c-format.o : c-format.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) langhooks.h \ $(C_COMMON_H) flags.h toplev.h intl.h diagnostic.h +c-bounded.o : c-bounded.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) langhooks.h \ + $(C_COMMON_H) flags.h toplev.h intl.h diagnostic.h + c-semantics.o : c-semantics.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \ flags.h toplev.h output.h c-pragma.h $(RTL_H) $(GGC_H) \ $(EXPR_H) $(PREDICT_H) diff --git a/gnu/usr.bin/gcc/gcc/builtin-attrs.def b/gnu/usr.bin/gcc/gcc/builtin-attrs.def index 7bdbd628086..0fe47a4fd04 100644 --- a/gnu/usr.bin/gcc/gcc/builtin-attrs.def +++ b/gnu/usr.bin/gcc/gcc/builtin-attrs.def @@ -71,13 +71,18 @@ DEF_ATTR_FOR_INT (4) CONCAT2 (ATTR_,VALUE1), CONCAT2 (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) #undef DEF_LIST_INT_INT /* Construct tress 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") @@ -88,7 +93,9 @@ DEF_ATTR_IDENT (ATTR_NOTHROW, "nothrow") DEF_ATTR_IDENT (ATTR_PRINTF, "printf") DEF_ATTR_IDENT (ATTR_PURE, "pure") DEF_ATTR_IDENT (ATTR_SCANF, "scanf") +DEF_ATTR_IDENT (ATTR_SIZE, "size") DEF_ATTR_IDENT (ATTR_STRFMON, "strfmon") +DEF_ATTR_IDENT (ATTR_STRING, "string") DEF_ATTR_IDENT (ATTR_STRFTIME, "strftime") DEF_ATTR_TREE_LIST (ATTR_NOTHROW_LIST, ATTR_NOTHROW, ATTR_NULL, ATTR_NULL) @@ -137,6 +144,45 @@ 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 (CONCAT4 (ATTR_,TYPE,_,VALUES), ATTR_NULL, \ + CONCAT2 (ATTR_,TYPE), CONCAT2 (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 (CONCAT4 (ATTR_NOTHROW_BOUNDED_,TYPE,_,VALUES), ATTR_BOUNDED, \ + CONCAT4 (ATTR_,TYPE,_,VALUES), ATTR_NOTHROW_LIST) +/* Chain multiple nothrow bounded buffer attributes together */ +#define DEF_MULTIPLE_NOTHROW_BOUNDED_BUFFER_ATTRIBUTE(VAL1,VAL2) \ + DEF_ATTR_TREE_LIST (CONCAT4(ATTR_NOTHROW_BOUNDED_BUFFER_,VAL1,_,VAL2),ATTR_BOUNDED,\ + CONCAT2(ATTR_BUFFER_,VAL1), \ + CONCAT2(ATTR_NOTHROW_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 (CONCAT4 (PREPEND,TYPE,_,VALUES), \ + ATTR_BOUNDED, CONCAT4 (ATTR_,TYPE,_,VALUES), \ + CONCAT2(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_BOUNDED_ATTRIBUTE(BUFFER,1_3) +DEF_NOTHROW_BOUNDED_ATTRIBUTE(BUFFER,2_3) +DEF_NOTHROW_BOUNDED_ATTRIBUTE(SIZE,1_3_2) +DEF_MULTIPLE_NOTHROW_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_BOUNDED_ATTRIBUTE +#undef DEF_MULTIPLE_NOTHROW_BOUNDED_ATTRIBUTE +#undef DEF_FORMAT_PRINTF_BOUNDED_ATTRIBUTE + /* Define an attribute for a function, along with the IDENTIFIER_NODE. */ #define DEF_FN_ATTR_IDENT(NAME, ATTRS, PREDICATE) \ DEF_ATTR_IDENT (CONCAT2(ATTR_,NAME), STRINGX(NAME)) \ diff --git a/gnu/usr.bin/gcc/gcc/builtins.def b/gnu/usr.bin/gcc/gcc/builtins.def index c208b751629..3b76ce401ac 100644 --- a/gnu/usr.bin/gcc/gcc/builtins.def +++ b/gnu/usr.bin/gcc/gcc/builtins.def @@ -230,7 +230,7 @@ DEF_BUILTIN (BUILT_IN_BZERO, BT_FN_VOID_PTR_SIZE, BT_FN_VOID_VAR, true, true, true, - ATTR_NOTHROW_LIST) + ATTR_NOTHROW_BOUNDED_BUFFER_1_2) DEF_BUILTIN (BUILT_IN_BCMP, "__builtin_bcmp", BUILT_IN_NORMAL, @@ -255,7 +255,7 @@ DEF_EXT_LIB_BUILTIN(BUILT_IN_RINDEX, DEF_LIB_BUILTIN(BUILT_IN_MEMCPY, "__builtin_memcpy", BT_FN_PTR_PTR_CONST_PTR_SIZE, - ATTR_NOTHROW_LIST) + ATTR_NOTHROW_BOUNDED_BUFFER_2_3_1_3) DEF_LIB_BUILTIN(BUILT_IN_MEMCMP, "__builtin_memcmp", BT_FN_INT_CONST_PTR_CONST_PTR_SIZE, @@ -263,7 +263,7 @@ DEF_LIB_BUILTIN(BUILT_IN_MEMCMP, DEF_LIB_BUILTIN(BUILT_IN_MEMSET, "__builtin_memset", BT_FN_PTR_PTR_INT_SIZE, - ATTR_NOTHROW_LIST) + ATTR_NOTHROW_BOUNDED_BUFFER_1_3) DEF_LIB_BUILTIN(BUILT_IN_STRCAT, "__builtin_strcat", @@ -272,7 +272,7 @@ DEF_LIB_BUILTIN(BUILT_IN_STRCAT, DEF_LIB_BUILTIN(BUILT_IN_STRNCAT, "__builtin_strncat", BT_FN_STRING_STRING_CONST_STRING_SIZE, - ATTR_NOTHROW_LIST) + ATTR_NOTHROW_BOUNDED_BUFFER_1_3) DEF_LIB_BUILTIN(BUILT_IN_STRCPY, "__builtin_strcpy", BT_FN_STRING_STRING_CONST_STRING, @@ -280,7 +280,7 @@ DEF_LIB_BUILTIN(BUILT_IN_STRCPY, DEF_LIB_BUILTIN(BUILT_IN_STRNCPY, "__builtin_strncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, - ATTR_NOTHROW_LIST) + ATTR_NOTHROW_BOUNDED_BUFFER_1_3) DEF_LIB_BUILTIN(BUILT_IN_STRCMP, "__builtin_strcmp", BT_FN_INT_CONST_STRING_CONST_STRING, @@ -542,7 +542,7 @@ DEF_LIB_BUILTIN(BUILT_IN_PUTS, DEF_C99_BUILTIN(BUILT_IN_SNPRINTF, "__builtin_snprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VAR, - ATTR_FORMAT_PRINTF_3_4) + ATTR_PRINTF_3_4_BOUNDED_STRING_1_2) DEF_LIB_BUILTIN(BUILT_IN_SPRINTF, "__builtin_sprintf", BT_FN_INT_STRING_CONST_STRING_VAR, @@ -570,7 +570,7 @@ DEF_C99_BUILTIN(BUILT_IN_VSSCANF, DEF_C99_BUILTIN(BUILT_IN_VSNPRINTF, "__builtin_vsnprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VALIST_ARG, - ATTR_FORMAT_PRINTF_3_0) + ATTR_PRINTF_3_0_BOUNDED_STRING_1_2) DEF_LIB_BUILTIN(BUILT_IN_VSPRINTF, "__builtin_vsprintf", BT_FN_INT_STRING_CONST_STRING_VALIST_ARG, @@ -597,7 +597,7 @@ DEF_BUILTIN (BUILT_IN_FPUTS, DEF_FALLBACK_BUILTIN(BUILT_IN_FWRITE, "__builtin_fwrite", BT_FN_SIZE_CONST_PTR_SIZE_SIZE_PTR, - ATTR_NOTHROW_LIST) + ATTR_NOTHROW_BOUNDED_SIZE_1_3_2) DEF_FRONT_END_LIB_BUILTIN(BUILT_IN_FPRINTF, "__builtin_fprintf", BT_FN_INT_PTR_CONST_STRING_VAR, diff --git a/gnu/usr.bin/gcc/gcc/c-bounded.c b/gnu/usr.bin/gcc/gcc/c-bounded.c new file mode 100644 index 00000000000..81d12e54ff3 --- /dev/null +++ b/gnu/usr.bin/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 "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 ("`%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 ("`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"); + 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 VPARAMS ((int *status, const char *msgid, ...)) +{ + diagnostic_info diagnostic ; + + VA_OPEN (ap, msgid); + VA_FIXEDARG (ap, int *, status); + VA_FIXEDARG (ap, const char *, msgid); + + if (status) + *status = 1; + else + { + /* This duplicates the warning function behavior. */ + diagnostic_set_info (&diagnostic, _(msgid), &ap, input_filename, lineno, + DK_WARNING); + report_diagnostic (&diagnostic); + } + + VA_CLOSE (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); + + /* Check for a possible sizeof(pointer) error in string functions */ + if (info->bounded_type == string_bound_type + && SIZEOF_PTR_DERIVED (length_expr)) + status_warning(status, "sizeof(pointer) possibly incorrect in argument %d", + info->bounded_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)); + 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 (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 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/usr.bin/gcc/gcc/c-common.c b/gnu/usr.bin/gcc/gcc/c-common.c index 7edd965cb97..941b52c2bae 100644 --- a/gnu/usr.bin/gcc/gcc/c-common.c +++ b/gnu/usr.bin/gcc/gcc/c-common.c @@ -39,7 +39,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "langhooks.h" #include "except.h" /* For USING_SJLJ_EXCEPTIONS. */ #include "tree-inline.h" -#include "c-tree.h" cpp_reader *parse_in; /* Declared in c-pragma.h. */ @@ -333,6 +332,9 @@ int warn_format_nonliteral; int warn_format_security; +/* Warn about buffer size mismatches. */ + +int warn_bounded; /* C/ObjC language option variables. */ @@ -765,8 +767,6 @@ static tree handle_deprecated_attribute PARAMS ((tree *, tree, tree, int, bool *)); static tree handle_vector_size_attribute PARAMS ((tree *, tree, tree, int, bool *)); -static tree handle_bounded_attribute PARAMS ((tree *, tree, tree, int, - bool *)); static tree handle_nonnull_attribute PARAMS ((tree *, tree, tree, int, bool *)); static tree handle_sentinel_attribute PARAMS ((tree *, tree, tree, int, @@ -3060,6 +3060,7 @@ c_sizeof_or_alignof_type (type, op, complain) const char *op_name; tree value = NULL; enum tree_code type_code = TREE_CODE (type); + bool sizeof_ptr_flag = false; my_friendly_assert (op == SIZEOF_EXPR || op == ALIGNOF_EXPR, 20020720); op_name = op == SIZEOF_EXPR ? "sizeof" : "__alignof__"; @@ -3091,10 +3092,15 @@ c_sizeof_or_alignof_type (type, op, complain) else { if (op == SIZEOF_EXPR) - /* Convert in case a char is more than one unit. */ - value = size_binop (CEIL_DIV_EXPR, TYPE_SIZE_UNIT (type), - size_int (TYPE_PRECISION (char_type_node) - / BITS_PER_UNIT)); + { + /* Convert in case a char is more than one unit. */ + value = size_binop (CEIL_DIV_EXPR, TYPE_SIZE_UNIT (type), + size_int (TYPE_PRECISION (char_type_node) + / BITS_PER_UNIT)); + + if (type_code == POINTER_TYPE) + sizeof_ptr_flag = true; + } else value = size_int (TYPE_ALIGN (type) / BITS_PER_UNIT); } @@ -3105,7 +3111,10 @@ c_sizeof_or_alignof_type (type, op, complain) `size_t', which is just a typedef for an ordinary integer type. */ value = fold (build1 (NOP_EXPR, size_type_node, value)); my_friendly_assert (!TYPE_IS_SIZETYPE (TREE_TYPE (value)), 20001021); - + + if (sizeof_ptr_flag) + SIZEOF_PTR_DERIVED (value) = 1; + return value; } @@ -6389,22 +6398,6 @@ vector_size_helper (type, bottom) return outer; } -/* Handle a "bounded" attribute; arguments as in - struct attribute_spec.handler. - Just a stub for now. */ - -static tree -handle_bounded_attribute (node, name, args, flags, no_add_attrs) - tree *node ATTRIBUTE_UNUSED; - tree name ATTRIBUTE_UNUSED; - tree args ATTRIBUTE_UNUSED; - int flags ATTRIBUTE_UNUSED; - bool *no_add_attrs; -{ - *no_add_attrs = true; - return NULL_TREE; -} - /* Handle a "sentinel" attribute. Just a stub for now. */ @@ -6633,6 +6626,9 @@ check_function_arguments (attrs, params) if (warn_format) check_function_format (NULL, attrs, params); + + if (warn_bounded) + check_function_bounded (NULL, attrs, params); } /* Generic argument checking recursion routine. PARAM is the argument to diff --git a/gnu/usr.bin/gcc/gcc/c-common.h b/gnu/usr.bin/gcc/gcc/c-common.h index 1f59ebd7d82..515d99a2cca 100644 --- a/gnu/usr.bin/gcc/gcc/c-common.h +++ b/gnu/usr.bin/gcc/gcc/c-common.h @@ -501,6 +501,9 @@ extern int warn_format_nonliteral; extern int warn_format_security; +/* Warn about buffer size mismatches. */ + +extern int warn_bounded; /* C/ObjC language option variables. */ @@ -866,7 +869,10 @@ extern void check_function_arguments_recurse PARAMS ((void (*) (void *, void *, tree, unsigned HOST_WIDE_INT)); extern void check_function_format PARAMS ((int *, tree, tree)); +extern void check_function_bounded PARAMS ((int *, tree, tree)); extern void set_Wformat PARAMS ((int)); +extern tree handle_bounded_attribute PARAMS ((tree *, tree, tree, + int, bool *)); extern tree handle_format_attribute PARAMS ((tree *, tree, tree, int, bool *)); extern tree handle_format_arg_attribute PARAMS ((tree *, tree, tree, diff --git a/gnu/usr.bin/gcc/gcc/c-format.c b/gnu/usr.bin/gcc/gcc/c-format.c index ad254ea61f9..d778849835a 100644 --- a/gnu/usr.bin/gcc/gcc/c-format.c +++ b/gnu/usr.bin/gcc/gcc/c-format.c @@ -540,6 +540,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; @@ -552,6 +556,8 @@ typedef struct format_wanted_type const char *name; /* The actual parameter to check against the wanted type. */ tree param; + /* Field width of type */ + int field_width; /* The argument number of that parameter. */ int arg_num; /* The next type to check for this format conversion, or NULL if none. */ @@ -1634,6 +1640,7 @@ check_format_info_main (status, res, info, format_chars, format_length, const format_length_info *fli = NULL; const format_char_info *fci = NULL; char flag_chars[256]; + int field_width = 0; int aflag = 0; if (*format_chars == 0) { @@ -1775,20 +1782,29 @@ check_format_info_main (status, res, info, format_chars, format_length, /* 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)) status_warning (status, "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; @@ -2186,10 +2202,15 @@ check_format_info_main (status, res, info, format_chars, format_length, main_wanted_type.wanted_type_name = wanted_type_name; main_wanted_type.pointer_count = fci->pointer_count + aflag; main_wanted_type.char_lenient_flag = 0; + main_wanted_type.field_width = field_width; if (strchr (fci->flags2, 'c') != 0) main_wanted_type.char_lenient_flag = 1; main_wanted_type.writing_in_flag = 0; main_wanted_type.reading_from_flag = 0; + if (strchr (fci->format_chars, 'c') != 0) + main_wanted_type.size_equals_width = 1; + else + main_wanted_type.size_equals_width = 0; if (aflag) main_wanted_type.writing_in_flag = 1; else @@ -2285,6 +2306,27 @@ check_format_types (status, types) else cur_param = 0; + /* Test static string bounds for sscan if -Wbounded is on as well */ + if (warn_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_domain = TYPE_DOMAIN (TREE_TYPE (cur_param)); + tree array_size_expr = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (cur_param))); + int f = types->size_equals_width ? 0 : 1; + if (array_size_expr != 0 && types->field_width > 0) + { + int array_size = TREE_INT_CST_LOW (array_size_expr) + 1; + if (array_size < (types->field_width + f)) + warning ("Array size (%d) smaller than format string size (%d)", + array_size, types->field_width + f); + } + } + /* 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/usr.bin/gcc/gcc/c-opts.c b/gnu/usr.bin/gcc/gcc/c-opts.c index 77a468ab184..193b6f16f1b 100644 --- a/gnu/usr.bin/gcc/gcc/c-opts.c +++ b/gnu/usr.bin/gcc/gcc/c-opts.c @@ -127,6 +127,7 @@ static void sanitize_cpp_opts PARAMS ((void)); OPT("Wabi", CL_CXX, OPT_Wabi) \ OPT("Wall", CL_ALL, OPT_Wall) \ OPT("Wbad-function-cast", CL_C, OPT_Wbad_function_cast) \ + OPT("Wbounded", CL_ALL, OPT_Wbounded) \ OPT("Wcast-qual", CL_ALL, OPT_Wcast_qual) \ OPT("Wchar-subscripts", CL_ALL, OPT_Wchar_subscripts) \ OPT("Wcomment", CL_ALL, OPT_Wcomment) \ @@ -694,6 +695,7 @@ c_common_decode_option (argc, argv) set_Wunused (on); set_Wformat (on); set_Wimplicit (on); + warn_bounded = on; warn_char_subscripts = on; warn_missing_braces = on; warn_parentheses = on; @@ -789,6 +791,10 @@ c_common_decode_option (argc, argv) warn_float_equal = on; break; + case OPT_Wbounded: + warn_bounded = on; + break; + case OPT_Wformat: set_Wformat (on); break; diff --git a/gnu/usr.bin/gcc/gcc/cp/Make-lang.in b/gnu/usr.bin/gcc/gcc/cp/Make-lang.in index d683d45d9b6..59d6ea39b9a 100644 --- a/gnu/usr.bin/gcc/gcc/cp/Make-lang.in +++ b/gnu/usr.bin/gcc/gcc/cp/Make-lang.in @@ -78,8 +78,8 @@ g++-cross$(exeext): g++$(exeext) # The compiler itself. # Shared with C front end: -CXX_C_OBJS = attribs.o c-common.o c-format.o c-pragma.o c-semantics.o c-lex.o \ - c-dump.o $(CXX_TARGET_OBJS) c-pretty-print.o c-opts.o +CXX_C_OBJS = attribs.o c-common.o c-format.o c-bounded.o c-pragma.o c-semantics.o \ + c-lex.o c-dump.o $(CXX_TARGET_OBJS) c-pretty-print.o c-opts.o # Language-specific object files. CXX_OBJS = cp/call.o cp/decl.o cp/expr.o cp/pt.o cp/typeck2.o \ diff --git a/gnu/usr.bin/gcc/gcc/cp/call.c b/gnu/usr.bin/gcc/gcc/cp/call.c index 1a1a493774b..60e79dfc050 100644 --- a/gnu/usr.bin/gcc/gcc/cp/call.c +++ b/gnu/usr.bin/gcc/gcc/cp/call.c @@ -4592,6 +4592,10 @@ build_over_call (cand, args, flags) check_function_format (NULL, TYPE_ATTRIBUTES (TREE_TYPE (fn)), converted_args); + if (warn_bounded) + check_function_bounded (NULL, TYPE_ATTRIBUTES (TREE_TYPE (fn)), + converted_args); + /* Avoid actually calling copy constructors and copy assignment operators, if possible. */ diff --git a/gnu/usr.bin/gcc/gcc/cp/typeck.c b/gnu/usr.bin/gcc/gcc/cp/typeck.c index 4b36cc2ceaa..96f914c7541 100644 --- a/gnu/usr.bin/gcc/gcc/cp/typeck.c +++ b/gnu/usr.bin/gcc/gcc/cp/typeck.c @@ -2739,6 +2739,9 @@ build_function_call_real (function, params, flags) if (warn_format) check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params); + if (warn_bounded) + check_function_bounded (NULL, TYPE_ATTRIBUTES (fntype), 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/usr.bin/gcc/gcc/fold-const.c b/gnu/usr.bin/gcc/gcc/fold-const.c index 94682d477ee..22db9633043 100644 --- a/gnu/usr.bin/gcc/gcc/fold-const.c +++ b/gnu/usr.bin/gcc/gcc/fold-const.c @@ -1036,6 +1036,10 @@ int_const_binop (code, arg1, arg2, notrunc) = (TREE_CODE (type) == INTEGER_TYPE && TYPE_IS_SIZETYPE (type)); 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); @@ -1203,6 +1207,10 @@ int_const_binop (code, arg1, arg2, notrunc) 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; } @@ -5039,6 +5047,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))); @@ -5082,6 +5093,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/usr.bin/gcc/gcc/tree.h b/gnu/usr.bin/gcc/gcc/tree.h index facaa9d0277..aa422537a56 100644 --- a/gnu/usr.bin/gcc/gcc/tree.h +++ b/gnu/usr.bin/gcc/gcc/tree.h @@ -698,6 +698,10 @@ extern void tree_vec_elt_check_failed PARAMS ((int, int, const char *, #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. */ |