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