b55756f2a0
not ready yet...
495 lines
16 KiB
Plaintext
495 lines
16 KiB
Plaintext
$OpenBSD: patch-gcc_c-bounded_c,v 1.1.1.1 2005/01/03 15:21:29 espie Exp $
|
|
--- gcc/c-bounded.c.orig Mon Jan 3 10:17:41 2005
|
|
+++ gcc/c-bounded.c Mon Jan 3 10:17:41 2005
|
|
@@ -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;
|
|
+ }
|
|
+ }
|
|
+}
|