aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-family
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2020-12-03 15:41:25 -0700
committerMartin Sebor <msebor@redhat.com>2020-12-03 15:43:32 -0700
commitdce6c58db87ebf7f4477bd3126228e73e4eeee97 (patch)
treef89f18c53c2f16c2d39a1951c21f99cdd4773c99 /gcc/c-family
parenta3f7a6957a674caf95c4aefa618be51092022e87 (diff)
downloadgcc-dce6c58db87ebf7f4477bd3126228e73e4eeee97.zip
gcc-dce6c58db87ebf7f4477bd3126228e73e4eeee97.tar.gz
gcc-dce6c58db87ebf7f4477bd3126228e73e4eeee97.tar.bz2
Add support for detecting mismatched allocation/deallocation calls.
PR c++/90629 - Support for -Wmismatched-new-delete PR middle-end/94527 - Add an __attribute__ that marks a function as freeing an object gcc/ChangeLog: PR c++/90629 PR middle-end/94527 * builtins.c (access_ref::access_ref): Initialize new member. (compute_objsize): Use access_ref::deref. Handle simple pointer assignment. (expand_builtin): Remove handling of the free built-in. (call_dealloc_argno): Same. (find_assignment_location): New function. (fndecl_alloc_p): Same. (gimple_call_alloc_p): Same. (call_dealloc_p): Same. (matching_alloc_calls_p): Same. (warn_dealloc_offset): Same. (maybe_emit_free_warning): Same. * builtins.h (struct access_ref): Declare new member. (maybe_emit_free_warning): Make extern. Make use of access_ref. Handle -Wmismatched-new-delete. * calls.c (initialize_argument_information): Call maybe_emit_free_warning. * doc/extend.texi (attribute malloc): Update. * doc/invoke.texi (-Wfree-nonheap-object): Expand documentation. (-Wmismatched-new-delete): Document new option. (-Wmismatched-dealloc): Document new option. gcc/c-family/ChangeLog: PR c++/90629 PR middle-end/94527 * c-attribs.c (handle_dealloc_attribute): New function. (handle_malloc_attribute): Handle argument forms of attribute. * c.opt (-Wmismatched-dealloc): New option. (-Wmismatched-new-delete): New option. gcc/testsuite/ChangeLog: PR c++/90629 PR middle-end/94527 * g++.dg/asan/asan_test.cc: Fix a bug. * g++.dg/warn/delete-array-1.C: Add expected warning. * g++.old-deja/g++.other/delete2.C: Add expected warning. * g++.dg/warn/Wfree-nonheap-object-2.C: New test. * g++.dg/warn/Wfree-nonheap-object.C: New test. * g++.dg/warn/Wmismatched-new-delete.C: New test. * g++.dg/warn/Wmismatched-dealloc-2.C: New test. * g++.dg/warn/Wmismatched-dealloc.C: New test. * gcc.dg/Wmismatched-dealloc.c: New test. * gcc.dg/analyzer/malloc-1.c: Prune out expected warning. * gcc.dg/attr-malloc.c: New test. * gcc.dg/free-1.c: Adjust text of expected warning. * gcc.dg/free-2.c: Same. * gcc.dg/torture/pr71816.c: Prune out expected warning. * gcc.dg/tree-ssa/pr19831-2.c: Add an expected warning. * gcc.dg/Wfree-nonheap-object-2.c: New test. * gcc.dg/Wfree-nonheap-object-3.c: New test. * gcc.dg/Wfree-nonheap-object.c: New test. libstdc++-v3/ChangeLog: * testsuite/ext/vstring/modifiers/clear/56166.cc: Suppress a false positive warning.
Diffstat (limited to 'gcc/c-family')
-rw-r--r--gcc/c-family/c-attribs.c180
-rw-r--r--gcc/c-family/c.opt10
2 files changed, 181 insertions, 9 deletions
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 99b6630..f7dad7a 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -113,6 +113,7 @@ static tree handle_no_instrument_function_attribute (tree *, tree,
static tree handle_no_profile_instrument_function_attribute (tree *, tree,
tree, int, bool *);
static tree handle_malloc_attribute (tree *, tree, tree, int, bool *);
+static tree handle_dealloc_attribute (tree *, tree, tree, int, bool *);
static tree handle_returns_twice_attribute (tree *, tree, tree, int, bool *);
static tree handle_no_limit_stack_attribute (tree *, tree, tree, int,
bool *);
@@ -364,7 +365,7 @@ const struct attribute_spec c_common_attribute_table[] =
{ "no_profile_instrument_function", 0, 0, true, false, false, false,
handle_no_profile_instrument_function_attribute,
NULL },
- { "malloc", 0, 0, true, false, false, false,
+ { "malloc", 0, 2, true, false, false, false,
handle_malloc_attribute, attr_alloc_exclusions },
{ "returns_twice", 0, 0, true, false, false, false,
handle_returns_twice_attribute,
@@ -524,6 +525,8 @@ const struct attribute_spec c_common_attribute_table[] =
handle_objc_root_class_attribute, NULL },
{ "objc_nullability", 1, 1, true, false, false, false,
handle_objc_nullability_attribute, NULL },
+ { "*dealloc", 1, 2, true, false, false, false,
+ handle_dealloc_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
@@ -3127,20 +3130,179 @@ handle_no_profile_instrument_function_attribute (tree *node, tree name, tree,
return NULL_TREE;
}
-/* Handle a "malloc" attribute; arguments as in
- struct attribute_spec.handler. */
+/* Handle the "malloc" attribute. */
static tree
-handle_malloc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+handle_malloc_attribute (tree *node, tree name, tree args,
int ARG_UNUSED (flags), bool *no_add_attrs)
{
- if (TREE_CODE (*node) == FUNCTION_DECL
- && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node))))
- DECL_IS_MALLOC (*node) = 1;
- else
+ tree fndecl = *node;
+
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored; valid only "
+ "for functions",
+ name);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ tree rettype = TREE_TYPE (TREE_TYPE (*node));
+ if (!POINTER_TYPE_P (rettype))
+ {
+ warning (OPT_Wattributes, "%qE attribute ignored on functions "
+ "returning %qT; valid only for pointer return types",
+ name, rettype);
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ if (!args)
+ {
+ /* Only the form of the attribute with no arguments declares
+ a function malloc-like. */
+ DECL_IS_MALLOC (*node) = 1;
+ return NULL_TREE;
+ }
+
+ tree dealloc = TREE_VALUE (args);
+ if (error_operand_p (dealloc))
+ {
+ /* If the argument is in error it will have already been diagnosed.
+ Avoid issuing redundant errors here. */
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ /* In C++ the argument may be wrapped in a cast to disambiguate one
+ of a number of overloads (such as operator delete). Strip it. */
+ STRIP_NOPS (dealloc);
+ if (TREE_CODE (dealloc) == ADDR_EXPR)
+ dealloc = TREE_OPERAND (dealloc, 0);
+
+ if (TREE_CODE (dealloc) != FUNCTION_DECL)
+ {
+ if (TREE_CODE (dealloc) == OVERLOAD)
+ {
+ /* Handle specially the common case of specifying one of a number
+ of overloads, such as operator delete. */
+ error ("%qE attribute argument 1 is ambiguous", name);
+ inform (input_location,
+ "use a cast to the expected type to disambiguate");
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ error ("%qE attribute argument 1 does not name a function", name);
+ if (DECL_P (dealloc))
+ inform (DECL_SOURCE_LOCATION (dealloc),
+ "argument references a symbol declared here");
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ /* Mentioning the deallocation function qualifies as its use. */
+ TREE_USED (dealloc) = 1;
+
+ tree fntype = TREE_TYPE (dealloc);
+ tree argpos = TREE_CHAIN (args) ? TREE_VALUE (TREE_CHAIN (args)) : NULL_TREE;
+ if (!argpos)
+ {
+ tree argtypes = TYPE_ARG_TYPES (fntype);
+ if (!argtypes)
+ {
+ /* Reject functions without a prototype. */
+ error ("%qE attribute argument 1 must take a pointer "
+ "type as its first argument", name);
+ inform (DECL_SOURCE_LOCATION (dealloc),
+ "refernced symbol declared here" );
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ tree argtype = TREE_VALUE (argtypes);
+ if (TREE_CODE (argtype) != POINTER_TYPE)
+ {
+ /* Reject functions that don't take a pointer as their first
+ argument. */
+ error ("%qE attribute argument 1 must take a pointer type "
+ "as its first argument; have %qT", name, argtype);
+ inform (DECL_SOURCE_LOCATION (dealloc),
+ "referenced symbol declared here" );
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ *no_add_attrs = false;
+ tree attr_free = build_tree_list (NULL_TREE, DECL_NAME (fndecl));
+ attr_free = build_tree_list (get_identifier ("*dealloc"), attr_free);
+ decl_attributes (&dealloc, attr_free, 0);
+ return NULL_TREE;
+ }
+
+ /* Validate the positional argument. */
+ argpos = positional_argument (fntype, name, argpos, POINTER_TYPE);
+ if (!argpos)
{
- warning (OPT_Wattributes, "%qE attribute ignored", name);
*no_add_attrs = true;
+ return NULL_TREE;
+ }
+
+ /* It's valid to declare the same function with multiple instances
+ of attribute malloc, each naming the same or different deallocator
+ functions, and each referencing either the same or a different
+ positional argument. */
+ *no_add_attrs = false;
+ tree attr_free = tree_cons (NULL_TREE, argpos, NULL_TREE);
+ attr_free = tree_cons (NULL_TREE, DECL_NAME (fndecl), attr_free);
+ attr_free = build_tree_list (get_identifier ("*dealloc"), attr_free);
+ decl_attributes (&dealloc, attr_free, 0);
+ return NULL_TREE;
+}
+
+/* Handle the internal "*dealloc" attribute added for functions declared
+ with the one- and two-argument forms of attribute malloc. Add it
+ to *NODE unless it's already there with the same arguments. */
+
+static tree
+handle_dealloc_attribute (tree *node, tree name, tree args, int,
+ bool *no_add_attrs)
+{
+ tree fndecl = *node;
+
+ tree attrs = DECL_ATTRIBUTES (fndecl);
+ if (!attrs)
+ return NULL_TREE;
+
+ tree arg_fname = TREE_VALUE (args);
+ args = TREE_CHAIN (args);
+ tree arg_pos = args ? TREE_VALUE (args) : NULL_TREE;
+
+ gcc_checking_assert (TREE_CODE (arg_fname) == IDENTIFIER_NODE);
+
+ const char* const namestr = IDENTIFIER_POINTER (name);
+ for (tree at = attrs; (at = lookup_attribute (namestr, at));
+ at = TREE_CHAIN (at))
+ {
+ tree alloc = TREE_VALUE (at);
+ if (!alloc)
+ continue;
+
+ tree pos = TREE_CHAIN (alloc);
+ alloc = TREE_VALUE (alloc);
+ pos = pos ? TREE_VALUE (pos) : NULL_TREE;
+ gcc_checking_assert (TREE_CODE (alloc) == IDENTIFIER_NODE);
+
+ if (alloc == arg_fname
+ && ((!pos && !arg_pos)
+ || (pos && arg_pos && tree_int_cst_equal (pos, arg_pos))))
+ {
+ /* The function already has the attribute either without any
+ arguments or with the same arguments as the attribute that's
+ being added. Return without adding another copy. */
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
}
return NULL_TREE;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 059f6c3..7947828 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -793,6 +793,16 @@ Wmisleading-indentation
C C++ Common Var(warn_misleading_indentation) Warning LangEnabledBy(C C++,Wall)
Warn when the indentation of the code does not reflect the block structure.
+Wmismatched-dealloc
+C ObjC C++ ObjC++ Var(warn_mismatched_alloc) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn for deallocation calls with arguments returned from mismatched allocation
+functions.
+
+Wmismatched-new-delete
+C++ ObjC++ Var(warn_mismatched_new_delete) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall)
+Warn for mismatches between calls to operator new or delete and the corrsponding
+call to the allocation or deallocation function.
+
Wmismatched-tags
C++ ObjC++ Var(warn_mismatched_tags) Warning
Warn when a class is redeclared or referenced using a mismatched class-key.