From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id C5A423858C33 for ; Fri, 13 Jan 2023 22:54:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C5A423858C33 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1673650472; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=FkeMzotw/MPAcjFTBf0BUyEyxQW/ew2Nhu+8k0AuNF0=; b=dp4fkcKkEecUKhkywFtMQaJPeDFfET5ZSNBo+jUkXZWEWzOGZATvqi3L9rXG+LPDCp+ClW duOiQJGuuRi3pfBJ6sRXGtseu3zJr+N3sRBmqOo5L/ZPZkONZkZbqZIFyN8cSuGLA7bMsV hXHtNlQR0RIpB/ceQubWCxHSN4wRv5k= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-232-C1C4wiBdMjejpRxIxcYmZg-1; Fri, 13 Jan 2023 17:54:31 -0500 X-MC-Unique: C1C4wiBdMjejpRxIxcYmZg-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id EBE7C8828C0 for ; Fri, 13 Jan 2023 22:54:30 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.2.16.103]) by smtp.corp.redhat.com (Postfix) with ESMTP id AFBCC1121314; Fri, 13 Jan 2023 22:54:30 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [committed] analyzer: add heuristics for switch on enum type [PR105273] Date: Fri, 13 Jan 2023 17:54:28 -0500 Message-Id: <20230113225428.380307-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.3 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Assume that switch on an enum doesn't follow an implicit default skipping all cases when all enum values are covered by cases. Fixes various false positives from -Wanalyzer-use-of-uninitialized-value such as this one seen in Doom: p_maputl.c: In function 'P_BoxOnLineSide': p_maputl.c:151:8: warning: use of uninitialized value 'p1' [CWE-457] [-Wanalyzer-use-of-uninitialized-value] 151 | if (p1 == p2) | ^ 'P_BoxOnLineSide': events 1-5 | | 115 | int p1; | | ^~ | | | | | (1) region created on stack here | | (2) capacity: 4 bytes |...... | 118 | switch (ld->slopetype) | | ~~~~~~ | | | | | (3) following 'default:' branch... |...... | 151 | if (p1 == p2) | | ~ | | | | | (4) ...to here | | (5) use of uninitialized value 'p1' here | where "ld->slopetype" is a "slopetype_t" enum, and for every value of that enum the switch has a case that initializes "p1". Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r13-5159-gccd4df81aa6537. gcc/analyzer/ChangeLog: PR analyzer/105273 * region-model.cc (has_nondefault_case_for_value_p): New. (has_nondefault_cases_for_all_enum_values_p): New. (region_model::apply_constraints_for_gswitch): Skip implicitly-created "default" when switching on an enum and all enum values have non-default cases. (rejected_default_case::dump_to_pp): New. * region-model.h (region_model_context::possibly_tainted_p): New decl. (class rejected_default_case): New. * sm-taint.cc (region_model_context::possibly_tainted_p): New. * supergraph.cc (switch_cfg_superedge::dump_label_to_pp): Dump when implicitly_created_default_p. (switch_cfg_superedge::implicitly_created_default_p): New. * supergraph.h (switch_cfg_superedge::implicitly_created_default_p): New decl. gcc/testsuite/ChangeLog: PR analyzer/105273 * gcc.dg/analyzer/switch-enum-1.c: New test. * gcc.dg/analyzer/switch-enum-2.c: New test. * gcc.dg/analyzer/switch-enum-pr105273-git-vreportf-2.c: New test. * gcc.dg/analyzer/switch-enum-taint-1.c: New test. * gcc.dg/analyzer/switch-wrong-enum.c: New test. * gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_floor.c: New test. * gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_maputl.c: New test. * gcc.dg/analyzer/torture/switch-enum-pr105273-git-vreportf-1.c: New test. Signed-off-by: David Malcolm --- gcc/analyzer/region-model.cc | 104 +++++++++++++- gcc/analyzer/region-model.h | 12 ++ gcc/analyzer/sm-taint.cc | 25 ++++ gcc/analyzer/supergraph.cc | 22 +++ gcc/analyzer/supergraph.h | 2 + gcc/testsuite/gcc.dg/analyzer/switch-enum-1.c | 136 ++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/switch-enum-2.c | 132 +++++++++++++++++ .../switch-enum-pr105273-git-vreportf-2.c | 40 ++++++ .../gcc.dg/analyzer/switch-enum-taint-1.c | 102 +++++++++++++ .../gcc.dg/analyzer/switch-wrong-enum.c | 27 ++++ .../switch-enum-pr105273-doom-p_floor.c | 89 ++++++++++++ .../switch-enum-pr105273-doom-p_maputl.c | 86 +++++++++++ .../switch-enum-pr105273-git-vreportf-1.c | 35 +++++ 13 files changed, 810 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/analyzer/switch-enum-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/switch-enum-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/switch-enum-pr105273-git-vreportf-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/switch-enum-taint-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/switch-wrong-enum.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_floor.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_maputl.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-git-vreportf-1.c diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 2e59fbaadd7..6a3a1b474bf 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -4341,6 +4341,72 @@ region_model::apply_constraints_for_gcond (const cfg_superedge &sedge, return add_constraint (lhs, op, rhs, ctxt, out); } +/* Return true iff SWITCH_STMT has a non-default label that contains + INT_CST. */ + +static bool +has_nondefault_case_for_value_p (const gswitch *switch_stmt, tree int_cst) +{ + /* We expect the initial label to be the default; skip it. */ + gcc_assert (CASE_LOW (gimple_switch_label (switch_stmt, 0)) == NULL); + unsigned min_idx = 1; + unsigned max_idx = gimple_switch_num_labels (switch_stmt) - 1; + + /* Binary search: try to find the label containing INT_CST. + This requires the cases to be sorted by CASE_LOW (done by the + gimplifier). */ + while (max_idx >= min_idx) + { + unsigned case_idx = (min_idx + max_idx) / 2; + tree label = gimple_switch_label (switch_stmt, case_idx); + tree low = CASE_LOW (label); + gcc_assert (low); + tree high = CASE_HIGH (label); + if (!high) + high = low; + if (tree_int_cst_compare (int_cst, low) < 0) + { + /* INT_CST is below the range of this label. */ + gcc_assert (case_idx > 0); + max_idx = case_idx - 1; + } + else if (tree_int_cst_compare (int_cst, high) > 0) + { + /* INT_CST is above the range of this case. */ + min_idx = case_idx + 1; + } + else + /* This case contains INT_CST. */ + return true; + } + /* Not found. */ + return false; +} + +/* Return true iff SWITCH_STMT (which must be on an enum value) + has nondefault cases handling all values in the enum. */ + +static bool +has_nondefault_cases_for_all_enum_values_p (const gswitch *switch_stmt) +{ + gcc_assert (switch_stmt); + tree type = TREE_TYPE (gimple_switch_index (switch_stmt)); + gcc_assert (TREE_CODE (type) == ENUMERAL_TYPE); + + for (tree enum_val_iter = TYPE_VALUES (type); + enum_val_iter; + enum_val_iter = TREE_CHAIN (enum_val_iter)) + { + tree enum_val = TREE_VALUE (enum_val_iter); + gcc_assert (TREE_CODE (enum_val) == CONST_DECL); + gcc_assert (TREE_CODE (DECL_INITIAL (enum_val)) == INTEGER_CST); + if (!has_nondefault_case_for_value_p (switch_stmt, + DECL_INITIAL (enum_val))) + return false; + } + return true; +} + /* Given an EDGE guarded by SWITCH_STMT, determine appropriate constraints for the edge to be taken. @@ -4357,11 +4423,37 @@ region_model::apply_constraints_for_gswitch (const switch_cfg_superedge &edge, region_model_context *ctxt, rejected_constraint **out) { + tree index = gimple_switch_index (switch_stmt); + const svalue *index_sval = get_rvalue (index, ctxt); + + /* If we're switching based on an enum type, assume that the user is only + working with values from the enum. Hence if this is an + implicitly-created "default", assume it doesn't get followed. + This fixes numerous "uninitialized" false positives where we otherwise + consider jumping past the initialization cases. */ + + if (/* Don't check during feasibility-checking (when ctxt is NULL). */ + ctxt + /* Must be an enum value. */ + && index_sval->get_type () + && TREE_CODE (TREE_TYPE (index)) == ENUMERAL_TYPE + && TREE_CODE (index_sval->get_type ()) == ENUMERAL_TYPE + /* If we have a constant, then we can check it directly. */ + && index_sval->get_kind () != SK_CONSTANT + && edge.implicitly_created_default_p () + && has_nondefault_cases_for_all_enum_values_p (switch_stmt) + /* Don't do this if there's a chance that the index is + attacker-controlled. */ + && !ctxt->possibly_tainted_p (index_sval)) + { + if (out) + *out = new rejected_default_case (*this); + return false; + } + bounded_ranges_manager *ranges_mgr = get_range_manager (); const bounded_ranges *all_cases_ranges = ranges_mgr->get_or_create_ranges_for_switch (&edge, switch_stmt); - tree index = gimple_switch_index (switch_stmt); - const svalue *index_sval = get_rvalue (index, ctxt); bool sat = m_constraints->add_bounded_ranges (index_sval, all_cases_ranges); if (!sat && out) *out = new rejected_ranges_constraint (*this, index, all_cases_ranges); @@ -5686,6 +5778,14 @@ rejected_op_constraint::dump_to_pp (pretty_printer *pp) const rhs_sval->dump_to_pp (pp, true); } +/* class rejected_default_case : public rejected_constraint. */ + +void +rejected_default_case::dump_to_pp (pretty_printer *pp) const +{ + pp_string (pp, "implicit default for enum"); +} + /* class rejected_ranges_constraint : public rejected_constraint. */ void diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index e8767e5ed41..4e1a5c679ec 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -703,6 +703,8 @@ class region_model_context return get_state_map_by_name ("taint", out_smap, out_sm, out_sm_idx, NULL); } + bool possibly_tainted_p (const svalue *sval); + /* Get the current statement, if any. */ virtual const gimple *get_stmt () const = 0; }; @@ -1010,6 +1012,16 @@ public: tree m_rhs; }; +class rejected_default_case : public rejected_constraint +{ +public: + rejected_default_case (const region_model &model) + : rejected_constraint (model) + {} + + void dump_to_pp (pretty_printer *pp) const final override; +}; + class rejected_ranges_constraint : public rejected_constraint { public: diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc index a2b442a4ef2..3a619b12d5c 100644 --- a/gcc/analyzer/sm-taint.cc +++ b/gcc/analyzer/sm-taint.cc @@ -1549,6 +1549,31 @@ region_model::mark_as_tainted (const svalue *sval, smap->set_state (this, sval, taint_sm.m_tainted, NULL, *ext_state); } +/* Return true if SVAL could possibly be attacker-controlled. */ + +bool +region_model_context::possibly_tainted_p (const svalue *sval) +{ + sm_state_map *smap; + const state_machine *sm; + unsigned sm_idx; + if (!get_taint_map (&smap, &sm, &sm_idx)) + return false; + + const taint_state_machine &taint_sm = (const taint_state_machine &)*sm; + + const extrinsic_state *ext_state = get_ext_state (); + if (!ext_state) + return false; + + const state_machine::state_t state = smap->get_state (sval, *ext_state); + gcc_assert (state); + + return (state == taint_sm.m_tainted + || state == taint_sm.m_has_lb + || state == taint_sm.m_has_ub); +} + } // namespace ana #endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/supergraph.cc b/gcc/analyzer/supergraph.cc index 8195fe8e7f8..c0bb2c6af94 100644 --- a/gcc/analyzer/supergraph.cc +++ b/gcc/analyzer/supergraph.cc @@ -1153,9 +1153,31 @@ switch_cfg_superedge::dump_label_to_pp (pretty_printer *pp, pp_printf (pp, "default"); } pp_character (pp, '}'); + if (implicitly_created_default_p ()) + { + pp_string (pp, " IMPLICITLY CREATED"); + } } } +/* Return true iff this edge is purely for an implicitly-created "default". */ + +bool +switch_cfg_superedge::implicitly_created_default_p () const +{ + if (m_case_labels.length () != 1) + return false; + + tree case_label = m_case_labels[0]; + gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); + if (CASE_LOW (case_label)) + return false; + + /* We have a single "default" case. + Assume that it was implicitly created if it has UNKNOWN_LOCATION. */ + return EXPR_LOCATION (case_label) == UNKNOWN_LOCATION; +} + /* Implementation of superedge::dump_label_to_pp for interprocedural superedges. */ diff --git a/gcc/analyzer/supergraph.h b/gcc/analyzer/supergraph.h index f66058cc3ec..d359e95b12d 100644 --- a/gcc/analyzer/supergraph.h +++ b/gcc/analyzer/supergraph.h @@ -570,6 +570,8 @@ class switch_cfg_superedge : public cfg_superedge { const vec &get_case_labels () const { return m_case_labels; } + bool implicitly_created_default_p () const; + private: auto_vec m_case_labels; }; diff --git a/gcc/testsuite/gcc.dg/analyzer/switch-enum-1.c b/gcc/testsuite/gcc.dg/analyzer/switch-enum-1.c new file mode 100644 index 00000000000..7cd871c894e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/switch-enum-1.c @@ -0,0 +1,136 @@ +#include "analyzer-decls.h" + +/* Verify the handling of "switch (enum_value)". */ + +enum e +{ + E_VAL0, + E_VAL1, + E_VAL2 +}; + +/* Verify that we assume that "switch (enum)" doesn't follow implicit + "default" if all enum values have cases */ + +int test_all_values_covered_implicit_default_1 (enum e x) +{ + switch (x) + { + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + case E_VAL2: + return 1945; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ +} + +int test_all_values_covered_implicit_default_2 (enum e x) +{ + int result; + switch (x) + { + case E_VAL0: + result = 1066; + break; + case E_VAL1: + result = 1776; + break; + case E_VAL2: + result = 1945; + break; + } + return result; /* { dg-bogus "uninitialized" } */ +} + +/* Verify that we consider paths that use the implicit default when not + all enum values are covered by cases. */ + +int test_missing_values_implicit_default_1 (enum e x) +{ + switch (x) /* { dg-message "following 'default:' branch" } */ + { + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + } + __analyzer_dump_path (); /* { dg-message "path" } */ + return 0; +} + +int test_missing_values_implicit_default_2 (enum e x) +{ + int result; + switch (x) /* { dg-message "following 'default:' branch" } */ + { + case E_VAL0: + result = 1066; + break; + case E_VAL1: + result = 1776; + break; + } + return result; /* { dg-warning "uninitialized" } */ +} + +/* Verify that explicit "default" isn't rejected. */ + +int test_all_values_covered_explicit_default_1 (enum e x) +{ + switch (x) + { + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + case E_VAL2: + return 1945; + default: + __analyzer_dump_path (); /* { dg-message "path" } */ + return 0; + } +} + +int test_missing_values_explicit_default_1 (enum e x) +{ + switch (x) + { + default: + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ + return 0; +} + +int test_missing_values_explicit_default_2 (enum e x) +{ + switch (x) + { + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + default: + __analyzer_dump_path (); /* { dg-message "path" } */ + return 1945; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ + return 0; +} + +int test_just_default (enum e x) +{ + switch (x) + { + default: + __analyzer_dump_path (); /* { dg-message "path" } */ + return 42; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/switch-enum-2.c b/gcc/testsuite/gcc.dg/analyzer/switch-enum-2.c new file mode 100644 index 00000000000..c3c7ae38aa7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/switch-enum-2.c @@ -0,0 +1,132 @@ +#include "analyzer-decls.h" + +/* Verify the handling of "switch (enum_value)". */ + +enum e +{ + E_VAL0, + E_VAL1, + E_VAL2, + + E_VAL10 = 10, + E_VAL11 = 11, + E_VAL12 = 12, + + E_VAL20 = 20, + E_VAL21 = 21, + E_VAL22 = 22 +}; + +/* Verify that we assume that "switch (enum)" doesn't follow implicit + "default" if all enum values have cases */ + +int test_all_values_covered_implicit_default_1 (enum e x) +{ + switch (x) + { + case E_VAL0...E_VAL2: + return 1066; + case E_VAL10...E_VAL12: + return 1776; + case E_VAL20...E_VAL22: + return 1945; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ +} + +int test_all_values_covered_implicit_default_2 (enum e x) +{ + int result; + switch (x) + { + case E_VAL0...E_VAL2: + result = 1066; + break; + case E_VAL10...E_VAL12: + result = 1776; + break; + case E_VAL20...E_VAL22: + result = 1945; + break; + } + return result; /* { dg-bogus "uninitialized" } */ +} + +/* Verify that we consider paths that use the implicit default when not + all enum values are covered by cases. */ + +int test_missing_values_implicit_default_1 (enum e x) +{ + switch (x) /* { dg-message "following 'default:' branch" } */ + { + case E_VAL0...E_VAL2: + return 1066; + case E_VAL10...E_VAL12: + return 1776; + } + __analyzer_dump_path (); /* { dg-message "path" } */ + return 0; +} + +int test_missing_values_implicit_default_2 (enum e x) +{ + int result; + switch (x) /* { dg-message "following 'default:' branch" } */ + { + case E_VAL0...E_VAL2: + result = 1066; + break; + case E_VAL10...E_VAL12: + result = 1776; + break; + } + return result; /* { dg-warning "uninitialized" } */ +} + +/* Verify that explicit "default" isn't rejected. */ + +int test_all_values_covered_explicit_default_1 (enum e x) +{ + switch (x) + { + case E_VAL0...E_VAL2: + return 1066; + case E_VAL10...E_VAL12: + return 1776; + case E_VAL20...E_VAL22: + return 1945; + default: + __analyzer_dump_path (); /* { dg-message "path" } */ + return 0; + } +} + +int test_missing_values_explicit_default_1 (enum e x) +{ + switch (x) + { + default: + case E_VAL0...E_VAL2: + return 1066; + case E_VAL10...E_VAL12: + return 1776; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ + return 0; +} + +int test_missing_values_explicit_default_2 (enum e x) +{ + switch (x) + { + case E_VAL0...E_VAL2: + return 1066; + case E_VAL10...E_VAL12: + return 1776; + default: + __analyzer_dump_path (); /* { dg-message "path" } */ + return 1945; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/switch-enum-pr105273-git-vreportf-2.c b/gcc/testsuite/gcc.dg/analyzer/switch-enum-pr105273-git-vreportf-2.c new file mode 100644 index 00000000000..336222759e3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/switch-enum-pr105273-git-vreportf-2.c @@ -0,0 +1,40 @@ +/* Currently the warning only fires at -O0 + (needs to inline the call without optimizing the + implicit default of the switch). */ + +/* { dg-additional-options "-O0" } */ + +typedef __SIZE_TYPE__ size_t; +int snprintf(char *str, size_t size, const char *format, ...); + +enum usage_kind { + USAGE_ERROR, + USAGE_BUG, +}; + +static void __analyzer_vreportf(enum usage_kind kind) +{ + char buf[256]; + const char *pfx; + + switch (kind) { /* { dg-message "following 'default:' branch" } */ + case USAGE_ERROR: + pfx = "error: "; + break; + case USAGE_BUG: + pfx = "BUG: "; + break; + } + + if (kind == USAGE_BUG) + snprintf(buf, sizeof(buf), "%s%s:%d: ", pfx, "file", 123); + else + snprintf(buf, sizeof(buf), "%s", pfx); /* { dg-warning "uninitialized" } */ +} + +int main(void) +{ + __analyzer_vreportf(42); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/switch-enum-taint-1.c b/gcc/testsuite/gcc.dg/analyzer/switch-enum-taint-1.c new file mode 100644 index 00000000000..db3bb5b4947 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/switch-enum-taint-1.c @@ -0,0 +1,102 @@ +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +#include "analyzer-decls.h" + +/* Verify the handling of "switch (enum_value)". */ + +enum e +{ + E_VAL0, + E_VAL1, + E_VAL2 +}; + +/* Verify that we consider that "switch (enum)" could follow implicit + "default" even when all enum values have cases if the value is + attacker-controlled. */ + +int __attribute__((tainted_args)) +test_all_values_covered_implicit_default_1 (enum e x) +{ + switch (x) /* { dg-message "following 'default:' branch" } */ + { + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + case E_VAL2: + return 1945; + } + __analyzer_dump_path (); /* { dg-message "path" } */ +} + +int __attribute__((tainted_args)) +test_all_values_covered_implicit_default_2 (enum e x) +{ + int result; + switch (x) /* { dg-message "following 'default:' branch" } */ + { + case E_VAL0: + result = 1066; + break; + case E_VAL1: + result = 1776; + break; + case E_VAL2: + result = 1945; + break; + } + return result; /* { dg-message "uninitialized" } */ +} + +/* Verify that explicit "default" isn't rejected. */ + +int __attribute__((tainted_args)) +test_all_values_covered_explicit_default_1 (enum e x) +{ + switch (x) + { + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + case E_VAL2: + return 1945; + default: + __analyzer_dump_path (); /* { dg-message "path" } */ + return 0; + } +} + +int __attribute__((tainted_args)) +test_missing_values_explicit_default_1 (enum e x) +{ + switch (x) + { + default: + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ + return 0; +} + +int __attribute__((tainted_args)) +test_missing_values_explicit_default_2 (enum e x) +{ + switch (x) + { + case E_VAL0: + return 1066; + case E_VAL1: + return 1776; + default: + __analyzer_dump_path (); /* { dg-message "path" } */ + return 1945; + } + __analyzer_dump_path (); /* { dg-bogus "path" } */ + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/switch-wrong-enum.c b/gcc/testsuite/gcc.dg/analyzer/switch-wrong-enum.c new file mode 100644 index 00000000000..0de25168a65 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/switch-wrong-enum.c @@ -0,0 +1,27 @@ +#include "analyzer-decls.h" + +enum color +{ + RED, + GREEN, + BLUE +}; + +enum fruit +{ + APPLE, + BANANA +}; + +int test_wrong_enum (enum color x) +{ + switch (x) + { + case APPLE: + return 1066; + case BANANA: + return 1776; + } + __analyzer_dump_path (); /* { dg-message "path" } */ + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_floor.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_floor.c new file mode 100644 index 00000000000..100586cdc88 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_floor.c @@ -0,0 +1,89 @@ +/* Reduced from linuxdoom-1.10's p_floor.c (GPLv2). */ + +#define FRACBITS 16 +#define FRACUNIT (1<= 0) + { + sec = §ors[secnum]; + + /* [...snip...] */ + + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + + /* [...snip...] */ + + switch(type) + { + case build8: + speed = FLOORSPEED/4; + stairsize = 8*FRACUNIT; + break; + case turbo16: + speed = FLOORSPEED*4; + stairsize = 16*FRACUNIT; + break; + } + floor->speed = speed; /* { dg-bogus "use of uninitialized value 'speed'" } */ + height = sec->floorheight + stairsize; /* { dg-bogus "use of uninitialized value 'stairsize'" } */ + floor->floordestheight = height; + + /* [...snip...] */ + } + return rtn; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_maputl.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_maputl.c new file mode 100644 index 00000000000..f686c731eff --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-doom-p_maputl.c @@ -0,0 +1,86 @@ +/* Reduced from linuxdoom-1.10's p_maputl.c (GPLv2). */ + +typedef int fixed_t; + +enum +{ + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; + +typedef struct +{ + fixed_t x; + fixed_t y; +} vertex_t; + +typedef enum +{ + ST_HORIZONTAL, + ST_VERTICAL, + ST_POSITIVE, + ST_NEGATIVE +} slopetype_t; + +typedef struct line_s +{ + vertex_t* v1; + /* [...snip...] */ + + fixed_t dx; + fixed_t dy; + + /* [...snip...] */ + slopetype_t slopetype; + /* [...snip...] */ +} line_t; + +int P_PointOnLineSide (fixed_t x, fixed_t y, line_t* line); + +int +P_BoxOnLineSide +( fixed_t* tmbox, + line_t* ld ) +{ + int p1; + int p2; + + switch (ld->slopetype) + { + case ST_HORIZONTAL: + p1 = tmbox[BOXTOP] > ld->v1->y; + p2 = tmbox[BOXBOTTOM] > ld->v1->y; + if (ld->dx < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_VERTICAL: + p1 = tmbox[BOXRIGHT] < ld->v1->x; + p2 = tmbox[BOXLEFT] < ld->v1->x; + if (ld->dy < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_POSITIVE: + p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld); + p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); + break; + + case ST_NEGATIVE: + p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld); + p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); + break; + } + + if (p1 == p2) /* { dg-bogus "use of uninitialized value" } */ + return p1; /* { dg-bogus "use of uninitialized value" } */ + return -1; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-git-vreportf-1.c b/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-git-vreportf-1.c new file mode 100644 index 00000000000..13e1ad289b2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/switch-enum-pr105273-git-vreportf-1.c @@ -0,0 +1,35 @@ +typedef __SIZE_TYPE__ size_t; +int snprintf(char *str, size_t size, const char *format, ...); + +enum usage_kind { + USAGE_ERROR, + USAGE_BUG, +}; + +static void __analyzer_vreportf(enum usage_kind kind) +{ + char buf[256]; + const char *pfx; + + switch (kind) { + case USAGE_ERROR: + pfx = "error: "; + break; + case USAGE_BUG: + pfx = "BUG: "; + break; + } + + if (kind == USAGE_BUG) + snprintf(buf, sizeof(buf), "%s%s:%d: ", pfx, "file", 123); + else + snprintf(buf, sizeof(buf), "%s", pfx); +} + +int main(void) +{ + __analyzer_vreportf(USAGE_ERROR); + __analyzer_vreportf(USAGE_BUG); + + return 0; +} -- 2.26.3