]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
analyzer: fix taint false positives with UNKNOWN [PR112850]
authorDavid Malcolm <dmalcolm@redhat.com>
Thu, 7 Dec 2023 00:25:26 +0000 (19:25 -0500)
committerDavid Malcolm <dmalcolm@redhat.com>
Thu, 7 Dec 2023 00:25:26 +0000 (19:25 -0500)
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 <dmalcolm@redhat.com>
51 files changed:
gcc/analyzer/analyzer.opt
gcc/analyzer/diagnostic-manager.cc
gcc/analyzer/engine.cc
gcc/analyzer/program-state.cc
gcc/analyzer/program-state.h
gcc/analyzer/region-model-manager.cc
gcc/analyzer/sm-taint.cc
gcc/analyzer/sm.h
gcc/doc/invoke.texi
gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158-2.c
gcc/testsuite/c-c++-common/analyzer/call-summaries-pr107158.c
gcc/testsuite/c-c++-common/analyzer/deref-before-check-pr109060-haproxy-cfgparse.c
gcc/testsuite/c-c++-common/analyzer/feasibility-3.c
gcc/testsuite/c-c++-common/analyzer/flex-with-call-summaries.c
gcc/testsuite/c-c++-common/analyzer/flex-without-call-summaries.c
gcc/testsuite/c-c++-common/analyzer/infinite-recursion-3.c
gcc/testsuite/c-c++-common/analyzer/null-deref-pr108251-smp_fetch_ssl_fc_has_early-O2.c
gcc/testsuite/c-c++-common/analyzer/null-deref-pr108251-smp_fetch_ssl_fc_has_early.c
gcc/testsuite/c-c++-common/analyzer/null-deref-pr108400-SoftEtherVPN-WebUi.c
gcc/testsuite/c-c++-common/analyzer/null-deref-pr108806-qemu.c
gcc/testsuite/c-c++-common/analyzer/null-deref-pr108830.c
gcc/testsuite/c-c++-common/analyzer/pr94596.c
gcc/testsuite/c-c++-common/analyzer/strtok-2.c
gcc/testsuite/c-c++-common/analyzer/strtok-4.c
gcc/testsuite/c-c++-common/analyzer/strtok-cppreference.c
gcc/testsuite/gcc.dg/analyzer/analyzer.exp
gcc/testsuite/gcc.dg/analyzer/attr-const-3.c
gcc/testsuite/gcc.dg/analyzer/call-summaries-pr107072.c
gcc/testsuite/gcc.dg/analyzer/doom-s_sound-pr108867.c
gcc/testsuite/gcc.dg/analyzer/explode-4.c
gcc/testsuite/gcc.dg/analyzer/null-deref-pr102671-1.c
gcc/testsuite/gcc.dg/analyzer/null-deref-pr105755.c
gcc/testsuite/gcc.dg/analyzer/out-of-bounds-curl.c
gcc/testsuite/gcc.dg/analyzer/pr101503.c
gcc/testsuite/gcc.dg/analyzer/pr103892.c
gcc/testsuite/gcc.dg/analyzer/pr94851-4.c
gcc/testsuite/gcc.dg/analyzer/pr96860-1.c
gcc/testsuite/gcc.dg/analyzer/pr96860-2.c
gcc/testsuite/gcc.dg/analyzer/pr98918.c
gcc/testsuite/gcc.dg/analyzer/pr99044-2.c
gcc/testsuite/gcc.dg/analyzer/uninit-pr108806-qemu.c
gcc/testsuite/gcc.dg/analyzer/use-after-free.c
gcc/testsuite/gcc.dg/plugin/plugin.exp
gcc/testsuite/gcc.dg/plugin/taint-CVE-2011-0521-4.c
gcc/testsuite/gcc.dg/plugin/taint-CVE-2011-0521-5-fixed.c
gcc/testsuite/gcc.dg/plugin/taint-CVE-2011-0521-5.c
gcc/testsuite/gcc.dg/plugin/taint-CVE-2011-0521-6.c
gcc/testsuite/gcc.dg/plugin/taint-pr112850-precise.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/plugin/taint-pr112850-too-complex.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/plugin/taint-pr112850-unsanitized.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/plugin/taint-pr112850.c [new file with mode: 0644]

index a3c30caf2abe5c21d6cc7722bd309447986a324c..d0fe5a437880fa7ff28d9a3532bf1c325b1ce7a9 100644 (file)
@@ -43,7 +43,7 @@ Common Joined UInteger Var(param_analyzer_max_recursion_depth) Init(2) Param
 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.
 
 -param=analyzer-max-svalue-depth=
-Common Joined UInteger Var(param_analyzer_max_svalue_depth) Init(12) Param
+Common Joined UInteger Var(param_analyzer_max_svalue_depth) Init(18) Param
 The maximum depth of a symbolic value, before approximating the value as unknown.
 
 -param=analyzer-min-snodes-for-call-summary=
@@ -262,6 +262,10 @@ Wanalyzer-use-of-uninitialized-value
 Common Var(warn_analyzer_use_of_uninitialized_value) Init(1) Warning
 Warn about code paths in which an uninitialized value is used.
 
+Wanalyzer-symbol-too-complex
+Common Var(warn_analyzer_symbol_too_complex) Init(0) Warning
+Warn if expressions are too complicated for the analyzer to fully track.
+
 Wanalyzer-too-complex
 Common Var(warn_analyzer_too_complex) Init(0) Warning
 Warn if the code is too complicated for the analyzer to fully explore.
index ecd57376b549182a4adbecebf1a019c2648955c0..38bd308a9a482533c7e1b1276786833a2790a800 100644 (file)
@@ -2043,6 +2043,11 @@ struct null_assignment_sm_context : public sm_context
     /* No-op.  */
   }
 
+  void clear_all_per_svalue_state () final override
+  {
+    /* No-op.  */
+  }
+
   void on_custom_transition (custom_transition *) final override
   {
   }
index 825b3af43fce6b8ca66ff3d9391304755eca674f..d2524e34f586ab7b4e0786057536471ab79dcbcd 100644 (file)
@@ -474,6 +474,11 @@ public:
     m_new_state->m_checker_states[m_sm_idx]->set_global_state (state);
   }
 
+  void clear_all_per_svalue_state () final override
+  {
+    m_new_state->m_checker_states[m_sm_idx]->clear_all_per_svalue_state ();
+  }
+
   void on_custom_transition (custom_transition *transition) final override
   {
     transition->impl_transition (&m_eg,
index 9bb81e6ddddc2c93a2da861e5216fe93ee2ffc42..78f739ef5efa44a9697e415f70b834baf907ef20 100644 (file)
@@ -526,6 +526,14 @@ sm_state_map::clear_any_state (const svalue *sval)
   m_map.remove (sval);
 }
 
+/* Clear all per-svalue state within this state map.  */
+
+void
+sm_state_map::clear_all_per_svalue_state ()
+{
+  m_map.empty ();
+}
+
 /* Set the "global" state within this state map to STATE.  */
 
 void
index c9b3aa0cbfceb973acf32505dd9e2fa025687598..ef1a2ad54a9809242c96bca865417ebc7a0d6c51 100644 (file)
@@ -146,6 +146,7 @@ public:
                       const svalue *origin,
                       const extrinsic_state &ext_state);
   void clear_any_state (const svalue *sval);
+  void clear_all_per_svalue_state ();
 
   void set_global_state (state_machine::state_t state);
   state_machine::state_t get_global_state () const;
index 921edc558681aa7beebc92ab751d05d50cc69aca..b631bcb04d041b75d41a6568aa24b023ead91530 100644 (file)
@@ -185,6 +185,16 @@ region_model_manager::reject_if_too_complex (svalue *sval)
       return false;
     }
 
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  sval->dump_to_pp (&pp, true);
+  if (warning_at (input_location, OPT_Wanalyzer_symbol_too_complex,
+                 "symbol too complicated: %qs",
+                 pp_formatted_text (&pp)))
+    inform (input_location,
+           "max_depth %i exceeds --param=analyzer-max-svalue-depth=%i",
+           c.m_max_depth, param_analyzer_max_svalue_depth);
+
   delete sval;
   return true;
 }
index d01e3f03951dea9aab69a13fd6151b5e9da3d839..6b5d51c62af92f5d97f8a034e29ee12701847e0e 100644 (file)
@@ -1038,6 +1038,20 @@ taint_state_machine::on_condition (sm_context *sm_ctxt,
   if (stmt == NULL)
     return;
 
+  if (lhs->get_kind () == SK_UNKNOWN
+      || rhs->get_kind () == SK_UNKNOWN)
+    {
+      /* If we have a comparison against UNKNOWN, then
+        we've presumably hit the svalue complexity limit,
+        and we don't know what is being sanitized.
+        Give up on any taint already found on this execution path.  */
+      // TODO: warn about this
+      if (get_logger ())
+       get_logger ()->log ("comparison against UNKNOWN; removing all taint");
+      sm_ctxt->clear_all_per_svalue_state ();
+      return;
+    }
+
   // TODO
   switch (op)
     {
index 3ff9c26078078c419f3a180dcb693765ef30dd3e..ef63d73a54152644bc029b695029228135664872 100644 (file)
@@ -299,6 +299,8 @@ public:
   virtual state_machine::state_t get_global_state () const = 0;
   virtual void set_global_state (state_machine::state_t) = 0;
 
+  virtual void clear_all_per_svalue_state () = 0;
+
   /* A vfunc for handling custom transitions, such as when registering
      a signal handler.  */
   virtual void on_custom_transition (custom_transition *transition) = 0;
index bff3645eedcf13132a20e0e7b39672d6a282b9bc..f8d6f799e11a777d7ffdc78d7d1c29f02516ae59 100644 (file)
@@ -491,6 +491,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wno-analyzer-tainted-divisor
 -Wno-analyzer-tainted-offset
 -Wno-analyzer-tainted-size
+-Wanalyzer-symbol-too-complex
 -Wanalyzer-too-complex
 -Wno-analyzer-undefined-behavior-strtok
 -Wno-analyzer-unsafe-call-within-signal-handler
@@ -10562,6 +10563,19 @@ Enabling this option effectively enables the following warnings:
 This option is only available if GCC was configured with analyzer
 support enabled.
 
+@opindex Wanalyzer-symbol-too-complex
+@opindex Wno-analyzer-symbol-too-complex
+@item -Wanalyzer-symbol-too-complex
+If @option{-fanalyzer} is enabled, the analyzer uses various heuristics
+to attempt to track the state of memory, but these can be defeated by
+sufficiently complicated code.
+
+By default, the analysis silently stops tracking values of expressions
+if they exceed the threshold defined by
+@option{--param analyzer-max-svalue-depth=@var{value}}, and falls back
+to an imprecise representation for such expressions.
+The @option{-Wanalyzer-symbol-too-complex} option warns if this occurs.
+
 @opindex Wanalyzer-too-complex
 @opindex Wno-analyzer-too-complex
 @item -Wanalyzer-too-complex
index 4561e10cafd03100353d2d7450867e0d6d38bfef..b395623cccacc89a9a39690161eb7a438f6e0c89 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-too-complex" } */
+/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
 /* { dg-skip-if "c++98 has no noreturn attribute" { c++98_only } } */
 
 #ifdef __cplusplus
index d4cf079cef84e909bd4c563f8d88a52ff5e045f4..de705836249111377f2984b05d69674262074524 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fanalyzer-call-summaries" } */
+/* { dg-additional-options "-fanalyzer-call-summaries -Wno-analyzer-symbol-too-complex" } */
 
 typedef __SIZE_TYPE__ size_t;
 enum { _ISspace = ((5) < 8 ? ((1 << (5)) << 8) : ((1 << (5)) >> 8)) };
index 1d28e10747cd5df1e3353cd53b302b6e909ed7c0..c4561fcd8a7eac5e0628b0312b5822b9ef82e124 100644 (file)
@@ -1,5 +1,7 @@
 /* Reduced from haproxy-2.7.1's cfgparse.c.  */
 
+/* { dg-additional-options "-Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
+
 typedef __SIZE_TYPE__ size_t;
 
 extern int
index 2fcd064e801f853883364855aba0557145801d74..06194f85069cec2a838a5b38f5b4cf5308e4cba3 100644 (file)
@@ -1,6 +1,8 @@
 /* Reduced and adapted from Linux: fs/proc/inode.c: proc_reg_open
    (GPL v2.0).  */
 
+/* { dg-additional-options "-Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
+
 /* Types.  */
 
 typedef unsigned char u8;
index 45edacf0e53d4104c78370aae49aa4605e0edbe0..963a84bc9ab8420851562b5f7a5a1c7a9d274947 100644 (file)
@@ -5,6 +5,7 @@
 /* { dg-skip-if "" { "avr-*-*" } } */
 /* { dg-additional-options "-fanalyzer-call-summaries" } */
 /* { dg-additional-options "-Wno-analyzer-too-complex" } */
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
 
 /* A lexical scanner generated by flex */
 
@@ -885,8 +886,7 @@ static int yy_get_next_buffer (void)
                                }
                        else
                                /* Can't grow it, we don't own it. */
-                               b->yy_ch_buf = NULL;  /* { dg-bogus "leak" "" { xfail *-*-* } } */
-                               /* TODO: leak false positive: PR analyzer/103546.  */
+                               b->yy_ch_buf = NULL;  /* { dg-bogus "leak" "PR analyzer/103546" } */
 
                        if ( ! b->yy_ch_buf )
                                YY_FATAL_ERROR(
index 5369f7685c286d69e89adcf41d005f6fd4f48195..b1c233121378383cccdf65b4817b05e7e2335285 100644 (file)
@@ -886,8 +886,7 @@ static int yy_get_next_buffer (void)
                                }
                        else
                                /* Can't grow it, we don't own it. */
-                               b->yy_ch_buf = NULL;  /* { dg-bogus "leak" "" { xfail *-*-* } } */
-                               /* TODO: leak false positive: PR analyzer/103546.  */
+                               b->yy_ch_buf = NULL;  /* { dg-bogus "leak" "PR analyzer/103546"  */
 
                        if ( ! b->yy_ch_buf )
                                YY_FATAL_ERROR(
index 68c4fa396caa11a0ee40b923b4cf7d9fc9a5d118..2ae20a1108a7d21522f35d23be4ab40da5d6b33e 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-fno-analyzer-call-summaries -Wno-analyzer-too-complex" } */
+/* { dg-additional-options "-fno-analyzer-call-summaries -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
 
 struct node
 {
index c46ffe91a6b4611dfb4afe7424b7227fe9b018bb..c1c8e6f6a39a14956e15de2ebe001f183ae39da0 100644 (file)
@@ -1,7 +1,7 @@
 /* Reduced from haproxy's src/ssl_sample.c  */
 
 /* { dg-require-effective-target ptr_eq_long } */
-/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-O2 -Wno-analyzer-symbol-too-complex" } */
 
 union sample_value {
   long long int sint;
index ef34a76c50d63cdb692d5843bb3db2afe4068ee1..c5f1fa42e6f193fe6a0374e040945e1d7846aef1 100644 (file)
@@ -1,6 +1,7 @@
 /* Reduced from haproxy's src/ssl_sample.c  */
 
 /* { dg-require-effective-target ptr_eq_long } */
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
 
 union sample_value {
   long long int sint;
index 1151d622519bd2990fdee85dc0f3049beeec477c..9dcf7aa31f1018910e9e26ef53333825345e9a6b 100644 (file)
@@ -1,4 +1,6 @@
 /* Reduced from SoftEtherVPN's src/Cedar/WebUI.c.   */
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
+
 #include "../../gcc.dg/analyzer/analyzer-decls.h"
 typedef int (COMPARE)(void *p1, void *p2);
 typedef unsigned int UINT;
index f7f6923927fc977fe1a2839dc34ebb09b6372f8f..16ef6574d2f48b6e9ea47ee6adfd0a18140fb849 100644 (file)
@@ -1,5 +1,7 @@
 /* Reduced from qemu-7.2.0's hw/intc/omap_intc.c */
 
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
+
 #include "../../gcc.dg/analyzer/analyzer-decls.h"
 
 typedef unsigned char __uint8_t;
index 0c95148ebd57f63f153f0d6d7021dc36e5240f60..1cb1ebe65f965d0333e7df795b7fcce60a5c4b9c 100644 (file)
@@ -1,6 +1,6 @@
 /* Reduced from apr-1.7.0/tables/apr_hash.c: 'apr_hash_merge' */
 
-/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+/* { dg-additional-options "-Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
 
 #include "../../gcc.dg/analyzer/analyzer-decls.h"
 
index 10ea549924e0728e35056dc0a8ea70eaa69bff52..0d6240941d7b2f8cd7232d8529845c6228be405d 100644 (file)
@@ -17,6 +17,9 @@
  */
 
 #include "../../gcc.dg/analyzer/analyzer-decls.h"
+
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
+
 typedef __SIZE_TYPE__ size_t;
 
 #ifndef __cplusplus
index 0336bf0cfe9d60432debc3d520bc750cf00d69f4..f34b4a7198a8fb319c8d8a366049e3a93aad20e0 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
+
 #include "../../gcc.dg/analyzer/analyzer-decls.h"
 
 extern char *strtok (char *str, const char *delim)
index b6b7d49e3c3c28842508a52a02c19d2f4741d65a..793c7fcb7f4049bd33d3536153b79335ed244583 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
+
 #include "../../gcc.dg/analyzer/analyzer-decls.h"
 
 extern char *strtok (char *str, const char *delim);
index a2e912341d6874c209f2c6f4131365b017d6650d..a396c643f11607d4b7b23d7d78912fadac3262d7 100644 (file)
@@ -11,6 +11,8 @@
      should be released under an equivalent license so that everyone could
      benefit from the modified versions. "  */
 
+/* { dg-additional-options " -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
+
 #define __STDC_WANT_LIB_EXT1__ 0
 #include <string.h>
 #include <stdio.h>
index cedf3c0466fd79ccf37b55a85badaec7bbad0d17..ba5aa680fc7aa1cb22959f589c44b671add9ee98 100644 (file)
@@ -30,7 +30,7 @@ if [info exists DEFAULT_CFLAGS] then {
 }
 
 # If a testcase doesn't have special options, use these.
-set DEFAULT_CFLAGS "-fanalyzer -Wanalyzer-too-complex -fanalyzer-call-summaries"
+set DEFAULT_CFLAGS "-fanalyzer -Wanalyzer-too-complex -Wanalyzer-symbol-too-complex -fanalyzer-call-summaries"
 
 if { [istarget "*-*-darwin*" ] } {
   # On macOS, system headers redefine by default some macros (memcpy,
index fc8527a5d0e703d2a0d7eaee8bdcbbd86a04ed13..11238a77a658f714a81576e68eb718b642c6d92d 100644 (file)
@@ -1,7 +1,7 @@
 /* Verify that we handle unknown values passed to  __attribute__ ((const))
    (by imposing a complexity limit).  */
 
-/* { dg-additional-options "--param analyzer-max-svalue-depth=4" } */
+/* { dg-additional-options "--param analyzer-max-svalue-depth=4 -Wno-analyzer-symbol-too-complex" } */
 
 #include "analyzer-decls.h"
 
index 6e583d0228ffbb7ef58eb723fad2fa9ec5f9d080..f59318b428f4c2fee1694404f11d63cbedb367f5 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-require-effective-target int32plus } */
-/* { dg-additional-options "-fanalyzer-call-summaries --param analyzer-min-snodes-for-call-summary=0" } */
+/* { dg-additional-options "-fanalyzer-call-summaries --param analyzer-min-snodes-for-call-summary=0 -Wno-analyzer-symbol-too-complex" } */
 
 /* There need to be at least two calls to a function for the
    call-summarization code to be used.
index ae58f03d3b32aa2da457e613166920c4c4f86a1d..fdc21a2bec3dff8955f9030e3ea847ed3dda434f 100644 (file)
@@ -1,6 +1,6 @@
 /* Reduced from Doom's linuxdoom-1.10/s_sound.c, which is GPLv2 or later.  */
 
-/* { dg-additional-options "-fno-analyzer-call-summaries -Wno-analyzer-too-complex" } */
+/* { dg-additional-options "-fno-analyzer-call-summaries -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
 /* { dg-require-effective-target size32plus } */
 
 typedef struct _IO_FILE FILE;
index 874b1e9c3008e6b640930b9a0d40491a4150836e..a98dfb56bf50ddef207f2ebea5db0d151352a453 100644 (file)
@@ -3,7 +3,7 @@
    conjured_svalues whilst handling a long chain of external
    function calls.  */
 
-/* { dg-additional-options "-Wno-implicit-function-declaration -Wno-int-conversion -Wno-analyzer-too-complex" } */
+/* { dg-additional-options "-Wno-implicit-function-declaration -Wno-int-conversion -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
 
 #define NULL ((void *)0)
 typedef unsigned char uint8_t;
index 3fe061bdbd8819047b53bc207ccf6d3738ed4d2c..65c7bac1f7e96f41d038f6f1dd961dabca402cc2 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-require-effective-target ptr_eq_long } */
-/* { dg-additional-options "-O2 -Wno-shift-count-overflow" } */
+/* { dg-additional-options "-O2 -Wno-shift-count-overflow -Wno-analyzer-symbol-too-complex" } */
 
 struct lisp;
 union vectorlike_header { long size; };
index 2b0ba292e00c3a20197c42c45607dbe5de654b37..5375b4dd6f6ff42e07315f3cb3122b6fe3034648 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-require-effective-target int32plus } */
-/* { dg-additional-options "-Wno-analyzer-too-complex -O2" } */
+/* { dg-additional-options "-Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex -O2" } */
 
 typedef long int ptrdiff_t;
 typedef long int EMACS_INT;
index e34b572966e23e8cf2af5339d42569efbd605c72..d14661cbc6aece2c6144407225c69d15ada1380e 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-O2 -Wno-analyzer-symbol-too-complex" } */
 #include <string.h>
 
 /* Reduced from curl lib/smb.c.  */
index 16faf6eac2f79b869ae8417c889e96a019fc7c54..cc4d801d8a6c53bbc127e5a37f1fe01ffbca69a7 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-additional-options "--param analyzer-max-svalue-depth=0" } */
+/* { dg-additional-options "--param analyzer-max-svalue-depth=0 -Wno-analyzer-symbol-too-complex" } */
 
 int val;
 
index d16cd83c472f31f6538d11d29b256f1c195d614b..a17c3b7e119e12bc53a304a8456059a3cc188570 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-O2 -Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
 
 /* C only: C++ FE optimizes argstr_get_word completely away
    and therefore the number of SN diminishes compared to C,
index 2a15a5d7f5bf682dada2b6c7293e976ccebc17bc..a5130c59cb0075df8205c533f04191381efe9588 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-O2 -Wno-analyzer-symbol-too-complex" } */
 
 #include <stdlib.h>
 
index 8f298ec04e7178363a7f198f1f6acd2c77a67f07..8be30b3a6dad390f2c651aa3e04a82209df9c0cb 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-require-effective-target int128 } */
-/* { dg-additional-options "--param analyzer-max-svalue-depth=0" } */
+/* { dg-additional-options "--param analyzer-max-svalue-depth=0 -Wno-analyzer-symbol-too-complex" } */
 
 void x7 (void)
 {
index 90a818cb2836a3cea66cc367891e1720a49fd3d8..d12b9a1e1fa69680bec116848f3aefee92b195df 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-additional-options "--param analyzer-max-svalue-depth=0" } */
+/* { dg-additional-options "--param analyzer-max-svalue-depth=0 -Wno-analyzer-symbol-too-complex" } */
 
 void x7 (void)
 {
index ac626ba1f3081b222212ded089e6331297bfd393..c3bbce3e6ec189d3c7c77bacf522780764dc91b5 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
+
 #include <stdlib.h>
 
 struct marker {
index fd71d35d7e4e7828408d31e1bf02f450b440077d..f7badb92f448b0fcfc7b7aed507d81f04f4ba93f 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
+
 struct node
 {
   struct node *next;
index 34fe802f4952d34847359b10db6292e99f18b34b..09272011128605ffc5f94f62d5b12373c55a3eab 100644 (file)
@@ -5,6 +5,8 @@
      struct omap_intr_handler_bank_s* bank;
  */
 
+/* { dg-additional-options "-Wno-analyzer-symbol-too-complex" } */
+
 typedef unsigned char __uint8_t;
 typedef unsigned int __uint32_t;
 typedef unsigned long int __uint64_t;
index d7e4bc2c6cac627fc58f53e70713876e2f669f92..76a85f5633500acf9f65a13d139148a710df9ac7 100644 (file)
@@ -1,3 +1,5 @@
+/* { dg-additional-options "-Wno-analyzer-too-complex -Wno-analyzer-symbol-too-complex" } */
+
 #include <stdlib.h>
 #include "analyzer-decls.h"
 
index f098a327d319ef551fc3bc99c3b65d1aa1b5c0b2..f0b4bb7a051fee73b8bce58afd7c617dcb325c74 100644 (file)
@@ -162,7 +162,11 @@ set plugin_test_list [list \
          taint-CVE-2011-0521-5.c \
          taint-CVE-2011-0521-5-fixed.c \
          taint-CVE-2011-0521-6.c \
-         taint-antipatterns-1.c } \
+         taint-antipatterns-1.c \
+         taint-pr112850.c \
+         taint-pr112850-precise.c \
+         taint-pr112850-too-complex.c \
+         taint-pr112850-unsanitized.c } \
     { analyzer_cpython_plugin.c \
          cpython-plugin-test-no-Python-h.c \
          cpython-plugin-test-PyList_Append.c \
index 06b3468fca58d970e6ee306beb84fe3350bc45cf..e268a8eab8fc37e4c7594029e6d1ec705787a241 100644 (file)
@@ -32,9 +32,9 @@ int test_1(struct file *file, unsigned int cmd, unsigned long arg)
                if (info->num > 1)
                        return -EINVAL;
                av7110->ci_slot[info->num].num = info->num; /* { dg-warning "attacker-controlled value" } */
-               av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? /* { dg-warning "attacker-controlled value" } */
+               av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?
                                                        CA_CI_LINK : CA_CI;
-               memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));  /* { dg-warning "attacker-controlled value" } */
+               memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));
        }
 
        copy_to_user((void __user *)arg, parg, sizeof(sbuf));
index 076ada3a20a84cc6fe3fc2fe424cf6a7f17e5d6f..b39e693da63207060a96233f08920f560f64d7d8 100644 (file)
@@ -39,8 +39,7 @@ int test_1(struct file *file, unsigned int cmd, unsigned long arg)
                av7110->ci_slot[info->num].num = info->num;
                av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?
                                                        CA_CI_LINK : CA_CI;
-               memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); /* { dg-bogus "use of attacker-controlled value in array lookup without bounds checking" "" { xfail *-*-* } } */
-               // FIXME: why the above false +ve?
+               memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); /* { dg-bogus "use of attacker-controlled value in array lookup without bounds checking" } */
        }
 
        copy_to_user((void __user *)arg, &sbuf, sizeof(sbuf));
index e27ee469df8f44fc06475b2f30e15d0f30f670e4..fe216c0a3c44b40b90a675af8ca0b4e464a09504 100644 (file)
@@ -37,9 +37,9 @@ int test_1(struct file *file, unsigned int cmd, unsigned long arg)
                __analyzer_dump_state ("taint", info->num); /* { dg-warning "has_ub" } */
 
                av7110->ci_slot[info->num].num = info->num; /* { dg-warning "use of attacker-controlled value '\\*info\\.num' in array lookup without checking for negative" } */
-               av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? /* { dg-warning "use of attacker-controlled value '\\*info\\.num' in array lookup without checking for negative" } */
+               av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?
                                                        CA_CI_LINK : CA_CI;
-               memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); /* { dg-warning "use of attacker-controlled value in array lookup without bounds checking" } */
+               memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));
        }
 
        copy_to_user((void __user *)arg, &sbuf, sizeof(sbuf));
index fea70ee57617d0ee2400cb9235db03e10d5eb3b1..5b68de324708b630497b7ddc84847f56cf3120e9 100644 (file)
@@ -34,9 +34,9 @@ int test_1(struct file *file, unsigned int cmd, unsigned long arg)
                //__analyzer_break ();
 
                av7110->ci_slot[info->num].num = info->num; /* { dg-warning "use of attacker-controlled value '\\*info\\.num' in array lookup without bounds checking" } */
-               av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?  /* { dg-warning "use of attacker-controlled value '\\*info\\.num' in array lookup without bounds checking" } */
+               av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?
                                                        CA_CI_LINK : CA_CI;
-               memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));  /* { dg-warning "use of attacker-controlled value in array lookup without bounds checking" } */
+               memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));
        }
 
        copy_to_user((void __user *)arg, &sbuf, sizeof(sbuf));
diff --git a/gcc/testsuite/gcc.dg/plugin/taint-pr112850-precise.c b/gcc/testsuite/gcc.dg/plugin/taint-pr112850-precise.c
new file mode 100644 (file)
index 0000000..558f0fb
--- /dev/null
@@ -0,0 +1,50 @@
+/* Reduced from false positive in Linux kernel in sound/core/rawmidi.c.
+
+   Use a value of --param=analyzer-max-svalue-depth= high enough to avoid
+   UNKNOWN svalues; make sure we don't get false positives with this case.  */
+
+/* { dg-do compile } */
+/* { dg-options "-fanalyzer -O2 -Wanalyzer-symbol-too-complex --param=analyzer-max-svalue-depth=13" } */
+/* { dg-require-effective-target analyzer } */
+
+typedef unsigned long __kernel_ulong_t;
+typedef __kernel_ulong_t __kernel_size_t;
+typedef __kernel_size_t size_t;
+typedef unsigned int gfp_t;
+
+extern unsigned long copy_from_user(void* to, const void* from, unsigned long n);
+
+extern
+__attribute__((__alloc_size__(1)))
+__attribute__((__malloc__)) void*
+kvzalloc(size_t size, gfp_t flags);
+
+struct snd_rawmidi_params
+{
+  int stream;
+  size_t buffer_size;
+};
+
+char *newbuf;
+
+static int
+resize_runtime_buffer(struct snd_rawmidi_params* params)
+{
+  if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) /* { dg-bogus "symbol too complicated" } */
+    return -22;
+  newbuf = kvzalloc(params->buffer_size, /* { dg-bogus "use of attacker-controlled value '\\*params.buffer_size' as allocation size without upper-bounds checking" "PR analyzer/112850" } */
+                   (((gfp_t)(0x400u | 0x800u)) | ((gfp_t)0x40u) | ((gfp_t)0x80u)));
+  if (!newbuf)
+    return -12;
+  return 0;
+}
+
+long
+snd_rawmidi_ioctl(unsigned long arg)
+{
+  void* argp = (void*)arg;
+  struct snd_rawmidi_params params;
+  if (copy_from_user(&params, argp, sizeof(struct snd_rawmidi_params)))
+    return -14;
+  return resize_runtime_buffer(&params);
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/taint-pr112850-too-complex.c b/gcc/testsuite/gcc.dg/plugin/taint-pr112850-too-complex.c
new file mode 100644 (file)
index 0000000..2a4ee81
--- /dev/null
@@ -0,0 +1,51 @@
+/* Reduced from false positive in Linux kernel in sound/core/rawmidi.c.
+
+   With --param=analyzer-max-svalue-depth=12, the value being compared
+   at the sanitization is too complex and becomes UNKNOWN; make sure
+   this doesn't lead to a false positive.  */
+
+/* { dg-do compile } */
+/* { dg-options "-fanalyzer -O2 -Wanalyzer-symbol-too-complex --param=analyzer-max-svalue-depth=12" } */
+/* { dg-require-effective-target analyzer } */
+
+typedef unsigned long __kernel_ulong_t;
+typedef __kernel_ulong_t __kernel_size_t;
+typedef __kernel_size_t size_t;
+typedef unsigned int gfp_t;
+
+extern unsigned long copy_from_user(void* to, const void* from, unsigned long n);
+
+extern
+__attribute__((__alloc_size__(1)))
+__attribute__((__malloc__)) void*
+kvzalloc(size_t size, gfp_t flags);
+
+struct snd_rawmidi_params
+{
+  int stream;
+  size_t buffer_size;
+};
+
+char *newbuf;
+
+static int
+resize_runtime_buffer(struct snd_rawmidi_params* params)
+{
+  if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) /* { dg-warning "symbol too complicated" } */
+    return -22;
+  newbuf = kvzalloc(params->buffer_size, /* { dg-bogus "use of attacker-controlled value '\\*params.buffer_size' as allocation size without upper-bounds checking" "PR analyzer/112850" } */
+                   (((gfp_t)(0x400u | 0x800u)) | ((gfp_t)0x40u) | ((gfp_t)0x80u)));
+  if (!newbuf)
+    return -12;
+  return 0;
+}
+
+long
+snd_rawmidi_ioctl(unsigned long arg)
+{
+  void* argp = (void*)arg;
+  struct snd_rawmidi_params params;
+  if (copy_from_user(&params, argp, sizeof(struct snd_rawmidi_params)))
+    return -14;
+  return resize_runtime_buffer(&params);
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/taint-pr112850-unsanitized.c b/gcc/testsuite/gcc.dg/plugin/taint-pr112850-unsanitized.c
new file mode 100644 (file)
index 0000000..e46fcb6
--- /dev/null
@@ -0,0 +1,50 @@
+/* Reduced from false positive in Linux kernel in sound/core/rawmidi.c,
+   with sanitization removed to make it a true positive.
+
+   Verify that we detect this (with default params).  */
+
+/* { dg-do compile } */
+/* { dg-options "-fanalyzer -O2 -Wanalyzer-too-complex" } */
+/* { dg-require-effective-target analyzer } */
+
+typedef unsigned long __kernel_ulong_t;
+typedef __kernel_ulong_t __kernel_size_t;
+typedef __kernel_size_t size_t;
+typedef unsigned int gfp_t;
+
+extern unsigned long copy_from_user(void* to, const void* from, unsigned long n);
+
+extern
+__attribute__((__alloc_size__(1)))
+__attribute__((__malloc__)) void*
+kvzalloc(size_t size, gfp_t flags);
+
+struct snd_rawmidi_params
+{
+  int stream;
+  size_t buffer_size;
+};
+
+char *newbuf;
+
+static int
+resize_runtime_buffer(struct snd_rawmidi_params* params)
+{
+  /* No sanitization, so we should complain.  */
+  
+  newbuf = kvzalloc(params->buffer_size, /* { dg-warning "use of attacker-controlled value '\\*params.buffer_size' as allocation size without upper-bounds checking" "PR analyzer/112850" } */
+                   (((gfp_t)(0x400u | 0x800u)) | ((gfp_t)0x40u) | ((gfp_t)0x80u)));
+  if (!newbuf)
+    return -12;
+  return 0;
+}
+
+long
+snd_rawmidi_ioctl(unsigned long arg)
+{
+  void* argp = (void*)arg;
+  struct snd_rawmidi_params params;
+  if (copy_from_user(&params, argp, sizeof(struct snd_rawmidi_params)))
+    return -14;
+  return resize_runtime_buffer(&params);
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/taint-pr112850.c b/gcc/testsuite/gcc.dg/plugin/taint-pr112850.c
new file mode 100644 (file)
index 0000000..6fa1d0f
--- /dev/null
@@ -0,0 +1,47 @@
+/* Reduced from false positive in Linux kernel in sound/core/rawmidi.c.  */
+
+/* { dg-do compile } */
+/* { dg-options "-fanalyzer -O2 -Wanalyzer-symbol-too-complex" } */
+/* { dg-require-effective-target analyzer } */
+
+typedef unsigned long __kernel_ulong_t;
+typedef __kernel_ulong_t __kernel_size_t;
+typedef __kernel_size_t size_t;
+typedef unsigned int gfp_t;
+
+extern unsigned long copy_from_user(void* to, const void* from, unsigned long n);
+
+extern
+__attribute__((__alloc_size__(1)))
+__attribute__((__malloc__)) void*
+kvzalloc(size_t size, gfp_t flags);
+
+struct snd_rawmidi_params
+{
+  int stream;
+  size_t buffer_size;
+};
+
+char *newbuf;
+
+static int
+resize_runtime_buffer(struct snd_rawmidi_params* params)
+{
+  if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) /* { dg-bogus "symbol too complicated" } */
+    return -22;
+  newbuf = kvzalloc(params->buffer_size, /* { dg-bogus "use of attacker-controlled value '\\*params.buffer_size' as allocation size without upper-bounds checking" "PR analyzer/112850" } */
+                   (((gfp_t)(0x400u | 0x800u)) | ((gfp_t)0x40u) | ((gfp_t)0x80u)));
+  if (!newbuf)
+    return -12;
+  return 0;
+}
+
+long
+snd_rawmidi_ioctl(unsigned long arg)
+{
+  void* argp = (void*)arg;
+  struct snd_rawmidi_params params;
+  if (copy_from_user(&params, argp, sizeof(struct snd_rawmidi_params)))
+    return -14;
+  return resize_runtime_buffer(&params);
+}