new file mode 100644
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+
+typedef struct _krb5_data {
+ char *data;
+} krb5_data;
+
+void
+test_1 (krb5_data inbuf, int flag)
+{
+ free(inbuf.data); /* { dg-message "first 'free' here" } */
+ free(inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */
+}
+
+void
+test_2 (krb5_data inbuf, int flag)
+{
+ if (flag) {
+ free(inbuf.data); /* { dg-message "first 'free' here" } */
+ }
+ free(inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */
+}
+
+void
+test_3 (krb5_data inbuf, int flag)
+{
+ if (flag) {
+ free((char *)inbuf.data); /* { dg-message "first 'free' here" } */
+ }
+ free((char *)inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */
+}
new file mode 100644
@@ -0,0 +1,71 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void foo ();
+extern void bar ();
+
+void test_1 (int i)
+{
+ if (i == 42)
+ abort ();
+
+ __analyzer_eval (i != 42); /* { dg-warning "TRUE" } */
+}
+
+void test_2 (int i)
+{
+ if (i)
+ foo ();
+ else
+ bar ();
+
+ foo ();
+
+ if (i)
+ foo ();
+ else
+ abort ();
+
+ __analyzer_eval (i != 0); /* { dg-warning "TRUE" } */
+}
+
+/**************************************************************************/
+
+void calls_abort (const char *msg)
+{
+ fprintf (stderr, "%s", msg);
+ abort ();
+}
+
+void test_3 (void *ptr)
+{
+ if (!ptr)
+ calls_abort ("ptr was NULL");
+
+ __analyzer_eval (ptr != 0); /* { dg-warning "TRUE" } */
+}
+
+/**************************************************************************/
+
+extern void marked_noreturn (const char *msg)
+ __attribute__ ((__noreturn__));
+
+void test_4 (void *ptr)
+{
+ if (!ptr)
+ marked_noreturn ("ptr was NULL");
+
+ __analyzer_eval (ptr != 0); /* { dg-warning "TRUE" } */
+}
+
+/**************************************************************************/
+
+void test_5 (int i)
+{
+ assert (i < 10);
+
+ /* We have not defined NDEBUG, so this will call __assert_fail if
+ i >= 10, which is labelled with __attribute__ ((__noreturn__)). */
+ __analyzer_eval (i < 10); /* { dg-warning "TRUE" } */
+}
new file mode 100644
@@ -0,0 +1,8 @@
+#include <alloca.h>
+
+void *test (void)
+{
+ void *ptr = alloca (64);
+ return ptr;
+}
+/* TODO: warn about escaping alloca. */
new file mode 100644
@@ -0,0 +1,162 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=0" } */
+
+#include <stdlib.h>
+
+void calls_free_1 (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_1 (void *ptr, int a, int b)
+{
+ if (a)
+ calls_free_1 (ptr);
+
+ if (b)
+ {
+ }
+ else
+ calls_free_1 (ptr);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_1': event 1
+ |
+ | NN | calls_free_1 (ptr);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (1) calling 'calls_free_1' from 'test_1'
+ |
+ +--> 'calls_free_1': event 2
+ |
+ | NN | free (ptr);
+ | | ^~~~~~~~~~
+ | | |
+ | | (2) first 'free' here
+ |
+ <------+
+ |
+ 'test_1': events 3-4
+ |
+ | NN | calls_free_1 (ptr);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (3) returning to 'test_1' from 'calls_free_1'
+ |......
+ | NN | calls_free_1 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (4) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
+ |
+ +--> 'calls_free_1': event 5
+ |
+ | NN | free (ptr);
+ | | ^~~~~~~~~~
+ | | |
+ | | (5) second 'free' here; first 'free' was at (2)
+ |
+ { dg-end-multiline-output "" } */
+
+void calls_free_2 (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void *ptr, int a, int b)
+{
+ switch (a)
+ {
+ default:
+ break;
+ case 1:
+ break;
+ case 3:
+ calls_free_2 (ptr);
+ break;
+ }
+
+ switch (b)
+ {
+ default:
+ calls_free_2 (ptr);
+ break;
+ case 1:
+ break;
+ case 42:
+ break;
+ }
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_2': event 1
+ |
+ | NN | calls_free_2 (ptr);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (1) calling 'calls_free_2' from 'test_2'
+ |
+ +--> 'calls_free_2': event 2
+ |
+ | NN | free (ptr);
+ | | ^~~~~~~~~~
+ | | |
+ | | (2) first 'free' here
+ |
+ <------+
+ |
+ 'test_2': events 3-4
+ |
+ | NN | calls_free_2 (ptr);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (3) returning to 'test_2' from 'calls_free_2'
+ |......
+ | NN | calls_free_2 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (4) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
+ |
+ +--> 'calls_free_2': event 5
+ |
+ | NN | free (ptr);
+ | | ^~~~~~~~~~
+ | | |
+ | | (5) second 'free' here; first 'free' was at (2)
+ |
+ { dg-end-multiline-output "" } */
+
+// TODO: range cases
+
+/* The call/return to this function shouldn't appear in the path. */
+
+void called_by_test_3 (void)
+{
+}
+
+void test_3 (void *ptr)
+{
+ free (ptr);
+ called_by_test_3 ();
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_3': events 1-2
+ |
+ | NN | free (ptr);
+ | | ^~~~~~~~~~
+ | | |
+ | | (1) first 'free' here
+ | NN | called_by_test_3 ();
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (2) second 'free' here; first 'free' was at (1)
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,190 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void calls_free_1 (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_1 (void *ptr, int a, int b)
+{
+ if (a)
+ calls_free_1 (ptr);
+
+ if (b)
+ {
+ }
+ else
+ calls_free_1 (ptr);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_1': events 1-2
+ |
+ | NN | void test_1 (void *ptr, int a, int b)
+ | | ^~~~~~
+ | | |
+ | | (1) entry to 'test_1'
+ |......
+ | NN | calls_free_1 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) calling 'calls_free_1' from 'test_1'
+ |
+ +--> 'calls_free_1': events 3-4
+ |
+ | NN | void calls_free_1 (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (3) entry to 'calls_free_1'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (4) first 'free' here
+ |
+ <------+
+ |
+ 'test_1': events 5-6
+ |
+ | NN | calls_free_1 (ptr);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (5) returning to 'test_1' from 'calls_free_1'
+ |......
+ | NN | calls_free_1 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (6) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
+ |
+ +--> 'calls_free_1': events 7-8
+ |
+ | NN | void calls_free_1 (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (7) entry to 'calls_free_1'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (8) second 'free' here; first 'free' was at (4)
+ |
+ { dg-end-multiline-output "" } */
+
+void calls_free_2 (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void *ptr, int a, int b)
+{
+ switch (a)
+ {
+ default:
+ break;
+ case 1:
+ break;
+ case 3:
+ calls_free_2 (ptr);
+ break;
+ }
+
+ switch (b)
+ {
+ default:
+ calls_free_2 (ptr);
+ break;
+ case 1:
+ break;
+ case 42:
+ break;
+ }
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_2': events 1-2
+ |
+ | NN | void test_2 (void *ptr, int a, int b)
+ | | ^~~~~~
+ | | |
+ | | (1) entry to 'test_2'
+ |......
+ | NN | calls_free_2 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) calling 'calls_free_2' from 'test_2'
+ |
+ +--> 'calls_free_2': events 3-4
+ |
+ | NN | void calls_free_2 (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (3) entry to 'calls_free_2'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (4) first 'free' here
+ |
+ <------+
+ |
+ 'test_2': events 5-6
+ |
+ | NN | calls_free_2 (ptr);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (5) returning to 'test_2' from 'calls_free_2'
+ |......
+ | NN | calls_free_2 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (6) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
+ |
+ +--> 'calls_free_2': events 7-8
+ |
+ | NN | void calls_free_2 (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (7) entry to 'calls_free_2'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (8) second 'free' here; first 'free' was at (4)
+ |
+ { dg-end-multiline-output "" } */
+
+/* The call/return to this function shouldn't appear in the path. */
+
+void called_by_test_3 (void)
+{
+}
+
+void test_3 (void *ptr)
+{
+ free (ptr);
+ called_by_test_3 ();
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_3': events 1-2
+ |
+ | NN | free (ptr);
+ | | ^~~~~~~~~~
+ | | |
+ | | (1) first 'free' here
+ | NN | called_by_test_3 ();
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (2) second 'free' here; first 'free' was at (1)
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,221 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=2" } */
+
+#include <stdlib.h>
+
+void calls_free_1 (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_1 (void *ptr, int a, int b)
+{
+ if (a)
+ calls_free_1 (ptr);
+
+ if (b)
+ {
+ }
+ else
+ calls_free_1 (ptr);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_1': events 1-4
+ |
+ | NN | void test_1 (void *ptr, int a, int b)
+ | | ^~~~~~
+ | | |
+ | | (1) entry to 'test_1'
+ | NN | {
+ | NN | if (a)
+ | | ~
+ | | |
+ | | (2) following 'true' branch (when 'a != 0')...
+ | NN | calls_free_1 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (3) ...to here
+ | | (4) calling 'calls_free_1' from 'test_1'
+ |
+ +--> 'calls_free_1': events 5-6
+ |
+ | NN | void calls_free_1 (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (5) entry to 'calls_free_1'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (6) first 'free' here
+ |
+ <------+
+ |
+ 'test_1': events 7-10
+ |
+ | NN | calls_free_1 (ptr);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) returning to 'test_1' from 'calls_free_1'
+ | NN |
+ | NN | if (b)
+ | | ~
+ | | |
+ | | (8) following 'false' branch (when 'b == 0')...
+ |......
+ | NN | calls_free_1 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (9) ...to here
+ | | (10) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1'
+ |
+ +--> 'calls_free_1': events 11-12
+ |
+ | NN | void calls_free_1 (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (11) entry to 'calls_free_1'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (12) second 'free' here; first 'free' was at (6)
+ |
+ { dg-end-multiline-output "" } */
+
+void calls_free_2 (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void *ptr, int a, int b)
+{
+ switch (a)
+ {
+ default:
+ break;
+ case 1:
+ break;
+ case 3:
+ calls_free_2 (ptr);
+ break;
+ }
+
+ switch (b)
+ {
+ default:
+ calls_free_2 (ptr);
+ break;
+ case 1:
+ break;
+ case 42:
+ break;
+ }
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_2': events 1-4
+ |
+ | NN | void test_2 (void *ptr, int a, int b)
+ | | ^~~~~~
+ | | |
+ | | (1) entry to 'test_2'
+ | NN | {
+ | NN | switch (a)
+ | | ~~~~~~
+ | | |
+ | | (2) following 'case 3:' branch...
+ |......
+ | NN | case 3:
+ | | ~~~~
+ | | |
+ | | (3) ...to here
+ | NN | calls_free_2 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (4) calling 'calls_free_2' from 'test_2'
+ |
+ +--> 'calls_free_2': events 5-6
+ |
+ | NN | void calls_free_2 (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (5) entry to 'calls_free_2'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (6) first 'free' here
+ |
+ <------+
+ |
+ 'test_2': events 7-10
+ |
+ | NN | calls_free_2 (ptr);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) returning to 'test_2' from 'calls_free_2'
+ |......
+ | NN | switch (b)
+ | | ~~~~~~
+ | | |
+ | | (8) following 'default:' branch...
+ | NN | {
+ | NN | default:
+ | | ~~~~~~~
+ | | |
+ | | (9) ...to here
+ | NN | calls_free_2 (ptr);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (10) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2'
+ |
+ +--> 'calls_free_2': events 11-12
+ |
+ | NN | void calls_free_2 (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (11) entry to 'calls_free_2'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (12) second 'free' here; first 'free' was at (6)
+ |
+ { dg-end-multiline-output "" } */
+
+// TODO: range cases
+
+/* The call/return to this function shouldn't appear in the path. */
+
+void called_by_test_3 (void)
+{
+}
+
+void test_3 (void *ptr)
+{
+ free (ptr);
+ called_by_test_3 ();
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_3': events 1-2
+ |
+ | NN | free (ptr);
+ | | ^~~~~~~~~~
+ | | |
+ | | (1) first 'free' here
+ | NN | called_by_test_3 ();
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (2) second 'free' here; first 'free' was at (1)
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,49 @@
+# Copyright (C) 2019 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib gcc-dg.exp
+
+# If the analyzer has not been enabled, bail.
+if { ![check_effective_target_analyzer] } {
+ return
+}
+
+global DEFAULT_CFLAGS
+if [info exists DEFAULT_CFLAGS] then {
+ set save_default_cflags $DEFAULT_CFLAGS
+}
+
+# If a testcase doesn't have special options, use these.
+set DEFAULT_CFLAGS "-fanalyzer -fdiagnostics-path-format=separate-events -Wanalyzer-too-complex -fanalyzer-call-summaries"
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \
+ "" $DEFAULT_CFLAGS
+
+# All done.
+dg-finish
+
+if [info exists save_default_cflags] {
+ set DEFAULT_CFLAGS $save_default_cflags
+} else {
+ unset DEFAULT_CFLAGS
+}
new file mode 100644
@@ -0,0 +1,81 @@
+#include <stdlib.h>
+
+extern void foo(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1 of 'foo' must be non-null" } */
+ __attribute__((nonnull (1, 3)));
+
+extern void bar(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1 of 'bar' must be non-null" } */
+ __attribute__((nonnull));
+
+// TODO: complain about NULL and possible NULL args
+// FIXME: ought to complain about NULL args
+
+void test_1 (void *p, void *q, void *r)
+{
+ foo(p, q, r);
+ foo(NULL, q, r);
+ foo(p, NULL, r);
+ foo(p, q, NULL);
+}
+
+void test_1a (void *q, void *r)
+{
+ void *p = NULL;
+ foo(p, q, r); /* { dg-warning "use of NULL 'p' where non-null expected" } */
+ /* { dg-message "argument 1 \\('p'\\) NULL where non-null expected" "" { target *-*-* } .-1 } */
+}
+
+void test_2 (void *p, void *q, void *r)
+{
+ bar(p, q, r);
+ bar(NULL, q, r);
+ bar(p, NULL, r);
+ bar(p, q, NULL);
+}
+
+void test_3 (void *q, void *r)
+{
+ void *p = malloc(1024); /* { dg-message "\\(1\\) this call could return NULL" } */
+
+ foo(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+ /* { dg-message "argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+
+ foo(p, q, r);
+
+ free(p);
+}
+
+void test_4 (void *q, void *r)
+{
+ void *p = malloc(1024); /* { dg-message "\\(1\\) this call could return NULL" } */
+
+ bar(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+ /* { dg-message "argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+
+ bar(p, q, r);
+
+ free(p);
+}
+
+/* Verify that we detect passing NULL to a __attribute__((nonnull)) function
+ when it's called via a function pointer. */
+
+typedef void (*bar_t)(void *ptrA, void *ptrB, void *ptrC);
+
+static bar_t __attribute__((noinline))
+get_bar (void)
+{
+ return bar;
+}
+
+void test_5 (void *q, void *r)
+{
+ void *p = malloc(1024); /* { dg-message "\\(1\\) this call could return NULL" } */
+ bar_t cb = get_bar ();
+ cb(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+ /* { dg-message "argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+ /* TODO: do we want an event showing where cb is assigned "bar"? */
+
+ cb(p, q, r);
+
+ free(p);
+}
new file mode 100644
@@ -0,0 +1,14 @@
+/* { dg-additional-options "-fanalyzer-call-summaries" } */
+
+#include <stdlib.h>
+
+void calls_free (void *p)
+{
+ free (p); /* { dg-warning "double-'free' of 'p'" } */
+}
+
+void test (void *q)
+{
+ calls_free (q);
+ calls_free (q);
+}
new file mode 100644
@@ -0,0 +1,44 @@
+// TODO: run this test case at every optimization level
+/* { dg-additional-options "-O2" } */
+
+#include <stddef.h>
+
+#define Z_NULL 0
+
+static void __attribute__((noinline))
+test_1_callee (void *p, void *q)
+{
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ __analyzer_eval (p == Z_NULL); /* { dg-warning "FALSE" } */
+ __analyzer_eval (p != Z_NULL); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q == Z_NULL); /* { dg-warning "FALSE" } */
+ __analyzer_eval (q != Z_NULL); /* { dg-warning "TRUE" } */
+}
+
+void test_1 (void *p, void *q)
+{
+ if (p == Z_NULL || q == Z_NULL)
+ return;
+
+ test_1_callee (p, q);
+}
+
+static void __attribute__((noinline))
+test_2_callee (void *p, void *q)
+{
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ __analyzer_eval (p == Z_NULL); /* { dg-warning "FALSE" } */
+ __analyzer_eval (p != Z_NULL); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q == Z_NULL); /* { dg-warning "FALSE" } */
+ __analyzer_eval (q != Z_NULL); /* { dg-warning "TRUE" } */
+}
+
+void test_2 (void *p, void *q)
+{
+ if (p != Z_NULL && q != Z_NULL)
+ test_2_callee (p, q);
+}
new file mode 100644
@@ -0,0 +1,45 @@
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+static void only_called_when_flag_a_true (int i)
+{
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+}
+
+static void only_called_when_flag_b_true (int i)
+{
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+int test_1 (int flag_a, int flag_b)
+{
+ int i = 17;
+
+ __analyzer_eval (flag_a); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+
+ if (flag_a)
+ {
+ __analyzer_eval (flag_a); /* { dg-warning "TRUE" } */
+ __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+ i = 42;
+ }
+
+ __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+
+ if (flag_a)
+ {
+ __analyzer_eval (flag_a); /* { dg-warning "TRUE" } */
+ __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i == 17); /* { dg-warning "FALSE" } */
+ only_called_when_flag_a_true (i);
+ }
+ else
+ {
+ __analyzer_eval (flag_a); /* { dg-warning "FALSE" } */
+ __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i == 42); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+ only_called_when_flag_b_true (i);
+ }
+}
new file mode 100644
@@ -0,0 +1,158 @@
+/* { dg-additional-options "-fno-analyzer-transitivity" } */
+
+void test (int i, int j)
+{
+ if (i > 4)
+ {
+ __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+
+ __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i != 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+
+ __analyzer_eval (i == 3); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+
+ __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i == 5); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i < 5); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i <= 5); /* { dg-warning "UNKNOWN" } */
+
+ /* Tests of transitivity. */
+ if (j < i)
+ {
+ __analyzer_eval (j < i); /* { dg-warning "TRUE" } */
+ __analyzer_eval (j <= 4); /* { dg-warning "UNKNOWN" } */
+ }
+ else
+ {
+ __analyzer_eval (j >= i); /* { dg-warning "TRUE" } */
+ __analyzer_eval (j > 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ }
+ }
+ else
+ {
+ __analyzer_eval (i > 4); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i > 5); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i == 5); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i != 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i < 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i <= 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ }
+}
+
+void test_2 (int i, int j, int k)
+{
+ if (i >= j)
+ {
+ __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+ if (j >= k)
+ {
+ __analyzer_eval (i >= k); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+ if (k >= i)
+ __analyzer_eval (i == k); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ }
+ }
+}
+
+void test_3 (int flag, unsigned int i)
+{
+ if (!flag) {
+ return;
+ }
+
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+
+ if (i>0) {
+ __analyzer_eval (i > 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+ } else {
+ __analyzer_eval (i <= 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+ }
+
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+}
+
+void test_range_int_gt_lt (int i)
+{
+ if (i > 3)
+ if (i < 5)
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
+
+void test_range_float_gt_lt (float f)
+{
+ if (f > 3)
+ if (f < 5)
+ __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_lt (int i)
+{
+ if (i >= 4)
+ if (i < 5)
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
+
+void test_range_float_ge_lt (float f)
+{
+ if (f >= 4)
+ if (f < 5)
+ __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_gt_le (int i)
+{
+ if (i > 3)
+ if (i <= 4)
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
+
+void test_range_float_gt_le (float f)
+{
+ if (f > 3)
+ if (f <= 4)
+ __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_le (int i)
+{
+ if (i >= 4)
+ if (i <= 4)
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
+
+void test_range_float_ge_le (float f)
+{
+ if (f >= 4)
+ if (f <= 4)
+ __analyzer_eval (f == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+}
new file mode 100644
@@ -0,0 +1,143 @@
+/* { dg-additional-options "-fanalyzer-transitivity" } */
+
+void test (int i, int j)
+{
+ if (i > 4)
+ {
+ __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i > 3); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i != 3); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */
+
+ __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i == 5); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i < 5); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i <= 5); /* { dg-warning "UNKNOWN" } */
+
+ /* Tests of transitivity. */
+ if (j < i)
+ {
+ __analyzer_eval (j < i); /* { dg-warning "TRUE" } */
+ __analyzer_eval (j <= 4); /* { dg-warning "UNKNOWN" } */
+ }
+ else
+ {
+ __analyzer_eval (j >= i); /* { dg-warning "TRUE" } */
+ __analyzer_eval (j > 4); /* { dg-warning "TRUE" } */
+ }
+ }
+ else
+ {
+ __analyzer_eval (i > 4); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i > 5); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i != 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i < 5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */
+ }
+}
+
+void test_2 (int i, int j, int k)
+{
+ if (i >= j)
+ {
+ __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+ if (j >= k)
+ {
+ __analyzer_eval (i >= k); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+ if (k >= i)
+ __analyzer_eval (i == k); /* { dg-warning "TRUE" } */
+ }
+ }
+}
+
+void test_3 (int flag, unsigned int i)
+{
+ if (!flag) {
+ return;
+ }
+
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+
+ if (i>0) {
+ __analyzer_eval (i > 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+ } else {
+ __analyzer_eval (i <= 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+ }
+
+ __analyzer_eval (flag); /* { dg-warning "TRUE" } */
+}
+
+void test_range_int_gt_lt (int i)
+{
+ if (i > 3)
+ if (i < 5)
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_range_float_gt_lt (float f)
+{
+ if (f > 3)
+ if (f < 5)
+ __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_lt (int i)
+{
+ if (i >= 4)
+ if (i < 5)
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_range_float_ge_lt (float f)
+{
+ if (f >= 4)
+ if (f < 5)
+ __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_gt_le (int i)
+{
+ if (i > 3)
+ if (i <= 4)
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_range_float_gt_le (float f)
+{
+ if (f > 3)
+ if (f <= 4)
+ __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_range_int_ge_le (int i)
+{
+ if (i >= 4)
+ if (i <= 4)
+ __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */
+}
+
+void test_range_float_ge_le (float f)
+{
+ if (f >= 4)
+ if (f <= 4)
+ __analyzer_eval (f == 4); /* { dg-warning "TRUE" } */
+}
new file mode 100644
@@ -0,0 +1,1084 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+struct foo
+{
+ int i;
+};
+
+/* Fields of a local. */
+
+void test_1 (void)
+{
+ struct foo f;
+ f.i = 1;
+ __analyzer_eval (f.i == 1); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a param. */
+
+void test_2 (struct foo f)
+{
+ __analyzer_eval (f.i == 2); /* { dg-warning "UNKNOWN" } */
+ f.i = 2;
+ __analyzer_eval (f.i == 2); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a param ptr. */
+
+void test_3 (struct foo *f)
+{
+ __analyzer_eval (f->i == 3); /* { dg-warning "UNKNOWN" } */
+ f->i = 3;
+ __analyzer_eval (f->i == 3); /* { dg-warning "TRUE" } */
+}
+
+/* Fields of a global ptr. */
+struct foo *global_foo_ptr;
+
+void test_3a (void)
+{
+ struct foo *tmp = global_foo_ptr;
+ __analyzer_eval (global_foo_ptr->i == 3); /* { dg-warning "UNKNOWN" } */
+ global_foo_ptr->i = 3;
+ __analyzer_eval (global_foo_ptr->i == 3); /* { dg-warning "TRUE" } */
+}
+
+/* Pointer to a local. */
+
+void test_4 (void)
+{
+ int i;
+ int *p = &i;
+ i = 1;
+ *p = 2;
+ __analyzer_eval (i == 2); /* { dg-warning "TRUE" } */
+}
+
+/* Local array. */
+
+void test_5 (void)
+{
+ int a[10];
+ a[3] = 5; /* ARRAY_REF. */
+ __analyzer_eval (a[3] == 5); /* { dg-warning "TRUE" } */
+}
+
+/* Local array, but using an unknown index. */
+
+void test_5a (int idx)
+{
+ int a[10];
+ a[idx] = 5; /* ARRAY_REF. */
+ __analyzer_eval (a[idx] == 5); /* { dg-warning "TRUE" } */
+}
+
+/* Array passed in as a param. */
+
+void test_6 (int a[10])
+{
+ /* POINTER_PLUS_EXPR then a MEM_REF. */
+ __analyzer_eval (a[3] == 42); /* { dg-warning "UNKNOWN" } */
+ a[3] = 42;
+ __analyzer_eval (a[3] == 42); /* { dg-warning "TRUE" } */
+}
+
+/* Array passed in as a param ptr. */
+
+void test_7 (int *a)
+{
+ __analyzer_eval (a[3] == 42); /* { dg-warning "UNKNOWN" } */
+ a[3] = 42;
+ __analyzer_eval (a[3] == 42); /* { dg-warning "TRUE" } */
+}
+
+/* Globals. */
+
+int glob_a;
+
+void test_10 (void)
+{
+ __analyzer_eval (glob_a == 42); /* { dg-warning "UNKNOWN" } */
+ glob_a = 42;
+ __analyzer_eval (glob_a == 42); /* { dg-warning "TRUE" } */
+}
+
+/* malloc. */
+
+void test_11 (void)
+{
+ void *p = malloc (256);
+ void *q = malloc (256);
+
+ /* malloc results should be unique. */
+ __analyzer_eval (p == q); /* { dg-warning "FALSE" } */
+ __analyzer_eval (p != q); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p <= q); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p >= q); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p < q); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p > q); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (p == p); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p != p); /* { dg-warning "FALSE" } */
+ __analyzer_eval (p <= p); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p >= p); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p < p); /* { dg-warning "FALSE" } */
+ __analyzer_eval (p > p); /* { dg-warning "FALSE" } */
+
+ free (p);
+ free (q);
+ // TODO: mark freed memory as freed
+ //__analyzer_break ();
+}
+
+/* alloca. */
+
+void test_12 (void)
+{
+ void *p = alloca (256);
+ void *q = alloca (256);
+
+ /* alloca results should be unique. */
+ __analyzer_eval (p == q); /* { dg-warning "FALSE" } */
+
+ // FIXME: complain about uses of poisoned values
+}
+
+/* Use of uninit value. */
+int test_12a (void)
+{
+ int i;
+ return i; // FIXME: do we see the return stmt?
+}
+
+void test_12b (void *p, void *q)
+{
+ __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */
+}
+
+int test_12c (void)
+{
+ int i;
+ int j;
+
+ j = i; // FIXME: should complain about this
+
+ return j;
+}
+
+struct coord
+{
+ int x;
+ int y;
+};
+
+int test_12d (struct coord c)
+{
+ struct coord d;
+ d = c;
+ __analyzer_eval (d.x == c.x); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): c and d share the same unknown value of type "coord", but
+ attempts to access the fields lead to different unknown values. */
+
+ __analyzer_eval (d.y == c.y); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail): likewise
+
+ __analyzer_eval (d.x == d.y); /* { dg-warning "UNKNOWN" } */
+ /* d and c share an unknown value of type "struct coord".
+ But d.x and d.y should be different unknown values (although they inherit
+ from d's region). */
+}
+
+/* Nested structs. */
+
+struct outer
+{
+ struct middle {
+ struct inner {
+ float f;
+ } in;
+ } mid;
+};
+
+void test_13 (struct outer *o)
+{
+ __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */
+ o->mid.in.f = 0.f;
+ __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "TRUE" } */
+}
+
+void test_14 (struct outer o)
+{
+ __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */
+ o.mid.in.f = 0.f;
+ __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "TRUE" } */
+}
+
+void test_15 (const char *str)
+{
+ char ch = str[0];
+ __analyzer_eval (ch == 'a'); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (ch == str[0]); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+
+ ch = 'a';
+ __analyzer_eval (ch == 'a'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (str[0] == 'a'); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16 (void)
+{
+ const char *msg = "hello world";
+
+ __analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+
+ __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+
+ __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
+}
+
+static const char *__attribute__((noinline))
+get_hello_world (void)
+{
+ return "hello world";
+}
+
+void test_16_alt (void)
+{
+ const char *msg = get_hello_world ();
+
+ __analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+
+ __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+
+ __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */
+}
+
+void test_16a (const char *msg)
+{
+ __analyzer_eval (strlen (msg) == 11); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16b (const char *msg)
+{
+ __analyzer_eval (strlen (msg) == strlen (msg)); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+}
+
+extern int unknown_result (void);
+
+void test_16c (int i)
+{
+ int j;
+
+ j = i;
+ __analyzer_eval (j == i); /* { dg-warning "TRUE" } */
+
+ j = unknown_result ();
+ __analyzer_eval (j == i); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_16c_a (void)
+{
+ int i, j;
+
+ i = unknown_result ();
+ j = unknown_result ();
+ __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+}
+
+int global_int_16d;
+
+void test_16d (int i)
+{
+ global_int_16d = i;
+ __analyzer_eval (global_int_16d == i); /* { dg-warning "TRUE" } */
+
+ global_int_16d = unknown_result ();
+ __analyzer_eval (global_int_16d == i); /* { dg-warning "UNKNOWN" } */
+}
+
+extern void might_write_to (int *);
+
+void test_16e (int i)
+{
+ int j;
+
+ j = i;
+ __analyzer_eval (j == i); /* { dg-warning "TRUE" } */
+
+ might_write_to (&j);
+ __analyzer_eval (j == i); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+ /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+}
+
+/* TODO: and more complicated graph-like examples, where anything that's
+ reachable from the pointer might be modified. */
+
+void test_17 (int i)
+{
+ int j = 42;
+ __analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+ i = j;
+ __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+}
+
+void test_18 (int i)
+{
+ int j;
+ __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+ j = i;
+
+ __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i >= j); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i <= j); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (i != j); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i > j); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i < j); /* { dg-warning "FALSE" } */
+}
+
+void test_19 (void)
+{
+ int i, j;
+ /* Compare two uninitialized locals. */
+ __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_20 (int i, int j)
+{
+ __analyzer_eval (i + 1); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i + j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i - 1); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i - j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i * 2); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i * j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i / 2); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i / j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i % 2); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i % j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i & 1); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i & j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i | 1); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i | j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i ^ 1); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i ^ j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i >> 1); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i >> j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i << 1); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i << j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i && 0); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i && 1); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i && j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i || 0); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (i || 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i || j); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval (~i); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (-i); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (+i); /* { dg-warning "UNKNOWN" } */
+
+ /* Anything added above should be added to the next function also. */
+}
+
+/* As above, but where the values are known due to the region model,
+ but not known to GCC's regular optimizations (folding and SSA). */
+
+void test_21 (void)
+{
+ int i, j, zero;
+ int *pi = &i;
+ int *pj = &j;
+ int *pzero = &zero;
+ *pi = 5;
+ *pj = 3;
+ *pzero = 0;
+
+ __analyzer_eval (i + j == 8); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i - j == 2); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i * j == 15); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i / j == 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i % j == 2); /* { dg-warning "TRUE" } */
+
+ /* Division by zero. */
+ // TODO: should we warn for this?
+ __analyzer_eval (i / zero); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i % zero); /* { dg-warning "UNKNOWN" } */
+
+ __analyzer_eval ((i & 1) == (5 & 1)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i & j) == (5 & 3)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i | 1) == (5 | 1)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i | j) == (5 | 3)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i ^ 1) == (5 ^ 1)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i ^ j) == (5 ^ 3)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i >> 1) == (5 >> 1)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i >> j) == (5 >> 3)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i << 1) == (5 << 1)); /* { dg-warning "TRUE" } */
+ __analyzer_eval ((i << j) == (5 << 3)); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i && 0); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i && 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i && j); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (i || 0); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i || 1); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i || j); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (~i == ~5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (-i == -5); /* { dg-warning "TRUE" } */
+ __analyzer_eval (+i == +5); /* { dg-warning "TRUE" } */
+}
+
+void test_22 (int i, int j)
+{
+ __analyzer_eval (i + j == i + j); /* { dg-warning "TRUE" } */
+ // FIXME: this is getting folded; can we build a non-folded equivalent?
+}
+
+void test_23 (struct foo *f, struct foo *g)
+{
+ int i, j, k;
+ i = f->i + g->i;
+ j = f->i + g->i;
+ k = f->i * g->i;
+ __analyzer_eval (i == j); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): we'd need to record that the two unknown values are both
+ the sum of the two unknown input values (and thus are the same); not
+ yet sure if we want arbitrary expression trees in the representation
+ (analysis termination concerns). */
+
+ __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_24 (struct foo *f)
+{
+ struct foo g;
+ g.i = 42;
+ __analyzer_eval (g.i == 42); /* { dg-warning "TRUE" } */
+
+ /* Overwriting a whole struct should invalidate our knowledge
+ about fields within it. */
+ g = *f;
+ __analyzer_eval (g.i == 42); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+ /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+}
+
+void test_25 (struct foo *f)
+{
+ struct foo g;
+ g.i = 42;
+ f->i = 43;
+ __analyzer_eval (f->i == 43); /* { dg-warning "TRUE" } */
+ __analyzer_eval (g.i == 42); /* { dg-warning "TRUE" } */
+
+ /* Overwriting a whole struct where we know things about the
+ source value should update our knowledge about fields within
+ the dest value. */
+ g = *f;
+ __analyzer_eval (g.i == 43); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+ // TODO(xfail)
+}
+
+void test_26 (struct coord *p, struct coord *q)
+{
+ p->x = 42;
+ q->y = 17;
+ __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p->y); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
+
+ /* Overwriting a whole struct where we know some things about the
+ source value should update our knowledge about fields within
+ the dest value. */
+ *p = *q;
+ __analyzer_eval (p->x); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+ /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */
+ // TODO(xfail): should have been overwritten
+ __analyzer_eval (p->y == 17); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail): should have been overwritten with q->y
+
+ __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */
+}
+
+void test_27 (struct coord *p)
+{
+ memset (p, 0, sizeof (struct coord));
+ __analyzer_eval (p->x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail):
+ __analyzer_eval (p->y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail):
+}
+
+void test_28 (struct coord *p)
+{
+ memset (p, 0, sizeof (struct coord) * 10);
+ __analyzer_eval (p[0].x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail):
+ __analyzer_eval (p[0].y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail):
+
+ __analyzer_eval (p[9].x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail):
+ __analyzer_eval (p[9].y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail):
+
+ __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_29 (struct coord *p)
+{
+ struct coord *q;
+
+ p[0].x = 100024;
+ p[0].y = 100025;
+
+ p[7].x = 107024;
+ p[7].y = 107025;
+
+ p[9].x = 109024;
+ p[9].y = 109025;
+
+ __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+ q = &p[7];
+
+ __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+ q += 2;
+
+ __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+ q -= 2;
+
+ __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_29a (struct coord p[])
+{
+ struct coord *q;
+
+ p[0].x = 100024;
+ p[0].y = 100025;
+
+ p[7].x = 107024;
+ p[7].y = 107025;
+
+ p[9].x = 109024;
+ p[9].y = 109025;
+
+ __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+ q = &p[7];
+
+ __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+ q += 2;
+
+ __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+ q -= 2;
+
+ __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_29b (void)
+{
+ struct coord p[11];
+ struct coord *q;
+
+ p[0].x = 100024;
+ p[0].y = 100025;
+
+ p[7].x = 107024;
+ p[7].y = 107025;
+
+ p[9].x = 109024;
+ p[9].y = 109025;
+
+ __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+ q = &p[7];
+
+ __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+ q += 2;
+
+ __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+ q -= 2;
+
+ __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_29c (int len)
+{
+ struct coord p[len];
+ struct coord *q;
+
+ p[0].x = 100024;
+ p[0].y = 100025;
+
+ p[7].x = 107024;
+ p[7].y = 107025;
+
+ p[9].x = 109024;
+ p[9].y = 109025;
+
+ __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */
+
+ q = &p[7];
+
+ __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */
+
+ q += 2;
+
+ __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */
+
+ q -= 2;
+
+ __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */
+}
+
+void test_30 (void *ptr)
+{
+ struct coord *p = (struct coord *)ptr;
+ struct coord *q = (struct coord *)ptr;
+
+ p->x = 42;
+
+ __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q->x == 42); /* { dg-warning "TRUE" } */
+}
+
+void test_31 (unsigned i)
+{
+ int j, k;
+
+ j = i < 100 ? i : 100; /* MIN_EXPR. */
+ k = i < 100 ? 100 : i; /* MAX_EXPR. */
+}
+
+enum color
+{
+ RED,
+ GREEN,
+ BLUE
+};
+
+void test_32 (enum color c)
+{
+ __analyzer_eval (c == GREEN); /* { dg-warning "UNKNOWN" } */
+
+ c = RED;
+
+ __analyzer_eval (c == RED); /* { dg-warning "TRUE" } */
+ __analyzer_eval (c == GREEN); /* { dg-warning "FALSE" } */
+}
+
+void test_33 (void)
+{
+ static int s;
+
+ __analyzer_eval (s == 42); /* { dg-warning "UNKNOWN" } */
+
+ s = 42;
+
+ __analyzer_eval (s == 42); /* { dg-warning "TRUE" } */
+}
+
+static int __attribute__((noinline))
+only_called_by_test_34 (int parm)
+{
+ __analyzer_eval (parm == 42); /* { dg-warning "TRUE" } */
+
+ return parm * 2;
+}
+
+void test_34 (void)
+{
+ int result = only_called_by_test_34 (42);
+ __analyzer_eval (result == 84); /* { dg-warning "TRUE" } */
+}
+
+void test_35 (int i, int j)
+{
+ __analyzer_eval (&i == &i); /* { dg-warning "TRUE" } */
+ __analyzer_eval (&i != &j); /* { dg-warning "TRUE" } */
+}
+
+static void __attribute__((noinline))
+write_through_ptr (int *dst, int val)
+{
+ *dst = val;
+}
+
+void test_36 (int i)
+{
+ __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+ write_through_ptr (&i, 42);
+
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+}
+
+/* Read through uninitialized pointer. */
+
+int test_37 (void)
+{
+ int *ptr;
+ return *ptr; /* { dg-warning "use of uninitialized value 'ptr'" } */
+}
+
+/* Write through uninitialized pointer. */
+
+void test_37a (int i)
+{
+ int *ptr;
+ *ptr = i; /* { dg-warning "use of uninitialized value 'ptr'" } */
+}
+
+// TODO: the various other ptr deref poisonings
+
+/* Read through NULL pointer. */
+
+int test_38 (void)
+{
+ int *ptr = NULL;
+ return *ptr; /* { dg-warning "dereference of NULL 'ptr'" } */
+}
+
+/* Write through NULL pointer. */
+
+int test_38a (int i)
+{
+ int *ptr = NULL;
+ *ptr = i; /* { dg-warning "dereference of NULL 'ptr'" } */
+}
+
+/* Read through non-NULL constant pointer. */
+
+int test_39 (void)
+{
+ int *ptr = (int *)0x1000;
+ return *ptr;
+}
+
+int test_40 (int flag)
+{
+ int i;
+ if (flag)
+ i = 43;
+ else
+ i = 17;
+
+ /* With state-merging, we lose the relationship between 'flag' and 'i'. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (flag)
+ __analyzer_eval (i == 43); /* { dg-warning "UNKNOWN" } */
+ else
+ __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
+
+struct link
+{
+ struct link *next;
+ int f;
+};
+
+/* Traversing a singly-linked list. */
+
+void foo (struct link *in)
+{
+ struct link a;
+ struct link b;
+ struct link c;
+ a.next = &b;
+ b.next = &c;
+ in->next = &a;
+ c.f = 42;
+ __analyzer_eval (in->next->next->next->f == 42); /* { dg-warning "TRUE" } */
+}
+
+union u
+{
+ int i;
+ int *ptr;
+};
+
+void test_41 (void)
+{
+ union u u;
+ u.i = 42;
+ __analyzer_eval (u.i == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (u.ptr == NULL); /* { dg-warning "UNKNOWN" } */
+
+ /* Writes to a union member should invalidate knowledge about other members. */
+ u.ptr = NULL;
+ __analyzer_eval (u.ptr == NULL); /* { dg-warning "TRUE" } */
+ __analyzer_eval (u.i == 42); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_42 (void)
+{
+ int i;
+ float f;
+ i = 42;
+ f = i;
+ __analyzer_eval (f == 42.0); /* { dg-warning "TRUE" } */
+}
+
+void test_43 (void)
+{
+ int i;
+ float f;
+ f = 42.0f;
+ i = f;
+ __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+}
+
+struct sbits
+{
+ int b0 : 1;
+ int b123 : 3;
+ int b456 : 3;
+ int b7 : 1;
+};
+
+void test_44 (void)
+{
+ struct sbits bits;
+ bits.b0 = 1;
+ __analyzer_eval (bits.b0 == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+ // TODO(xfail): ^^^^
+
+ bits.b456 = 5;
+ __analyzer_eval (bits.b456 == 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */
+ // TODO(xfail): ^^^^
+};
+
+struct ubits
+{
+ unsigned int b0 : 1;
+ unsigned int b123 : 3;
+ unsigned int b456 : 3;
+ unsigned int b7 : 1;
+};
+
+/* FIXME: this requires BIT_FIELD_REF to work. */
+
+void test_45 (void)
+{
+ struct ubits bits;
+ bits.b0 = 1;
+ __analyzer_eval (bits.b0 == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail): ^^^^
+
+ bits.b456 = 5;
+ __analyzer_eval (bits.b456 == 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ // TODO(xfail): ^^^^
+};
+
+extern const char *char_ptr;
+
+int test_46 (void)
+{
+ if (strcmp("literal", char_ptr))
+ return 1;
+ return 0;
+}
+
+char test_47 (void)
+{
+ static const char* my_version = "1.1.3";
+ return my_version[0];
+}
+
+unsigned test_48 (unsigned char *p, unsigned char *q)
+{
+ return (unsigned int)(p - q);
+}
+
+typedef struct {
+ const char *filename;
+ short lineno;
+} loc;
+
+static loc loc_last;
+
+void test_49 (void)
+{
+ loc_last = __extension__(loc) { "", 328 };
+ loc_last = __extension__(loc) { "", 333 };
+}
+
+void test_50 (void *p, void *q)
+{
+ __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p == p); /* { dg-warning "TRUE" } */
+ __analyzer_eval (q == q); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p == (struct coord *)p); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p == (const struct coord *)p); /* { dg-warning "TRUE" } */
+
+ struct coord *cp = (struct coord *)p;
+ __analyzer_eval (p == cp); /* { dg-warning "TRUE" } */
+
+ struct coord *cq = (struct coord *)q;
+ __analyzer_eval (q == cq); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (cp == cq); /* { dg-warning "UNKNOWN" } */
+}
+
+void test_51 (struct coord c)
+{
+ struct coord d;
+ memcpy (&d, &c, sizeof (struct coord));
+ __analyzer_eval (c.x == d.x); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ __analyzer_eval (c.y == d.y); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
+
+struct big
+{
+ int ia[1024];
+};
+
+void test_52 (struct big b)
+{
+ struct big d;
+ memcpy (&d, &b, sizeof (struct big));
+ __analyzer_eval (b.ia[0] == d.ia[0]); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
+
+void test_53 (const char *msg)
+{
+ (void)fprintf(stderr, "LOG: %s", msg);
+}
new file mode 100644
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+struct foo
+{
+ char **m_f;
+};
+
+struct foo *
+test (void)
+{
+ struct foo *new_table = (struct foo *) malloc(sizeof(struct foo));
+ if (!new_table)
+ return NULL;
+ new_table->m_f = (char **)malloc(sizeof(char **));
+ *new_table->m_f = NULL; /* { dg-warning "dereference of possibly-NULL '<unknown>'" } */ // FIXME: something better than "unknown" here
+ return new_table;
+}
new file mode 100644
@@ -0,0 +1,6 @@
+int test (void)
+{
+ unsigned char *s = "abc";
+ char *t = "xyz";
+ return s[1] + t[1];
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* Mismatching decl of foo. */
+
+int foo ();
+
+int bar (void)
+{
+ return foo() + 1;
+}
+
+int foo (int x, int y)
+{
+ return x * y;
+}
new file mode 100644
@@ -0,0 +1,21 @@
+#include <stdlib.h>
+
+union
+{
+ void *ptr_val;
+ int int_val;
+} global_union;
+
+void test_1 (void)
+{
+ global_union.ptr_val = malloc (1024);
+}
+
+void test_2 (void)
+{
+ global_union.ptr_val = malloc (1024); /* { dg-message "allocated here" } */
+ global_union.int_val = 0;
+} /* { dg-warning "leak of '<unknown>' " } */
+/* TODO: something better than "<unknown>". */
+/* TODO: better location for the leak. */
+
new file mode 100644
@@ -0,0 +1,24 @@
+/* FIXME: we shouldn't need this. */
+/* { dg-additional-options "-fanalyzer-fine-grained" } */
+
+#include <stdlib.h>
+
+void *global_ptr;
+
+void test_1 (int i)
+{
+ global_ptr = malloc (1024); /* { dg-message "allocated here" } */
+ *(int *)&global_ptr = i; /* { dg-warning "leak of '<unknown>'" } */
+ // TODO: something better than "<unknown>" here ^^^
+}
+
+void test_2 (int i)
+{
+ void *p = malloc (1024); /* { dg-message "allocated here" "" { xfail *-*-* } } */
+ // TODO(xfail)
+ global_ptr = p;
+ *(int *)&p = i;
+ p = global_ptr;
+ free (p);
+ free (global_ptr); /* { dg-warning "double-'free' of 'p'" } */
+}
new file mode 100644
@@ -0,0 +1,34 @@
+#include <string.h>
+
+struct coord
+{
+ double x;
+ double y;
+ double z;
+};
+
+struct tri {
+ struct coord verts[3];
+};
+
+double test_1 (void)
+{
+ struct tri t;
+ memset (&t, 0, sizeof (struct tri));
+ return t.verts[1].y;
+}
+
+int test_2 (const struct coord *c1, const struct coord *c2, double r_squared)
+{
+ double dx = c1->x - c2->x;
+ double dy = c1->y - c2->y;
+ double dz = c1->z - c2->z;
+ return (dx * dx) + (dy * dy) + (dz * dz) <= r_squared;
+}
+
+int test_3 (const struct coord *c1, const struct coord *c2, struct coord *out)
+{
+ out->x = c1->x + c2->x;
+ out->y = c1->y + c2->y;
+ out->z = c1->z + c2->z;
+}
new file mode 100644
@@ -0,0 +1,50 @@
+/* Labels as values. */
+
+extern void foo (void);
+
+void *x, *y, *z;
+
+void test (void)
+{
+ label0:
+ foo ();
+ label1:
+ foo ();
+ label2:
+ foo ();
+
+ x = &&label0;
+ y = &&label1;
+ z = &&label2;
+
+ __analyzer_eval (x == x); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x == y); /* { dg-warning "FALSE" } */
+}
+
+void test_2 (int i)
+{
+ static void *array[] = { &&label0, &&label1, &&label2 };
+ goto *array[i];
+
+ label0:
+ foo ();
+ label1:
+ foo ();
+ label2:
+ foo ();
+}
+
+void test_3 (int i)
+{
+ static const int array[] = { &&label0 - &&label0,
+ &&label1 - &&label0,
+ &&label2 - &&label0 };
+ goto *(&&label0 + array[i]);
+
+ label0:
+ foo ();
+ label1:
+ foo ();
+ label2:
+ foo ();
+}
new file mode 100644
@@ -0,0 +1,20 @@
+typedef struct foo {} foo_t;
+
+typedef void (*func_t)(foo_t *s);
+
+void cb_1 (foo_t *s);
+void cb_2 (foo_t *s);
+
+typedef struct config_s {
+ func_t func;
+} config;
+
+static const config table[2] = {
+ { cb_1 },
+ { cb_2 }
+};
+
+int deflate (foo_t *s, int which)
+{
+ (*(table[which].func))(s);
+}
new file mode 100644
@@ -0,0 +1,20 @@
+void test (int *p, int i, int j)
+{
+ p[3] = 42;
+ __analyzer_eval (p[3] == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (*(p + 3) == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[i] == 42); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p[j] == 42); /* { dg-warning "UNKNOWN" } */
+
+ //__analyzer_dump ();
+
+ p[i] = 17;
+
+ //__analyzer_dump ();
+
+ __analyzer_eval (p[3] == 42); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (p[i] == 17); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p[j] == 17); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */
+ /* { dg-bogus "TRUE" "" { xfail *-*-* } .-1 } */
+ // FIXME(xfails) ^^^
+}
new file mode 100644
@@ -0,0 +1,31 @@
+/* { dg-additional-options "-fgimple" } */
+
+typedef long long int i64;
+
+int __GIMPLE (ssa)
+test (i64 * pA, i64 iB)
+{
+ __complex__ long long int D_37702;
+ int D_37701;
+ long long int _1;
+ long long int _2;
+ long long int _3;
+ _Bool _4;
+ __complex__ long long int _8;
+ int _10;
+
+ __BB(2):
+ _1 = __MEM <i64> (pA_6(D));
+ _8 = .ADD_OVERFLOW (_1, iB_7(D));
+ _2 = __real _8;
+ __MEM <i64> (pA_6(D)) = _2;
+ _3 = __imag _8;
+ _4 = (_Bool) _3;
+ _10 = (int) _4;
+ goto __BB3;
+
+ __BB(3):
+L0:
+ return _10;
+
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* { dg-additional-options "-O2" } */
+/* TODO:is there a way to automatically run the tests on various
+ optimizations levels, and with/without debuginfo, rather than
+ hardcoding options? Adapt from torture .exp, presumably. */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+int test_1 (void)
+{
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,15 @@
+/* { dg-additional-options "-O2" } */
+/* TODO:is there a way to automatically run the tests on various
+ optimizations levels, and with/without debuginfo, rather than
+ hardcoding options? Adapt from torture .exp, presumably. */
+
+#include <stdio.h>
+int
+main ()
+{
+ FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-additional-options "-fexceptions" } */
+/* TODO:is there a way to automatically run the tests on various
+ optimizations levels, and with/without debuginfo, rather than
+ hardcoding options? Adapt from torture .exp, presumably. */
+
+#include <stdio.h>
+int
+main ()
+{
+ FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+
new file mode 100644
@@ -0,0 +1,100 @@
+/* A toy re-implementation of CPython's object model. */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj
+{
+ struct type_obj *ob_type;
+ int ob_refcnt;
+} base_obj;
+
+typedef struct type_obj
+{
+ base_obj tp_base;
+ void (*tp_dealloc) (base_obj *);
+} type_obj;
+
+typedef struct tuple_obj
+{
+ base_obj tup_base;
+ int num_elements;
+ base_obj elements[];
+} tuple_obj;
+
+typedef struct list_obj
+{
+ base_obj list_base;
+ int num_elements;
+ base_obj *elements;
+} list_obj;
+
+typedef struct string_obj
+{
+ base_obj str_base;
+ size_t str_len;
+ char str_buf[];
+} string_obj;
+
+extern void type_del (base_obj *);
+extern void tuple_del (base_obj *);
+extern void str_del (base_obj *);
+
+type_obj type_type = {
+ { &type_type, 1},
+ type_del
+};
+
+type_obj tuple_type = {
+ { &type_type, 1},
+ tuple_del
+};
+
+type_obj str_type = {
+ { &str_type, 1},
+ str_del
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+ base_obj *obj = (base_obj *)malloc (sz);
+ if (!obj)
+ return NULL;
+ obj->ob_type = ob_type;
+ obj->ob_refcnt = 1;
+ return obj;
+}
+
+base_obj *new_string_obj (const char *str)
+{
+ //__analyzer_dump ();
+ size_t len = strlen (str);
+#if 1
+ string_obj *str_obj
+ = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1);
+#else
+ string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1);
+ if (!str_obj)
+ return NULL;
+ str_obj->str_base.ob_type = &str_type;
+ str_obj->str_base.ob_refcnt = 1;
+#endif
+ str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */
+ memcpy (str_obj->str_buf, str, len);
+ str_obj->str_buf[len] = '\0';
+ return (base_obj *)str_obj;
+}
+
+void unref (base_obj *obj)
+{
+ if (--obj->ob_refcnt == 0) /* { dg-bogus "dereference of uninitialized pointer 'obj'" } */
+ obj->ob_type->tp_dealloc (obj);
+}
+
+void test_1 (const char *str)
+{
+ base_obj *obj = new_string_obj (str);
+ //__analyzer_dump();
+ unref (obj);
+} /* { dg-bogus "leak" } */
new file mode 100644
@@ -0,0 +1,91 @@
+/* A toy re-implementation of CPython's object model. */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+ struct type_obj *ob_type;
+ int ob_refcnt;
+};
+
+struct type_obj
+{
+ base_obj tp_base;
+ void (*tp_dealloc) (base_obj *);
+};
+
+struct string_obj
+{
+ base_obj str_base;
+ size_t str_len;
+ char str_buf[];
+};
+
+extern void type_del (base_obj *);
+extern void str_del (base_obj *);
+
+type_obj type_type = {
+ { &type_type, 1},
+ type_del
+};
+
+type_obj str_type = {
+ { &str_type, 1},
+ str_del
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+ base_obj *obj = (base_obj *)malloc (sz);
+ if (!obj)
+ return NULL;
+ obj->ob_type = ob_type;
+ obj->ob_refcnt = 1;
+ return obj;
+}
+
+string_obj *new_string_obj (const char *str)
+{
+ //__analyzer_dump ();
+ size_t len = strlen (str);
+#if 1
+ string_obj *str_obj
+ = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1);
+#else
+ string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1);
+ if (!str_obj)
+ return NULL;
+ str_obj->str_base.ob_type = &str_type;
+ str_obj->str_base.ob_refcnt = 1;
+#endif
+ str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */
+ memcpy (str_obj->str_buf, str, len);
+ str_obj->str_buf[len] = '\0';
+ return str_obj;
+}
+
+void unref (string_obj *obj)
+{
+ //__analyzer_dump();
+ if (--obj->str_base.ob_refcnt == 0)
+ {
+ //__analyzer_dump();
+ obj->str_base.ob_type->tp_dealloc ((base_obj *)obj); /* { dg-bogus "use of uninitialized value '<unknown>'" "" { xfail *-*-* } } */
+ // TODO (xfail): not sure what's going on here
+ }
+}
+
+void test_1 (const char *str)
+{
+ string_obj *obj = new_string_obj (str);
+ //__analyzer_dump();
+ if (obj)
+ unref (obj);
+} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
+// TODO (xfail): not sure why this is treated as leaking
new file mode 100644
@@ -0,0 +1,84 @@
+/* A toy re-implementation of CPython's object model. */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+ struct type_obj *ob_type;
+ int ob_refcnt;
+};
+
+struct type_obj
+{
+ base_obj tp_base;
+};
+
+struct string_obj
+{
+ base_obj str_base;
+ size_t str_len;
+ char str_buf[];
+};
+
+type_obj type_type = {
+ { &type_type, 1},
+};
+
+type_obj str_type = {
+ { &str_type, 1},
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+ base_obj *obj = (base_obj *)malloc (sz);
+ if (!obj)
+ return NULL;
+ obj->ob_type = ob_type;
+ obj->ob_refcnt = 1;
+ return obj;
+}
+
+string_obj *new_string_obj (const char *str)
+{
+ //__analyzer_dump ();
+ size_t len = strlen (str);
+#if 1
+ string_obj *str_obj
+ = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1);
+#else
+ string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1);
+ if (!str_obj)
+ return NULL;
+ str_obj->str_base.ob_type = &str_type;
+ str_obj->str_base.ob_refcnt = 1;
+#endif
+ str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */
+ memcpy (str_obj->str_buf, str, len);
+ str_obj->str_buf[len] = '\0';
+ return str_obj;
+}
+
+void unref (string_obj *obj)
+{
+ //__analyzer_dump();
+ if (--obj->str_base.ob_refcnt == 0)
+ {
+ //__analyzer_dump();
+ free (obj);
+ }
+}
+
+void test_1 (const char *str)
+{
+ string_obj *obj = new_string_obj (str);
+ //__analyzer_dump();
+ if (obj)
+ unref (obj);
+} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */
+// TODO (xfail): not sure why this is treated as leaking
new file mode 100644
@@ -0,0 +1,63 @@
+/* A toy re-implementation of CPython's object model. */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+typedef struct base_obj base_obj;
+typedef struct type_obj type_obj;
+typedef struct string_obj string_obj;
+
+struct base_obj
+{
+ struct type_obj *ob_type;
+ int ob_refcnt;
+};
+
+struct type_obj
+{
+ base_obj tp_base;
+};
+
+struct string_obj
+{
+ base_obj str_base;
+ size_t str_len;
+ char str_buf[];
+};
+
+type_obj type_type = {
+ { &type_type, 1},
+};
+
+type_obj str_type = {
+ { &str_type, 1},
+};
+
+base_obj *alloc_obj (type_obj *ob_type, size_t sz)
+{
+ base_obj *obj = (base_obj *)malloc (sz);
+ if (!obj)
+ return NULL;
+ obj->ob_type = ob_type;
+ obj->ob_refcnt = 1;
+ return obj;
+}
+
+void unref (base_obj *obj)
+{
+ //__analyzer_dump();
+ if (--obj->ob_refcnt == 0)
+ free (obj);
+}
+
+void test_1 ()
+{
+ base_obj *obj = alloc_obj (&str_type, sizeof (string_obj));
+ if (obj)
+ {
+ __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */
+ unref (obj);
+ __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+ }
+}
new file mode 100644
@@ -0,0 +1,13 @@
+#include <stdlib.h>
+
+/* Verify that we don't accumulate state after a malloc/free pair. */
+
+void test (void)
+{
+ void *ptr;
+ __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+ ptr = malloc (1024);
+ __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */
+ free (ptr);
+ __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */
+}
new file mode 100644
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fno-analyzer-state-merge" } */
+
+int test_40 (int flag)
+{
+ int i;
+ if (flag)
+ i = 43;
+ else
+ i = 17;
+
+ /* Without state-merging, we retain the relationship between 'flag' and 'i'. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (flag)
+ __analyzer_eval (i == 43); /* { dg-warning "TRUE" } */
+ else
+ __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
new file mode 100644
@@ -0,0 +1,24 @@
+struct base
+{
+ int i;
+};
+
+struct sub
+{
+ struct base b;
+ int j;
+};
+
+void test (void)
+{
+ struct sub s;
+ s.b.i = 3;
+ s.j = 4;
+ __analyzer_eval (s.b.i == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (s.j == 4); /* { dg-warning "TRUE" } */
+
+ struct base *bp = (struct base *)&s;
+
+ __analyzer_eval (bp->i == 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+}
new file mode 100644
@@ -0,0 +1,32 @@
+#include <stdlib.h>
+#include <string.h>
+
+struct foo
+{
+ int i;
+};
+
+/* TODO: verify that we know that calloc zeros its memory. */
+
+void test_1 (void)
+{
+ struct foo *f = calloc (1, sizeof (struct foo));
+ if (f == NULL)
+ return;
+ __analyzer_eval (f->i == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ free (f);
+}
+
+/* TODO: verify that we know the behavior of memset. */
+
+void test_2 (void)
+{
+ struct foo *f = malloc (sizeof (struct foo));
+ if (f == NULL)
+ return;
+ memset (f, 0, sizeof (struct foo));
+ __analyzer_eval (f->i == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */
+ free (f);
+}
new file mode 100644
@@ -0,0 +1,13 @@
+#include <stddef.h>
+
+static int *__attribute__((noinline))
+callee (void)
+{
+ return NULL;
+}
+
+void test_1 (void)
+{
+ int *p = callee (); /* { dg-message "return of NULL to 'test_1' from 'callee'" } */
+ *p = 42; /* { dg-warning "dereference of NULL 'p'" } */
+}
new file mode 100644
@@ -0,0 +1,10 @@
+/* Verify that we can override -fanalyzer with -fno-analyzer. */
+/* { dg-additional-options "-fno-analyzer" } */
+
+#include <stdlib.h>
+
+void test (void *ptr)
+{
+ free (ptr);
+ free (ptr); /* { dg-bogus "free" } */
+}
new file mode 100644
@@ -0,0 +1,33 @@
+/* Verify that the various .dot output files from the analyzer are readable
+ by .dot. */
+
+/* { dg-require-dot "" } */
+/* { dg-additional-options "-fdump-analyzer-callgraph -fdump-analyzer-exploded-graph -fdump-analyzer-state-purge -fdump-analyzer-supergraph" } */
+
+#include <stdlib.h>
+
+int some_call (int i, char ch)
+{
+ return i * i;
+}
+
+int *test (int *buf, int n, int *out)
+{
+ int i;
+ int *result = malloc (sizeof (int) * n);
+
+ /* A loop, to ensure we have phi nodes. */
+ for (i = 0; i < n; i++)
+ result[i] = buf[i] + i; /* { dg-warning "possibly-NULL" "" { xfail *-*-* } } */
+ /* TODO(xfail): why isn't the warning appearing? */
+
+ /* Example of a "'" (to test quoting). */
+ *out = some_call (i, 'a');
+
+ return result;
+}
+
+/* { dg-final { dg-check-dot "dot-output.c.callgraph.dot" } } */
+/* { dg-final { dg-check-dot "dot-output.c.eg.dot" } } */
+/* { dg-final { dg-check-dot "dot-output.c.state-purge.dot" } } */
+/* { dg-final { dg-check-dot "dot-output.c.supergraph.dot" } } */
new file mode 100644
@@ -0,0 +1,16 @@
+/* { dg-do link } */
+/* { dg-require-effective-target lto } */
+/* { dg-additional-options "-flto" } */
+/* { dg-additional-sources double-free-lto-1-b.c } */
+
+#include <stdlib.h>
+#include "double-free-lto-1.h"
+
+void test (void *ptr)
+{
+ calls_free (ptr); /* { dg-message "calling 'calls_free' from 'test'" } */
+ free (ptr); /* { dg-warning "double-'free' of 'ptr_.+'" } */
+ // TODO: report "ptr", rather than an SSA name
+}
+
+int main() { return 0; }
new file mode 100644
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+
+#include "double-free-lto-1.h"
+
+extern void calls_free (void *ptr)
+{
+ free (ptr);
+}
new file mode 100644
@@ -0,0 +1 @@
+extern void calls_free (void *ptr);
new file mode 100644
@@ -0,0 +1,29 @@
+void test (int p, int q, int r)
+{
+ if (p == 42)
+ {
+ __analyzer_eval (p == 42); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p != 42); /* { dg-warning "FALSE" } */
+ if (q == 42)
+ {
+ __analyzer_eval (p == q); /* { dg-warning "TRUE" } */
+ }
+ else
+ {
+ __analyzer_eval (p != q); /* { dg-warning "TRUE" } */
+ }
+ }
+ else
+ {
+ __analyzer_eval (p == 42); /* { dg-warning "FALSE" } */
+ __analyzer_eval (p != 42); /* { dg-warning "TRUE" } */
+ if (q == 42)
+ {
+ __analyzer_eval (p == q); /* { dg-warning "FALSE" } */
+ }
+ else
+ {
+ __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,60 @@
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+#include <stdlib.h>
+
+extern int get (void);
+
+/* In theory each of p0...p8 can be in various malloc states,
+ independently, so the total combined number of states
+ at any program point within the loop is NUM_VARS * NUM_STATES. */
+
+void test (void)
+{
+ void *p0, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+ void **pp;
+ while (get ())
+ {
+ switch (get ())
+ {
+ default:
+ case 0:
+ pp = &p0;
+ break;
+ case 1:
+ pp = &p1;
+ break;
+ case 2:
+ pp = &p2;
+ break;
+ case 3:
+ pp = &p3;
+ break;
+ case 4:
+ pp = &p4;
+ break;
+ case 5:
+ pp = &p5;
+ break;
+ case 6:
+ pp = &p6;
+ break;
+ case 7:
+ pp = &p7;
+ break;
+ }
+
+ switch (get ())
+ {
+ default:
+ case 0:
+ *pp = malloc (16);
+ break;
+ case 1:
+ free (*pp);
+ break;
+ case 2:
+ /* no-op. */
+ break;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,50 @@
+/* In theory each of p0...p3 can be in various malloc states,
+ independently, so the total combined number of states
+ at any program point within the loop is NUM_VARS * NUM_STATES.
+
+ Set the limits high enough that we can fully explore this. */
+
+/* { dg-additional-options "--param analyzer-max-enodes-per-program-point=200 --param analyzer-bb-explosion-factor=50" } */
+
+#include <stdlib.h>
+
+extern int get (void);
+
+void test (void)
+{
+ void *p0, *p1, *p2, *p3;
+ while (get ())
+ {
+ switch (get ())
+ {
+ default:
+ case 0:
+ p0 = malloc (16);
+ break;
+ case 1:
+ free (p0); /* { dg-warning "double-'free' of 'p0'" } */
+ break;
+
+ case 2:
+ p1 = malloc (16);
+ break;
+ case 3:
+ free (p1); /* { dg-warning "double-'free' of 'p1'" } */
+ break;
+
+ case 4:
+ p2 = malloc (16);
+ break;
+ case 5:
+ free (p2); /* { dg-warning "double-'free' of 'p2'" } */
+ break;
+
+ case 6:
+ p3 = malloc (16);
+ break;
+ case 7:
+ free (p3); /* { dg-warning "double-'free' of 'p3'" } */
+ break;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,7 @@
+int factorial (int n)
+{
+ if (n > 1)
+ return n * factorial (n - 1);
+ else
+ return 1;
+}
new file mode 100644
@@ -0,0 +1,9 @@
+int fib (int n)
+{
+ if (n > 1)
+ return fib (n - 1) + fib (n - 2);
+ else
+ return n;
+}
+
+/* { dg-regexp "\[^\n\r\]+: warning: analysis bailed out early \\(\[0-9\]+ 'after-snode' enodes; \[0-9\]+ enodes\\) \[^\n\r\]*" } */
new file mode 100644
@@ -0,0 +1,41 @@
+typedef long unsigned int size_t;
+
+extern size_t strlen (const char *__s)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__pure__))
+ __attribute__ ((__nonnull__ (1)));
+
+extern void *malloc (size_t __size)
+ __attribute__ ((__nothrow__ , __leaf__))
+ __attribute__ ((__malloc__)) ;
+
+extern void free (void *__ptr)
+ __attribute__ ((__nothrow__ , __leaf__));
+
+typedef struct _krb5_data {
+ unsigned int length;
+ char *data;
+} krb5_data;
+
+typedef struct _krb5_error {
+ krb5_data text;
+} krb5_error;
+
+extern const char *error_message (int);
+
+int
+recvauth_common (int problem)
+{
+ if (problem) {
+ krb5_error error;
+ const char *message = error_message(problem);
+ error.text.length = strlen(message) + 1;
+ if (!(error.text.data = malloc(error.text.length))) {
+ goto cleanup;
+ }
+ free(error.text.data);
+ }
+
+ cleanup:
+ return problem; /* { dg-bogus "leak" } */
+}
new file mode 100644
@@ -0,0 +1,37 @@
+#include <stdio.h>
+
+void
+test_1 (const char *path)
+{
+ FILE *f = fopen (path, "r"); /* { dg-message "opened here" } */
+ if (!f)
+ return;
+
+ fclose (f); /* { dg-message "\\(4\\) \\.\\.\\.to here" } */
+ /* { dg-message "\\(5\\) first 'fclose' here" "" { target *-*-* } .-1 } */
+ fclose (f); /* { dg-warning "double 'fclose' of FILE 'f'" } */
+ /* { dg-message "second 'fclose' here; first 'fclose' was at \\(5\\)" "" { target *-*-* } .-1 } */
+}
+
+void
+test_2 (const char *src, const char *dst)
+{
+ FILE *f_in = fopen (src, "r"); /* { dg-message "\\(1\\) opened here" } */
+ if (!f_in)
+ return;
+
+ FILE *f_out = fopen (src, "w");
+ if (!f_out)
+ return; /* { dg-warning "leak of FILE 'f_in'" } */
+ /* { dg-message "\\(7\\) 'f_in' leaks here; was opened at \\(1\\)" "" { target *-*-* } .-1 } */
+
+ fclose (f_out);
+ fclose (f_in);
+}
+
+void
+test_3 (const char *path)
+{
+ FILE *f = fopen (path, "r"); /* { dg-message "opened here" } */
+ return; /* { dg-warning "leak of FILE 'f'" } */
+}
new file mode 100644
@@ -0,0 +1,18 @@
+#include <stdio.h>
+
+struct foo
+{
+ FILE *m_f;
+};
+
+void test (const char *path)
+{
+ struct foo f;
+ f.m_f = fopen (path, "r");
+
+ if (!f.m_f)
+ return; /* { dg-bogus "leak of FILE" } */
+
+ fclose (f.m_f);
+ fclose (f.m_f); /* { dg-warning "double 'fclose' of FILE 'f.m_f'" } */
+}
new file mode 100644
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+
+typedef void *(*fn_ptr_t) (void *);
+
+void *test_1 (fn_ptr_t fn_ptr, void *data)
+{
+ return fn_ptr (data);
+}
new file mode 100644
@@ -0,0 +1,43 @@
+#include <stdlib.h>
+
+typedef void (*fn_ptr_t) (void *);
+
+void
+calls_free (void *victim)
+{
+ free (victim);
+}
+
+void
+no_op (void *ptr)
+{
+}
+
+void test_1 (void *ptr)
+{
+ fn_ptr_t fn_ptr = calls_free;
+ __analyzer_eval (fn_ptr == calls_free); /* { dg-warning "TRUE" } */
+ __analyzer_eval (fn_ptr != NULL); /* { dg-warning "TRUE" } */
+ __analyzer_eval (fn_ptr == NULL); /* { dg-warning "FALSE" } */
+ __analyzer_eval (fn_ptr == no_op); /* { dg-warning "FALSE" } */
+
+ fn_ptr (ptr);
+ fn_ptr (ptr);
+}
+// TODO: issue a double-'free' warning at 2nd call to fn_ptr.
+
+/* As above, but with an extra indirection to try to thwart
+ the optimizer. */
+
+void test_2 (void *ptr, fn_ptr_t *fn_ptr)
+{
+ *fn_ptr = calls_free;
+ __analyzer_eval (*fn_ptr == calls_free); /* { dg-warning "TRUE" } */
+ __analyzer_eval (*fn_ptr != NULL); /* { dg-warning "TRUE" } */
+ __analyzer_eval (*fn_ptr == NULL); /* { dg-warning "FALSE" } */
+ __analyzer_eval (*fn_ptr == no_op); /* { dg-warning "FALSE" } */
+
+ (*fn_ptr) (ptr);
+ (*fn_ptr) (ptr);
+}
+// TODO: issue a double-'free' warning at 2nd call to fn_ptr.
new file mode 100644
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+typedef void *(*alloc_func_t) (size_t);
+typedef void (*free_func_t) (void *);
+
+typedef struct callbacks
+{
+ alloc_func_t alloc_cb;
+ free_func_t dealloc_cb;
+} callbacks_t;
+
+void test (void)
+{
+ callbacks_t cb;
+ cb.alloc_cb = (alloc_func_t)0;
+ cb.dealloc_cb = (free_func_t)0;
+}
new file mode 100644
@@ -0,0 +1,11 @@
+void gzseek (long offset, int whence)
+{
+ if (whence == 2)
+ return;
+ if (whence == 0)
+ offset -= 1;
+ if (offset < 0)
+ return;
+ while (offset > 0) {
+ }
+}
new file mode 100644
@@ -0,0 +1,31 @@
+typedef long unsigned int size_t;
+typedef struct _IO_FILE FILE;
+extern size_t fread(void *__restrict __ptr, size_t __size, size_t __n,
+ FILE *__restrict __stream);
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+typedef struct z_stream_s {
+ uInt avail_in;
+ uInt avail_out;
+} z_stream;
+
+typedef struct gz_stream {
+ z_stream stream;
+ FILE *file;
+} gz_stream;
+
+void test_1_callee(gz_stream *s, Byte *buf) {
+ Byte *next_out = buf;
+ uInt n = s->stream.avail_in;
+ if (n > 0) {
+ next_out += n;
+ }
+ s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out, s->file);
+}
+
+void test_1_caller(gz_stream *s) {
+ unsigned char c;
+ test_1_callee(s, &c);
+}
new file mode 100644
@@ -0,0 +1,27 @@
+typedef long unsigned int size_t;
+typedef struct _IO_FILE FILE;
+extern size_t fread(void *__restrict __ptr, size_t __size, size_t __n,
+ FILE *__restrict __stream);
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+typedef struct gz_stream {
+ FILE *file;
+ uInt avail_in;
+ uInt avail_out;
+} gz_stream;
+
+void test_1_callee(gz_stream *s, Byte *buf) {
+ Byte *next_out = buf;
+ uInt n = s->avail_in;
+ if (n > 0) {
+ next_out += n;
+ }
+ s->avail_out -= fread(next_out, 1, s->avail_out, s->file);
+}
+
+void test_1_caller(gz_stream *s) {
+ unsigned char c;
+ test_1_callee(s, &c);
+}
new file mode 100644
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+typedef struct z_stream_s {
+ unsigned char *next_out;
+} z_stream;
+typedef struct gz_stream {
+ z_stream stream;
+ unsigned char *outbuf;
+} gz_stream;
+gz_stream *s;
+static void gz_open(const char *path)
+{
+ s->stream.next_out = s->outbuf = (unsigned char *)malloc(16384); /* { dg-bogus "leak" } */
+}
+void gzopen(const char *path)
+{
+ gz_open(path);
+}
new file mode 100644
@@ -0,0 +1,55 @@
+extern void marker_A(void);
+extern void marker_B(void);
+extern void marker_C(void);
+extern void marker_D(void);
+
+void test(int flag)
+{
+ marker_A();
+
+ if (flag) {
+ marker_B();
+
+ /* Recurse, infinitely, as it happens: */
+ test(flag);
+
+ marker_C();
+ }
+
+ marker_D();
+}
+
+/* A cycle of 4 mutually-recursive functions (but only for certain inputs). */
+
+extern void mutual_test_1 (int flag);
+extern void mutual_test_2 (int flag);
+extern void mutual_test_3 (int flag);
+extern void mutual_test_4 (int flag);
+
+void mutual_test_1 (int flag)
+{
+ marker_A ();
+ if (flag)
+ mutual_test_2 (flag);
+}
+
+void mutual_test_2 (int flag)
+{
+ marker_B ();
+ if (flag)
+ mutual_test_3 (flag);
+}
+
+void mutual_test_3 (int flag)
+{
+ marker_C ();
+ if (flag)
+ mutual_test_4 (flag);
+}
+
+void mutual_test_4 (int flag)
+{
+ marker_D ();
+ if (flag)
+ mutual_test_1 (flag);
+}
new file mode 100644
@@ -0,0 +1,36 @@
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+struct s
+{
+ int i;
+};
+
+void test(void)
+{
+ struct s s;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+
+ for (s.i=0; s.i<256; s.i++) {
+ __analyzer_eval (s.i < 256); /* { dg-warning "TRUE" } */
+ /* (should report TRUE twice). */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ //__analyzer_eval (s.i == 0); /* { d-todo-g-warning "UNKNOWN" "" { xfail *-*-* } } */
+ /* { d-todo-g-warning "TRUE" "" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): we're only capturing the first iteration, so
+ we erroneously get i == 0. */
+
+ //__analyzer_eval (s.i >= 0); /* { d-todo-g-warning "TRUE" } */
+ }
+
+ __analyzer_eval (s.i >= 256); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (s.i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): ideally it should figure out i == 256 at exit. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
new file mode 100644
@@ -0,0 +1,39 @@
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+union u
+{
+ int i;
+};
+
+void test(void)
+{
+ union u u;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+
+ for (u.i=0; u.i<256; u.i++) {
+ __analyzer_eval (u.i < 256); /* { dg-warning "TRUE" } */
+ /* { dg-warning "TRUE" "" { xfail *-*-* } .-1 } */
+ /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-2 } */
+ /* (should report TRUE twice). */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ //__analyzer_eval (u.i == 0); /* { d-todo-g-warning "UNKNOWN" "" { xfail *-*-* } } */
+ /* { d-todo-g-warning "TRUE" "" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): we're only capturing the first iteration, so
+ we erroneously get i == 0. */
+
+ //__analyzer_eval (u.i >= 0); /* { d-todo-g-warning "TRUE" } */
+ }
+
+ __analyzer_eval (u.i >= 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+
+ __analyzer_eval (u.i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): ideally it should figure out i == 256 at exit. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
new file mode 100644
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+
+void test(int c)
+{
+ int i;
+ char *buffer = (char*)malloc(256);
+
+ for (i=0; i<255; i++) {
+ buffer[i] = c; /* { dg-warning "use after 'free' of 'buffer'" } */
+ /* BUG: the malloc could have failed
+ TODO: the checker doesn't yet pick up on this, perhaps
+ due to the pointer arithmetic not picking up on the
+ state */
+ free(buffer); /* { dg-warning "double-'free' of 'buffer'" } */
+ }
+
+}
new file mode 100644
@@ -0,0 +1,41 @@
+// FIXME:
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+/* Example of nested loops. */
+
+void test(void)
+{
+ int i, j, k;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ for (i=0; i<256; i++) {
+
+ __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+
+ __analyzer_eval (i < 256); /* { dg-warning "TRUE" } */
+
+ for (j=0; j<256; j++) {
+
+ __analyzer_eval (j >= 0); /* { dg-warning "TRUE" } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+
+ __analyzer_eval (j < 256); /* { dg-warning "TRUE" } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+ for (k=0; k<256; k++) {
+
+ __analyzer_eval (k >= 0); /* { dg-warning "TRUE" } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+
+ __analyzer_eval (k < 256); /* { dg-warning "TRUE" } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+ }
+ }
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
new file mode 100644
@@ -0,0 +1,33 @@
+/* { dg-additional-options "-fno-analyzer-state-purge" } */
+
+void test(void)
+{
+ int i;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ for (i=0; i<256; i++) {
+ __analyzer_eval (i < 256); /* { dg-warning "TRUE" } */
+ /* (should report TRUE twice). */
+
+ __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */
+ /* { dg-warning "FALSE" "" { xfail *-*-* } .-1 } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */
+
+ __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */
+ /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-2 } */
+ /* TODO(xfail^^^): ideally we ought to figure out i >= 0 for all iterations. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ }
+
+ __analyzer_eval (i >= 256); /* { dg-warning "TRUE" } */
+
+ __analyzer_eval (i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): it only figures out i >= 256, rather than i == 256. */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
new file mode 100644
@@ -0,0 +1,585 @@
+#include <stdlib.h>
+#include <string.h>
+
+extern int foo (void);
+extern int bar (void);
+extern void could_free (void *);
+extern void cant_free (const void *); /* since it's a const void *. */
+
+void test_1 (void)
+{
+ void *ptr = malloc (1024);
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2 (void *ptr)
+{
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_2a (void *ptr)
+{
+ __builtin_free (ptr);
+ __builtin_free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int *test_3 (void)
+{
+ int *ptr = (int *)malloc (sizeof (int));
+ *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+ return ptr;
+}
+
+int *test_3a (void)
+{
+ int *ptr = (int *)__builtin_malloc (sizeof (int));
+ *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+ return ptr;
+}
+
+int *test_4 (void)
+{
+ int *ptr = (int *)malloc (sizeof (int));
+ if (ptr)
+ *ptr = 42;
+ else
+ *ptr = 43; /* { dg-warning "dereference of NULL 'ptr'" } */
+ return ptr;
+}
+
+int test_5 (int *ptr)
+{
+ free (ptr);
+ return *ptr; /* { dg-warning "use after 'free' of 'ptr'" } */
+}
+
+void test_6 (void *ptr)
+{
+ void *q;
+ q = ptr;
+ free (ptr);
+ free (q); /* { dg-warning "double-'free' of 'q'" } */
+ /* The above case requires us to handle equivalence classes in
+ state transitions. */
+}
+
+void test_7 (void)
+{
+ void *ptr = malloc(4096);
+ if (!ptr)
+ return;
+ memset(ptr, 0, 4096);
+ free(ptr);
+}
+
+void *test_8 (void)
+{
+ void *ptr = malloc(4096);
+ if (!ptr)
+ return NULL;
+ memset(ptr, 0, 4096);
+ return ptr;
+ /* This needs phi nodes to affect equivalence classes, or we get a false report
+ of a leak. */
+}
+
+void test_9 (void)
+{
+ void *ptr = malloc (1024);
+
+ int i;
+ for (i = 0; i < 1024; i++)
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_10 (void)
+{
+ void *ptr = malloc (1024);
+
+ int i;
+ for (i = 0; i < 1024; i++)
+ foo ();
+
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_11 (void)
+{
+ void *ptr = malloc (1024);
+
+ while (foo ())
+ bar ();
+
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+void test_12 (void)
+{
+ void *ptr = malloc (1024);
+
+ while (1)
+ {
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+ }
+}
+
+void test_13 (void)
+{
+ void *p = malloc (1024); /* { dg-message "allocated here" } */
+ void *q = malloc (1024);
+
+ foo ();
+ if (!q)
+ {
+ free (q);
+ return; /* { dg-warning "leak of 'p'" } */
+ }
+ bar ();
+ free (q);
+ free (p);
+}
+
+void test_14 (void)
+{
+ void *p, *q;
+ p = malloc (1024);
+ if (!p)
+ return;
+
+ q = malloc (1024);
+ if (!q)
+ {
+ free (p);
+ free (q);
+ /* oops: missing "return". */
+ }
+ bar ();
+ free (q); /* Although this looks like a double-'free' of q,
+ it's known to be NULL for the case where free is
+ called twice on it. */
+ free (p); /* { dg-warning "double-'free' of 'p'" } */
+}
+
+void test_15 (void)
+{
+ void *p = NULL, *q = NULL;
+
+ p = malloc (1024);
+ if (!p)
+ goto fail;
+
+ foo ();
+
+ q = malloc (1024);
+ if (!q)
+ goto fail;
+
+ bar ();
+
+ fail:
+ free (q);
+ free (p);
+}
+
+void test_16 (void)
+{
+ void *p, *q;
+
+ p = malloc (1024);
+ if (!p)
+ goto fail;
+
+ foo ();
+
+ q = malloc (1024);
+ if (!q)
+ goto fail;
+
+ bar ();
+
+ fail:
+ free (q); /* { dg-warning "free of uninitialized 'q'" "" { xfail *-*-* } } */
+ /* TODO(xfail): implement uninitialized detection. */
+ free (p);
+}
+
+void test_17 (void)
+{
+ void *ptr = malloc (1024); /* { dg-message "allocated here" } */
+} /* { dg-warning "leak of 'ptr'" } */
+
+void test_18 (void)
+{
+ void *ptr = malloc (64); /* { dg-message "allocated here" } */
+ ptr = NULL; /* { dg-warning "leak of 'ptr'" } */
+}
+
+void test_19 (void)
+{
+ void *ptr = malloc (64);
+ free (ptr);
+ ptr = NULL;
+ free (ptr);
+}
+
+void *global_ptr_20;
+
+void test_20 (void)
+{
+ global_ptr_20 = malloc (1024);
+}
+
+int *test_21 (int i)
+{
+ int *ptr = malloc (sizeof (int));
+ if (!ptr)
+ abort ();
+ *ptr = i;
+ return ptr;
+}
+
+void test_22 (void)
+{
+ void *ptr = malloc (1024);
+
+ int i;
+ for (i = 5; i < 10; i++)
+ foo ();
+
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int *test_23 (int n)
+{
+ int *ptr = (int *)calloc (n, sizeof (int));
+ ptr[0] = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+ return ptr;
+}
+
+int *test_23a (int n)
+{
+ int *ptr = (int *)__builtin_calloc (n, sizeof (int));
+ ptr[0] = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+ return ptr;
+}
+
+int test_24 (void)
+{
+ void *ptr = alloca (sizeof (int)); /* { dg-message "memory is allocated on the stack here" } */
+ free (ptr); /* { dg-warning "'free' of memory allocated on the stack by 'alloca' \\('ptr'\\) will corrupt the heap \\\[CWE-590\\\]" } */
+}
+
+int test_25 (void)
+{
+ char tmp[100];
+ void *p = tmp; /* { dg-message "pointer is from here" } */
+ free (p); /* { dg-warning "'free' of 'p' which points to memory not on the heap \\\[CWE-590\\\]" } */
+ /* TODO: more precise messages here. */
+}
+
+char global_buffer[100];
+
+int test_26 (void)
+{
+ void *p = global_buffer; /* { dg-message "pointer is from here" } */
+ free (p); /* { dg-warning "'free' of 'p' which points to memory not on the heap \\\[CWE-590\\\]" } */
+ /* TODO: more precise messages here. */
+}
+
+struct coord {
+ float x;
+ float y;
+};
+
+struct coord *test_27 (void)
+{
+ struct coord *p = (struct coord *) malloc (sizeof (struct coord)); /* { dg-message "this call could return NULL" } */
+ p->x = 0.f; /* { dg-warning "dereference of possibly-NULL 'p'" } */
+
+ /* Only the first such usage should be reported: */
+ p->y = 0.f;
+
+ return p;
+}
+
+struct coord *test_28 (void)
+{
+ struct coord *p = NULL;
+ p->x = 0.f; /* { dg-warning "dereference of NULL 'p'" } */
+
+ /* Only the first such usage should be reported: */
+ p->y = 0.f;
+
+ return p;
+}
+
+struct link
+{
+ struct link *m_ptr;
+};
+
+struct link *test_29 (void)
+{
+ struct link *res = (struct link *)malloc (sizeof (struct link));
+ if (!res)
+ return NULL;
+ res->m_ptr = (struct link *)malloc (sizeof (struct link));
+ return res;
+}
+
+struct link *test_29a (void)
+{
+ struct link *res = (struct link *)malloc (sizeof (struct link));
+ if (!res)
+ return NULL;
+ res->m_ptr = (struct link *)malloc (sizeof (struct link));
+ if (!res->m_ptr)
+ {
+ free (res);
+ return NULL;
+ }
+ res->m_ptr->m_ptr = (struct link *)malloc (sizeof (struct link));
+ return res;
+}
+
+/* Without consolidation by EC, this one shows two leaks:
+ warning: leak of '<unknown>'
+ warning: leak of 'tmp.m_ptr'
+ We should only show the latter (favoring the most user-readable
+ expression in the equivalence class). */
+void test_30 (void)
+{
+ struct link tmp;
+ tmp.m_ptr = (struct link *)malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
+} /* { dg-warning "leak of 'tmp.m_ptr'" } */
+/* { dg-bogus "leak of '<unknown>'" "" { xfail *-*-* } .-1 } */
+
+void test_31 (void)
+{
+ struct link tmp;
+ void *ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
+ tmp.m_ptr = (struct link *)ptr;
+} /* { dg-warning "leak of 'ptr'" } */
+/* { dg-bogus "leak of 'tmp.m_ptr'" "" { xfail *-*-* } .-1 } */
+
+void test_32 (void)
+{
+ void *ptr = malloc (1024);
+ could_free (ptr);
+} /* { dg-bogus "leak" } */
+
+void test_33 (void)
+{
+ void *ptr = malloc (1024); /* { dg-message "allocated here" } */
+ cant_free (ptr);
+} /* { dg-warning "leak of 'ptr'" } */
+
+void test_34 (void)
+{
+ float *q;
+ struct coord *p = malloc (sizeof (struct coord));
+ if (!p)
+ return;
+ p->x = 0.0f;
+ q = &p->x;
+ free (p);
+ *q = 1.0f; /* { dg-warning "use after 'free' of 'q'" } */
+};
+
+int test_35 (void)
+{
+ void *ptr = malloc(4096);
+ if (!ptr)
+ return -1;
+ memset(ptr, 0, 4096);
+ free(ptr);
+ return 0;
+}
+
+void test_36 (void)
+{
+ void *ptr = malloc(4096);
+ if (!ptr)
+ return;
+ memset(ptr, 0, 4096);
+ free(ptr);
+}
+
+void *test_37a (void)
+{
+ void *ptr = malloc(4096); /* { dg-message "this call could return NULL" } */
+ memset(ptr, 0, 4096); /* { dg-warning "use of possibly-NULL 'ptr' where non-null expected" } */
+ return ptr;
+}
+
+int test_37b (void)
+{
+ void *p = malloc(4096);
+ void *q = malloc(4096); /* { dg-message "this call could return NULL" } */
+ if (p) {
+ memset(p, 0, 4096); /* Not a bug: checked */
+ } else {
+ memset(q, 0, 4096); /* { dg-warning "use of possibly-NULL 'q' where non-null expected" } */
+ }
+ free(p);
+ free(q);
+ return 0;
+}
+
+extern void might_use_ptr (void *ptr);
+
+void test_38(int i)
+{
+ void *p;
+
+ p = malloc(1024);
+ if (p) {
+ free(p);
+ might_use_ptr(p); /* { dg-warning "use after 'free' of 'p'" "" { xfail *-*-* } } */
+ // TODO: xfail
+ }
+}
+
+int *
+test_39 (int i)
+{
+ int *p = (int*)malloc(sizeof(int*)); /* { dg-message "this call could return NULL" } */
+ *p = i; /* { dg-warning "dereference of possibly-NULL 'p'" } */
+ return p;
+}
+
+int *
+test_40 (int i)
+{
+ int *p = (int*)malloc(sizeof(int*));
+ i = *p; /* { dg-warning "dereference of possibly-NULL 'p'" } */
+ /* TODO: (it's also uninitialized) */
+ return p;
+}
+
+char *
+test_41 (int flag)
+{
+ char *buffer;
+
+ if (flag) {
+ buffer = (char*)malloc(4096);
+ } else {
+ buffer = NULL;
+ }
+
+ buffer[0] = 'a'; /* { dg-warning "dereference of possibly-NULL 'buffer'" } */
+ /* { dg-warning "dereference of NULL 'buffer'" "" { target *-*-* } .-1 } */
+
+ return buffer;
+}
+
+void test_42a (void)
+{
+ void *p = malloc (1024); /* { dg-message "allocated here" } */
+ free (p + 64); /* this could well corrupt the heap. */
+ /* TODO: ^^^ we should warn about this. */
+} /* { dg-warning "leak of 'p'" } */
+/* TODO: presumably we should complain about the bogus free, but then
+ maybe not complain about the leak. */
+// CWE-761: Free of Pointer not at Start of Buffer
+
+void test_42b (void)
+{
+ void *p = malloc (1024); /* { dg-message "allocated here" } */
+ free (p - 64); /* this could well corrupt the heap. */
+ /* TODO: ^^^ we should warn about this. */
+} /* { dg-warning "leak of 'p'" } */
+/* TODO: presumably we should complain about the bogus free, but then
+ maybe not complain about the leak. */
+// CWE-761: Free of Pointer not at Start of Buffer
+
+void test_42c (void)
+{
+ void *p = malloc (1024);
+ void *q = p + 64;
+ free (q - 64); /* this is probably OK. */
+} /* { dg-bogus "leak of 'p'" "" { xfail *-*-* } } */
+// TODO(xfail)
+
+#if 0
+void test_31 (void *p)
+{
+ void *q = realloc (p, 1024);
+ free (p); /* FIXME: this is a double-'free'. */
+ free (q);
+}
+
+void test_32 (void)
+{
+ void *p = malloc (64);
+ p = realloc (p, 1024); /* FIXME: this leaks if it fails. */
+ free (p);
+}
+#endif
+
+struct link global_link;
+
+void test_43 (void)
+{
+ global_link.m_ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
+ global_link.m_ptr = NULL;
+} /* { dg-warning "leak of '<unknown>'" } */
+/* TODO: should be more precise than just '<unknown>', and
+ ideally would be at the assigment to NULL. */
+
+struct link *global_ptr;
+
+void test_44 (void)
+{
+ global_ptr = malloc (sizeof (struct link));
+ if (!global_ptr)
+ return;
+ global_ptr->m_ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */
+ free (global_ptr); /* { dg-warning "leak of '<unknown>'" } */
+ /* TODO: should be more precise than just '<unknown>'. */
+}
+
+extern void might_take_ownership (void *ptr);
+
+void test_45 (void)
+{
+ void *p = malloc (1024);
+ might_take_ownership (p);
+}
+
+void test_46 (void)
+{
+ struct link *p = (struct link *)malloc (sizeof (struct link));
+ if (!p)
+ return;
+ struct link *q = (struct link *)malloc (sizeof (struct link));
+ p->m_ptr = q;
+ might_take_ownership (p);
+}
+
+extern int maybe_alloc (char **);
+
+int test_47 (void)
+{
+ char *p = ((void *)0);
+ int p_size = 0;
+
+ p = malloc (16);
+ if (p) {
+ free (p);
+ } else {
+ int retval = maybe_alloc (&p); /* this might write to "p". */
+ if (retval)
+ return (retval);
+ p_size = strlen(p); /* { dg-bogus "non-null expected" } */
+ free (p);
+ }
+ return p_size;
+}
new file mode 100644
@@ -0,0 +1,23 @@
+/* Tests for precision-of-wording within malloc warnings. */
+
+typedef __SIZE_TYPE__ size_t;
+extern void *malloc(size_t);
+extern void free(void *);
+extern char *strcpy(char *__restrict __dest, const char *__restrict __src)
+ __attribute__((__nothrow__, __leaf__)) __attribute__((__nonnull__(1, 2)));
+
+void test_1 (void)
+{
+ void *p = malloc (1024); /* { dg-message "\\(1\\) this call could return NULL" } */
+ strcpy ((char *)p, "hello world"); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */
+ /* { dg-message "\\(2\\) argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */
+ free (p);
+}
+
+int *test_2 (void)
+{
+ int *i = malloc (sizeof (int)); /* { dg-message "\\(1\\) this call could return NULL" } */
+ *i = 42; /* { dg-warning "dereference of possibly-NULL 'i'" } */
+ /* { dg-message "\\(2\\) 'i' could be NULL: unchecked value from \\(1\\)" "" { target *-*-* } .-1 } */
+ return i;
+}
new file mode 100644
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+
+/* Don't complain about leaks due to exiting from "main". */
+
+void main (void)
+{
+ void *p = malloc (1024);
+}
new file mode 100644
@@ -0,0 +1,84 @@
+#include <stdlib.h>
+
+typedef void *(*allocator_t) (size_t);
+typedef void (*deallocator_t) (void *);
+
+static allocator_t __attribute__((noinline))
+get_malloc (void)
+{
+ return malloc;
+}
+
+static allocator_t __attribute__((noinline))
+get_alloca (void)
+{
+ return alloca;
+}
+
+static deallocator_t __attribute__((noinline))
+get_free (void)
+{
+ return free;
+}
+
+void test_1 (void *ptr)
+{
+ deallocator_t dealloc_fn = free;
+ dealloc_fn (ptr); /* { dg-message "first 'free' here" } */
+ dealloc_fn (ptr); /* { dg-warning "double-'free'" } */
+}
+
+void test_2 (void *ptr)
+{
+ deallocator_t dealloc_fn = get_free ();
+ dealloc_fn (ptr); /* { dg-message "first 'free' here" } */
+ dealloc_fn (ptr); /* { dg-warning "double-'free'" } */
+}
+
+static void __attribute__((noinline))
+called_by_test_3 (void *ptr, deallocator_t dealloc_fn)
+{
+ dealloc_fn (ptr); /* { dg-warning "double-'free'" } */
+}
+
+void test_3 (void *ptr)
+{
+ called_by_test_3 (ptr, free);
+ called_by_test_3 (ptr, free);
+}
+
+int *test_4 (void)
+{
+ allocator_t alloc_fn = get_malloc ();
+ int *ptr = alloc_fn (sizeof (int)); /* { dg-message "this call could return NULL" } */
+ *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+ return ptr;
+}
+
+int *test_5 (void)
+{
+ allocator_t alloc_fn = get_alloca ();
+ deallocator_t dealloc_fn = get_free ();
+ int *ptr = alloc_fn (sizeof (int)); /* { dg-message "pointer is from here" } */
+ /* TODO: message should read "memory is allocated on the stack here". */
+ dealloc_fn (ptr); /* { dg-warning "'free' of 'ptr' which points to memory not on the heap" } */
+}
+
+static void __attribute__((noinline))
+called_by_test_6a (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free'" "" { xfail *-*-* } } */
+}
+
+static deallocator_t __attribute__((noinline))
+called_by_test_6b (void)
+{
+ return called_by_test_6a;
+}
+
+void test_6 (void *ptr)
+{
+ deallocator_t dealloc_fn = called_by_test_6b ();
+ dealloc_fn (ptr);
+ dealloc_fn (ptr);
+}
new file mode 100644
@@ -0,0 +1,12 @@
+/* { dg-additional-options "-O2" } */
+
+#include <stdlib.h>
+
+void test(void)
+{
+ void *ptr = malloc(512);
+ free(ptr);
+ free(ptr); /* { dg-warning "double-'free'" "" { xfail *-*-* } } */
+}
+/* With optimization, the whole of test() goes away in the "cddce" pass
+ before the analysis pass sees it, and hence we get no error message. */
new file mode 100644
@@ -0,0 +1,46 @@
+#include <stdlib.h>
+
+extern void foo (void);
+extern void bar (void);
+
+void test_1 (int flag, int n);
+
+void caller_1_of_test_1 (int n)
+{
+ test_1 (1, n); /* { dg-bogus "test_1" } */
+ test_1 (0, n); /* { dg-bogus "test_1" } */
+}
+
+void __attribute__((noinline))
+test_1 (int flag, int n)
+{
+ int *ptr = (int *)malloc (sizeof (int));
+
+ if (flag)
+ {
+ int i;
+ for (i = 0; i < n; i++)
+ foo ();
+ }
+ else
+ bar ();
+
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free'" } */
+ /* FIXME: we get duplicates intraprocedurally, as there are two paths
+ through the function.
+ The calls in test_2 also generate additional duplicates.
+ How to verify lack of duplicates?
+ Putting a bogus on the interprocedual one detects that, at least. */
+
+ if (flag)
+ foo ();
+ else
+ bar ();
+}
+
+void caller_2_of_test_1 (int n)
+{
+ test_1 (1, n); /* { dg-bogus "test_1" } */
+ test_1 (0, n); /* { dg-bogus "test_1" } */
+}
new file mode 100644
@@ -0,0 +1,24 @@
+#include <stdlib.h>
+
+void *
+calls_malloc (void)
+{
+ void *result = malloc (1024);
+ return result;
+}
+
+int *test_1 (int i)
+{
+ int *ptr = (int *)calls_malloc ();
+ *ptr = i; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+ return ptr;
+}
+
+/* Same as test_1, to exercise the caches. */
+
+int *test_2 (int i)
+{
+ int *ptr = (int *)calls_malloc ();
+ *ptr = i; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+ return ptr;
+}
new file mode 100644
@@ -0,0 +1,32 @@
+#include <stdlib.h>
+
+void
+calls_free (void *victim) /* { dg-message "\\(3\\) entry to 'calls_free'" } */
+/* { dg-message "\\(7\\) entry to 'calls_free'" "" { target *-*-* } .-1 } */
+{
+ free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+ /* { dg-message "\\(4\\) first 'free' here" "" { target *-*-* } .-1 } */
+ /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(4\\)" "" { target *-*-* } .-2 } */
+
+ /* TODO: would this be better emitted at the callsite,
+ for such a simple wrapper? */
+}
+
+void do_stuff (void)
+{
+ /* Empty. Irrelevant, and thus should not be expanded into in paths. */
+}
+
+void test (void *ptr) /* { dg-message "\\(1\\) entry to 'test'" } */
+{
+ do_stuff ();
+
+ calls_free (ptr); /* { dg-message "\\(2\\) calling 'calls_free' from 'test'" } */
+ /* { dg-message "\\(5\\) returning to 'test' from 'calls_free'" "" { target *-*-* } .-1 } */
+
+ do_stuff ();
+
+ calls_free (ptr); /* { dg-message "\\(6\\) passing freed pointer 'ptr' in call to 'calls_free' from 'test'" } */
+
+ do_stuff ();
+}
new file mode 100644
@@ -0,0 +1,95 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <stdlib.h>
+
+extern int some_condition ();
+extern void do_stuff (int);
+
+void
+may_call_free (void *victim)
+{
+ if (some_condition ())
+ return;
+
+ free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+}
+
+void test (void *ptr)
+{
+ do_stuff (1);
+
+ may_call_free (ptr);
+
+ do_stuff (2);
+
+ may_call_free (ptr);
+
+ do_stuff (3);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | free (victim);
+ | ^~~~~~~~~~~~~
+ 'test': events 1-2
+ |
+ | NN | void test (void *ptr)
+ | | ^~~~
+ | | |
+ | | (1) entry to 'test'
+ |......
+ | NN | may_call_free (ptr);
+ | | ~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) calling 'may_call_free' from 'test'
+ |
+ +--> 'may_call_free': events 3-6
+ |
+ | NN | may_call_free (void *victim)
+ | | ^~~~~~~~~~~~~
+ | | |
+ | | (3) entry to 'may_call_free'
+ | NN | {
+ | NN | if (some_condition ())
+ | | ~
+ | | |
+ | | (4) following 'false' branch...
+ |......
+ | NN | free (victim);
+ | | ~~~~~~~~~~~~~
+ | | |
+ | | (5) ...to here
+ | | (6) first 'free' here
+ |
+ <------+
+ |
+ 'test': events 7-8
+ |
+ | NN | may_call_free (ptr);
+ | | ^~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) returning to 'test' from 'may_call_free'
+ |......
+ | NN | may_call_free (ptr);
+ | | ~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (8) passing freed pointer 'ptr' in call to 'may_call_free' from 'test'
+ |
+ +--> 'may_call_free': events 9-12
+ |
+ | NN | may_call_free (void *victim)
+ | | ^~~~~~~~~~~~~
+ | | |
+ | | (9) entry to 'may_call_free'
+ | NN | {
+ | NN | if (some_condition ())
+ | | ~
+ | | |
+ | | (10) following 'false' branch...
+ |......
+ | NN | free (victim);
+ | | ~~~~~~~~~~~~~
+ | | |
+ | | (11) ...to here
+ | | (12) second 'free' here; first 'free' was at (6)
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+
+void recursive_free (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+ recursive_free (ptr);
+}
new file mode 100644
@@ -0,0 +1,30 @@
+/* { dg-additional-options "-fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void
+calls_free (void *victim)
+{
+ free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+}
+
+extern void do_stuff (void);
+
+struct foo
+{
+ void *m_p;
+};
+
+void test (struct foo f)
+{
+ do_stuff ();
+
+ calls_free (f.m_p);
+
+ do_stuff ();
+
+ calls_free (f.m_p); /* { dg-message "passing freed pointer '<unknown>' in call to 'calls_free' from 'test'" } */
+ // TODO: something better than '<unknown>'
+
+ do_stuff ();
+}
new file mode 100644
@@ -0,0 +1,34 @@
+/* { dg-additional-options "-fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void
+calls_free (void *victim) /* { dg-message "\\(3\\) entry to 'calls_free'" } */
+/* { dg-message "\\(7\\) entry to 'calls_free'" "" { target *-*-* } .-1 } */
+{
+ free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+ /* { dg-message "\\(4\\) first 'free' here" "" { target *-*-* } .-1 } */
+ /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(4\\)" "" { target *-*-* } .-2 } */
+
+ /* TODO: would this be better emitted at the callsite,
+ for such a simple wrapper? */
+}
+
+extern void do_stuff (void);
+
+void test (void *ptr) /* { dg-message "\\(1\\) entry to 'test'" } */
+{
+ do_stuff ();
+
+ calls_free (ptr); /* { dg-message "\\(2\\) calling 'calls_free' from 'test'" } */
+ /* { dg-message "\\(5\\) returning to 'test' from 'calls_free'" "" { target *-*-* } .-1 } */
+
+ do_stuff ();
+
+ calls_free (ptr); /* { dg-message "\\(6\\) passing freed pointer 'ptr' in call to 'calls_free' from 'test'" } */
+
+ do_stuff ();
+}
+
+
+
new file mode 100644
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+
+void *
+calls_malloc (void)
+{
+ void *result = malloc (1024);
+ return result;
+}
+
+void
+calls_free (void *victim)
+{
+ free (victim); /* { dg-warning "double-'free' of 'victim'" } */
+ /* TODO: this would be better emitted at the callsite,
+ for such a simple wrapper. */
+}
+
+void test (void)
+{
+ void *ptr = calls_malloc ();
+ calls_free (ptr);
+ calls_free (ptr); /* BUG: double-'free'. */
+}
new file mode 100644
@@ -0,0 +1,13 @@
+#include <stdlib.h>
+
+static void calls_free(int *q)
+{
+ free(q);
+}
+
+void test(void *p)
+{
+ calls_free(p);
+
+ free(p); /* { dg-warning "double-'free' of 'p'" } */
+}
new file mode 100644
@@ -0,0 +1,13 @@
+#include <stdlib.h>
+
+static int *calls_malloc(void)
+{
+ return malloc(sizeof(int));
+}
+
+int *test(void)
+{
+ int *p = calls_malloc(); /* { dg-message "possible return of NULL to 'test' from 'calls_malloc'" } */
+ *p = 42; /* { dg-warning "dereference of possibly-NULL 'p'" } */
+ return p;
+}
new file mode 100644
@@ -0,0 +1,22 @@
+#include <stdlib.h>
+
+void *
+calls_malloc (void)
+{
+ void *result = malloc (1024); /* { dg-message "allocated here" } */
+ return result; /* { dg-warning "leak of 'result'" } */
+}
+
+void test_1 ()
+{
+ calls_malloc (); /* { dg-message "calling 'calls_malloc' from 'test_1'" } */
+}
+
+static void callee (int i)
+{
+}
+
+void test_2 (int i)
+{
+ callee (i);
+}
new file mode 100644
@@ -0,0 +1,29 @@
+#include <stdlib.h>
+
+/**************************************************************************/
+
+static void maybe_calls_free_1(int *q, int flag)
+{
+ if (flag)
+ free(q); /* { dg-warning "double-'free' of 'q'" } */
+}
+
+void test_1(void *p)
+{
+ maybe_calls_free_1(p, 1);
+ maybe_calls_free_1(p, 1);
+}
+
+/**************************************************************************/
+
+static void maybe_calls_free_2(int *q, int flag)
+{
+ if (flag)
+ free(q); /* { dg-bogus "double-'free'" } */
+}
+
+void test_2(void *p)
+{
+ maybe_calls_free_2(p, 0);
+ maybe_calls_free_2(p, 0);
+}
new file mode 100644
@@ -0,0 +1,172 @@
+/* Example of a multilevel wrapper around malloc/free, with a double-'free'. */
+
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fanalyzer-verbose-state-changes -fdiagnostics-show-caret" } */
+
+#include <stdlib.h>
+
+void *wrapped_malloc (size_t size)
+{
+ return malloc (size);
+}
+
+void wrapped_free (void *ptr)
+{
+ free (ptr); /* { dg-warning "double-'free' of 'ptr' \\\[CWE-415\\\]" } */
+}
+
+typedef struct boxed_int
+{
+ int i;
+} boxed_int;
+
+boxed_int *
+make_boxed_int (int i)
+{
+ boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+ if (!result)
+ abort ();
+ result->i = i;
+ return result;
+}
+
+void
+free_boxed_int (boxed_int *bi)
+{
+ wrapped_free (bi);
+}
+
+void test (int i)
+{
+ boxed_int *obj = make_boxed_int (i);
+
+ free_boxed_int (obj);
+
+ free_boxed_int (obj);
+}
+
+/* double-'free'. */
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test': events 1-2
+ |
+ | NN | void test (int i)
+ | | ^~~~
+ | | |
+ | | (1) entry to 'test'
+ | NN | {
+ | NN | boxed_int *obj = make_boxed_int (i);
+ | | ~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) calling 'make_boxed_int' from 'test'
+ |
+ +--> 'make_boxed_int': events 3-6
+ |
+ | NN | make_boxed_int (int i)
+ | | ^~~~~~~~~~~~~~
+ | | |
+ | | (3) entry to 'make_boxed_int'
+ |......
+ | NN | if (!result)
+ | | ~
+ | | |
+ | | (4) following 'false' branch (when 'result' is non-NULL)...
+ | NN | abort ();
+ | NN | result->i = i;
+ | | ~~~~~~~~~~~~~
+ | | |
+ | | (5) ...to here
+ | NN | return result;
+ | | ~~~~~~
+ | | |
+ | | (6) state of '<return-value>': 'start' -> 'nonnull' (origin: NULL)
+ |
+ <------+
+ |
+ 'test': events 7-8
+ |
+ | NN | boxed_int *obj = make_boxed_int (i);
+ | | ^~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) returning to 'test' from 'make_boxed_int'
+ | NN |
+ | NN | free_boxed_int (obj);
+ | | ~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (8) calling 'free_boxed_int' from 'test'
+ |
+ +--> 'free_boxed_int': events 9-10
+ |
+ | NN | free_boxed_int (boxed_int *bi)
+ | | ^~~~~~~~~~~~~~
+ | | |
+ | | (9) entry to 'free_boxed_int'
+ | NN | {
+ | NN | wrapped_free (bi);
+ | | ~~~~~~~~~~~~~~~~~
+ | | |
+ | | (10) calling 'wrapped_free' from 'free_boxed_int'
+ |
+ +--> 'wrapped_free': events 11-12
+ |
+ | NN | void wrapped_free (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (11) entry to 'wrapped_free'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (12) first 'free' here (state of 'ptr': 'nonnull' -> 'freed', origin: NULL)
+ |
+ <------+
+ |
+ 'free_boxed_int': event 13
+ |
+ | NN | wrapped_free (bi);
+ | | ^~~~~~~~~~~~~~~~~
+ | | |
+ | | (13) returning to 'free_boxed_int' from 'wrapped_free'
+ |
+ <------+
+ |
+ 'test': events 14-15
+ |
+ | NN | free_boxed_int (obj);
+ | | ^~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (14) returning to 'test' from 'free_boxed_int'
+ | NN |
+ | NN | free_boxed_int (obj);
+ | | ~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (15) passing freed pointer 'obj' in call to 'free_boxed_int' from 'test'
+ |
+ +--> 'free_boxed_int': events 16-17
+ |
+ | NN | free_boxed_int (boxed_int *bi)
+ | | ^~~~~~~~~~~~~~
+ | | |
+ | | (16) entry to 'free_boxed_int'
+ | NN | {
+ | NN | wrapped_free (bi);
+ | | ~~~~~~~~~~~~~~~~~
+ | | |
+ | | (17) passing freed pointer 'bi' in call to 'wrapped_free' from 'free_boxed_int'
+ |
+ +--> 'wrapped_free': events 18-19
+ |
+ | NN | void wrapped_free (void *ptr)
+ | | ^~~~~~~~~~~~
+ | | |
+ | | (18) entry to 'wrapped_free'
+ | NN | {
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (19) second 'free' here; first 'free' was at (12) ('ptr' is in state 'freed')
+ |
+ { dg-end-multiline-output "" } */
+
+/* TODO: the event describing the allocation is uninteresting and probably
+ should be purged. */
new file mode 100644
@@ -0,0 +1,12 @@
+#include <stdlib.h>
+#include "malloc-ipa-8-lto.h"
+
+void *wrapped_malloc (size_t size)
+{
+ return malloc (size);
+}
+
+void wrapped_free (void *ptr)
+{
+ free (ptr);
+}
new file mode 100644
@@ -0,0 +1,18 @@
+#include <stdlib.h>
+#include "malloc-ipa-8-lto.h"
+
+boxed_int *
+make_boxed_int (int i)
+{
+ boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+ if (!result)
+ abort ();
+ result->i = i;
+ return result;
+}
+
+void
+free_boxed_int (boxed_int *bi)
+{
+ wrapped_free (bi);
+}
new file mode 100644
@@ -0,0 +1,17 @@
+/* { dg-do link } */
+/* { dg-require-effective-target lto } */
+/* { dg-additional-options "-flto" } */
+/* { dg-additional-sources "malloc-ipa-8-lto-a.c malloc-ipa-8-lto-b.c" } */
+
+#include <stdlib.h>
+#include "malloc-ipa-8-lto.h"
+
+void test (int i)
+{
+ boxed_int *obj = make_boxed_int (i);
+
+ free_boxed_int (obj);
+ free (obj); /* { dg-warning "double-free" } */
+}
+
+int main() { return 0; }
new file mode 100644
@@ -0,0 +1,12 @@
+#include <stddef.h>
+
+extern void *wrapped_malloc (size_t size);
+extern void wrapped_free (void *ptr);
+
+typedef struct boxed_int
+{
+ int i;
+} boxed_int;
+
+extern boxed_int *make_boxed_int (int i);
+extern void free_boxed_int (boxed_int *bi);
new file mode 100644
@@ -0,0 +1,66 @@
+/* Example of a multilevel wrapper around malloc, with an unchecked write. */
+
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fdiagnostics-show-caret -fanalyzer-verbose-state-changes" } */
+
+#include <stdlib.h>
+
+void *wrapped_malloc (size_t size)
+{
+ return malloc (size);
+}
+
+typedef struct boxed_int
+{
+ int i;
+} boxed_int;
+
+boxed_int *
+make_boxed_int (int i)
+{
+ boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+ result->i = i; /* { dg-warning "dereference of possibly-NULL 'result'" } */
+ return result;
+}
+
+/* "dereference of possibly-NULL 'result' [CWE-690]". */
+/* { dg-begin-multiline-output "" }
+ NN | result->i = i;
+ | ~~~~~~~~~~^~~
+ 'make_boxed_int': events 1-2
+ |
+ | NN | make_boxed_int (int i)
+ | | ^~~~~~~~~~~~~~
+ | | |
+ | | (1) entry to 'make_boxed_int'
+ | NN | {
+ | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) calling 'wrapped_malloc' from 'make_boxed_int'
+ |
+ +--> 'wrapped_malloc': events 3-4
+ |
+ | NN | void *wrapped_malloc (size_t size)
+ | | ^~~~~~~~~~~~~~
+ | | |
+ | | (3) entry to 'wrapped_malloc'
+ | NN | {
+ | NN | return malloc (size);
+ | | ~~~~~~~~~~~~~
+ | | |
+ | | (4) this call could return NULL (state of '<return-value>': 'start' -> 'unchecked', origin: NULL)
+ |
+ <------+
+ |
+ 'make_boxed_int': events 5-6
+ |
+ | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
+ | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (5) possible return of NULL to 'make_boxed_int' from 'wrapped_malloc'
+ | NN | result->i = i;
+ | | ~~~~~~~~~~~~~
+ | | |
+ | | (6) 'result' could be NULL: unchecked value from (4) ('result' is in state 'unchecked')
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,18 @@
+/* { dg-additional-options "-fdiagnostics-path-format=none -fanalyzer-verbosity=1" } */
+
+#include <stdlib.h>
+
+void
+two_frees (void *p, void *q)
+{
+ free (p);
+ free (q); /* { dg-warning "double-'free' of 'q'" } */
+ /* TODO: could be useful to identify that p == q when called from 'test'. */
+}
+
+extern void do_stuff (void);
+
+void test (void *ptr)
+{
+ two_frees (ptr, ptr);
+}
new file mode 100644
@@ -0,0 +1,45 @@
+/* Test path-printing in the face of macros. */
+
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include "malloc-macro.h"
+
+/* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } 2 } */
+
+int test (void *ptr)
+{
+ WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+ WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+
+ /* Erase the spans indicating the header file
+ (to avoid embedding path assumptions). */
+ /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */
+ /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */
+
+ /* { dg-begin-multiline-output "" }
+ NN | #define WRAPPED_FREE(PTR) free(PTR)
+ | ^~~~~~~~~
+ NN | WRAPPED_FREE (ptr);
+ | ^~~~~~~~~~~~
+ 'test': event 1
+ |
+ |
+ | NN | #define WRAPPED_FREE(PTR) free(PTR)
+ | | ^~~~~~~~~
+ | | |
+ | | (1) first 'free' here
+ | NN | WRAPPED_FREE (ptr);
+ | | ^~~~~~~~~~~~
+ |
+ 'test': event 2
+ |
+ |
+ | NN | #define WRAPPED_FREE(PTR) free(PTR)
+ | | ^~~~~~~~~
+ | | |
+ | | (2) second 'free' here; first 'free' was at (1)
+ | NN | WRAPPED_FREE (ptr);
+ | | ^~~~~~~~~~~~
+ |
+ { dg-end-multiline-output "" } */
+}
new file mode 100644
@@ -0,0 +1,15 @@
+/* Test path-printing in the face of macros. */
+
+/* { dg-additional-options "-fdiagnostics-path-format=separate-events" } */
+
+#include "malloc-macro.h"
+
+/* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } 2 } */
+/* { dg-message "first 'free' here" "" { target *-*-* } 2 } */
+/* { dg-message "second 'free' here" "" { target *-*-* } 2 } */
+
+int test (void *ptr)
+{
+ WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+ WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+}
new file mode 100644
@@ -0,0 +1,2 @@
+#include <stdlib.h>
+#define WRAPPED_FREE(PTR) free(PTR)
new file mode 100644
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+
+/* Ensure that we don't need to laboriously walk every path to get
+ to the end of the function. */
+
+int test_1 (int n)
+{
+ int i, j, k;
+ k = 0;
+ for (int i = 0; i < n; i++)
+ for (int j = 0; j < 1000; j++)
+ k++;
+ return k;
+}
new file mode 100644
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+
+/* Ensure that we don't get an exponential growth in paths due to
+ repeated diamonds in the CFG. */
+
+typedef struct obj {
+ int ob_refcnt;
+} PyObject;
+
+extern void Py_Dealloc (PyObject *op);
+
+#define Py_DECREF(op) \
+ do { \
+ if (--((PyObject*)(op))->ob_refcnt == 0) \
+ Py_Dealloc((PyObject *)(op)); \
+ } while (0)
+
+int test (PyObject *obj_01, PyObject *obj_02, PyObject *obj_03,
+ PyObject *obj_04, PyObject *obj_05, PyObject *obj_06,
+ PyObject *obj_07, PyObject *obj_08, PyObject *obj_09,
+ PyObject *obj_10, PyObject *obj_11, PyObject *obj_12,
+ PyObject *obj_13, PyObject *obj_14, PyObject *obj_15
+)
+{
+ Py_DECREF (obj_01); Py_DECREF (obj_02); Py_DECREF (obj_03);
+ Py_DECREF (obj_04); Py_DECREF (obj_05); Py_DECREF (obj_06);
+ Py_DECREF (obj_07); Py_DECREF (obj_08); Py_DECREF (obj_09);
+ Py_DECREF (obj_10); Py_DECREF (obj_11); Py_DECREF (obj_12);
+ Py_DECREF (obj_13); Py_DECREF (obj_14); Py_DECREF (obj_15);
+}
new file mode 100644
@@ -0,0 +1,36 @@
+#include <stdlib.h>
+
+extern int foo (void);
+
+int successes;
+int failures;
+
+#define ONE_DIAMOND \
+ do { \
+ void *ptr = malloc (128); \
+ if (foo ()) \
+ successes++; \
+ else \
+ failures++; \
+ free (ptr); \
+ } while (0)
+
+#define TEN_DIAMONDS \
+ do { \
+ ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; \
+ ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; \
+ } while (0)
+
+void test_3 (void *ptr)
+{
+ free (ptr);
+#if 1
+ ONE_DIAMOND;
+#else
+ /* TODO: enabling this leads to numerous duplicated reports,
+ all of them detailing all the extraneous info about the malloc/free
+ within the diamonds. */
+ TEN_DIAMONDS;
+#endif
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
new file mode 100644
@@ -0,0 +1,15 @@
+/* Verify that we emit sane paths for state machine errors. */
+
+#include <stdlib.h>
+
+void test_1 (void)
+{
+ void *ptr = malloc (1024); /* { dg-line malloc } */
+ free (ptr); /* { dg-line first_free } */
+ free (ptr); /* { dg-line second_free } */
+
+ /* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } second_free } */
+ /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc } */
+ /* { dg-message "\\(2\\) first 'free' here" "" { target *-*-* } first_free } */
+ /* { dg-message "\\(3\\) second 'free' here; first 'free' was at \\(2\\)" "" { target *-*-* } second_free } */
+}
new file mode 100644
@@ -0,0 +1,19 @@
+#include <stdlib.h>
+
+int test (int flag)
+{
+ int other_flag;
+ if (flag)
+ other_flag = 1;
+ else
+ other_flag = 0;
+
+ /* With state-merging, we lose the relationship between 'flag' and 'other_flag'. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (other_flag)
+ __analyzer_eval (flag); /* { dg-warning "UNKNOWN" } */
+ else
+ __analyzer_eval (flag); /* { dg-warning "UNKNOWN" } */
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* Verify that we emit sane paths for state machine errors. */
+
+#include <stdlib.h>
+
+void test_2 (void *ptr)
+{
+ free (ptr); /* { dg-line first_free } */
+ free (ptr); /* { dg-line second_free } */
+
+ /* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } second_free } */
+ /* { dg-message "\\(1\\) first 'free' here" "" { target *-*-* } first_free } */
+ /* { dg-message "\\(2\\) second 'free' here; first 'free' was at \\(1\\)" "" { target *-*-* } second_free } */
+}
new file mode 100644
@@ -0,0 +1,14 @@
+/* Verify that we emit sane paths for state machine errors. */
+
+#include <stdlib.h>
+
+int *test_3 (void)
+{
+ int *ptr = (int *)malloc (sizeof (int)); /* { dg-line malloc } */
+ *ptr = 42; /* { dg-line unchecked_deref } */
+ return ptr;
+
+ /* { dg-warning "dereference of possibly-NULL 'ptr'" "" { target *-*-* } unchecked_deref } */
+ /* { dg-message "\\(1\\) this call could return NULL" "" { target *-*-* } malloc } */
+ /* { dg-message "\\(2\\) 'ptr' could be NULL" "" { target *-*-* } unchecked_deref } */
+}
new file mode 100644
@@ -0,0 +1,20 @@
+/* Verify that we emit sane paths for state machine errors. */
+
+#include <stdlib.h>
+
+int *test_4 (void)
+{
+ int *ptr = (int *)malloc (sizeof (int)); /* { dg-line malloc } */
+ if (ptr) /* { dg-line cond } */
+ *ptr = 42;
+ else
+ *ptr = 43; /* { dg-line on_null_ptr } */
+ return ptr;
+
+ /* { dg-warning "dereference of NULL 'ptr'" "" { target *-*-* } on_null_ptr } */
+ /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc } */
+ /* { dg-message "\\(2\\) assuming 'ptr' is NULL" "" { target *-*-* } cond } */
+ /* { dg-message "\\(3\\) following 'false' branch \\(when 'ptr' is NULL\\)\\.\\.\\." "" { target *-*-* } cond } */
+ /* { dg-message "\\(4\\) \\.\\.\\.to here" "" { target *-*-* } on_null_ptr } */
+ /* { dg-message "\\(5\\) dereference of NULL 'ptr'" "" { target *-*-* } on_null_ptr } */
+}
new file mode 100644
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void do_stuff (void);
+
+int test (const char *filename, int flag)
+{
+ FILE *f;
+ int *p, *q;
+ int i;
+
+ p = (int *)malloc (sizeof (int)); /* { dg-line malloc_of_p } */
+ if (!p) /* { dg-line test_of_p } */
+ {
+ free (p);
+ return -1;
+ }
+
+ q = (int *)malloc (sizeof (int)); /* { dg-line malloc_of_q } */
+ if (!q) /* { dg-line test_of_q } */
+ {
+ free (p); /* { dg-line first_free_of_p } */
+ /* oops: forgot the "return" here, so it falls through. */
+ }
+
+ do_stuff ();
+
+ free (p); /* { dg-line second_free_of_p } */
+ free (q);
+ return 0;
+
+ /* { dg-warning "double-'free' of 'p'" "" { target *-*-* } second_free_of_p } */
+ /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc_of_p } */
+ /* { dg-message "\\(2\\) assuming 'p' is non-NULL" "" { target *-*-* } test_of_p } */
+ /* { dg-message "\\(3\\) following 'false' branch \\(when 'p' is non-NULL\\)\\.\\.\\." "" { target *-*-* } test_of_p } */
+ /* { dg-message "\\(4\\) \\.\\.\\.to here" "" { target *-*-* } malloc_of_q } */
+ /* { dg-message "\\(5\\) following 'true' branch \\(when 'q' is NULL\\)\\.\\.\\." "" { target *-*-* } test_of_q } */
+ /* { dg-message "\\(6\\) \\.\\.\\.to here" "" { target *-*-* } first_free_of_p } */
+ /* { dg-message "\\(7\\) first 'free' here" "" { target *-*-* } first_free_of_p } */
+ /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(7\\)" "" { target *-*-* } second_free_of_p } */
+
+ /* We don't care about the state changes to q. */
+}
new file mode 100644
@@ -0,0 +1,11 @@
+#include <stdlib.h>
+
+void test (void *ptr)
+{
+ void *q;
+ q = ptr;
+ free (ptr);
+ free (q); /* { dg-warning "double-'free' of 'q'" } */
+ /* The above case requires us to handle equivalence classes in
+ state transitions. */
+}
new file mode 100644
@@ -0,0 +1,21 @@
+#include <stdlib.h>
+
+extern int foo (void);
+extern int bar (void);
+
+void test (void)
+{
+ void *p = malloc (1024); /* { dg-message "\\(1\\) allocated here" } */
+ void *q = malloc (1024);
+
+ foo ();
+ if (!q) /* { dg-message "\\(2\\) following 'true' branch \\(when 'q' is NULL\\)\\.\\.\\." } */
+ {
+ free (q); /* { dg-message "\\(3\\) \\.\\.\\.to here" } */
+ return; /* { dg-warning "leak of 'p'" } */
+ /* { dg-message "\\(4\\) 'p' leaks here; was allocated at \\(1\\)" "" { target *-*-* } .-1 } */
+ }
+ bar ();
+ free (q);
+ free (p);
+}
new file mode 100644
@@ -0,0 +1,54 @@
+/* { dg-additional-options "-fanalyzer-transitivity" } */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+extern void do_stuff (const void *);
+
+#define LIMIT 1024
+
+void test_1 (size_t sz)
+{
+ void *ptr;
+ if (sz >= LIMIT)
+ ptr = malloc (sz);
+ else
+ ptr = alloca (sz);
+
+ do_stuff (ptr);
+
+ if (sz >= LIMIT)
+ free (ptr);
+}
+
+void test_2 (size_t sz)
+{
+ void *ptr;
+ if (sz < LIMIT)
+ ptr = alloca (sz);
+ else
+ ptr = malloc (sz);
+
+ do_stuff (ptr);
+
+ if (sz >= LIMIT)
+ free (ptr);
+}
+
+void test_3 (size_t sz)
+{
+ void *ptr;
+ if (sz <= LIMIT)
+ ptr = alloca (sz); /* { dg-message "memory is allocated on the stack here" } */
+ else
+ ptr = malloc (sz);
+
+ do_stuff (ptr);
+
+ /* Bug: the "sz <= LIMIT" above should have been "sz < LIMIT",
+ so there's a free-of-alloca when sz == LIMIT. */
+ if (sz >= LIMIT)
+ free (ptr); /* { dg-warning "'free' of memory allocated on the stack by 'alloca'" } */
+}
+/* { dg-bogus "leak of 'ptr'" } */
+/* This can't happen, as "sz > 1024" && "sz <= 1023" is impossible. */
new file mode 100644
@@ -0,0 +1,298 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <stdlib.h>
+
+void test_1 (void)
+{
+ void *ptr = malloc (1024);
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_1': events 1-3
+ |
+ | NN | void *ptr = malloc (1024);
+ | | ^~~~~~~~~~~~~
+ | | |
+ | | (1) allocated here
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (2) first 'free' here
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (3) second 'free' here; first 'free' was at (2)
+ |
+ { dg-end-multiline-output "" } */
+
+void test_2 (int x, int y)
+{
+ void *ptr = malloc (1024);
+ if (x)
+ free (ptr);
+ if (y)
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+} /* { dg-warning "leak of 'ptr'" } */
+
+/* "double-'free' of 'ptr'". */
+/* { dg-begin-multiline-output "" }
+ NN | free (ptr);
+ | ^~~~~~~~~~
+ 'test_2': events 1-7
+ |
+ | NN | void *ptr = malloc (1024);
+ | | ^~~~~~~~~~~~~
+ | | |
+ | | (1) allocated here
+ | NN | if (x)
+ | | ~
+ | | |
+ | | (2) following 'true' branch (when 'x != 0')...
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (3) ...to here
+ | | (4) first 'free' here
+ | NN | if (y)
+ | | ~
+ | | |
+ | | (5) following 'true' branch (when 'y != 0')...
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (6) ...to here
+ | | (7) second 'free' here; first 'free' was at (4)
+ |
+ { dg-end-multiline-output "" } */
+
+/* "leak of 'ptr'. */
+/* { dg-begin-multiline-output "" }
+ NN | }
+ | ^
+ 'test_2': events 1-6
+ |
+ | NN | void *ptr = malloc (1024);
+ | | ^~~~~~~~~~~~~
+ | | |
+ | | (1) allocated here
+ | NN | if (x)
+ | | ~
+ | | |
+ | | (2) following 'false' branch (when 'x == 0')...
+ | NN | free (ptr);
+ | NN | if (y)
+ | | ~
+ | | |
+ | | (3) ...to here
+ | | (4) following 'false' branch (when 'y == 0')...
+ | NN | free (ptr);
+ | NN | }
+ | | ~
+ | | |
+ | | (5) ...to here
+ | | (6) 'ptr' leaks here; was allocated at (1)
+ |
+ { dg-end-multiline-output "" } */
+
+int test_3 (int x, int y)
+{
+ int *ptr = (int *)malloc (sizeof (int));
+ *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+ if (x)
+ free (ptr);
+
+ *ptr = 19; /* { dg-warning "use after 'free' of 'ptr'" } */
+ // TODO: two warnings here: one is from sm-malloc, the other from region model
+
+ if (y)
+ free (ptr); /* No double-'free' warning: we've already attempted
+ to dereference it above. */
+ return *ptr; /* { dg-warning "use after 'free' of 'ptr'" } */
+ // TODO: two warnings here: one is from sm-malloc, the other from region model
+ /* { dg-warning "leak of 'ptr'" "" { target *-*-* } .-2 } */
+}
+
+/* "dereference of possibly-NULL 'ptr'". */
+/* { dg-begin-multiline-output "" }
+ NN | *ptr = 42;
+ | ~~~~~^~~~
+ 'test_3': events 1-2
+ |
+ | NN | int *ptr = (int *)malloc (sizeof (int));
+ | | ^~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (1) this call could return NULL
+ | NN | *ptr = 42;
+ | | ~~~~~~~~~
+ | | |
+ | | (2) 'ptr' could be NULL: unchecked value from (1)
+ |
+ { dg-end-multiline-output "" } */
+
+/* "use after 'free' of 'ptr'". */
+/* { dg-begin-multiline-output "" }
+ NN | *ptr = 19;
+ | ~~~~~^~~~
+ 'test_3': events 1-6
+ |
+ | NN | int *ptr = (int *)malloc (sizeof (int));
+ | | ^~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (1) allocated here
+ | NN | *ptr = 42;
+ | | ~~~~~~~~~
+ | | |
+ | | (2) assuming 'ptr' is non-NULL
+ | NN | if (x)
+ | | ~
+ | | |
+ | | (3) following 'true' branch (when 'x != 0')...
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (4) ...to here
+ | | (5) freed here
+ | NN |
+ | NN | *ptr = 19;
+ | | ~~~~~~~~~
+ | | |
+ | | (6) use after 'free' of 'ptr'; freed at (5)
+ |
+ { dg-end-multiline-output "" } */
+
+/* "use after 'free' of 'ptr'". */
+/* { dg-begin-multiline-output "" }
+ NN | return *ptr;
+ | ^~~~
+ 'test_3': events 1-8
+ |
+ | NN | int *ptr = (int *)malloc (sizeof (int));
+ | | ^~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (1) allocated here
+ | NN | *ptr = 42;
+ | | ~~~~~~~~~
+ | | |
+ | | (2) assuming 'ptr' is non-NULL
+ | NN | if (x)
+ | | ~
+ | | |
+ | | (3) following 'false' branch (when 'x == 0')...
+ |......
+ | NN | *ptr = 19;
+ | | ~~~~~~~~~
+ | | |
+ | | (4) ...to here
+ |......
+ | NN | if (y)
+ | | ~
+ | | |
+ | | (5) following 'true' branch (when 'y != 0')...
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (6) ...to here
+ | | (7) freed here
+ | NN |
+ | NN | return *ptr;
+ | | ~~~~
+ | | |
+ | | (8) use after 'free' of 'ptr'; freed at (7)
+ |
+ { dg-end-multiline-output "" } */
+
+/* "leak of 'ptr'". */
+/* { dg-begin-multiline-output "" }
+ NN | return *ptr;
+ | ^~~~
+ 'test_3': events 1-7
+ |
+ | NN | int *ptr = (int *)malloc (sizeof (int));
+ | | ^~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (1) allocated here
+ | NN | *ptr = 42;
+ | | ~~~~~~~~~
+ | | |
+ | | (2) assuming 'ptr' is non-NULL
+ | NN | if (x)
+ | | ~
+ | | |
+ | | (3) following 'false' branch (when 'x == 0')...
+ |......
+ | NN | *ptr = 19;
+ | | ~~~~~~~~~
+ | | |
+ | | (4) ...to here
+ |......
+ | NN | if (y)
+ | | ~
+ | | |
+ | | (5) following 'false' branch (when 'y == 0')...
+ |......
+ | NN | return *ptr;
+ | | ~~~~
+ | | |
+ | | (6) ...to here
+ | | (7) 'ptr' leaks here; was allocated at (1)
+ |
+ { dg-end-multiline-output "" } */
+
+/* "use after 'free' of 'ptr'". */
+/* { dg-begin-multiline-output "" }
+ NN | *ptr = 19;
+ | ~~~~~^~~~
+ 'test_3': events 1-3
+ |
+ | NN | if (x)
+ | | ^
+ | | |
+ | | (1) following 'true' branch (when 'x != 0')...
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (2) ...to here
+ | NN |
+ | NN | *ptr = 19;
+ | | ~~~~~~~~~
+ | | |
+ | | (3) use after 'free' of 'ptr' here
+ |
+ { dg-end-multiline-output "" } */
+
+/* "use after 'free' of 'ptr'". */
+/* { dg-begin-multiline-output "" }
+ NN | return *ptr;
+ | ^~~~
+ 'test_3': events 1-5
+ |
+ | NN | if (x)
+ | | ^
+ | | |
+ | | (1) following 'false' branch (when 'x == 0')...
+ |......
+ | NN | *ptr = 19;
+ | | ~~~~~~~~~
+ | | |
+ | | (2) ...to here
+ |......
+ | NN | if (y)
+ | | ~
+ | | |
+ | | (3) following 'true' branch (when 'y != 0')...
+ | NN | free (ptr);
+ | | ~~~~~~~~~~
+ | | |
+ | | (4) ...to here
+ | NN | to dereference it above
+ | NN | return *ptr;
+ | | ~~~~
+ | | |
+ | | (5) use after 'free' of 'ptr' here
+ |
+ { dg-end-multiline-output "" } */
+/* TODO: this is really a duplicate; can we either eliminate it, or
+ improve the path? */
new file mode 100644
@@ -0,0 +1,180 @@
+/* { dg-additional-options "-fno-analyzer-call-summaries -fanalyzer-transitivity" } */
+
+#include <stdlib.h>
+
+extern int foo (int);
+
+static int __attribute__((noinline))
+do_stuff (int *p, int n)
+{
+ int sum = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ for (i = 0; i < n; i++)
+ sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+ return sum;
+}
+
+static int __attribute__((noinline))
+do_stuff_2 (int *p, int n)
+{
+ return 0;
+}
+
+/* Various examples of functions that use either a malloc buffer
+ or a local buffer, do something, then conditionally free the
+ buffer, tracking whether "free" is necessary in various
+ ways.
+
+ In each case, there ought to be only two paths through the function,
+ not four. */
+
+/* Repeated (n > 10) predicate. */
+
+int test_repeated_predicate_1 (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ // FIXME: why 3 here?
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ // FIXME: why 3 here?
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* A simpler version of the above. */
+
+int test_repeated_predicate_2 (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff_2 (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* A predicate that sets a flag for the 2nd test. */
+
+int test_explicit_flag (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+ int need_to_free = 0;
+
+ if (n > 10)
+ {
+ ptr = (int *)malloc (sizeof (int) * n);
+ need_to_free = 1;
+ }
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ // FIXME: why 3 here?
+
+ if (need_to_free)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* Pointer comparison. */
+
+int test_pointer_comparison (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ // FIXME: why 3 here?
+
+ if (ptr != buf)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* Set a flag based on a conditional, then use it, then reuse the
+ conditional. */
+
+int test_initial_flag (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+ int on_heap = 0;
+
+ if (n > 10)
+ on_heap = 1;
+ else
+ on_heap = 0;
+
+ /* Due to state-merging, we lose the relationship between 'n > 10'
+ and 'on_heap' here; we have to rely on feasibility-checking
+ in the diagnostic_manager to reject the false warnings. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (on_heap)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */
+ // FIXME: why 5 here?
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
new file mode 100644
@@ -0,0 +1,175 @@
+/* { dg-additional-options "-fanalyzer-call-summaries" } */
+
+#include <stdlib.h>
+
+extern int foo (int);
+
+static int __attribute__((noinline))
+do_stuff (int *p, int n)
+{
+ int sum = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ for (i = 0; i < n; i++)
+ sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+ return sum;
+}
+
+static int __attribute__((noinline))
+do_stuff_2 (int *p, int n)
+{
+ return 0;
+}
+
+/* Various examples of functions that use either a malloc buffer
+ or a local buffer, do something, then conditionally free the
+ buffer, tracking whether "free" is necessary in various
+ ways.
+
+ In each case, there ought to be only two paths through the function,
+ not four. */
+
+/* Repeated (n > 10) predicate. */
+
+int test_repeated_predicate_1 (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* A simpler version of the above. */
+
+int test_repeated_predicate_2 (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff_2 (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* A predicate that sets a flag for the 2nd test. */
+
+int test_explicit_flag (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+ int need_to_free = 0;
+
+ if (n > 10)
+ {
+ ptr = (int *)malloc (sizeof (int) * n);
+ need_to_free = 1;
+ }
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (need_to_free)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* Pointer comparison. */
+
+int test_pointer_comparison (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (ptr != buf)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* Set a flag based on a conditional, then use it, then reuse the
+ conditional. */
+
+int test_initial_flag (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+ int on_heap = 0;
+
+ if (n > 10)
+ on_heap = 1;
+ else
+ on_heap = 0;
+
+ /* Due to state-merging, we lose the relationship between 'n > 10'
+ and 'on_heap' here; we have to rely on feasibility-checking
+ in the diagnostic_manager to reject the false warnings. */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (on_heap)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
new file mode 100644
@@ -0,0 +1,178 @@
+#include <stdlib.h>
+
+extern int foo (int);
+
+static int __attribute__((noinline))
+do_stuff_2 (int *p, int n)
+{
+ return 0;
+}
+
+/* As malloc-vs-local.c, but hand-inlining the logic. */
+
+/* Repeated (n > 10) predicate. */
+
+int test_repeated_predicate_1 (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ {
+ int *p = ptr;
+ int sum = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ for (i = 0; i < n; i++)
+ sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+ result = sum;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* As above, but with just one loop. */
+
+int test_repeated_predicate_1a (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ {
+ int *p = ptr;
+ int sum = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ result = sum;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* A simpler version of the above. */
+
+int test_repeated_predicate_2 (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff_2 (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (n > 10)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* A predicate that sets a flag for the 2nd test. */
+
+int test_explicit_flag (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+ int need_to_free = 0;
+
+ if (n > 10)
+ {
+ ptr = (int *)malloc (sizeof (int) * n);
+ need_to_free = 1;
+ }
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ {
+ int *p = ptr;
+ int sum = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ for (i = 0; i < n; i++)
+ sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+ result = sum;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+ if (need_to_free)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
+
+/* Pointer comparison. */
+
+int test_pointer_comparison (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ {
+ int *p = ptr;
+ int sum = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ for (i = 0; i < n; i++)
+ sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+ result = sum;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+ if (ptr != buf)
+ free (ptr); /* { dg-bogus "not on the heap" } */
+
+ return result; /* { dg-bogus "leak" } */
+}
new file mode 100644
@@ -0,0 +1,65 @@
+#include <stdlib.h>
+
+extern int foo (int);
+
+static int __attribute__((noinline))
+do_stuff_2 (int *p, int n)
+{
+ return 0;
+}
+
+/* As malloc-vs-local-2.c, but with a memory leak for the "on the heap case"
+ by not attempting to free at the end. */
+
+int test_1 (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ {
+ int *p = ptr;
+ int sum = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ for (i = 0; i < n; i++)
+ sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+ result = sum;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+ return result; /* { dg-message "leak of 'p'" } */
+ /* FIXME: should this be 'ptr'? */
+}
+
+/* A simpler version of the above. */
+
+int test_2 (int n)
+{
+ int buf[10];
+ int *ptr;
+ int result;
+
+ if (n > 10)
+ ptr = (int *)malloc (sizeof (int) * n);
+ else
+ ptr = buf;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ result = do_stuff_2 (ptr, n);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ return result; /* { dg-message "leak of 'ptr'" } */
+}
new file mode 100644
@@ -0,0 +1,40 @@
+#include <stdlib.h>
+
+void __attribute__((noinline)) callee_1 (int *ptr)
+{
+ *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */
+}
+
+int test_1 (int i, int flag)
+{
+ /* Double diamond CFG; either use &i, or a malloc-ed buffer. */
+ int *ptr = &i;
+ if (flag)
+ ptr = (int *)malloc (sizeof (int));
+ callee_1 (ptr);
+ if (flag)
+ free (ptr);
+ return i;
+}
+
+void __attribute__((noinline)) callee_2 (int *ptr)
+{
+ *ptr = 42;
+}
+
+int test_2 (int flag)
+{
+ int i;
+
+ if (flag)
+ callee_2 (&i);
+
+ callee_2 (&i);
+
+ if (!flag)
+ {
+ void *ptr = malloc (16);
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+ }
+}
new file mode 100644
@@ -0,0 +1,42 @@
+void test (int i, int j)
+{
+ int k, m;
+
+ if (i > 42) {
+ __analyzer_eval (i > 42); /* { dg-warning "TRUE" } */
+
+ i += 3;
+
+ __analyzer_eval (i > 45); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): do we really know this? what about overflow? */
+
+ i -= 1;
+
+ __analyzer_eval (i > 44); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): do we really know this? what about overflow? */
+
+ i = 3 * i;
+
+ __analyzer_eval (i > 132); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): do we really know this? what about overflow? */
+
+ i /= 2;
+
+ __analyzer_eval (i > 66); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): do we really know this? what about overflow? */
+
+ /* We don't know anything about j, so we don't know anything about k: */
+ k = i + j;
+ __analyzer_eval (k == 0); /* { dg-warning "UNKNOWN" } */
+
+ /* However, we should now know that m > 67: */
+ m = i + 1;
+ __analyzer_eval (m > 67); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): do we really know this? what about overflow? */
+ }
+}
new file mode 100644
@@ -0,0 +1,16 @@
+#include <stdlib.h>
+
+static void ensure_equal (int a, int b)
+{
+ if (a != b)
+ abort ();
+}
+
+void test(int i, int j)
+{
+ __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
+
+ ensure_equal (i, j);
+
+ __analyzer_eval (i == j); /* { dg-warning "TRUE" } */
+}
new file mode 100644
@@ -0,0 +1,32 @@
+static int called_function(int j)
+{
+ int k;
+
+ __analyzer_eval (j > 4); /* { dg-warning "TRUE" } */
+
+ k = j - 1;
+
+ __analyzer_eval (k > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): we're not then updating based on the assignment. */
+
+ return k;
+}
+
+void test(int i)
+{
+ __analyzer_eval (i > 4); /* { dg-warning "UNKNOWN" } */
+
+ if (i > 4) {
+
+ __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */
+
+ i = called_function(i);
+
+ __analyzer_eval (i > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail): we're not updating from the returned value. */
+ }
+
+ __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */
+}
new file mode 100644
@@ -0,0 +1,16 @@
+struct foo
+{
+ int m_flag;
+};
+
+extern void bar (int);
+
+void test (struct foo *pf)
+{
+ if (pf->m_flag)
+ bar (0);
+ else
+ bar (1);
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
new file mode 100644
@@ -0,0 +1,16 @@
+union foo
+{
+ int m_flag;
+};
+
+extern void bar (int);
+
+void test (union foo *pf)
+{
+ if (pf->m_flag)
+ bar (0);
+ else
+ bar (1);
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+}
new file mode 100644
@@ -0,0 +1,25 @@
+int test (int a)
+{
+ if (a != 42 && a != 113) {
+ return (-2);
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ return 0;
+}
+
+int test_2 (int a)
+{
+ if (a != 42 && a != 113 && a != 666) {
+ return (-2);
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,48 @@
+/* { dg-additional-options "-fanalyzer-transitivity" } */
+
+#include <stdlib.h>
+
+int test_1 (int a, int b)
+{
+ void *p;
+
+ if (a > 5)
+ if (b)
+ p = malloc (16);
+ else
+ p = malloc (32);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (a > 5)
+ {
+ free (p);
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ }
+
+ return 0; /* { dg-bogus "leak" } */
+}
+
+int test_2 (int a, int b)
+{
+ void *p;
+
+ if (a > 5)
+ if (b)
+ p = malloc (16);
+ else
+ p = malloc (32);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (a > 6) /* different condition */
+ {
+ free (p);
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ }
+
+ return 0; /* { dg-warning "leak of 'p'" } */
+ /* leaks when a == 5. */
+}
new file mode 100644
@@ -0,0 +1,49 @@
+struct state
+{
+ int mode;
+ int data;
+};
+
+extern void do_stuff (struct state *, int);
+
+int test_1 (struct state *s)
+{
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ while (1)
+ {
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ do_stuff (s, s->mode);
+ }
+}
+
+int test_2 (struct state *s)
+{
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ while (1)
+ {
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ switch (s->mode)
+ {
+ case 0:
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ do_stuff (s, 0);
+ break;
+ case 1:
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ do_stuff (s, 17);
+ break;
+ case 2:
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ do_stuff (s, 5);
+ break;
+ case 3:
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ return 42;
+ case 4:
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ return -3;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,10 @@
+void test (int *p, int n)
+{
+ int i;
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ for (i = 0; i < n; i++)
+ {
+ p[i] = i; /* { dg-bogus "uninitialized" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ }
+}
new file mode 100644
@@ -0,0 +1,118 @@
+#include <stdlib.h>
+
+/* Verify that ordering of writes doesn't matter when merging states. */
+
+/* Test with locals. */
+
+void test_1 (int flag)
+{
+ int a, b;
+ if (flag)
+ {
+ a = 3;
+ b = 4;
+ }
+ else
+ {
+ b = 4;
+ a = 3;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ // FIXME: the above can vary between 2 and 3 exploded nodes
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ __analyzer_eval (a == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (b == 4); /* { dg-warning "TRUE" } */
+}
+
+/* Test with globals. */
+
+int f, g, h;
+void test_2 (int flag)
+{
+ if (flag)
+ {
+ f = 3;
+ g = 4;
+ }
+ else
+ {
+ g = 4;
+ f = 3;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ __analyzer_eval (f == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (g == 4); /* { dg-warning "TRUE" } */
+}
+
+/* All 6 orderings of writes to 3 globals. */
+
+void test_3 (int i)
+{
+ switch (i)
+ {
+ default:
+ case 0:
+ f = 3;
+ g = 4;
+ h = 5;
+ break;
+
+ case 1:
+ f = 3;
+ h = 5;
+ g = 4;
+ break;
+
+ case 2:
+ g = 4;
+ f = 3;
+ h = 5;
+ break;
+
+ case 3:
+ g = 4;
+ h = 5;
+ f = 3;
+ break;
+
+ case 4:
+ h = 5;
+ f = 3;
+ g = 4;
+ break;
+
+ case 5:
+ h = 5;
+ g = 4;
+ f = 3;
+ break;
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "6 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded node" } */
+ __analyzer_eval (f == 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (g == 4); /* { dg-warning "TRUE" } */
+ __analyzer_eval (h == 5); /* { dg-warning "TRUE" } */
+}
+
+void test_4 (int flag)
+{
+ void *p, *q;
+ if (flag)
+ {
+ p = malloc (256);
+ q = malloc (256);
+ }
+ else
+ {
+ q = malloc (256);
+ p = malloc (256);
+ }
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+ free (p);
+ free (q);
+}
new file mode 100644
@@ -0,0 +1,58 @@
+#include <stdlib.h>
+
+extern int foo (int);
+
+int test (int flag, void *ptr, int *p, int n)
+{
+ int result;
+ int sum = 0;
+ int i;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (flag)
+ free (ptr);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ for (i = 0; i < n; i++)
+ sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+ result = sum;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ if (flag)
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+ return result;
+}
+
+int test_2 (int flag, int *p, int n)
+{
+ int result;
+ int sum = 0;
+ int i;
+
+ void *ptr = malloc (16);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (flag)
+ free (ptr);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+
+ for (i = 0; i < n; i++)
+ p[i] = i;
+ for (i = 0; i < n; i++)
+ sum += foo (p[i]); /* { dg-bogus "uninitialized" } */
+ result = sum;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */
+ // FIXME: why 5 here?
+
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+ return result;
+}
new file mode 100644
@@ -0,0 +1,28 @@
+/* { dg-additional-options "-fanalyzer-checker=pattern-test" } */
+
+#include <stdlib.h>
+
+extern void foo(void *);
+extern void bar(void *);
+
+void test1(void *ptr)
+{
+ if (ptr) { /* { dg-warning "pattern match on 'ptr != 0'" } */
+ /* { dg-warning "pattern match on 'ptr == 0'" "" { target *-*-* } .-1 } */
+ foo(ptr);
+ } else {
+ bar(ptr);
+ }
+}
+
+void test_2 (void *p, void *q)
+{
+ if (p == NULL || q == NULL) /* { dg-line cond_2 } */
+ return;
+ foo(p);
+
+ /* { dg-warning "pattern match on 'p == 0'" "" { target *-*-* } cond_2 } */
+ /* { dg-warning "pattern match on 'q == 0'" "" { target *-*-* } cond_2 } */
+ /* { dg-warning "pattern match on 'p != 0'" "" { target *-*-* } cond_2 } */
+ /* { dg-warning "pattern match on 'q != 0'" "" { target *-*-* } cond_2 } */
+}
new file mode 100644
@@ -0,0 +1,29 @@
+/* { dg-additional-options "-fanalyzer-checker=pattern-test -O2" } */
+// TODO: run this at every optimization level
+
+#include <stdlib.h>
+
+extern void foo(void *);
+extern void bar(void *);
+
+void test1(void *ptr)
+{
+ if (ptr) { /* { dg-warning "pattern match on 'ptr != 0'" } */
+ /* { dg-warning "pattern match on 'ptr == 0'" "" { target *-*-* } .-1 } */
+ foo(ptr);
+ } else {
+ bar(ptr);
+ }
+}
+
+void test_2 (void *p, void *q)
+{
+ if (p == NULL || q == NULL) /* { dg-line cond_2 } */
+ return;
+ foo(p);
+
+ /* { dg-warning "pattern match on '<unknown> == 0'" "" { target *-*-* } cond_2 } */
+ /* { dg-warning "pattern match on '<unknown> != 0'" "" { target *-*-* } cond_2 } */
+ /* { dg-warning "pattern match on 'p != 0'" "" { target *-*-* } cond_2 } */
+ /* { dg-warning "pattern match on 'q != 0'" "" { target *-*-* } cond_2 } */
+}
new file mode 100644
@@ -0,0 +1,16 @@
+static char * __attribute__((noinline))
+test_1_callee (int flag, char *a, char *b)
+{
+ char *p;
+ if (flag)
+ p = a;
+ else
+ p = b;
+ return p;
+}
+
+char test_1_caller(int flag) {
+ char a = 42;
+ char b = 43;
+ return *test_1_callee(flag, &a, &b);
+}
new file mode 100644
@@ -0,0 +1,2 @@
+/* { dg-additional-options "-Wno-int-conversion" } */
+#include "../pr61861.c"
new file mode 100644
@@ -0,0 +1,26 @@
+/* Verify that we can disable analyzer warnings via pragmas. */
+
+#include <stdlib.h>
+
+void test_1 (void *ptr)
+{
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free'" } */
+}
+
+void test_2 (void *ptr)
+{
+ _Pragma("GCC diagnostic push")
+ _Pragma("GCC diagnostic ignored \"-Wanalyzer-double-free\"")
+
+ free (ptr);
+ free (ptr); /* { dg-bogus "double-'free'" } */
+
+ _Pragma("GCC diagnostic pop")
+}
+
+void test_3 (void *ptr)
+{
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free'" } */
+}
new file mode 100644
@@ -0,0 +1,23 @@
+#include <stdlib.h>
+
+int test_1 (void)
+{
+ {
+ int *q = malloc (1024);
+ }
+
+ return 42; /* { dg-warning "leak of 'q'" } */
+ // FIXME: would be better to report it at the close-of-scope
+}
+
+int test_2 (void)
+{
+ {
+ void *q = malloc (1024);
+ }
+
+ int q = 42;
+
+ return q; /* { dg-warning "leak of 'q'" } */
+ // FIXME: would be better to report it at the close-of-scope
+}
new file mode 100644
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+char test_1 (FILE *logfile)
+{
+ char *password = getpass (">"); /* { dg-message "\\(1\\) sensitive value acquired here" } */
+ fprintf (logfile, "got password %s\n", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+ /* { dg-message "\\(2\\) sensitive value 'password' written to output file; acquired at \\(1\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_2 (FILE *logfile, int i)
+{
+ char *password = getpass (">"); /* { dg-message "\\(1\\) sensitive value acquired here" } */
+ fprintf (logfile, "got password[%i]: %s\n", i, password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+ /* { dg-message "\\(2\\) sensitive value 'password' written to output file; acquired at \\(1\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_3 (FILE *logfile)
+{
+ char *password = getpass (">"); /* { dg-message "\\(1\\) sensitive value acquired here" } */
+ printf ("got password %s\n", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+ /* { dg-message "\\(2\\) sensitive value 'password' written to output file; acquired at \\(1\\)" "" { target *-*-* } .-1 } */
+}
+
+char test_4 (FILE *logfile)
+{
+ char *password = getpass (">"); /* { dg-message "\\(1\\) sensitive value acquired here" } */
+ fwrite (password, strlen (password), 1, logfile); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+ /* { dg-message "\\(2\\) sensitive value 'password' written to output file; acquired at \\(1\\)" "" { target *-*-* } .-1 } */
+}
+
+static void called_by_test_5 (const char *value)
+{
+ printf ("%s", value); /* { dg-warning "sensitive value 'value' written to output file \\\[CWE-532\\\]" } */
+}
+
+char test_5 (FILE *logfile)
+{
+ char *password = getpass (">");
+ called_by_test_5 (password); /* { dg-message "passing sensitive value 'password' in call to 'called_by_test_5' from 'test_5'" } */
+}
+
+static char *called_by_test_6 (void)
+{
+ return getpass (">"); /* { dg-message "sensitive value acquired here" } */
+}
+
+char test_6 (FILE *logfile)
+{
+ char *password = called_by_test_6 (); /* { dg-message "returning sensitive value to 'test_6' from 'called_by_test_6'" } */
+ printf ("%s", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */
+}
+
+/* TODO: strdup etc, strcpy, memcpy, etc. */
new file mode 100644
@@ -0,0 +1 @@
+#include "../pr26983.c"
new file mode 100644
@@ -0,0 +1,97 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+
+extern void foo (int);
+
+void test_1 (void)
+{
+ setjmp (NULL);
+}
+
+void test_2 (void)
+{
+ jmp_buf env;
+ int i;
+
+ foo (0);
+
+ i = setjmp(env);
+
+ foo (1);
+
+ if (i != 0)
+ {
+ foo (2);
+ __analyzer_dump_path (); /* { dg-message "path" } */
+ }
+ else
+ longjmp (env, 1);
+
+ foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | __analyzer_dump_path ();
+ | ^~~~~~~~~~~~~~~~~~~~~~~
+ 'test_2': event 1
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (1) 'setjmp' called here
+ |
+ 'test_2': events 2-4
+ |
+ | NN | if (i != 0)
+ | | ^
+ | | |
+ | | (2) following 'false' branch (when 'i == 0')...
+ |......
+ | NN | longjmp (env, 1);
+ | | ~~~~~~~~~~~~~~~~
+ | | |
+ | | (3) ...to here
+ | | (4) rewinding within 'test_2' from 'longjmp'...
+ |
+ 'test_2': event 5
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (5) ...to 'setjmp' (saved at (1))
+ |
+ 'test_2': events 6-8
+ |
+ | NN | if (i != 0)
+ | | ^
+ | | |
+ | | (6) following 'true' branch (when 'i != 0')...
+ | NN | {
+ | NN | foo (2);
+ | | ~~~~~~~
+ | | |
+ | | (7) ...to here
+ | NN | __analyzer_dump_path ();
+ | | ~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (8) here
+ |
+ { dg-end-multiline-output "" } */
+
+void test_3 (void)
+{
+ longjmp (NULL, 0);
+}
+
+void test_4 (void)
+{
+ longjmp (NULL, 1);
+}
+
+void test_5 (void)
+{
+ jmp_buf env;
+ longjmp (env, 1);
+}
new file mode 100644
@@ -0,0 +1,106 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+ longjmp (env, 1);
+}
+
+void outer (void)
+{
+ int i;
+
+ foo (0);
+
+ i = setjmp(env);
+
+ if (i != 0)
+ {
+ foo (2);
+ __analyzer_dump_path (); /* { dg-message "path" } */
+ }
+ else
+ {
+ foo (1);
+ inner ();
+ }
+ foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | __analyzer_dump_path ();
+ | ^~~~~~~~~~~~~~~~~~~~~~~
+ 'outer': event 1
+ |
+ | NN | void outer (void)
+ | | ^~~~~
+ | | |
+ | | (1) entry to 'outer'
+ |
+ 'outer': event 2
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (2) 'setjmp' called here
+ |
+ 'outer': events 3-5
+ |
+ | NN | if (i != 0)
+ | | ^
+ | | |
+ | | (3) following 'false' branch (when 'i == 0')...
+ |......
+ | NN | foo (1);
+ | | ~~~~~~~
+ | | |
+ | | (4) ...to here
+ | NN | inner ();
+ | | ~~~~~~~~
+ | | |
+ | | (5) calling 'inner' from 'outer'
+ |
+ +--> 'inner': events 6-7
+ |
+ | NN | static void inner (void)
+ | | ^~~~~
+ | | |
+ | | (6) entry to 'inner'
+ | NN | {
+ | NN | longjmp (env, 1);
+ | | ~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) rewinding from 'longjmp' in 'inner'...
+ |
+ <------+
+ |
+ 'outer': event 8
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (8) ...to 'setjmp' in 'outer' (saved at (2))
+ |
+ 'outer': events 9-11
+ |
+ | NN | if (i != 0)
+ | | ^
+ | | |
+ | | (9) following 'true' branch (when 'i != 0')...
+ | NN | {
+ | NN | foo (2);
+ | | ~~~~~~~
+ | | |
+ | | (10) ...to here
+ | NN | __analyzer_dump_path ();
+ | | ~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (11) here
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,107 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+
+extern int foo (int);
+static jmp_buf buf;
+
+void inner (int x)
+{
+ foo (x);
+ longjmp (buf, 1);
+ foo (x);
+}
+
+void outer (int y)
+{
+ foo (y);
+ inner (y);
+ foo (y);
+}
+
+int main (void)
+{
+ if (!setjmp(buf))
+ outer (42);
+ else
+ __analyzer_dump_path (); /* { dg-message "path" } */
+ return 0;
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | __analyzer_dump_path ();
+ | ^~~~~~~~~~~~~~~~~~~~~~~
+ 'main': event 1
+ |
+ | NN | int main (void)
+ | | ^~~~
+ | | |
+ | | (1) entry to 'main'
+ |
+ 'main': event 2
+ |
+ | NN | if (!setjmp(buf))
+ | | ^~~~~~
+ | | |
+ | | (2) 'setjmp' called here
+ |
+ 'main': events 3-5
+ |
+ | NN | if (!setjmp(buf))
+ | | ^
+ | | |
+ | | (3) following 'true' branch...
+ | NN | outer (42);
+ | | ~~~~~~~~~~
+ | | |
+ | | (4) ...to here
+ | | (5) calling 'outer' from 'main'
+ |
+ +--> 'outer': events 6-7
+ |
+ | NN | void outer (int y)
+ | | ^~~~~
+ | | |
+ | | (6) entry to 'outer'
+ |......
+ | NN | inner (y);
+ | | ~~~~~~~~~
+ | | |
+ | | (7) calling 'inner' from 'outer'
+ |
+ +--> 'inner': events 8-9
+ |
+ | NN | void inner (int x)
+ | | ^~~~~
+ | | |
+ | | (8) entry to 'inner'
+ |......
+ | NN | longjmp (buf, 1);
+ | | ~~~~~~~~~~~~~~~~
+ | | |
+ | | (9) rewinding from 'longjmp' in 'inner'...
+ |
+ <-------------+
+ |
+ 'main': event 10
+ |
+ | NN | if (!setjmp(buf))
+ | | ^~~~~~
+ | | |
+ | | (10) ...to 'setjmp' in 'main' (saved at (2))
+ |
+ 'main': events 11-13
+ |
+ | NN | if (!setjmp(buf))
+ | | ^
+ | | |
+ | | (11) following 'false' branch...
+ |......
+ | NN | __analyzer_dump_path ();
+ | | ~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (12) ...to here
+ | | (13) here
+ |
+ { dg-end-multiline-output "" } */
+
new file mode 100644
@@ -0,0 +1,65 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+
+static jmp_buf env;
+
+static void inner (void)
+{
+ setjmp (env);
+}
+
+void outer (void)
+{
+ int i;
+
+ inner ();
+
+ longjmp (env, 42); /* { dg-warning "'longjmp' called after enclosing function of 'setjmp' has returned" } */
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | longjmp (env, 42);
+ | ^~~~~~~~~~~~~~~~~
+ 'outer': events 1-2
+ |
+ | NN | void outer (void)
+ | | ^~~~~
+ | | |
+ | | (1) entry to 'outer'
+ |......
+ | NN | inner ();
+ | | ~~~~~~~~
+ | | |
+ | | (2) calling 'inner' from 'outer'
+ |
+ +--> 'inner': event 3
+ |
+ | NN | static void inner (void)
+ | | ^~~~~
+ | | |
+ | | (3) entry to 'inner'
+ |
+ 'inner': event 4
+ |
+ | NN | setjmp (env);
+ | | ^~~~~~
+ | | |
+ | | (4) 'setjmp' called here
+ |
+ <------+
+ |
+ 'outer': events 5-6
+ |
+ | NN | inner ();
+ | | ^~~~~~~~
+ | | |
+ | | (5) returning to 'outer' from 'inner'
+ | NN |
+ | NN | longjmp (env, 42);
+ | | ~~~~~~~~~~~~~~~~~
+ | | |
+ | | (6) here
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,31 @@
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+ void *ptr = malloc (1024); /* { dg-message "allocated here" } */
+ longjmp (env, 1); /* { dg-warning "leak of 'ptr'" } */
+ free (ptr);
+}
+
+void outer (void)
+{
+ int i;
+
+ foo (0);
+
+ i = setjmp(env);
+
+ if (i == 0)
+ {
+ foo (1);
+ inner ();
+ }
+
+ foo (3);
+}
new file mode 100644
@@ -0,0 +1,36 @@
+#include <setjmp.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+ longjmp (env, 1); /* { dg-warning "leak of 'ptr'" } */
+}
+
+static void middle (void)
+{
+ void *ptr = malloc (1024); /* { dg-message "allocated here" } */
+ inner ();
+ free (ptr);
+}
+
+void outer (void)
+{
+ int i;
+
+ foo (0);
+
+ i = setjmp(env);
+
+ if (i == 0)
+ {
+ foo (1);
+ middle ();
+ }
+
+ foo (3);
+}
new file mode 100644
@@ -0,0 +1,110 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stdlib.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+ longjmp (env, 1); /* { dg-warning "leak of 'ptr'" } */
+}
+
+static void middle (void)
+{
+ void *ptr = malloc (1024);
+ inner ();
+ free (ptr);
+}
+
+void outer (void)
+{
+ int i;
+
+ foo (0);
+
+ i = setjmp(env);
+
+ if (i == 0)
+ {
+ foo (1);
+ middle ();
+ }
+
+ foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | longjmp (env, 1);
+ | ^~~~~~~~~~~~~~~~
+ 'outer': event 1
+ |
+ | NN | void outer (void)
+ | | ^~~~~
+ | | |
+ | | (1) entry to 'outer'
+ |
+ 'outer': event 2
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (2) 'setjmp' called here
+ |
+ 'outer': events 3-5
+ |
+ | NN | if (i == 0)
+ | | ^
+ | | |
+ | | (3) following 'true' branch (when 'i == 0')...
+ | NN | {
+ | NN | foo (1);
+ | | ~~~~~~~
+ | | |
+ | | (4) ...to here
+ | NN | middle ();
+ | | ~~~~~~~~~
+ | | |
+ | | (5) calling 'middle' from 'outer'
+ |
+ +--> 'middle': events 6-8
+ |
+ | NN | static void middle (void)
+ | | ^~~~~~
+ | | |
+ | | (6) entry to 'middle'
+ | NN | {
+ | NN | void *ptr = malloc (1024);
+ | | ~~~~~~~~~~~~~
+ | | |
+ | | (7) allocated here
+ | NN | inner ();
+ | | ~~~~~~~~
+ | | |
+ | | (8) calling 'inner' from 'middle'
+ |
+ +--> 'inner': events 9-11
+ |
+ | NN | static void inner (void)
+ | | ^~~~~
+ | | |
+ | | (9) entry to 'inner'
+ | NN | {
+ | NN | longjmp (env, 1);
+ | | ~~~~~~~~~~~~~~~~
+ | | |
+ | | (10) 'ptr' leaks here; was allocated at (7)
+ | | (11) rewinding from 'longjmp' in 'inner'...
+ |
+ <-------------+
+ |
+ 'outer': event 12
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (12) ...to 'setjmp' in 'outer' (saved at (2))
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,107 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+static void inner (void)
+{
+ /* Pass known 0 to longjmp. */
+ longjmp (env, 0);
+}
+
+void outer (void)
+{
+ int i;
+
+ foo (0);
+
+ i = setjmp(env);
+
+ if (i != 0)
+ {
+ foo (2);
+ __analyzer_dump_path (); /* { dg-message "path" } */
+ }
+ else
+ {
+ foo (1);
+ inner ();
+ }
+ foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | __analyzer_dump_path ();
+ | ^~~~~~~~~~~~~~~~~~~~~~~
+ 'outer': event 1
+ |
+ | NN | void outer (void)
+ | | ^~~~~
+ | | |
+ | | (1) entry to 'outer'
+ |
+ 'outer': event 2
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (2) 'setjmp' called here
+ |
+ 'outer': events 3-5
+ |
+ | NN | if (i != 0)
+ | | ^
+ | | |
+ | | (3) following 'false' branch (when 'i == 0')...
+ |......
+ | NN | foo (1);
+ | | ~~~~~~~
+ | | |
+ | | (4) ...to here
+ | NN | inner ();
+ | | ~~~~~~~~
+ | | |
+ | | (5) calling 'inner' from 'outer'
+ |
+ +--> 'inner': events 6-7
+ |
+ | NN | static void inner (void)
+ | | ^~~~~
+ | | |
+ | | (6) entry to 'inner'
+ |......
+ | NN | longjmp (env, 0);
+ | | ~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) rewinding from 'longjmp' in 'inner'...
+ |
+ <------+
+ |
+ 'outer': event 8
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (8) ...to 'setjmp' in 'outer' (saved at (2))
+ |
+ 'outer': events 9-11
+ |
+ | NN | if (i != 0)
+ | | ^
+ | | |
+ | | (9) following 'true' branch (when 'i != 0')...
+ | NN | {
+ | NN | foo (2);
+ | | ~~~~~~~
+ | | |
+ | | (10) ...to here
+ | NN | __analyzer_dump_path ();
+ | | ~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (11) here
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,109 @@
+/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <setjmp.h>
+#include <stddef.h>
+
+extern void foo (int);
+
+static jmp_buf env;
+
+extern int unknown_val;
+
+static void inner (void)
+{
+ /* Pass value that might be 0 to longjmp. */
+ longjmp (env, unknown_val);
+}
+
+void outer (void)
+{
+ int i;
+
+ foo (0);
+
+ i = setjmp(env);
+
+ if (i != 0)
+ {
+ foo (2);
+ __analyzer_dump_path (); /* { dg-message "path" } */
+ }
+ else
+ {
+ foo (1);
+ inner ();
+ }
+ foo (3);
+}
+
+/* { dg-begin-multiline-output "" }
+ NN | __analyzer_dump_path ();
+ | ^~~~~~~~~~~~~~~~~~~~~~~
+ 'outer': event 1
+ |
+ | NN | void outer (void)
+ | | ^~~~~
+ | | |
+ | | (1) entry to 'outer'
+ |
+ 'outer': event 2
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (2) 'setjmp' called here
+ |
+ 'outer': events 3-5
+ |
+ | NN | if (i != 0)
+ | | ^
+ | | |
+ | | (3) following 'false' branch (when 'i == 0')...
+ |......
+ | NN | foo (1);
+ | | ~~~~~~~
+ | | |
+ | | (4) ...to here
+ | NN | inner ();
+ | | ~~~~~~~~
+ | | |
+ | | (5) calling 'inner' from 'outer'
+ |
+ +--> 'inner': events 6-7
+ |
+ | NN | static void inner (void)
+ | | ^~~~~
+ | | |
+ | | (6) entry to 'inner'
+ |......
+ | NN | longjmp (env, unknown_val);
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) rewinding from 'longjmp' in 'inner'...
+ |
+ <------+
+ |
+ 'outer': event 8
+ |
+ | NN | i = setjmp(env);
+ | | ^~~~~~
+ | | |
+ | | (8) ...to 'setjmp' in 'outer' (saved at (2))
+ |
+ 'outer': events 9-11
+ |
+ | NN | if (i != 0)
+ | | ^
+ | | |
+ | | (9) following 'true' branch (when 'i != 0')...
+ | NN | {
+ | NN | foo (2);
+ | | ~~~~~~~
+ | | |
+ | | (10) ...to here
+ | NN | __analyzer_dump_path ();
+ | | ~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (11) here
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,31 @@
+/* Example of a bad call within a signal handler.
+ 'handler' calls 'custom_logger' which calls 'fprintf', and 'fprintf' is
+ not allowed from a signal handler. */
+
+#include <stdio.h>
+#include <signal.h>
+
+extern void body_of_program(void);
+
+void custom_logger(const char *msg)
+{
+ fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void handler(int signum)
+{
+ custom_logger("got signal");
+}
+
+int main(int argc, const char *argv)
+{
+ custom_logger("started");
+
+ signal(SIGINT, handler); /* { dg-message "registering 'handler' as signal handler" } */
+
+ body_of_program();
+
+ custom_logger("stopped");
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,34 @@
+/* Example of a bad call within a signal handler.
+ 'handler' calls 'custom_logger' which calls 'fprintf', and 'fprintf' is
+ not allowed from a signal handler. */
+
+#include <stdio.h>
+#include <signal.h>
+
+extern void body_of_program(void);
+
+int logging = 1;
+
+void custom_logger(const char *msg)
+{
+ if (logging)
+ fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void handler(int signum)
+{
+ custom_logger("got signal");
+}
+
+int main(int argc, const char *argv)
+{
+ custom_logger("started");
+
+ signal(SIGINT, handler); /* { dg-message "registering 'handler' as signal handler" } */
+
+ body_of_program();
+
+ custom_logger("stopped");
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+extern void body_of_program(void);
+
+void custom_logger(const char *msg)
+{
+ fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void handler(int signum)
+{
+ custom_logger("got signal");
+}
+
+void test (void)
+{
+ void *ptr = malloc (1024);
+ signal(SIGINT, handler); /* { dg-message "registering 'handler' as signal handler" } */
+ body_of_program();
+ free (ptr);
+}
new file mode 100644
@@ -0,0 +1,74 @@
+/* Verify how paths are printed for signal-handler diagnostics. */
+
+/* { dg-options "-fanalyzer -fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+extern void body_of_program(void);
+
+void custom_logger(const char *msg)
+{
+ fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void int_handler(int signum)
+{
+ custom_logger("got signal");
+}
+
+void test (void)
+{
+ void *ptr = malloc (1024);
+ signal(SIGINT, int_handler);
+ body_of_program();
+ free (ptr);
+}
+
+/* "call to 'fprintf' from within signal handler [CWE-479]". */
+/* { dg-begin-multiline-output "" }
+ NN | fprintf(stderr, "LOG: %s", msg);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'test': events 1-2
+ |
+ | NN | void test (void)
+ | | ^~~~
+ | | |
+ | | (1) entry to 'test'
+ |......
+ | NN | signal(SIGINT, int_handler);
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) registering 'int_handler' as signal handler
+ |
+ event 3
+ |
+ |cc1:
+ | (3): later on, when the signal is delivered to the process
+ |
+ +--> 'int_handler': events 4-5
+ |
+ | NN | static void int_handler(int signum)
+ | | ^~~~~~~~~~~
+ | | |
+ | | (4) entry to 'int_handler'
+ | NN | {
+ | NN | custom_logger("got signal");
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (5) calling 'custom_logger' from 'int_handler'
+ |
+ +--> 'custom_logger': events 6-7
+ |
+ | NN | void custom_logger(const char *msg)
+ | | ^~~~~~~~~~~~~
+ | | |
+ | | (6) entry to 'custom_logger'
+ | NN | {
+ | NN | fprintf(stderr, "LOG: %s", msg);
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) call to 'fprintf' from within signal handler
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,89 @@
+/* Verify how paths are printed for signal-handler diagnostics. */
+
+/* { dg-options "-fanalyzer -fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+
+extern void body_of_program(void);
+
+void custom_logger(const char *msg)
+{
+ fprintf(stderr, "LOG: %s", msg); /* { dg-warning "call to 'fprintf' from within signal handler" } */
+}
+
+static void int_handler(int signum)
+{
+ custom_logger("got signal");
+}
+
+static void register_handler ()
+{
+ signal(SIGINT, int_handler);
+}
+
+void test (void)
+{
+ register_handler ();
+ body_of_program();
+}
+
+/* "call to 'fprintf' from within signal handler [CWE-479]". */
+/* { dg-begin-multiline-output "" }
+ NN | fprintf(stderr, "LOG: %s", msg);
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 'test': events 1-2
+ |
+ | NN | void test (void)
+ | | ^~~~
+ | | |
+ | | (1) entry to 'test'
+ | NN | {
+ | NN | register_handler ();
+ | | ~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (2) calling 'register_handler' from 'test'
+ |
+ +--> 'register_handler': events 3-4
+ |
+ | NN | static void register_handler ()
+ | | ^~~~~~~~~~~~~~~~
+ | | |
+ | | (3) entry to 'register_handler'
+ | NN | {
+ | NN | signal(SIGINT, int_handler);
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (4) registering 'int_handler' as signal handler
+ |
+ event 5
+ |
+ |cc1:
+ | (5): later on, when the signal is delivered to the process
+ |
+ +--> 'int_handler': events 6-7
+ |
+ | NN | static void int_handler(int signum)
+ | | ^~~~~~~~~~~
+ | | |
+ | | (6) entry to 'int_handler'
+ | NN | {
+ | NN | custom_logger("got signal");
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (7) calling 'custom_logger' from 'int_handler'
+ |
+ +--> 'custom_logger': events 8-9
+ |
+ | NN | void custom_logger(const char *msg)
+ | | ^~~~~~~~~~~~~
+ | | |
+ | | (8) entry to 'custom_logger'
+ | NN | {
+ | NN | fprintf(stderr, "LOG: %s", msg);
+ | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | | |
+ | | (9) call to 'fprintf' from within signal handler
+ |
+ { dg-end-multiline-output "" } */
new file mode 100644
@@ -0,0 +1,35 @@
+/* Verify that we clarify the sense of paths involving strcmp. */
+
+#include <string.h>
+#include <stdlib.h>
+
+int test_1 (const char *str, char *ptr)
+{
+ if (strcmp (str, "VALUE")) /* { dg-message "following 'true' branch \\(when the strings are non-equal\\)\\.\\.\\." } */
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int test_2 (const char *str, char *ptr)
+{
+ if (strcmp (str, "VALUE") == 0) /* { dg-message "following 'true' branch \\(when the strings are equal\\)\\.\\.\\." } */
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int test_3 (const char *str, char *ptr)
+{
+ if (!strcmp (str, "VALUE")) /* { dg-message "following 'true' branch \\(when the strings are equal\\)\\.\\.\\." } */
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
+
+int test_4 (const char *str, char *ptr)
+{
+ if (strcmp (str, "VALUE")) /* { dg-message "following 'false' branch \\(when the strings are equal\\)\\.\\.\\." } */
+ {
+ }
+ else
+ free (ptr);
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
new file mode 100644
@@ -0,0 +1,28 @@
+/* { dg-additional-options "-fanalyzer-transitivity" } */
+
+void test (int i)
+{
+ switch (i)
+ {
+ case 0:
+ __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */
+ break;
+
+ case 3 ... 5:
+ __analyzer_eval (i >= 3); /* { dg-warning "TRUE" } */
+ __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */
+ break;
+
+ default:
+ __analyzer_eval (i == 0); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i == 2); /* { dg-warning "UNKNOWN" } */
+ __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i == 4); /* { dg-warning "FALSE" "" { xfail *-*-* } } */
+ /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */
+ /* TODO(xfail^^^): we're only checking against endpoints of case
+ ranges, not the insides. */
+ __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */
+ __analyzer_eval (i == 6); /* { dg-warning "UNKNOWN" } */
+ break;
+ }
+}
new file mode 100644
@@ -0,0 +1,128 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct foo
+{
+ signed int i;
+ char buf[256];
+};
+
+char test_1(FILE *f)
+{
+ struct foo tmp;
+
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) { /* { dg-message "\\(1\\) 'tmp' gets an unchecked value here" } */
+ /* { dg-message "\\(2\\) following 'true' branch\\.\\.\\." "" { target *-*-* } .-1 } */
+ /* BUG: the following array lookup trusts that the input data's index is
+ in the range 0 <= i < 256; otherwise it's accessing the stack */
+ return tmp.buf[tmp.i]; // { dg-warning "use of tainted value 'tmp.i' in array lookup without bounds checking" } */
+ /* { dg-message "23: \\(3\\) \\.\\.\\.to here" "" { target *-*-* } .-1 } */
+ /* { dg-message "23: \\(4\\) 'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "" { target *-*-* } .-2 } */
+ /* { dg-message "\\(5\\) use of tainted value 'tmp.i' in array lookup without bounds checking" "" { target *-*-* } .-3 } */
+
+ // TOOD: better messages for state changes
+ }
+ return 0;
+}
+
+char test_2(struct foo *f, int i)
+{
+ /* not a bug: the data is not known to be tainted: */
+ return f->buf[f->i];
+}
+
+char test_3(FILE *f)
+{
+ struct foo tmp;
+
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+ if (tmp.i >= 0 && tmp.i < 256) {
+ /* not a bug: the access is guarded by upper and lower bounds: */
+ return tmp.buf[tmp.i];
+ }
+ }
+ return 0;
+}
+
+char test_4(FILE *f)
+{
+ struct foo tmp;
+
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+ if (tmp.i >= 0) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" } */
+ /* { dg-message "'tmp.i' has its lower bound checked here" "" { target *-*-* } .-1 } */
+ return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */
+ }
+ }
+ return 0;
+}
+
+char test_5(FILE *f)
+{
+ struct foo tmp;
+
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+ if (tmp.i < 256) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" } */
+ /* { dg-message "'tmp.i' has its upper bound checked here" "" { target *-*-* } .-1 } */
+ return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without lower-bounds checking" } */
+ }
+ }
+ return 0;
+}
+
+/* unsigned types have a natural lower bound of 0 */
+struct bar
+{
+ unsigned int i;
+ char buf[256];
+};
+
+char test_6(FILE *f)
+{
+ struct bar tmp;
+
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+ return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */
+ }
+ return 0;
+}
+
+char test_7(FILE *f)
+{
+ struct bar tmp;
+
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+ if (tmp.i >= 0) {
+ return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */
+ }
+ }
+ return 0;
+}
+
+char test_8(FILE *f)
+{
+ struct bar tmp;
+
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+ if (tmp.i < 256) {
+ /* not a bug: has an upper bound, and an implicit lower bound: */
+ return tmp.buf[tmp.i];
+ }
+ }
+ return 0;
+}
+
+char test_9(FILE *f)
+{
+ struct foo tmp;
+
+ if (1 == fread(&tmp, sizeof(tmp), 1, f)) {
+ if (tmp.i == 42) {
+ /* not a bug: tmp.i compared against a specific value: */
+ return tmp.buf[tmp.i]; /* { dg-bogus "tainted" "" { xfail *-*-* } } */
+ // TODO: xfail
+ }
+ }
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,67 @@
+typedef void (*free_func)(void *opaque, void *address);
+
+typedef struct z_stream_s {
+ struct internal_state *state;
+ free_func zfree;
+ void *opaque;
+} z_stream;
+
+struct internal_state {
+ z_stream *strm;
+ int status;
+ unsigned char *pending_buf;
+ unsigned char *window;
+ unsigned short *prev;
+ unsigned short *head;
+};
+
+int deflateEnd(z_stream *strm)
+{
+ int status;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (strm == 0 || strm->state == 0)
+ return (-2);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ status = strm->state->status;
+ if (status != 42 && status != 113 && status != 666) {
+ return (-2);
+ }
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (strm->state->pending_buf)
+ (*(strm->zfree))(strm->opaque, (void *)(strm->state->pending_buf));
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (strm->state->head)
+ (*(strm->zfree))(strm->opaque, (void *)(strm->state->head));
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (strm->state->prev)
+ (*(strm->zfree))(strm->opaque, (void *)(strm->state->prev));
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ if (strm->state->window)
+ (*(strm->zfree))(strm->opaque, (void *)(strm->state->window));
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ (*(strm->zfree))(strm->opaque, (void *)(strm->state));
+ strm->state = 0;
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ return status == 113 ? (-3) : 0;
+}
new file mode 100644
@@ -0,0 +1,51 @@
+/* { dg-additional-options "-Wno-analyzer-too-complex" } */
+
+typedef void * (*alloc_func)(void * opaque, unsigned items, unsigned size);
+typedef void (*free_func)(void * opaque, void * address);
+
+typedef struct z_stream_s {
+ char *msg;
+ alloc_func zalloc;
+ free_func zfree;
+ void * opaque;
+} z_stream;
+
+void * zcalloc(void * opaque, unsigned items, unsigned size);
+void zcfree(void * opaque, void * ptr);
+
+int deflateInit2_(z_stream *strm, int level, int method, int windowBits,
+ int memLevel, int strategy, const char *version,
+ int stream_size) {
+ int noheader = 0;
+ static const char *my_version = "1.1.3";
+
+ if (version == 0 || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return (-6);
+ }
+ if (strm == 0)
+ return (-2);
+
+ strm->msg = 0;
+ if (strm->zalloc == 0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (void *)0;
+ }
+ if (strm->zfree == 0)
+ strm->zfree = zcfree;
+
+ if (level == (-1))
+ level = 6;
+
+ if (windowBits < 0) {
+ noheader = 1;
+ windowBits = -windowBits;
+ }
+ if (memLevel < 1 || memLevel > 9 || method != 8 || windowBits < 8 ||
+ windowBits > 15 || level < 0 || level > 9 || strategy < 0 ||
+ strategy > 2) {
+ return (-2);
+ }
+ (*((strm)->zalloc))((strm)->opaque, (1), 112);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,214 @@
+/* { dg-additional-options "-O3 -Wno-analyzer-too-complex" } */
+/* TODO: reduce this; was triggering this assert:
+ gcc_assert (pruned_state != existing_state);
+*/
+
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+
+typedef void *voidp;
+
+typedef voidp (*alloc_func)(voidp opaque, uInt items, uInt size);
+
+typedef struct z_stream_s {
+ alloc_func zalloc;
+ voidp opaque;
+} z_stream;
+
+typedef z_stream *z_streamp;
+
+typedef struct inflate_huft_s inflate_huft;
+
+struct inflate_huft_s {
+ struct {
+ Byte Exop;
+ Byte Bits;
+ } what;
+ uInt base;
+};
+
+static int huft_build(uInt *, uInt, uInt, const uInt *, const uInt *,
+ inflate_huft **, uInt *, inflate_huft *, uInt *, uInt *);
+
+static int huft_build(uInt *b, uInt n, uInt s, const uInt *d, const uInt *e,
+ inflate_huft **t, uInt *m, inflate_huft *hp, uInt *hn,
+ uInt *v) {
+
+ uInt a;
+ uInt c[15 + 1];
+ uInt f;
+ int g;
+ int h;
+ register uInt i;
+ register uInt j;
+ register int k;
+ int l;
+ uInt mask;
+ register uInt *p;
+ inflate_huft *q;
+ struct inflate_huft_s r;
+ inflate_huft *u[15];
+ register int w;
+ uInt x[15 + 1];
+ uInt *xp;
+ int y;
+ uInt z;
+
+ p = c;
+
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ p = b;
+ i = n;
+ do {
+ c[*p++]++;
+ } while (--i);
+ if (c[0] == n) {
+ *t = (inflate_huft *)0;
+ *m = 0;
+ return 0;
+ }
+
+ l = *m;
+ for (j = 1; j <= 15; j++)
+ if (c[j])
+ break;
+ k = j;
+ if ((uInt)l < j)
+ l = j;
+ for (i = 15; i; i--)
+ if (c[i])
+ break;
+ g = i;
+ if ((uInt)l > i)
+ l = i;
+ *m = l;
+
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ if ((y -= c[j]) < 0)
+ return (-3);
+ if ((y -= c[i]) < 0)
+ return (-3);
+ c[i] += y;
+
+ x[1] = j = 0;
+ p = c + 1;
+ xp = x + 2;
+ while (--i) {
+ *xp++ = (j += *p++);
+ }
+
+ p = b;
+ i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+ n = x[g];
+
+ x[0] = i = 0;
+ p = v;
+ h = -1;
+ w = -l;
+ u[0] = (inflate_huft *)0;
+ q = (inflate_huft *)0;
+ z = 0;
+
+ for (; k <= g; k++) {
+ a = c[k];
+ while (a--) {
+
+ while (k > w + l) {
+ h++;
+ w += l;
+
+ z = g - w;
+ z = z > (uInt)l ? l : z;
+ if ((f = 1 << (j = k - w)) > a + 1) {
+ f -= a + 1;
+ xp = c + k;
+ if (j < z)
+ while (++j < z) {
+ if ((f <<= 1) <= *++xp)
+ break;
+ f -= *xp;
+ }
+ }
+ z = 1 << j;
+
+ if (*hn + z > 1440)
+ return (-4);
+ u[h] = q = hp + *hn;
+ *hn += z;
+
+ if (h) {
+ x[h] = i;
+ r.what.Bits = (Byte)l;
+ r.what.Exop = (Byte)j;
+ j = i >> (w - l);
+ r.base = (uInt)(q - u[h - 1] - j);
+ u[h - 1][j] = r;
+ } else
+ *t = q;
+ }
+
+ r.what.Bits = (Byte)(k - w);
+ if (p >= v + n)
+ r.what.Exop = 128 + 64;
+ else if (*p < s) {
+ r.what.Exop = (Byte)(*p < 256 ? 0 : 32 + 64);
+ r.base = *p++;
+ } else {
+ r.what.Exop = (Byte)(e[*p - s] + 16 + 64);
+ r.base = d[*p++ - s];
+ }
+
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ q[j] = r;
+
+ mask = (1 << w) - 1;
+ while ((i & mask) != x[h]) {
+ h--;
+ w -= l;
+ mask = (1 << w) - 1;
+ }
+ }
+ }
+
+ return y != 0 && g != 1 ? (-5) : 0;
+}
+
+extern const uInt cplens[31];
+extern const uInt cplext[31];
+extern const uInt cpdist[30];
+extern const uInt cpdext[30];
+
+int inflate_trees_dynamic(uInt nl, uInt nd, uInt *c, uInt *bl, uInt *bd,
+ inflate_huft **tl, inflate_huft **td,
+ inflate_huft *hp, z_streamp z) {
+ int r;
+ uInt hn = 0;
+ uInt *v;
+
+ if ((v = (uInt *)(*((z)->zalloc))((z)->opaque, (288), (sizeof(uInt)))) == 0)
+ return (-4);
+
+ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
+ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,20 @@
+#include <stdlib.h>
+#include <string.h>
+
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+#define Z_NULL 0
+
+void test ()
+{
+ uLong comprLen = 10000*sizeof(int);
+ uLong uncomprLen = comprLen;
+ Byte *compr = (Byte*)calloc((uInt)comprLen, 1);
+ Byte *uncompr = (Byte*)calloc((uInt)uncomprLen, 1);
+ if (compr == Z_NULL || uncompr == Z_NULL)
+ exit (1);
+ strcpy((char*)uncompr, "garbage");
+ exit (0);
+}
new file mode 100644
@@ -0,0 +1,49 @@
+/* { dg-additional-options "-O3" } */
+
+typedef long unsigned int size_t;
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+extern size_t strlen(const char *__s) __attribute__((__nothrow__, __leaf__))
+ __attribute__((__pure__)) __attribute__((__nonnull__(1)));
+extern void exit(int __status) __attribute__((__nothrow__, __leaf__))
+ __attribute__((__noreturn__));
+extern char *strcpy(char *__restrict __dest, const char *__restrict __src)
+ __attribute__((__nothrow__, __leaf__)) __attribute__((__nonnull__(1, 2)));
+extern void *calloc(size_t __nmemb, size_t __size)
+ __attribute__((__nothrow__, __leaf__)) __attribute__((__malloc__));
+
+extern int compress(Byte *dest, uLong *destLen, const Byte *source,
+ uLong sourceLen);
+
+const char hello[] = "hello, hello!";
+
+void test_compress(Byte *compr, uLong comprLen, Byte *uncompr,
+ uLong uncomprLen) {
+ int err;
+ uLong len = strlen(hello) + 1;
+
+ err = compress(compr, &comprLen, (const Byte *)hello, len);
+ if (err != 0)
+ exit(1);
+ strcpy((char *)uncompr, "garbage"); /* { dg-bogus "NULL" } */
+}
+
+int main(int argc, char *argv[]) {
+ Byte *compr, *uncompr;
+ uLong comprLen = 10000 * sizeof(int);
+ uLong uncomprLen = comprLen;
+
+ compr = (Byte *)calloc((uInt)comprLen, 1);
+ uncompr = (Byte *)calloc((uInt)uncomprLen, 1);
+ if (compr == 0 || uncompr == 0)
+ exit(1);
+
+ __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */
+
+ test_compress(compr, comprLen, uncompr, uncomprLen);
+
+ exit(0);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,47 @@
+typedef unsigned char Byte;
+typedef unsigned int uInt;
+typedef unsigned long uLong;
+
+typedef struct z_stream_s {
+ Byte *next_in;
+ uInt avail_in;
+ uLong total_in;
+} z_stream;
+
+typedef struct inflate_blocks_state {
+ uInt bitk;
+ uLong bitb;
+ Byte *write;
+} inflate_blocks_statef;
+
+extern int inflate_flush(inflate_blocks_statef *, z_stream *, int);
+
+int inflate_blocks(inflate_blocks_statef *s, z_stream *z, int r) {
+ uInt t;
+ uLong b;
+ uInt k;
+ Byte *p;
+ uInt n;
+ Byte *q;
+ uInt m;
+
+ while (k < (3)) {
+ {
+ if (n)
+ r = 0;
+ else {
+ {
+ s->bitb = b;
+ s->bitk = k;
+ z->avail_in = n;
+ z->total_in += p - z->next_in;
+ z->next_in = p;
+ s->write = q;
+ }
+ return inflate_flush(s, z, r);
+ }
+ };
+ b |= ((uLong)(n--, *p++)) << k; /* { dg-warning "use of uninitialized value" } */
+ k += 8;
+ }
+}
@@ -403,3 +403,24 @@ proc handle-dg-regexps { text } {
return $text
}
+
+# Verify that the initial arg is a valid .dot file
+# (by running dot -Tpng on it, and verifying the exit code is 0).
+
+proc dg-check-dot { args } {
+ verbose "dg-check-dot: args: $args" 2
+
+ set testcase [testname-for-summary]
+
+ set dotfile [lindex $args 0]
+ verbose " dotfile: $dotfile" 2
+
+ set status [remote_exec host "dot" "-O -Tpng $dotfile"]
+ verbose " status: $status" 2
+ if { [lindex $status 0] != 0 } {
+ fail "$testcase dg-check-dot $dotfile"
+ return 0
+ }
+
+ pass "$testcase dg-check-dot $dotfile"
+}
@@ -180,6 +180,16 @@ proc dg-require-iconv { args } {
}
}
+# If this host does not have "dot", skip this test.
+
+proc dg-require-dot { args } {
+ verbose "dg-require-dot" 2
+ if { ![ check_dot_available ] } {
+ upvar dg-do-what dg-do-what
+ set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"]
+ }
+}
+
# If this target does not have sufficient stack size, skip this test.
proc dg-require-stack-size { args } {
@@ -514,6 +514,19 @@ proc check_gc_sections_available { } {
}]
}
+# Returns 1 if "dot" is supported on the host.
+
+proc check_dot_available { } {
+ verbose "check_dot_available" 2
+
+ set status [remote_exec host "dot" "-V"]
+ verbose " status: $status" 2
+ if { [lindex $status 0] != 0 } {
+ return 0
+ }
+ return 1
+}
+
# Return 1 if according to target_info struct and explicit target list
# target is supposed to support trampolines.
@@ -8503,6 +8516,14 @@ proc check_effective_target_lto_incremental { } {
} "-flto -r -nostdlib"]
}
+# Return 1 if the compiler has been configured with analyzer support.
+
+proc check_effective_target_analyzer { } {
+ return [check_no_compiler_messages analyzer object {
+ void foo (void) { }
+ } "-fanalyzer"]
+}
+
# Return 1 if -mx32 -maddress-mode=short can compile, 0 otherwise.
proc check_effective_target_maybe_x32 { } {