Révision | d777b38cde91a87f2345dcd13901862a9513562a (tree) |
---|---|
l'heure | 2022-11-14 07:53:23 |
Auteur | David Malcolm <dmalcolm@redh...> |
Commiter | David Malcolm |
analyzer: new warning: -Wanalyzer-tainted-assertion [PR106235]
This patch adds a new -Wanalyzer-tainted-assertion warning to
-fanalyzer's "taint" mode (which also requires -fanalyzer-checker=taint).
It complains about attacker-controlled values being used in assertions,
or in any expression affecting control flow that guards a "noreturn"
function. As noted in the docs part of the patch, in such cases:
For example, given:
#include <assert.h>
int attribute((tainted_args))
test_tainted_assert (int n)
{
}
compiling with
gives:
t.c: In function 'test_tainted_assert':
t.c:6:3: warning: use of attacked-controlled value in condition for assertion [CWE-617] [-Wanalyzer-tainted-assertion]
The testcases have various examples for BUG and BUG_ON from the
Linux kernel; there, the diagnostic treats "panic" as an assertion
failure handler, due to 'attribute((noreturn))'.
gcc/analyzer/ChangeLog:
PR analyzer/106235
* analyzer.opt (Wanalyzer-tainted-assertion): New.
* checker-path.cc (checker_path::fixup_locations): Pass false to
pending_diagnostic::fixup_location.
* diagnostic-manager.cc (get_emission_location): Pass true to
pending_diagnostic::fixup_location.
* pending-diagnostic.cc (pending_diagnostic::fixup_location): Add
bool param.
* pending-diagnostic.h (pending_diagnostic::fixup_location): Add
bool param to decl.
* sm-taint.cc (taint_state_machine::m_tainted_control_flow): New.
(taint_diagnostic::describe_state_change): Drop "final".
(class tainted_assertion): New.
(taint_state_machine::taint_state_machine): Initialize
m_tainted_control_flow.
(taint_state_machine::alt_get_inherited_state): Support
comparisons being tainted, based on their arguments.
(is_assertion_failure_handler_p): New.
(taint_state_machine::on_stmt): Complain about calls to assertion
failure handlers guarded by an attacker-controller conditional.
Detect attacker-controlled gcond conditionals and gswitch index
values.
(taint_state_machine::check_control_flow_arg_for_taint): New.
gcc/ChangeLog:
PR analyzer/106235
* doc/gcc/gcc-command-options/option-summary.rst: Add
-Wno-analyzer-tainted-assertion.
* doc/gcc/gcc-command-options/options-that-control-static-analysis.rst:
Add -Wno-analyzer-tainted-assertion.
gcc/testsuite/ChangeLog:
PR analyzer/106235
* gcc.dg/analyzer/taint-assert-BUG_ON.c: New test.
* gcc.dg/analyzer/taint-assert-macro-expansion.c: New test.
* gcc.dg/analyzer/taint-assert.c: New test.
* gcc.dg/analyzer/taint-assert-system-header.c: New test.
* gcc.dg/analyzer/test-assert.h: New header.
* gcc.dg/plugin/analyzer_gil_plugin.c
(gil_diagnostic::fixup_location): Add bool param.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
@@ -174,6 +174,10 @@ Wanalyzer-tainted-array-index | ||
174 | 174 | Common Var(warn_analyzer_tainted_array_index) Init(1) Warning |
175 | 175 | Warn about code paths in which an unsanitized value is used as an array index. |
176 | 176 | |
177 | +Wanalyzer-tainted-assertion | |
178 | +Common Var(warn_analyzer_tainted_assertion) Init(1) Warning | |
179 | +Warn about code paths in which an 'assert()' is made involving an unsanitized value. | |
180 | + | |
177 | 181 | Wanalyzer-tainted-divisor |
178 | 182 | Common Var(warn_analyzer_tainted_divisor) Init(1) Warning |
179 | 183 | Warn about code paths in which an unsanitized value is used as a divisor. |
@@ -1316,7 +1316,7 @@ void | ||
1316 | 1316 | checker_path::fixup_locations (pending_diagnostic *pd) |
1317 | 1317 | { |
1318 | 1318 | for (checker_event *e : m_events) |
1319 | - e->set_location (pd->fixup_location (e->get_location ())); | |
1319 | + e->set_location (pd->fixup_location (e->get_location (), false)); | |
1320 | 1320 | } |
1321 | 1321 | |
1322 | 1322 | /* Return true if there is a (start_cfg_edge_event, end_cfg_edge_event) pair |
@@ -933,7 +933,7 @@ get_emission_location (const gimple *stmt, function *fun, | ||
933 | 933 | location_t loc = get_stmt_location (stmt, fun); |
934 | 934 | |
935 | 935 | /* Allow the pending_diagnostic to fix up the location. */ |
936 | - loc = pd.fixup_location (loc); | |
936 | + loc = pd.fixup_location (loc, true); | |
937 | 937 | |
938 | 938 | return loc; |
939 | 939 | } |
@@ -153,7 +153,7 @@ fixup_location_in_macro_p (cpp_hashnode *macro) | ||
153 | 153 | Don't unwind inside macros for which fixup_location_in_macro_p is true. */ |
154 | 154 | |
155 | 155 | location_t |
156 | -pending_diagnostic::fixup_location (location_t loc) const | |
156 | +pending_diagnostic::fixup_location (location_t loc, bool) const | |
157 | 157 | { |
158 | 158 | if (linemap_location_from_macro_expansion_p (line_table, loc)) |
159 | 159 | { |
@@ -214,10 +214,10 @@ class pending_diagnostic | ||
214 | 214 | diagnostic deduplication. */ |
215 | 215 | static bool same_tree_p (tree t1, tree t2); |
216 | 216 | |
217 | - /* A vfunc for fixing up locations (both the primary location for the | |
218 | - diagnostic, and for events in their paths), e.g. to avoid unwinding | |
219 | - inside specific macros. */ | |
220 | - virtual location_t fixup_location (location_t loc) const; | |
217 | + /* Vfunc for fixing up locations, e.g. to avoid unwinding | |
218 | + inside specific macros. PRIMARY is true for the primary location | |
219 | + for the diagnostic, and FALSE for events in their paths. */ | |
220 | + virtual location_t fixup_location (location_t loc, bool primary) const; | |
221 | 221 | |
222 | 222 | /* For greatest precision-of-wording, the various following "describe_*" |
223 | 223 | virtual functions give the pending diagnostic a way to describe events |
@@ -109,6 +109,10 @@ public: | ||
109 | 109 | state_t combine_states (state_t s0, state_t s1) const; |
110 | 110 | |
111 | 111 | private: |
112 | + void check_control_flow_arg_for_taint (sm_context *sm_ctxt, | |
113 | + const gimple *stmt, | |
114 | + tree expr) const; | |
115 | + | |
112 | 116 | void check_for_tainted_size_arg (sm_context *sm_ctxt, |
113 | 117 | const supernode *node, |
114 | 118 | const gcall *call, |
@@ -130,6 +134,9 @@ public: | ||
130 | 134 | |
131 | 135 | /* Stop state, for a value we don't want to track any more. */ |
132 | 136 | state_t m_stop; |
137 | + | |
138 | + /* Global state, for when the last condition had tainted arguments. */ | |
139 | + state_t m_tainted_control_flow; | |
133 | 140 | }; |
134 | 141 | |
135 | 142 | /* Class for diagnostics relating to taint_state_machine. */ |
@@ -149,8 +156,7 @@ public: | ||
149 | 156 | && m_has_bounds == other.m_has_bounds); |
150 | 157 | } |
151 | 158 | |
152 | - label_text describe_state_change (const evdesc::state_change &change) | |
153 | - final override | |
159 | + label_text describe_state_change (const evdesc::state_change &change) override | |
154 | 160 | { |
155 | 161 | if (change.m_new_state == m_sm.m_tainted) |
156 | 162 | { |
@@ -761,6 +767,100 @@ private: | ||
761 | 767 | enum memory_space m_mem_space; |
762 | 768 | }; |
763 | 769 | |
770 | +/* Concrete taint_diagnostic subclass for reporting attacker-controlled | |
771 | + value being used as part of the condition of an assertion. */ | |
772 | + | |
773 | +class tainted_assertion : public taint_diagnostic | |
774 | +{ | |
775 | +public: | |
776 | + tainted_assertion (const taint_state_machine &sm, tree arg, | |
777 | + tree assert_failure_fndecl) | |
778 | + : taint_diagnostic (sm, arg, BOUNDS_NONE), | |
779 | + m_assert_failure_fndecl (assert_failure_fndecl) | |
780 | + { | |
781 | + gcc_assert (m_assert_failure_fndecl); | |
782 | + } | |
783 | + | |
784 | + const char *get_kind () const final override | |
785 | + { | |
786 | + return "tainted_assertion"; | |
787 | + } | |
788 | + | |
789 | + bool subclass_equal_p (const pending_diagnostic &base_other) const override | |
790 | + { | |
791 | + if (!taint_diagnostic::subclass_equal_p (base_other)) | |
792 | + return false; | |
793 | + const tainted_assertion &other | |
794 | + = (const tainted_assertion &)base_other; | |
795 | + return m_assert_failure_fndecl == other.m_assert_failure_fndecl; | |
796 | + } | |
797 | + | |
798 | + int get_controlling_option () const final override | |
799 | + { | |
800 | + return OPT_Wanalyzer_tainted_assertion; | |
801 | + } | |
802 | + | |
803 | + bool emit (rich_location *rich_loc) final override | |
804 | + { | |
805 | + diagnostic_metadata m; | |
806 | + /* "CWE-617: Reachable Assertion". */ | |
807 | + m.add_cwe (617); | |
808 | + | |
809 | + return warning_meta (rich_loc, m, get_controlling_option (), | |
810 | + "use of attacked-controlled value in" | |
811 | + " condition for assertion"); | |
812 | + } | |
813 | + | |
814 | + location_t fixup_location (location_t loc, | |
815 | + bool primary) const final override | |
816 | + { | |
817 | + if (primary) | |
818 | + /* For the primary location we want to avoid being in e.g. the | |
819 | + <assert.h> system header, since this would suppress the | |
820 | + diagnostic. */ | |
821 | + return expansion_point_location_if_in_system_header (loc); | |
822 | + else if (in_system_header_at (loc)) | |
823 | + /* For events, we want to show the implemenation of the assert | |
824 | + macro when we're describing them. */ | |
825 | + return linemap_resolve_location (line_table, loc, | |
826 | + LRK_SPELLING_LOCATION, | |
827 | + NULL); | |
828 | + else | |
829 | + return pending_diagnostic::fixup_location (loc, primary); | |
830 | + } | |
831 | + | |
832 | + label_text describe_state_change (const evdesc::state_change &change) override | |
833 | + { | |
834 | + if (change.m_new_state == m_sm.m_tainted_control_flow) | |
835 | + return change.formatted_print | |
836 | + ("use of attacker-controlled value for control flow"); | |
837 | + return taint_diagnostic::describe_state_change (change); | |
838 | + } | |
839 | + | |
840 | + label_text describe_final_event (const evdesc::final_event &ev) final override | |
841 | + { | |
842 | + if (mention_noreturn_attribute_p ()) | |
843 | + return ev.formatted_print | |
844 | + ("treating %qE as an assertion failure handler" | |
845 | + " due to %<__attribute__((__noreturn__))%>", | |
846 | + m_assert_failure_fndecl); | |
847 | + else | |
848 | + return ev.formatted_print | |
849 | + ("treating %qE as an assertion failure handler", | |
850 | + m_assert_failure_fndecl); | |
851 | + } | |
852 | + | |
853 | +private: | |
854 | + bool mention_noreturn_attribute_p () const | |
855 | + { | |
856 | + if (fndecl_built_in_p (m_assert_failure_fndecl, BUILT_IN_UNREACHABLE)) | |
857 | + return false; | |
858 | + return true; | |
859 | + } | |
860 | + | |
861 | + tree m_assert_failure_fndecl; | |
862 | +}; | |
863 | + | |
764 | 864 | /* taint_state_machine's ctor. */ |
765 | 865 | |
766 | 866 | taint_state_machine::taint_state_machine (logger *logger) |
@@ -770,6 +870,7 @@ taint_state_machine::taint_state_machine (logger *logger) | ||
770 | 870 | m_has_lb = add_state ("has_lb"); |
771 | 871 | m_has_ub = add_state ("has_ub"); |
772 | 872 | m_stop = add_state ("stop"); |
873 | + m_tainted_control_flow = add_state ("tainted-control-flow"); | |
773 | 874 | } |
774 | 875 | |
775 | 876 | state_machine::state_t |
@@ -810,6 +911,15 @@ taint_state_machine::alt_get_inherited_state (const sm_state_map &map, | ||
810 | 911 | { |
811 | 912 | default: |
812 | 913 | break; |
914 | + | |
915 | + case EQ_EXPR: | |
916 | + case GE_EXPR: | |
917 | + case LE_EXPR: | |
918 | + case NE_EXPR: | |
919 | + case GT_EXPR: | |
920 | + case LT_EXPR: | |
921 | + case UNORDERED_EXPR: | |
922 | + case ORDERED_EXPR: | |
813 | 923 | case PLUS_EXPR: |
814 | 924 | case MINUS_EXPR: |
815 | 925 | case MULT_EXPR: |
@@ -823,17 +933,6 @@ taint_state_machine::alt_get_inherited_state (const sm_state_map &map, | ||
823 | 933 | } |
824 | 934 | break; |
825 | 935 | |
826 | - case EQ_EXPR: | |
827 | - case GE_EXPR: | |
828 | - case LE_EXPR: | |
829 | - case NE_EXPR: | |
830 | - case GT_EXPR: | |
831 | - case LT_EXPR: | |
832 | - case UNORDERED_EXPR: | |
833 | - case ORDERED_EXPR: | |
834 | - /* Comparisons are just booleans. */ | |
835 | - return m_start; | |
836 | - | |
837 | 936 | case BIT_AND_EXPR: |
838 | 937 | case RSHIFT_EXPR: |
839 | 938 | return NULL; |
@@ -844,6 +943,19 @@ taint_state_machine::alt_get_inherited_state (const sm_state_map &map, | ||
844 | 943 | return NULL; |
845 | 944 | } |
846 | 945 | |
946 | +/* Return true iff FNDECL should be considered to be an assertion failure | |
947 | + handler by -Wanalyzer-tainted-assertion. */ | |
948 | + | |
949 | +static bool | |
950 | +is_assertion_failure_handler_p (tree fndecl) | |
951 | +{ | |
952 | + // i.e. "noreturn" | |
953 | + if (TREE_THIS_VOLATILE (fndecl)) | |
954 | + return true; | |
955 | + | |
956 | + return false; | |
957 | +} | |
958 | + | |
847 | 959 | /* Implementation of state_machine::on_stmt vfunc for taint_state_machine. */ |
848 | 960 | |
849 | 961 | bool |
@@ -871,6 +983,14 @@ taint_state_machine::on_stmt (sm_context *sm_ctxt, | ||
871 | 983 | /* External function with "access" attribute. */ |
872 | 984 | if (sm_ctxt->unknown_side_effects_p ()) |
873 | 985 | check_for_tainted_size_arg (sm_ctxt, node, call, callee_fndecl); |
986 | + | |
987 | + if (is_assertion_failure_handler_p (callee_fndecl) | |
988 | + && sm_ctxt->get_global_state () == m_tainted_control_flow) | |
989 | + { | |
990 | + sm_ctxt->warn (node, call, NULL_TREE, | |
991 | + make_unique<tainted_assertion> (*this, NULL_TREE, | |
992 | + callee_fndecl)); | |
993 | + } | |
874 | 994 | } |
875 | 995 | // TODO: ...etc; many other sources of untrusted data |
876 | 996 |
@@ -897,9 +1017,46 @@ taint_state_machine::on_stmt (sm_context *sm_ctxt, | ||
897 | 1017 | } |
898 | 1018 | } |
899 | 1019 | |
1020 | + if (const gcond *cond = dyn_cast <const gcond *> (stmt)) | |
1021 | + { | |
1022 | + /* Reset the state of "tainted-control-flow" before each | |
1023 | + control flow statement, so that only the last one before | |
1024 | + an assertion-failure-handler counts. */ | |
1025 | + sm_ctxt->set_global_state (m_start); | |
1026 | + check_control_flow_arg_for_taint (sm_ctxt, cond, gimple_cond_lhs (cond)); | |
1027 | + check_control_flow_arg_for_taint (sm_ctxt, cond, gimple_cond_rhs (cond)); | |
1028 | + } | |
1029 | + | |
1030 | + if (const gswitch *switch_ = dyn_cast <const gswitch *> (stmt)) | |
1031 | + { | |
1032 | + /* Reset the state of "tainted-control-flow" before each | |
1033 | + control flow statement, so that only the last one before | |
1034 | + an assertion-failure-handler counts. */ | |
1035 | + sm_ctxt->set_global_state (m_start); | |
1036 | + check_control_flow_arg_for_taint (sm_ctxt, switch_, | |
1037 | + gimple_switch_index (switch_)); | |
1038 | + } | |
1039 | + | |
900 | 1040 | return false; |
901 | 1041 | } |
902 | 1042 | |
1043 | +/* If EXPR is tainted, mark this execution path with the | |
1044 | + "tainted-control-flow" global state, in case we're about | |
1045 | + to call an assertion-failure-handler. */ | |
1046 | + | |
1047 | +void | |
1048 | +taint_state_machine::check_control_flow_arg_for_taint (sm_context *sm_ctxt, | |
1049 | + const gimple *stmt, | |
1050 | + tree expr) const | |
1051 | +{ | |
1052 | + const region_model *old_model = sm_ctxt->get_old_region_model (); | |
1053 | + const svalue *sval = old_model->get_rvalue (expr, NULL); | |
1054 | + state_t state = sm_ctxt->get_state (stmt, sval); | |
1055 | + enum bounds b; | |
1056 | + if (get_taint (state, TREE_TYPE (expr), &b)) | |
1057 | + sm_ctxt->set_global_state (m_tainted_control_flow); | |
1058 | +} | |
1059 | + | |
903 | 1060 | /* Implementation of state_machine::on_condition vfunc for taint_state_machine. |
904 | 1061 | Potentially transition state 'tainted' to 'has_ub' or 'has_lb', |
905 | 1062 | and states 'has_ub' and 'has_lb' to 'stop'. */ |
@@ -309,6 +309,7 @@ in the following sections. | ||
309 | 309 | :option:`-Wno-analyzer-shift-count-overflow` |gol| |
310 | 310 | :option:`-Wno-analyzer-stale-setjmp-buffer` |gol| |
311 | 311 | :option:`-Wno-analyzer-tainted-allocation-size` |gol| |
312 | + :option:`-Wno-analyzer-tainted-assertion` |gol| | |
312 | 313 | :option:`-Wno-analyzer-tainted-array-index` |gol| |
313 | 314 | :option:`-Wno-analyzer-tainted-divisor` |gol| |
314 | 315 | :option:`-Wno-analyzer-tainted-offset` |gol| |
@@ -549,6 +549,66 @@ Options That Control Static Analysis | ||
549 | 549 | |
550 | 550 | Default setting; overrides :option:`-Wno-analyzer-tainted-allocation-size`. |
551 | 551 | |
552 | +.. option:: -Wno-analyzer-tainted-assertion | |
553 | + | |
554 | + This warning requires both :option:`-fanalyzer` and | |
555 | + :option:`-fanalyzer-checker=taint` to enable it; | |
556 | + use :option:`-Wno-analyzer-tainted-assertion` to disable it. | |
557 | + | |
558 | + This diagnostic warns for paths through the code in which a value | |
559 | + that could be under an attacker's control is used as part of a | |
560 | + condition without being first sanitized, and that condition guards a | |
561 | + call to a function marked with attribute :fn-attr:`noreturn` | |
562 | + (such as the function ``__builtin_unreachable``). Such functions | |
563 | + typically indicate abnormal termination of the program, such as for | |
564 | + assertion failure handlers. For example: | |
565 | + | |
566 | + .. code-block:: c | |
567 | + | |
568 | + assert (some_tainted_value < SOME_LIMIT); | |
569 | + | |
570 | + In such cases: | |
571 | + | |
572 | + * when assertion-checking is enabled: an attacker could trigger | |
573 | + a denial of service by injecting an assertion failure | |
574 | + | |
575 | + * when assertion-checking is disabled, such as by defining ``NDEBUG``, | |
576 | + an attacker could inject data that subverts the process, since it | |
577 | + presumably violates a precondition that is being assumed by the code. | |
578 | + | |
579 | + Note that when assertion-checking is disabled, the assertions are | |
580 | + typically removed by the preprocessor before the analyzer has a chance | |
581 | + to "see" them, so this diagnostic can only generate warnings on builds | |
582 | + in which assertion-checking is enabled. | |
583 | + | |
584 | + For the purpose of this warning, any function marked with attribute | |
585 | + :fn-attr:`noreturn` is considered as a possible assertion failure | |
586 | + handler, including ``__builtin_unreachable``. Note that these functions | |
587 | + are sometimes removed by the optimizer before the analyzer "sees" them. | |
588 | + Hence optimization should be disabled when attempting to trigger this | |
589 | + diagnostic. | |
590 | + | |
591 | + See `CWE-617: Reachable Assertion <https://cwe.mitre.org/data/definitions/617.html>`_. | |
592 | + | |
593 | + The warning can also report problematic constructions such as | |
594 | + | |
595 | + .. code-block:: c | |
596 | + | |
597 | + switch (some_tainted_value) { | |
598 | + case 0: | |
599 | + /* [...etc; various valid cases omitted...] */ | |
600 | + break; | |
601 | + | |
602 | + default: | |
603 | + __builtin_unreachable (); /* BUG: attacker can trigger this */ | |
604 | + } | |
605 | + | |
606 | + despite the above not being an assertion failure, strictly speaking. | |
607 | + | |
608 | +.. option:: -Wanalyzer-tainted-assertion | |
609 | + | |
610 | + Default setting; overrides :option:`-Wno-analyzer-tainted-assertion`. | |
611 | + | |
552 | 612 | .. option:: -Wno-analyzer-tainted-array-index |
553 | 613 | |
554 | 614 | This warning requires both :option:`-fanalyzer` and |
@@ -0,0 +1,76 @@ | ||
1 | +// TODO: remove need for this option | |
2 | +/* { dg-additional-options "-fanalyzer-checker=taint" } */ | |
3 | + | |
4 | +/* We need this, otherwise the warnings are emitted inside the macros, which | |
5 | + makes it hard to write the DejaGnu directives. */ | |
6 | +/* { dg-additional-options " -ftrack-macro-expansion=0" } */ | |
7 | + | |
8 | +/* Adapted from code in the Linux kernel, which has this: */ | |
9 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
10 | + | |
11 | +#define __noreturn __attribute__ ((__noreturn__)) | |
12 | + | |
13 | +void panic(const char *fmt, ...) __noreturn; | |
14 | + | |
15 | +int _printk(const char *fmt, ...); | |
16 | +#define __printk_index_emit(...) do {} while (0) | |
17 | +#define printk_index_wrap(_p_func, _fmt, ...) \ | |
18 | + ({ \ | |
19 | + __printk_index_emit(_fmt, NULL, NULL); \ | |
20 | + _p_func(_fmt, ##__VA_ARGS__); \ | |
21 | + }) | |
22 | +#define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__) | |
23 | +#define barrier_before_unreachable() do { } while (0) | |
24 | + | |
25 | +#define BUG() do { \ | |
26 | + printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \ | |
27 | + barrier_before_unreachable(); \ | |
28 | + panic("BUG!"); \ | |
29 | +} while (0) | |
30 | + | |
31 | +#define BUG_ON(condition) do { if (condition) BUG(); } while (0) | |
32 | + | |
33 | +void __attribute__((tainted_args)) | |
34 | +test_BUG(int n) | |
35 | +{ | |
36 | + if (n > 100) /* { dg-message "use of attacker-controlled value for control flow" } */ | |
37 | + BUG(); /* { dg-warning "-Wanalyzer-tainted-assertion" "warning" } */ | |
38 | + /* { dg-message "treating 'panic' as an assertion failure handler due to '__attribute__\\(\\(__noreturn__\\)\\)'" "final event" { target *-*-* } .-1 } */ | |
39 | +} | |
40 | + | |
41 | +void __attribute__((tainted_args)) | |
42 | +test_BUG_ON(int n) | |
43 | +{ | |
44 | + BUG_ON(n > 100); /* { dg-warning "-Wanalyzer-tainted-assertion" "warning" } */ | |
45 | + /* { dg-message "treating 'panic' as an assertion failure handler due to '__attribute__\\(\\(__noreturn__\\)\\)'" "final event" { target *-*-* } .-1 } */ | |
46 | +} | |
47 | + | |
48 | +int __attribute__((tainted_args)) | |
49 | +test_switch_BUG_1(int n) | |
50 | +{ | |
51 | + switch (n) { /* { dg-message "use of attacker-controlled value for control flow" } */ | |
52 | + default: | |
53 | + case 0: | |
54 | + return 5; | |
55 | + case 1: | |
56 | + return 22; | |
57 | + case 2: | |
58 | + return -1; | |
59 | + case 42: | |
60 | + BUG (); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
61 | + } | |
62 | +} | |
63 | + | |
64 | +int __attribute__((tainted_args)) | |
65 | +test_switch_BUG(int n) | |
66 | +{ | |
67 | + switch (n) { /* { dg-message "use of attacker-controlled value for control flow" } */ | |
68 | + case 0: | |
69 | + return 5; | |
70 | + case 1: | |
71 | + return 22; | |
72 | + case 2: | |
73 | + return -1; | |
74 | + } | |
75 | + BUG (); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
76 | +} |
@@ -0,0 +1,96 @@ | ||
1 | +/* Integration test of how the execution path looks for | |
2 | + -Wanalyzer-tainted-assertion with macro-tracking enabled | |
3 | + (the default). */ | |
4 | + | |
5 | +// TODO: remove need for this option | |
6 | +/* { dg-additional-options "-fanalyzer-checker=taint" } */ | |
7 | + | |
8 | +/* { dg-additional-options "-fdiagnostics-show-path-depths" } */ | |
9 | +/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ | |
10 | + | |
11 | +/* An assertion macro that has a call to a __noreturn__ function. */ | |
12 | + | |
13 | +extern void my_assert_fail (const char *expr, const char *file, int line) | |
14 | + __attribute__ ((__noreturn__)); | |
15 | + | |
16 | +#define MY_ASSERT_1(EXPR) \ | |
17 | + do { if (!(EXPR)) my_assert_fail (#EXPR, __FILE__, __LINE__); } while (0) /* { dg-warning "use of attacked-controlled value in condition for assertion \\\[CWE-617\\\] \\\[-Wanalyzer-tainted-assertion\\\]" } */ | |
18 | + | |
19 | +int __attribute__((tainted_args)) | |
20 | +test_tainted_MY_ASSERT_1 (int n) | |
21 | +{ | |
22 | + MY_ASSERT_1 (n > 0); | |
23 | + return n * n; | |
24 | +} | |
25 | + | |
26 | +/* { dg-begin-multiline-output "" } | |
27 | + do { if (!(EXPR)) my_assert_fail (#EXPR, __FILE__, __LINE__); } while (0) | |
28 | + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
29 | + { dg-end-multiline-output "" } */ | |
30 | +// note: in expansion of macro 'MY_ASSERT_1' | |
31 | +/* { dg-begin-multiline-output "" } | |
32 | + MY_ASSERT_1 (n > 0); | |
33 | + ^~~~~~~~~~~ | |
34 | + 'test_tainted_MY_ASSERT_1': event 1 (depth 0) | |
35 | + | | |
36 | + | test_tainted_MY_ASSERT_1 (int n) | |
37 | + | ^~~~~~~~~~~~~~~~~~~~~~~~ | |
38 | + | | | |
39 | + | (1) function 'test_tainted_MY_ASSERT_1' marked with '__attribute__((tainted_args))' | |
40 | + | | |
41 | + +--> 'test_tainted_MY_ASSERT_1': event 2 (depth 1) | |
42 | + | | |
43 | + | test_tainted_MY_ASSERT_1 (int n) | |
44 | + | ^~~~~~~~~~~~~~~~~~~~~~~~ | |
45 | + | | | |
46 | + | (2) entry to 'test_tainted_MY_ASSERT_1' | |
47 | + | | |
48 | + 'test_tainted_MY_ASSERT_1': event 3 (depth 1) | |
49 | + | | |
50 | + | do { if (!(EXPR)) my_assert_fail (#EXPR, __FILE__, __LINE__); } while (0) | |
51 | + | ^ | |
52 | + | | | |
53 | + | (3) use of attacker-controlled value for control flow | |
54 | + { dg-end-multiline-output "" } */ | |
55 | +// note: in expansion of macro 'MY_ASSERT_1' | |
56 | +/* { dg-begin-multiline-output "" } | |
57 | + | MY_ASSERT_1 (n > 0); | |
58 | + | ^~~~~~~~~~~ | |
59 | + | | |
60 | + 'test_tainted_MY_ASSERT_1': event 4 (depth 1) | |
61 | + | | |
62 | + | do { if (!(EXPR)) my_assert_fail (#EXPR, __FILE__, __LINE__); } while (0) | |
63 | + | ^ | |
64 | + | | | |
65 | + | (4) following 'true' branch (when 'n <= 0')... | |
66 | + { dg-end-multiline-output "" } */ | |
67 | +// note: in expansion of macro 'MY_ASSERT_1' | |
68 | +/* { dg-begin-multiline-output "" } | |
69 | + | MY_ASSERT_1 (n > 0); | |
70 | + | ^~~~~~~~~~~ | |
71 | + | | |
72 | + 'test_tainted_MY_ASSERT_1': event 5 (depth 1) | |
73 | + | | |
74 | + | do { if (!(EXPR)) my_assert_fail (#EXPR, __FILE__, __LINE__); } while (0) | |
75 | + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
76 | + | | | |
77 | + | (5) ...to here | |
78 | + { dg-end-multiline-output "" } */ | |
79 | +// note: in expansion of macro 'MY_ASSERT_1' | |
80 | +/* { dg-begin-multiline-output "" } | |
81 | + | MY_ASSERT_1 (n > 0); | |
82 | + | ^~~~~~~~~~~ | |
83 | + | | |
84 | + 'test_tainted_MY_ASSERT_1': event 6 (depth 1) | |
85 | + | | |
86 | + | do { if (!(EXPR)) my_assert_fail (#EXPR, __FILE__, __LINE__); } while (0) | |
87 | + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
88 | + | | | |
89 | + | (6) treating 'my_assert_fail' as an assertion failure handler due to '__attribute__((__noreturn__))' | |
90 | + { dg-end-multiline-output "" } */ | |
91 | +// note: in expansion of macro 'MY_ASSERT_1' | |
92 | +/* { dg-begin-multiline-output "" } | |
93 | + | MY_ASSERT_1 (n > 0); | |
94 | + | ^~~~~~~~~~~ | |
95 | + | | |
96 | + { dg-end-multiline-output "" } */ |
@@ -0,0 +1,52 @@ | ||
1 | +/* Integration test of how the execution path looks for | |
2 | + -Wanalyzer-tainted-assertion with macro-tracking enabled | |
3 | + (the default), where the assertion macro is defined in | |
4 | + a system header. */ | |
5 | + | |
6 | +// TODO: remove need for this option | |
7 | +/* { dg-additional-options "-fanalyzer-checker=taint" } */ | |
8 | + | |
9 | +/* { dg-additional-options "-fdiagnostics-show-path-depths" } */ | |
10 | +/* { dg-additional-options "-fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ | |
11 | + | |
12 | +/* An assertion macro that has a call to a __noreturn__ function. */ | |
13 | + | |
14 | +/* This is marked as a system header. */ | |
15 | +#include "test-assert.h" | |
16 | + | |
17 | +int __attribute__((tainted_args)) | |
18 | +test_tainted_assert (int n) | |
19 | +{ | |
20 | + assert (n > 0); /* { dg-warning "use of attacked-controlled value in condition for assertion \\\[CWE-617\\\] \\\[-Wanalyzer-tainted-assertion\\\]" } */ | |
21 | + return n * n; | |
22 | +} | |
23 | + | |
24 | +/* { dg-begin-multiline-output "" } | |
25 | + assert (n > 0); | |
26 | + ^~~~~~ | |
27 | + 'test_tainted_assert': event 1 (depth 0) | |
28 | + | | |
29 | + | test_tainted_assert (int n) | |
30 | + | ^~~~~~~~~~~~~~~~~~~ | |
31 | + | | | |
32 | + | (1) function 'test_tainted_assert' marked with '__attribute__((tainted_args))' | |
33 | + | | |
34 | + +--> 'test_tainted_assert': event 2 (depth 1) | |
35 | + | | |
36 | + | test_tainted_assert (int n) | |
37 | + | ^~~~~~~~~~~~~~~~~~~ | |
38 | + | | | |
39 | + | (2) entry to 'test_tainted_assert' | |
40 | + | | |
41 | + 'test_tainted_assert': events 3-6 (depth 1) | |
42 | + | | |
43 | + | | |
44 | + | do { if (!(EXPR)) __assert_fail (#EXPR, __FILE__, __LINE__); } while (0) | |
45 | + | ^ ~~~~~~~~~~~~~ | |
46 | + | | | | |
47 | + | | (5) ...to here | |
48 | + | | (6) treating '__assert_fail' as an assertion failure handler due to '__attribute__((__noreturn__))' | |
49 | + | (3) use of attacker-controlled value for control flow | |
50 | + | (4) following 'true' branch (when 'n <= 0')... | |
51 | + | | |
52 | + { dg-end-multiline-output "" } */ |
@@ -0,0 +1,346 @@ | ||
1 | +// TODO: remove need for this option | |
2 | +/* { dg-additional-options "-fanalyzer-checker=taint" } */ | |
3 | + | |
4 | +/* We need this, otherwise the warnings are emitted inside the macros, which | |
5 | + makes it hard to write the DejaGnu directives. */ | |
6 | +/* { dg-additional-options " -ftrack-macro-expansion=0" } */ | |
7 | + | |
8 | +#include "analyzer-decls.h" | |
9 | + | |
10 | +/* An assertion macro that has a call to a __noreturn__ function. */ | |
11 | + | |
12 | +extern void my_assert_fail (const char *expr, const char *file, int line) | |
13 | + __attribute__ ((__noreturn__)); | |
14 | + | |
15 | +#define MY_ASSERT_1(EXPR) \ | |
16 | + do { if (!(EXPR)) my_assert_fail (#EXPR, __FILE__, __LINE__); } while (0) | |
17 | + | |
18 | +int | |
19 | +test_not_tainted_MY_ASSERT_1 (int n) | |
20 | +{ | |
21 | + MY_ASSERT_1 (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
22 | + return n * n; | |
23 | +} | |
24 | + | |
25 | +int __attribute__((tainted_args)) | |
26 | +test_tainted_MY_ASSERT_1 (int n) | |
27 | +{ | |
28 | + MY_ASSERT_1 (n > 0); /* { dg-warning "use of attacked-controlled value in condition for assertion \\\[CWE-617\\\] \\\[-Wanalyzer-tainted-assertion\\\]" "warning" } */ | |
29 | + /* { dg-message "treating 'my_assert_fail' as an assertion failure handler due to '__attribute__\\(\\(__noreturn__\\)\\)'" "final event" { target *-*-* } .-1 } */ | |
30 | + return n * n; | |
31 | +} | |
32 | + | |
33 | + | |
34 | +/* An assertion macro that has a call to __builtin_unreachable. */ | |
35 | + | |
36 | +#define MY_ASSERT_2(EXPR) \ | |
37 | + do { if (!(EXPR)) __builtin_unreachable (); } while (0) | |
38 | + | |
39 | +int | |
40 | +test_not_tainted_MY_ASSERT_2 (int n) | |
41 | +{ | |
42 | + MY_ASSERT_2 (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
43 | + return n * n; | |
44 | +} | |
45 | + | |
46 | +int __attribute__((tainted_args)) | |
47 | +test_tainted_MY_ASSERT_2 (int n) | |
48 | +{ | |
49 | + MY_ASSERT_2 (n > 0); /* { dg-warning "-Wanalyzer-tainted-assertion" "warning" } */ | |
50 | + /* { dg-message "treating '__builtin_unreachable' as an assertion failure handler" "final event" { target *-*-* } .-1 } */ | |
51 | + return n * n; | |
52 | +} | |
53 | + | |
54 | + | |
55 | +/* An assertion macro that's preprocessed away. | |
56 | + The analyzer doesn't see this, so can't warn. */ | |
57 | + | |
58 | +#define MY_ASSERT_3(EXPR) do { } while (0) | |
59 | + | |
60 | +int | |
61 | +test_not_tainted_MY_ASSERT_3 (int n) | |
62 | +{ | |
63 | + MY_ASSERT_3 (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
64 | + return n * n; | |
65 | +} | |
66 | + | |
67 | +int __attribute__((tainted_args)) | |
68 | +test_tainted_MY_ASSERT_3 (int n) | |
69 | +{ | |
70 | + MY_ASSERT_3 (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
71 | + return n * n; | |
72 | +} | |
73 | + | |
74 | + | |
75 | +/* A macro that isn't an assertion. */ | |
76 | + | |
77 | +extern void do_something_benign (); | |
78 | + | |
79 | +#define NOT_AN_ASSERT(EXPR) \ | |
80 | + do { if (!(EXPR)) do_something_benign (); } while (0) | |
81 | + | |
82 | +int | |
83 | +test_not_tainted_NOT_AN_ASSERT (int n) | |
84 | +{ | |
85 | + NOT_AN_ASSERT (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
86 | + return n * n; | |
87 | +} | |
88 | + | |
89 | +int __attribute__((tainted_args)) | |
90 | +test_tainted_NOT_AN_ASSERT (int n) | |
91 | +{ | |
92 | + NOT_AN_ASSERT (n > 0); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
93 | + return n * n; | |
94 | +} | |
95 | + | |
96 | + | |
97 | +/* A condition that isn't an assertion. */ | |
98 | + | |
99 | +int __attribute__((tainted_args)) | |
100 | +test_tainted_condition (int n) | |
101 | +{ | |
102 | + if (n > 0) /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
103 | + return 1; | |
104 | + else | |
105 | + return -1; | |
106 | +} | |
107 | + | |
108 | + | |
109 | +/* More complicated expressions in assertions. */ | |
110 | + | |
111 | +int g; | |
112 | + | |
113 | +void __attribute__((tainted_args)) | |
114 | +test_compound_condition_in_assert_1 (int n) | |
115 | +{ | |
116 | + MY_ASSERT_1 ((n * 2) < (g + 3)); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
117 | +} | |
118 | + | |
119 | +void __attribute__((tainted_args)) | |
120 | +test_compound_condition_in_assert_2 (int x, int y) | |
121 | +{ | |
122 | + MY_ASSERT_1 (x < 100 && y < 100); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
123 | +} | |
124 | + | |
125 | +void __attribute__((tainted_args)) | |
126 | +test_compound_condition_in_assert_3 (int x, int y) | |
127 | +{ | |
128 | + MY_ASSERT_1 (x < 100 || y < 100); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
129 | +} | |
130 | + | |
131 | +void __attribute__((tainted_args)) | |
132 | +test_sanitized_expression_in_assert (int n) | |
133 | +{ | |
134 | + __analyzer_dump_state ("taint", n); /* { dg-warning "state: 'tainted'" } */ | |
135 | + if (n < 0 || n >= 100) | |
136 | + return; | |
137 | + __analyzer_dump_state ("taint", n); /* { dg-warning "state: 'stop'" } */ | |
138 | + MY_ASSERT_1 (n < 200); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
139 | +} | |
140 | + | |
141 | +void __attribute__((tainted_args)) | |
142 | +test_sanitization_then_ok_assertion (unsigned n) | |
143 | +{ | |
144 | + if (n >= 100) | |
145 | + return; | |
146 | + | |
147 | + /* Shouldn't warn here, as g isn't attacker-controlled. */ | |
148 | + MY_ASSERT_1 (g > 42); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
149 | +} | |
150 | + | |
151 | +void __attribute__((tainted_args)) | |
152 | +test_good_assert_then_bad_assert (unsigned n) | |
153 | +{ | |
154 | + /* Shouldn't warn here, as g isn't attacker-controlled. */ | |
155 | + MY_ASSERT_1 (g > 42); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
156 | + | |
157 | + /* ...but n is: */ | |
158 | + MY_ASSERT_1 (n < 100); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
159 | +} | |
160 | + | |
161 | +void __attribute__((tainted_args)) | |
162 | +test_bad_assert_then_good_assert (unsigned n) | |
163 | +{ | |
164 | + MY_ASSERT_1 (n < 100); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
165 | + MY_ASSERT_1 (g > 42); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
166 | +} | |
167 | + | |
168 | + | |
169 | +/* */ | |
170 | + | |
171 | +void __attribute__((tainted_args)) | |
172 | +test_zero_MY_ASSERT_1 (unsigned n) | |
173 | +{ | |
174 | + if (n >= 100) | |
175 | + MY_ASSERT_1 (0); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
176 | +} | |
177 | + | |
178 | +void __attribute__((tainted_args)) | |
179 | +test_nonzero_MY_ASSERT_1 (unsigned n) | |
180 | +{ | |
181 | + if (n >= 100) | |
182 | + MY_ASSERT_1 (1); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
183 | +} | |
184 | + | |
185 | +void __attribute__((tainted_args)) | |
186 | +test_zero_MY_ASSERT_2 (unsigned n) | |
187 | +{ | |
188 | + if (n >= 100) | |
189 | + MY_ASSERT_2 (0); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
190 | +} | |
191 | + | |
192 | +void __attribute__((tainted_args)) | |
193 | +test_nonzero_MY_ASSERT_2 (unsigned n) | |
194 | +{ | |
195 | + if (n >= 100) | |
196 | + MY_ASSERT_2 (1); /* { dg-bogus "-Wanalyzer-tainted-assertion" } */ | |
197 | +} | |
198 | + | |
199 | + | |
200 | +/* Assertions that call a subroutine to do validity checking. */ | |
201 | + | |
202 | +static int | |
203 | +__analyzer_valid_1 (int x) | |
204 | +{ | |
205 | + return x < 100; | |
206 | +} | |
207 | + | |
208 | +void __attribute__((tainted_args)) | |
209 | +test_assert_calling_valid_1 (int n) | |
210 | +{ | |
211 | + MY_ASSERT_1 (__analyzer_valid_1 (n)); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
212 | +} | |
213 | + | |
214 | +static int | |
215 | +__analyzer_valid_2 (int x) | |
216 | +{ | |
217 | + return x < 100; | |
218 | +} | |
219 | + | |
220 | +void __attribute__((tainted_args)) | |
221 | +test_assert_calling_valid_2 (int n) | |
222 | +{ | |
223 | + MY_ASSERT_1 (__analyzer_valid_2 (n)); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
224 | +} | |
225 | + | |
226 | +static int | |
227 | +__analyzer_valid_3 (int x, int y) | |
228 | +{ | |
229 | + if (x >= 100) | |
230 | + return 0; | |
231 | + if (y >= 100) | |
232 | + return 0; | |
233 | + return 1; | |
234 | +} | |
235 | + | |
236 | +void __attribute__((tainted_args)) | |
237 | +test_assert_calling_valid_3 (int a, int b) | |
238 | +{ | |
239 | + MY_ASSERT_1 (__analyzer_valid_3 (a, b)); /* { dg-warning "-Wanalyzer-tainted-assertion" "TODO" { xfail *-*-* } } */ | |
240 | +} | |
241 | + | |
242 | + | |
243 | +/* 'switch' statements with supposedly unreachable cases/defaults. */ | |
244 | + | |
245 | +int __attribute__((tainted_args)) | |
246 | +test_switch_default (int n) | |
247 | +{ | |
248 | + switch (n) /* { dg-message "use of attacker-controlled value for control flow" "why" } */ | |
249 | + /* { dg-message "following 'default:' branch" "dest" { target *-*-* } .-1 } */ | |
250 | + { | |
251 | + case 0: | |
252 | + return 5; | |
253 | + case 1: | |
254 | + return 22; | |
255 | + case 2: | |
256 | + return -1; | |
257 | + default: | |
258 | + /* The wording is rather inaccurate here. */ | |
259 | + __builtin_unreachable (); /* { dg-warning "use of attacked-controlled value in condition for assertion" } */ | |
260 | + } | |
261 | +} | |
262 | + | |
263 | +int __attribute__((tainted_args)) | |
264 | +test_switch_unhandled_case (int n) | |
265 | +{ | |
266 | + switch (n) /* { dg-message "use of attacker-controlled value for control flow" "why" } */ | |
267 | + /* { dg-message "following 'default:' branch" "dest" { target *-*-* } .-1 } */ | |
268 | + { | |
269 | + case 0: | |
270 | + return 5; | |
271 | + case 1: | |
272 | + return 22; | |
273 | + case 2: | |
274 | + return -1; | |
275 | + } | |
276 | + | |
277 | + /* The wording is rather inaccurate here. */ | |
278 | + __builtin_unreachable (); /* { dg-warning "use of attacked-controlled value in condition for assertion" } */ | |
279 | +} | |
280 | + | |
281 | +int __attribute__((tainted_args)) | |
282 | +test_switch_bogus_case_MY_ASSERT_1 (int n) | |
283 | +{ | |
284 | + switch (n) | |
285 | + { | |
286 | + default: | |
287 | + case 0: | |
288 | + return 5; | |
289 | + case 1: | |
290 | + return 22; | |
291 | + case 2: | |
292 | + return -1; | |
293 | + case 42: | |
294 | + MY_ASSERT_1 (0); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
295 | + } | |
296 | +} | |
297 | + | |
298 | +int __attribute__((tainted_args)) | |
299 | +test_switch_bogus_case_MY_ASSERT_2 (int n) | |
300 | +{ | |
301 | + switch (n) | |
302 | + { | |
303 | + default: | |
304 | + case 0: | |
305 | + return 5; | |
306 | + case 1: | |
307 | + return 22; | |
308 | + case 2: | |
309 | + return -1; | |
310 | + case 42: | |
311 | + MY_ASSERT_2 (0); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
312 | + } | |
313 | +} | |
314 | + | |
315 | +int __attribute__((tainted_args)) | |
316 | +test_switch_bogus_case_unreachable (int n) | |
317 | +{ | |
318 | + switch (n) | |
319 | + { | |
320 | + default: | |
321 | + case 0: | |
322 | + return 5; | |
323 | + case 1: | |
324 | + return 22; | |
325 | + case 2: | |
326 | + return -1; | |
327 | + case 42: | |
328 | + /* This case gets optimized away before we see it. */ | |
329 | + __builtin_unreachable (); | |
330 | + } | |
331 | +} | |
332 | + | |
333 | + | |
334 | +/* Contents of a struct. */ | |
335 | + | |
336 | +struct s | |
337 | +{ | |
338 | + int x; | |
339 | + int y; | |
340 | +}; | |
341 | + | |
342 | +int __attribute__((tainted_args)) | |
343 | +test_assert_struct (struct s *p) | |
344 | +{ | |
345 | + MY_ASSERT_1 (p->x < p->y); /* { dg-warning "-Wanalyzer-tainted-assertion" } */ | |
346 | +} |
@@ -0,0 +1,7 @@ | ||
1 | +#pragma GCC system_header | |
2 | + | |
3 | +extern void __assert_fail (const char *expr, const char *file, int line) | |
4 | + __attribute__ ((__noreturn__)); | |
5 | + | |
6 | +#define assert(EXPR) \ | |
7 | + do { if (!(EXPR)) __assert_fail (#EXPR, __FILE__, __LINE__); } while (0) |
@@ -89,7 +89,8 @@ public: | ||
89 | 89 | return 0; |
90 | 90 | } |
91 | 91 | |
92 | - location_t fixup_location (location_t loc) const final override | |
92 | + location_t fixup_location (location_t loc, | |
93 | + bool) const final override | |
93 | 94 | { |
94 | 95 | /* Ideally we'd check for specific macros here, and only |
95 | 96 | resolve certain macros. */ |