From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from www523.your-server.de (www523.your-server.de [159.69.224.22]) by sourceware.org (Postfix) with ESMTPS id C268F3858418 for ; Wed, 22 Jun 2022 14:59:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C268F3858418 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=tim-lange.me Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=tim-lange.me DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=tim-lange.me; s=default2108; h=Content-Transfer-Encoding:Content-Type: MIME-Version:References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender :Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID; bh=A+fL1/zsGw5Xm1UbbWSpQJWaFduyvaqyFafmRSaJNHQ=; b=tR0SCseh8HrCQC7z462ETx8s75 WNEQXRobV3N+2+UXu/dNU7Cazn4JDSvDEiWAkm+0xw4lDciicnJ86224gTz5zYfC1HFJfhMzGRjSV e71wNM2YEgq34169t/fzTMpfn6MYjjwUe4Vo4mW99p96+f74A83AoL+NaP4jMxy+RvHXnM4cEXSq0 dZNdzM+8HrdV5faZ8rt9ZPYEuoO+RqxWMFX3X8KQposZ4g11hiyYrY1hJBgFB2+0Kurk6Unvwcujp 1xh7Yuxy2RSzpmD/a+v7SnVByZjTmV2G9DqWW9qhaEtFmea/fDjsdSD5d4y9shLzTQccWt6j5Ttj/ 1qSbYlnQ==; Received: from sslproxy04.your-server.de ([78.46.152.42]) by www523.your-server.de with esmtpsa (TLSv1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.92.3) (envelope-from ) id 1o41pD-000Dov-V8; Wed, 22 Jun 2022 16:59:24 +0200 Received: from [2a02:908:1861:d6a0::de4e] (helo=fedora..) by sslproxy04.your-server.de with esmtpsa (TLSv1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1o41pD-000Sp6-Nn; Wed, 22 Jun 2022 16:59:23 +0200 From: Tim Lange To: dmalcolm@redhat.com Cc: gcc@gcc.gnu.org, Tim Lange Subject: Re: [RFC] analyzer: allocation size warning Date: Wed, 22 Jun 2022 16:57:55 +0200 Message-Id: <20220622145755.38734-1-mail@tim-lange.me> X-Mailer: git-send-email 2.36.1 In-Reply-To: <5e65c86d518c6e26a970b5fb23c2ed507abd2085.camel@redhat.com> References: <5e65c86d518c6e26a970b5fb23c2ed507abd2085.camel@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Authenticated-Sender: mail@tim-lange.me X-Virus-Scanned: Clear (ClamAV 0.103.6/26580/Wed Jun 22 10:06:47 2022) X-Spam-Status: No, score=-11.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_INFOUSMEBIZ, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Jun 2022 14:59:31 -0000 The checker reaches region-model.cc#3083 in my patch with the impl_region_model_context on the 'after' node of create_buffer() but then discards the warning inside impl_region_model_context::warn because m_stmt is null. Even if m_stmt were not be NULL at the 'after' node, my warning would be emitted before the return edge was taken and thus be wrongly indented like shown below: /path/to/.c:10:16: warning: Allocated buffer size is not a multiple of the pointee's size [CWE-131] [-Wanalyzer-allocation-size] 10 | int *buf = create_buffer(42); | ^~~~~~~~~~~~~~~~~ ‘main’: events 1-2 | | 9 | int main (int argc, char **argv) { | | ^~~~ | | | | | (1) entry to ‘main’ | 10 | int *buf = create_buffer(42); | | ~~~~~~~~~~~~~~~~~ | | | | | (2) calling ‘create_buffer’ from ‘main’ | +--> ‘create_buffer’: events 3-4 | | 4 | void *create_buffer(int n) | | ^~~~~~~~~~~~~ | | | | | (3) entry to ‘create_buffer’ | 5 | { | 6 | return malloc(n); | | ~~~~~~~~~ | | | | | (4) allocated 42 bytes here | ‘main’: event 5 | | 10 | int *buf = create_buffer(42); | | ^~~~~~~~~~~~~~~~~ | | | | | (5) Assigned to ‘int *’ here | The correct warning should be: /path/to/.c:10:16: warning: Allocated buffer size is not a multiple of the pointee's size [CWE-131] [-Wanalyzer-allocation-size] 10 | int *buf = create_buffer(42); | ^~~~~~~~~~~~~~~~~ ‘main’: events 1-2 | | 9 | int main (int argc, char **argv) { | | ^~~~ | | | | | (1) entry to ‘main’ | 10 | int *buf = create_buffer(42); | | ~~~~~~~~~~~~~~~~~ | | | | | (2) calling ‘create_buffer’ from ‘main’ | +------------> ‘create_buffer’: events 3-4 | | 4 | void *create_buffer(int n) | | ^~~~~~~~~~~~~ | | | | | (3) entry to ‘create_buffer’ | 5 | { | 6 | return malloc(n); | | ~~~~~~~~~ | | | | | (4) allocated 42 bytes here | ‘main’: event 5 <--+ | | 10 | int *buf = create_buffer(42); | | ^~~~~~~~~~~~~~~~~ | | | | | (5) Assigned to ‘int *’ here | For that, the return edge has to be visited to be part of the emission_path. This is currently not the case as the assignment of the to the caller lhs is handled inside pop_frame, which is transitively called from program_state::on_edge of the 'after' node of the callee. I tried to defer the set_value(caller lhs, ) call to the 'before' node after the return edge but failed to do elegantly. My last try is in the patch commented out with // FIXME. My main problem is that I can not pop the frame and later get the return value easily. Deferring the whole pop_frame to the before node breaks the assumptions inside exploded_graph::get_or_create_node. I don't know what's the best/elegant way of solving this. Is a solution to attach the return svalue to the return edge and then use it later in the PK_BEFORE_SUPERNODE? Signed-off-by: Tim Lange --- gcc/analyzer/analyzer.opt | 4 + gcc/analyzer/checker-path.cc | 12 +- gcc/analyzer/checker-path.h | 2 +- gcc/analyzer/engine.cc | 12 + gcc/analyzer/pending-diagnostic.h | 21 ++ gcc/analyzer/region-model.cc | 322 ++++++++++++++++++ gcc/analyzer/region-model.h | 4 + .../gcc.dg/analyzer/allocation-size-1.c | 63 ++++ .../gcc.dg/analyzer/allocation-size-2.c | 44 +++ .../gcc.dg/analyzer/allocation-size-3.c | 48 +++ .../gcc.dg/analyzer/allocation-size-4.c | 92 +++++ gcc/testsuite/gcc.dg/analyzer/attr-malloc-6.c | 2 + gcc/testsuite/gcc.dg/analyzer/malloc-4.c | 2 +- gcc/testsuite/gcc.dg/analyzer/pr96639.c | 2 + 14 files changed, 627 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt index 4aea52d3a87..f213989e0bb 100644 --- a/gcc/analyzer/analyzer.opt +++ b/gcc/analyzer/analyzer.opt @@ -78,6 +78,10 @@ Wanalyzer-malloc-leak Common Var(warn_analyzer_malloc_leak) Init(1) Warning Warn about code paths in which a heap-allocated pointer leaks. +Wanalyzer-allocation-size +Common Var(warn_analyzer_allocation_size) Init(1) Warning +Warn about code paths in which a buffer is assigned to a incompatible type. + Wanalyzer-mismatching-deallocation Common Var(warn_analyzer_mismatching_deallocation) Init(1) Warning Warn about code paths in which the wrong deallocation function is called. diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc index 0133dc94137..4ad75e636c1 100644 --- a/gcc/analyzer/checker-path.cc +++ b/gcc/analyzer/checker-path.cc @@ -302,8 +302,18 @@ region_creation_event::region_creation_event (const region *reg, region_creation_event. */ label_text -region_creation_event::get_desc (bool) const +region_creation_event::get_desc (bool can_colorize) const { + if (m_pending_diagnostic) + { + label_text custom_desc + = m_pending_diagnostic->describe_region_creation_event + (evdesc::region_creation(can_colorize, m_reg)); + if (custom_desc.m_buffer) + return custom_desc; + } + + switch (m_reg->get_memory_space ()) { default: diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h index 24decf5ce3d..8e48d8a07ab 100644 --- a/gcc/analyzer/checker-path.h +++ b/gcc/analyzer/checker-path.h @@ -219,7 +219,7 @@ public: region_creation_event (const region *reg, location_t loc, tree fndecl, int depth); - label_text get_desc (bool) const final override; + label_text get_desc (bool can_colorize) const final override; private: const region *m_reg; diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 7237cc1a1ca..5bf8697d8f7 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -3740,6 +3740,18 @@ exploded_graph::process_node (exploded_node *node) program_state::detect_leaks (state, next_state, NULL, get_ext_state (), &ctxt); } + // FIX ME: Other way than calling return again here? + // else + // { + // const supernode *snode = point.get_supernode (); + // if (snode->m_returning_call) + // { + // impl_region_model_context ctxt (*this, node, + // &state, &next_state, + // &uncertainty, NULL, snode->m_returning_call); + // next_state.m_region_model->set_return (snode->m_returning_call, &ctxt); + // } + // } program_point next_point (point.get_next ()); exploded_node *next = get_or_create_node (next_point, next_state, node); diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h index 9e1c656bf0a..3f79444ef40 100644 --- a/gcc/analyzer/pending-diagnostic.h +++ b/gcc/analyzer/pending-diagnostic.h @@ -1,3 +1,4 @@ + /* Classes for analyzer diagnostics. Copyright (C) 2019-2022 Free Software Foundation, Inc. Contributed by David Malcolm . @@ -58,6 +59,17 @@ struct event_desc bool m_colorize; }; +/* For use by pending_diagnostic::describe_region_creation. */ + +struct region_creation : public event_desc +{ + region_creation (bool colorize, const region *reg) + : event_desc (colorize), m_reg(reg) + {} + + const region *m_reg; +}; + /* For use by pending_diagnostic::describe_state_change. */ struct state_change : public event_desc @@ -215,6 +227,15 @@ class pending_diagnostic description; NULL otherwise (falling back on a more generic description). */ + /* Precision-of-wording vfunc for describing a region creation event + triggered by the mark_interesting_stuff vfunc. */ + virtual label_text + describe_region_creation_event (const evdesc::region_creation &) + { + /* Default no-op implementation. */ + return label_text (); + } + /* Precision-of-wording vfunc for describing a critical state change within the diagnostic_path. diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 6b49719d521..acb8bd1bfca 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -73,6 +73,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-operands.h" #include "ssa-iterators.h" #include "calls.h" +#include "print-tree.h" #if ENABLE_ANALYZER @@ -653,6 +654,66 @@ private: tree m_count_cst; }; +/* Concrete subclass for casts of pointers that lead to trailing bytes. */ + +class dubious_allocation_size +: public pending_diagnostic_subclass +{ +public: + dubious_allocation_size (const region *lhs, const region *rhs, + const svalue *capacity) + : m_lhs(lhs), m_rhs(rhs), m_capacity(capacity) {} + + const char *get_kind () const final override + { + return "dubious_allocation_size"; + } + + bool operator== (const dubious_allocation_size &other) const + { + return m_lhs == other.m_lhs && m_rhs == other.m_rhs;; + } + + int get_controlling_option () const final override + { + return OPT_Wanalyzer_allocation_size; + } + + bool emit (rich_location *rich_loc) final override + { + diagnostic_metadata m; + m.add_cwe (131); + return warning_meta (rich_loc, m, get_controlling_option (), + "Allocated buffer size is not a multiple of the pointee's size"); + } + + label_text + describe_region_creation_event (const evdesc::region_creation &ev) final override + { + // TODO: better way to print the capacity + return ev.formatted_print ("allocated %s here", + m_capacity->get_desc(true).m_buffer); + } + + label_text describe_final_event (const evdesc::final_event &ev) final override + { + return ev.formatted_print ("Assigned to %qT here", m_lhs->get_type ()); + } + + void mark_interesting_stuff (interesting_t *interest) final override + { + if (m_lhs) + interest->add_region_creation (m_lhs); + if (m_rhs) + interest->add_region_creation (m_rhs); + } + +private: + const region *m_lhs; + const region *m_rhs; + const svalue *m_capacity; +}; + /* If ASSIGN is a stmt that can be modelled via set_value (lhs_reg, SVALUE, CTXT) for some SVALUE, get the SVALUE. @@ -2799,6 +2860,241 @@ region_model::check_region_for_read (const region *src_reg, check_region_access (src_reg, DIR_READ, ctxt); } +/* Returns the trailing bytes on dubious allocation sizes. */ + +static unsigned HOST_WIDE_INT +capacity_compatible_with_type (tree cst, tree pointee_size_tree) +{ + unsigned HOST_WIDE_INT pointee_size = TREE_INT_CST_LOW (pointee_size_tree); + if (pointee_size == 0) + return 0; + unsigned HOST_WIDE_INT alloc_size = TREE_INT_CST_LOW (cst); + + return alloc_size % pointee_size; +} + +/* Visits svalues and checks whether the + size_cst is a operand of the svalue. */ + +class size_visitor : public visitor +{ +public: + size_visitor(tree size_cst, const svalue *sval, constraint_manager *cm) + : m_size_cst(size_cst), m_sval(sval), m_cm(cm) + { + sval->accept(this); + } + + bool get_result() + { + /* The result_set gradually builts from atomtic nodes upwards. If a node is + in the result_set, itself or one/all of its children have an operand that + is a multiple of the size_cst. If the root is inside, the given sval + is valid aka a multiple of the size_cst.*/ + return result_set.contains(m_sval); + } + + void + visit_constant_svalue (const constant_svalue *sval) final override + { + unsigned HOST_WIDE_INT sval_int + = TREE_INT_CST_LOW (sval->get_constant ()); + unsigned HOST_WIDE_INT size_cst_int = TREE_INT_CST_LOW (m_size_cst); + if (size_cst_int == 0 || sval_int % size_cst_int == 0) + result_set.add (sval); + } + + void + visit_unknown_svalue (const unknown_svalue *sval ATTRIBUTE_UNUSED) + final override + { + result_set.add (sval); + } + + void + visit_poisoned_svalue (const poisoned_svalue *sval ATTRIBUTE_UNUSED) + final override + { + result_set.add (sval); + } + + void visit_unaryop_svalue (const unaryop_svalue *sval) + { + const svalue *arg = sval->get_arg (); + arg->accept (this); + if (result_set.contains (arg)) + result_set.add (sval); + } + + void visit_binop_svalue (const binop_svalue *sval) final override + { + const svalue *arg0 = sval->get_arg0 (); + const svalue *arg1 = sval->get_arg1 (); + + arg0->accept (this); + arg1->accept (this); + if (sval->get_op () == MULT_EXPR) + { + if (result_set.contains (arg0) || result_set.contains (arg1)) + result_set.add (sval); + } + else + { + if (result_set.contains (arg0) && result_set.contains (arg1)) + result_set.add (sval); + } + } + + void visit_repeated_svalue (const repeated_svalue *sval) + { + sval->get_inner_svalue ()->accept(this); + if (result_set.contains (sval->get_inner_svalue ())) + result_set.add (sval); + } + + void visit_unmergeable_svalue (const unmergeable_svalue *sval) final override + { + sval->get_arg ()->accept (this); + if (result_set.contains (sval->get_arg ())) + result_set.add (sval); + } + + void visit_widening_svalue (const widening_svalue *sval) final override + { + const svalue *base = sval->get_base_svalue (); + const svalue *iter = sval->get_iter_svalue (); + + base->accept(this); + iter->accept(this); + if (result_set.contains (base) && result_set.contains (iter)) + result_set.add (sval); + } + + void visit_conjured_svalue (const conjured_svalue *sval ATTRIBUTE_UNUSED) + final override + { + if (m_cm->get_equiv_class_by_svalue (sval, NULL)) + result_set.add (sval); + } + + void visit_asm_output_svalue (const asm_output_svalue *sval ATTRIBUTE_UNUSED) + final override + { + // TODO: Should we do something else than assume it could be correct + result_set.add (sval); + } + + void visit_const_fn_result_svalue (const const_fn_result_svalue + *sval ATTRIBUTE_UNUSED) final override + { + // TODO: Should we do something else than assume it could be correct + result_set.add (sval); + } + +private: + tree m_size_cst; + const svalue *m_sval; + constraint_manager *m_cm; + svalue_set result_set; /* Used as a mapping of svalue*->bool. */ +}; + +/* Returns true if there is a constant tree with + the same constant value inside the sval. */ + +static bool +const_operand_in_sval_p (tree type_size_cst, const svalue *sval, + constraint_manager *cm) +{ + size_visitor v(type_size_cst, sval, cm); + // sval->accept(&v); + return v.get_result (); +} + +/* Special handling for structs with "inheritance" or that hold an unbounded + type. Those will be skipped to prevent false positives. */ + +static bool +struct_or_union_with_inheritance_p (tree maybe_struct) +{ + if (RECORD_OR_UNION_TYPE_P (maybe_struct)) + { + tree iter = TYPE_FIELDS (maybe_struct); + if (iter == NULL_TREE) + return false; + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (iter))) + return true; + + tree last_field; + while (iter != NULL_TREE) + { + last_field = iter; + iter = DECL_CHAIN (iter); + } + + if (last_field != NULL_TREE + && COMPLETE_OR_UNBOUND_ARRAY_TYPE_P (TREE_TYPE (last_field))) + return true; + } + return false; +} + +void +region_model::check_region_size (const region *lhs_reg, const svalue *rhs_sval, + region_model_context *ctxt) const +{ + if (!ctxt) + return; + + const region_svalue *reg_sval = dyn_cast (rhs_sval); + if (!reg_sval) + return; + + tree pointer_type = lhs_reg->get_type (); + if (pointer_type == NULL_TREE || !POINTER_TYPE_P (pointer_type)) + return; + + tree pointee_type = TREE_TYPE (pointer_type); + /* void * is always compatible and make sure that the pointee_type actually + has a size, or else size_in_bytes might fail. */ + if (pointee_type == NULL_TREE || VOID_TYPE_P (pointee_type) + || TYPE_SIZE_UNIT (pointee_type) == NULL_TREE) + return; + if (struct_or_union_with_inheritance_p (pointee_type)) + return; + + tree pointee_size_tree = size_in_bytes(pointee_type); + /* The size might be unknown e.g. being a array with n elements + or casting to char * never has any trailing bytes. */ + if (TREE_CODE (pointee_size_tree) != INTEGER_CST + || TREE_INT_CST_LOW (pointee_size_tree) == 1) + return; + + const svalue *capacity = get_capacity (reg_sval->get_pointee ()); + switch (capacity->get_kind ()) + { + case svalue_kind::SK_CONSTANT: + { + const constant_svalue *cap_sval = capacity->dyn_cast_constant_svalue (); + tree cap = cap_sval->get_constant (); + unsigned HOST_WIDE_INT size_diff + = capacity_compatible_with_type (cap, pointee_size_tree); + if (size_diff != 0) + { + ctxt->warn (new dubious_allocation_size (lhs_reg, reg_sval->get_pointee (), capacity)); + } + } + break; + default: + { + if (!const_operand_in_sval_p (pointee_size_tree, capacity, m_constraints)) + { + ctxt->warn (new dubious_allocation_size (lhs_reg, reg_sval->get_pointee (), capacity)); + } + } + break; + } +} + /* Set the value of the region given by LHS_REG to the value given by RHS_SVAL. Use CTXT to report any warnings associated with writing to LHS_REG. */ @@ -2810,6 +3106,8 @@ region_model::set_value (const region *lhs_reg, const svalue *rhs_sval, gcc_assert (lhs_reg); gcc_assert (rhs_sval); + check_region_size(lhs_reg, rhs_sval, ctxt); + check_region_for_write (lhs_reg, ctxt); m_store.set_value (m_mgr->get_store_manager(), lhs_reg, rhs_sval, @@ -3990,6 +4288,30 @@ region_model::pop_frame (tree result_lvalue, unbind_region_and_descendents (frame_reg,POISON_KIND_POPPED_STACK); } +// FIXME: How to call the call_lhs <- on the caller context +// with the call site set as a stmt? +void +region_model::set_return (const gcall *call, region_model_context *ctxt) +{ + if (0) + { + tree lhs = gimple_call_lhs (call); + tree fndecl = gimple_call_fndecl (call); + tree result = DECL_RESULT (fndecl); + const svalue *retval = NULL; + if (result && TREE_TYPE (result) != void_type_node) + retval = get_rvalue (result, ctxt); + + if (lhs && retval) + { + /* Compute result_dst_reg using RESULT_LVALUE *after* popping + the frame, but before poisoning pointers into the old frame. */ + const region *result_dst_reg = get_lvalue (lhs, ctxt); + set_value (result_dst_reg, retval, ctxt); + } + } +} + /* Get the number of frames in this region_model's stack. */ int diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index 1bfa56a8cd2..5dbbf2cd546 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -668,6 +668,8 @@ class region_model void update_for_return_gcall (const gcall *call_stmt, region_model_context *ctxt); + void set_return (const gcall *call_stmt, region_model_context *ctxt); + const region *push_frame (function *fun, const vec *arg_sids, region_model_context *ctxt); const frame_region *get_current_frame () const { return m_current_frame; } @@ -857,6 +859,8 @@ class region_model region_model_context *ctxt) const; void check_region_for_read (const region *src_reg, region_model_context *ctxt) const; + void check_region_size (const region *lhs_reg, const svalue *rhs_sval, + region_model_context *ctxt) const; void check_call_args (const call_details &cd) const; void check_external_function_for_access_attr (const gcall *call, diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c new file mode 100644 index 00000000000..cb3df5516e7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-1.c @@ -0,0 +1,63 @@ +#include + +/* Tests with constant buffer sizes. */ + +void test_1 (void) +{ + short *ptr = malloc (21 * sizeof(short)); + free (ptr); +} + +void test_2 (void) +{ + int *ptr = malloc (21 * sizeof (short)); /* { dg-line malloc2 } */ + free (ptr); + + /* { dg-warning "" "" { target *-*-* } malloc2 } */ + /* { dg-message "" "" { target *-*-* } malloc2 } */ +} + +void test_3 (void) +{ + void *ptr = malloc (21 * sizeof (short)); + short *sptr = (short *)ptr; + free (sptr); +} + +void test_4 (void) +{ + void *ptr = malloc (21 * sizeof (short)); /* { dg-message } */ + int *iptr = (int *)ptr; /* { dg-line assign } */ + free (iptr); + + /* { dg-warning "" "" { target *-*-* } assign } */ + /* { dg-message "" "" { target *-*-* } assign } */ +} + +struct s { + int i; +}; + +void test_5 (void) +{ + struct s *ptr = malloc (5 * sizeof (struct s)); + free (ptr); +} + +void test_6 (void) +{ + long *ptr = malloc (5 * sizeof (struct s)); /* { dg-line malloc6 } */ + free (ptr); + + /* { dg-warning "" "" { target *-*-* } malloc6 } */ + /* { dg-message "" "" { target *-*-* } malloc6 } */ +} + +void test_7 (void) +{ + char buf[2]; + int *ptr = (int *)buf; /* { dg-line malloc7 } */ + + /* { dg-warning "" "" { target *-*-* } malloc7 } */ + /* { dg-message "" "" { target *-*-* } malloc7 } */ +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c new file mode 100644 index 00000000000..a619a786a4e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-2.c @@ -0,0 +1,44 @@ +#include +#include + +/* Tests with symbolic buffer sizes. */ + +void test_1 (void) +{ + int n; + scanf("%i", &n); + short *ptr = malloc (n * sizeof(short)); + free (ptr); +} + +void test_2 (void) +{ + int n; + scanf("%i", &n); + int *ptr = malloc (n * sizeof (short)); /* { dg-line malloc } */ + free (ptr); + + /* { dg-warning "" "" { target *-*-* } malloc } */ + /* { dg-message "" "" { target *-*-* } malloc } */ +} + +void test_3 (void) +{ + int n; + scanf("%i", &n); + void *ptr = malloc (n * sizeof (short)); + short *sptr = (short *)ptr; + free (sptr); +} + +void test_4 (void) +{ + int n; + scanf("%i", &n); + void *ptr = malloc (n * sizeof (short)); /* { dg-message } */ + int *iptr = (int *)ptr; /* { dg-line assign } */ + free (iptr); + + /* { dg-warning "" "" { target *-*-* } assign } */ + /* { dg-message "" "" { target *-*-* } assign } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c new file mode 100644 index 00000000000..dafc0e73c63 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-3.c @@ -0,0 +1,48 @@ +#include +#include + +/* CWE-131 example 5 */ +void test_1(void) +{ + int *id_sequence = (int *) malloc (3); /* { dg-line malloc1 } */ + if (id_sequence == NULL) exit (1); + + id_sequence[0] = 13579; + id_sequence[1] = 24680; + id_sequence[2] = 97531; + + free (id_sequence); + + /* { dg-warning "" "" { target *-*-* } malloc1 } */ + /* { dg-message "" "" { target *-*-* } malloc1 } */ +} + +void test_2(void) +{ + int *ptr = malloc (10 + sizeof(int)); /* { dg-line malloc2 } */ + free (ptr); + + /* { dg-warning "" "" { target *-*-* } malloc2 } */ + /* { dg-message "" "" { target *-*-* } malloc2 } */ +} + +void test_3(void) +{ + int n; + scanf("%i", &n); + int *ptr = malloc (n + sizeof (int)); /* { dg-line malloc3 } */ + free (ptr); + + /* { dg-warning "" "" { target *-*-* } malloc3 } */ + /* { dg-message "" "" { target *-*-* } malloc3 } */ +} + +void test_4(void) +{ + int n; + scanf("%i", &n); + int m; + scanf("%i", &m); + int *ptr = malloc ((n + m) * sizeof (int)); + free (ptr); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c b/gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c new file mode 100644 index 00000000000..32e14bad6ec --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/allocation-size-4.c @@ -0,0 +1,92 @@ +#include +#include +#include + +/* Flow warnings */ + +void *create_buffer(int n) +{ + return malloc(n); +} + +void test_1(void) +{ + // FIXME + int *buf = create_buffer(42); /* { dg-warning "" "" { xfail *-*-* } } */ + free (buf); +} + +void test_2(void) +{ + void *buf = create_buffer(42); /* { dg-message } */ + int *ibuf = buf; /* { dg-line assign2 } */ + free (ibuf); + + /* { dg-warning "" "" { target *-*-* } assign2 } */ + /* { dg-message "" "" { target *-*-* } assign2 } */ +} + +void test_3(void) +{ + void *buf = malloc(42); /* { dg-message } */ + if (buf != NULL) /* { dg-message } */ + { + int *ibuf = buf; /* { dg-line assign3 } */ + free (ibuf); + } + + /* { dg-warning "" "" { target *-*-* } assign3 } */ + /* { dg-message "" "" { target *-*-* } assign3 } */ +} + +void test_4(void) +{ + int n; + scanf("%i", &n); + + int size; + if (n == 0) + size = 1; + else if (n == 1) + size = 10; + else + size = 20; + + int *buf = malloc(size); // Size should be 'unknown' at this point + free (buf); +} + +void test_5(void) +{ + int n; + scanf("%i", &n); + + int size; + if (n == 0) + size = 2; + else + size = 10; + + short *buf = malloc(size); // Size should be widened to 2 and 10, both fit + free (buf); +} + + +void test_6(void) +{ + int n; + scanf("%i", &n); + + int size; + if (n == 0) + size = 1; + else + size = 10; + + short *buf = malloc(size); /* { dg-line malloc6 } */ + free (buf); + + + /* { dg-warning "" "" { target *-*-* } malloc6 } */ + /* { dg-message "" "" { target *-*-* } malloc6 } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/attr-malloc-6.c b/gcc/testsuite/gcc.dg/analyzer/attr-malloc-6.c index bd28107d0d7..8fa6a6eb570 100644 --- a/gcc/testsuite/gcc.dg/analyzer/attr-malloc-6.c +++ b/gcc/testsuite/gcc.dg/analyzer/attr-malloc-6.c @@ -1,7 +1,9 @@ +/* { dg-additional-options -Wno-analyzer-allocation-size } */ /* Adapted from gcc.dg/Wmismatched-dealloc.c. */ #define A(...) __attribute__ ((malloc (__VA_ARGS__))) +struct FILE; typedef struct FILE FILE; typedef __SIZE_TYPE__ size_t; diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-4.c b/gcc/testsuite/gcc.dg/analyzer/malloc-4.c index 908bb28ee50..f9a73c79403 100644 --- a/gcc/testsuite/gcc.dg/analyzer/malloc-4.c +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-4.c @@ -1,4 +1,4 @@ -/* { dg-additional-options "-Wno-incompatible-pointer-types" } */ +/* { dg-additional-options "-Wno-incompatible-pointer-types -Wno-analyzer-allocation-size" } */ #include diff --git a/gcc/testsuite/gcc.dg/analyzer/pr96639.c b/gcc/testsuite/gcc.dg/analyzer/pr96639.c index 02ca3f084a2..6f365c3cb5d 100644 --- a/gcc/testsuite/gcc.dg/analyzer/pr96639.c +++ b/gcc/testsuite/gcc.dg/analyzer/pr96639.c @@ -1,3 +1,5 @@ +/* { dg-additional-options -Wno-analyzer-allocation-size } */ + void *calloc (__SIZE_TYPE__, __SIZE_TYPE__); int -- 2.36.1