diff options
author | Martin Sebor <msebor@redhat.com> | 2020-12-03 15:41:25 -0700 |
---|---|---|
committer | Martin Sebor <msebor@redhat.com> | 2020-12-03 15:43:32 -0700 |
commit | dce6c58db87ebf7f4477bd3126228e73e4eeee97 (patch) | |
tree | f89f18c53c2f16c2d39a1951c21f99cdd4773c99 /gcc/c-family | |
parent | a3f7a6957a674caf95c4aefa618be51092022e87 (diff) | |
download | gcc-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.c | 180 | ||||
-rw-r--r-- | gcc/c-family/c.opt | 10 |
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. |