Skip to content

Commit 08b7462

Browse files
committed
analyzer: fix taint false positives with UNKNOWN [PR112850]
PR analyzer/112850 reports a false positive from -Wanalyzer-tainted-allocation-size on the Linux kernel [1] where -fanalyzer complains that an allocation size is attacker-controlled despite the value being correctly sanitized against upper and lower limits. The root cause is that the expression is sufficiently complex to exceed the -param=analyzer-max-svalue-depth= threshold, currently at 12, with depth 13, and so it is treated as UNKNOWN. Hence the sanitizations are seen as comparisons of an UNKNOWN symbolic value against constants, and these were being ignored by the taint state machine. The expression in question is relatively typical for those seen in Linux kernel ioctl handlers, and I was surprised that it had exceeded the analyzer's default expression complexity limit. This patch addresses this problem in three ways: (a) the default value of the threshold parameter is increased, from 12 to 18, so that such expressions are precisely handled (b) adding a new -Wanalyzer-symbol-too-complex to warn when the symbol complexity limit is reached. This is off by default for users, and on by default in the test suite. (c) the taint state machine handles comparisons against UNKNOWN svalues by dropping all taint information on that execution path, so that if the complexity limit has been exceeded we don't generate false positives As well as fixing the taint false positive (PR analyzer/112850), the patch also fixes a couple of leak false positives seen on flex-generated scanners (PR analyzer/103546). [1] specifically, in sound/core/rawmidi.c's handler for SNDRV_RAWMIDI_STREAM_OUTPUT. gcc/ChangeLog: PR analyzer/103546 PR analyzer/112850 * doc/invoke.texi: Add -Wanalyzer-symbol-too-complex. gcc/analyzer/ChangeLog: PR analyzer/103546 PR analyzer/112850 * analyzer.opt (-param=analyzer-max-svalue-depth=): Increase from 12 to 18. (Wanalyzer-symbol-too-complex): New. * diagnostic-manager.cc (null_assignment_sm_context::clear_all_per_svalue_state): New. * engine.cc (impl_sm_context::clear_all_per_svalue_state): New. * program-state.cc (sm_state_map::clear_all_per_svalue_state): New. * program-state.h (sm_state_map::clear_all_per_svalue_state): New decl. * region-model-manager.cc (region_model_manager::reject_if_too_complex): Add -Wanalyzer-symbol-too-complex. * sm-taint.cc (taint_state_machine::on_condition): Handle comparisons against UNKNOWN. * sm.h (sm_context::clear_all_per_svalue_state): New. gcc/testsuite/ChangeLog: PR analyzer/103546 PR analyzer/112850 * c-c++-common/analyzer/call-summaries-pr107158-2.c: Add -Wno-analyzer-symbol-too-complex. * c-c++-common/analyzer/call-summaries-pr107158.c: Likewise. * c-c++-common/analyzer/deref-before-check-pr109060-haproxy-cfgparse.c: Likewise. * c-c++-common/analyzer/feasibility-3.c: Add -Wno-analyzer-too-complex and -Wno-analyzer-symbol-too-complex. * c-c++-common/analyzer/flex-with-call-summaries.c: Add -Wno-analyzer-symbol-too-complex. Remove fail for PR analyzer/103546 leak false positive. * c-c++-common/analyzer/flex-without-call-summaries.c: Remove xfail for PR analyzer/103546 leak false positive. * c-c++-common/analyzer/infinite-recursion-3.c: Add -Wno-analyzer-symbol-too-complex. * c-c++-common/analyzer/null-deref-pr108251-smp_fetch_ssl_fc_has_early-O2.c: Likewise. * c-c++-common/analyzer/null-deref-pr108251-smp_fetch_ssl_fc_has_early.c: Likewise. * c-c++-common/analyzer/null-deref-pr108400-SoftEtherVPN-WebUi.c: Likewise. * c-c++-common/analyzer/null-deref-pr108806-qemu.c: Likewise. * c-c++-common/analyzer/null-deref-pr108830.c: Likewise. * c-c++-common/analyzer/pr94596.c: Likewise. * c-c++-common/analyzer/strtok-2.c: Likewise. * c-c++-common/analyzer/strtok-4.c: Add -Wno-analyzer-too-complex and -Wno-analyzer-symbol-too-complex. * c-c++-common/analyzer/strtok-cppreference.c: Likewise. * gcc.dg/analyzer/analyzer.exp: Add -Wanalyzer-symbol-too-complex to DEFAULT_CFLAGS. * gcc.dg/analyzer/attr-const-3.c: Add -Wno-analyzer-symbol-too-complex. * gcc.dg/analyzer/call-summaries-pr107072.c: Likewise. * gcc.dg/analyzer/doom-s_sound-pr108867.c: Likewise. * gcc.dg/analyzer/explode-4.c: Likewise. * gcc.dg/analyzer/null-deref-pr102671-1.c: Likewise. * gcc.dg/analyzer/null-deref-pr105755.c: Likewise. * gcc.dg/analyzer/out-of-bounds-curl.c: Likewise. * gcc.dg/analyzer/pr101503.c: Likewise. * gcc.dg/analyzer/pr103892.c: Add -Wno-analyzer-too-complex and -Wno-analyzer-symbol-too-complex. * gcc.dg/analyzer/pr94851-4.c: Add -Wno-analyzer-symbol-too-complex. * gcc.dg/analyzer/pr96860-1.c: Likewise. * gcc.dg/analyzer/pr96860-2.c: Likewise. * gcc.dg/analyzer/pr98918.c: Likewise. * gcc.dg/analyzer/pr99044-2.c: Likewise. * gcc.dg/analyzer/uninit-pr108806-qemu.c: Likewise. * gcc.dg/analyzer/use-after-free.c: Add -Wno-analyzer-too-complex and -Wno-analyzer-symbol-too-complex. * gcc.dg/plugin/plugin.exp: Add new tests for analyzer_kernel_plugin.c. * gcc.dg/plugin/taint-CVE-2011-0521-4.c: Update expected results. * gcc.dg/plugin/taint-CVE-2011-0521-5.c: Likewise. * gcc.dg/plugin/taint-CVE-2011-0521-6.c: Likewise. * gcc.dg/plugin/taint-CVE-2011-0521-5-fixed.c: Remove xfail. * gcc.dg/plugin/taint-pr112850-precise.c: New test. * gcc.dg/plugin/taint-pr112850-too-complex.c: New test. * gcc.dg/plugin/taint-pr112850-unsanitized.c: New test. * gcc.dg/plugin/taint-pr112850.c: New test. Signed-off-by: David Malcolm <[email protected]>
1 parent ae9e48e commit 08b7462

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+321
-32
lines changed

gcc/analyzer/analyzer.opt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Common Joined UInteger Var(param_analyzer_max_recursion_depth) Init(2) Param
4343
The maximum number of times a callsite can appear in a call stack within the analyzer, before terminating analysis of a call that would recurse deeper.
4444

4545
-param=analyzer-max-svalue-depth=
46-
Common Joined UInteger Var(param_analyzer_max_svalue_depth) Init(12) Param
46+
Common Joined UInteger Var(param_analyzer_max_svalue_depth) Init(18) Param
4747
The maximum depth of a symbolic value, before approximating the value as unknown.
4848

4949
-param=analyzer-min-snodes-for-call-summary=
@@ -262,6 +262,10 @@ Wanalyzer-use-of-uninitialized-value
262262
Common Var(warn_analyzer_use_of_uninitialized_value) Init(1) Warning
263263
Warn about code paths in which an uninitialized value is used.
264264

265+
Wanalyzer-symbol-too-complex
266+
Common Var(warn_analyzer_symbol_too_complex) Init(0) Warning
267+
Warn if expressions are too complicated for the analyzer to fully track.
268+
265269
Wanalyzer-too-complex
266270
Common Var(warn_analyzer_too_complex) Init(0) Warning
267271
Warn if the code is too complicated for the analyzer to fully explore.

gcc/analyzer/diagnostic-manager.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2043,6 +2043,11 @@ struct null_assignment_sm_context : public sm_context
20432043
/* No-op. */
20442044
}
20452045

2046+
void clear_all_per_svalue_state () final override
2047+
{
2048+
/* No-op. */
2049+
}
2050+
20462051
void on_custom_transition (custom_transition *) final override
20472052
{
20482053
}

gcc/analyzer/engine.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,11 @@ class impl_sm_context : public sm_context
474474
m_new_state->m_checker_states[m_sm_idx]->set_global_state (state);
475475
}
476476

477+
void clear_all_per_svalue_state () final override
478+
{
479+
m_new_state->m_checker_states[m_sm_idx]->clear_all_per_svalue_state ();
480+
}
481+
477482
void on_custom_transition (custom_transition *transition) final override
478483
{
479484
transition->impl_transition (&m_eg,

gcc/analyzer/program-state.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,14 @@ sm_state_map::clear_any_state (const svalue *sval)
526526
m_map.remove (sval);
527527
}
528528

529+
/* Clear all per-svalue state within this state map. */
530+
531+
void
532+
sm_state_map::clear_all_per_svalue_state ()
533+
{
534+
m_map.empty ();
535+
}
536+
529537
/* Set the "global" state within this state map to STATE. */
530538

531539
void

gcc/analyzer/program-state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class sm_state_map
146146
const svalue *origin,
147147
const extrinsic_state &ext_state);
148148
void clear_any_state (const svalue *sval);
149+
void clear_all_per_svalue_state ();
149150

150151
void set_global_state (state_machine::state_t state);
151152
state_machine::state_t get_global_state () const;

gcc/analyzer/region-model-manager.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,16 @@ region_model_manager::reject_if_too_complex (svalue *sval)
185185
return false;
186186
}
187187

188+
pretty_printer pp;
189+
pp_format_decoder (&pp) = default_tree_printer;
190+
sval->dump_to_pp (&pp, true);
191+
if (warning_at (input_location, OPT_Wanalyzer_symbol_too_complex,
192+
"symbol too complicated: %qs",
193+
pp_formatted_text (&pp)))
194+
inform (input_location,
195+
"max_depth %i exceeds --param=analyzer-max-svalue-depth=%i",
196+
c.m_max_depth, param_analyzer_max_svalue_depth);
197+
188198
delete sval;
189199
return true;
190200
}

gcc/analyzer/sm-taint.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,20 @@ taint_state_machine::on_condition (sm_context *sm_ctxt,
10381038
if (stmt == NULL)
10391039
return;
10401040

1041+
if (lhs->get_kind () == SK_UNKNOWN
1042+
|| rhs->get_kind () == SK_UNKNOWN)
1043+
{
1044+
/* If we have a comparison against UNKNOWN, then
1045+
we've presumably hit the svalue complexity limit,
1046+
and we don't know what is being sanitized.
1047+
Give up on any taint already found on this execution path. */
1048+
// TODO: warn about this
1049+
if (get_logger ())
1050+
get_logger ()->log ("comparison against UNKNOWN; removing all taint");
1051+
sm_ctxt->clear_all_per_svalue_state ();
1052+
return;
1053+
}
1054+
10411055
// TODO
10421056
switch (op)
10431057
{

gcc/analyzer/sm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ class sm_context
299299
virtual state_machine::state_t get_global_state () const = 0;
300300
virtual void set_global_state (state_machine::state_t) = 0;
301301

302+
virtual void clear_all_per_svalue_state () = 0;
303+
302304
/* A vfunc for handling custom transitions, such as when registering
303305
a signal handler. */
304306
virtual void on_custom_transition (custom_transition *transition) = 0;

gcc/doc/invoke.texi

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ Objective-C and Objective-C++ Dialects}.
491491
-Wno-analyzer-tainted-divisor
492492
-Wno-analyzer-tainted-offset
493493
-Wno-analyzer-tainted-size
494+
-Wanalyzer-symbol-too-complex
494495
-Wanalyzer-too-complex
495496
-Wno-analyzer-undefined-behavior-strtok
496497
-Wno-analyzer-unsafe-call-within-signal-handler
@@ -10562,6 +10563,19 @@ Enabling this option effectively enables the following warnings:
1056210563
This option is only available if GCC was configured with analyzer
1056310564
support enabled.
1056410565

10566+
@opindex Wanalyzer-symbol-too-complex
10567+
@opindex Wno-analyzer-symbol-too-complex
10568+
@item -Wanalyzer-symbol-too-complex
10569+
If @option{-fanalyzer} is enabled, the analyzer uses various heuristics
10570+
to attempt to track the state of memory, but these can be defeated by
10571+
sufficiently complicated code.
10572+
10573+
By default, the analysis silently stops tracking values of expressions
10574+
if they exceed the threshold defined by
10575+
@option{--param analyzer-max-svalue-depth=@var{value}}, and falls back
10576+
to an imprecise representation for such expressions.
10577+
The @option{-Wanalyzer-symbol-too-complex} option warns if this occurs.
10578+
1056510579
@opindex Wanalyzer-too-complex
1056610580
@opindex Wno-analyzer-too-complex
1056710581
@item -Wanalyzer-too-complex

gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158-2.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-too-complex" } */
1+
/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
22
/* { dg-skip-if "c++98 has no noreturn attribute" { c++98_only } } */
33

44
#ifdef __cplusplus

0 commit comments

Comments
 (0)