From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2209) id 1FD3C3888C4E; Fri, 18 Mar 2022 23:20:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1FD3C3888C4E MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: David Malcolm To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-7717] analyzer: add tests of boxed values [PR104943] X-Act-Checkin: gcc X-Git-Author: David Malcolm X-Git-Refname: refs/heads/master X-Git-Oldrev: 0c016888ffd569c4b70722cf7df2efcc003f397b X-Git-Newrev: 1c1daca1cdf7bc0156d57bb2b9083ee70c66b000 Message-Id: <20220318232043.1FD3C3888C4E@sourceware.org> Date: Fri, 18 Mar 2022 23:20:43 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 18 Mar 2022 23:20:43 -0000 https://gcc.gnu.org/g:1c1daca1cdf7bc0156d57bb2b9083ee70c66b000 commit r12-7717-g1c1daca1cdf7bc0156d57bb2b9083ee70c66b000 Author: David Malcolm Date: Thu Mar 17 18:12:46 2022 -0400 analyzer: add tests of boxed values [PR104943] This patch adds various regression tests as preparatory work for purging irrelevant local decls from state (PR analyzer/104943) gcc/testsuite/ChangeLog: PR analyzer/104943 * gcc.dg/analyzer/boxed-malloc-1-29.c: New test. * gcc.dg/analyzer/boxed-malloc-1.c: New test. * gcc.dg/analyzer/taint-alloc-5.c: New test. * gcc.dg/analyzer/torture/boxed-int-1.c: New test. * gcc.dg/analyzer/torture/boxed-ptr-1.c: New test. Signed-off-by: David Malcolm Diff: --- gcc/testsuite/gcc.dg/analyzer/boxed-malloc-1-29.c | 36 ++ gcc/testsuite/gcc.dg/analyzer/boxed-malloc-1.c | 476 +++++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/taint-alloc-5.c | 21 + .../gcc.dg/analyzer/torture/boxed-int-1.c | 170 ++++++++ .../gcc.dg/analyzer/torture/boxed-ptr-1.c | 82 ++++ 5 files changed, 785 insertions(+) diff --git a/gcc/testsuite/gcc.dg/analyzer/boxed-malloc-1-29.c b/gcc/testsuite/gcc.dg/analyzer/boxed-malloc-1-29.c new file mode 100644 index 00000000000..9e38f97fc8e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/boxed-malloc-1-29.c @@ -0,0 +1,36 @@ +/* Isolating this false positive from boxed-malloc-1.c since it's + reported within boxed_malloc. */ + +#include + +typedef struct boxed_ptr { void *value; } boxed_ptr; + +boxed_ptr +boxed_malloc (size_t sz) +{ + boxed_ptr result; + result.value = malloc (sz); + return result; /* { dg-bogus "leak" "leak false +ve (PR analyzer/104979)" { xfail *-*-* } } */ +} + +boxed_ptr +boxed_free (boxed_ptr ptr) +{ + free (ptr.value); +} + +const boxed_ptr boxed_null = {NULL}; + +struct link +{ + boxed_ptr m_ptr; +}; + +boxed_ptr test_29 (void) +{ + boxed_ptr res = boxed_malloc (sizeof (struct link)); + if (!res.value) + return boxed_null; + ((struct link *)res.value)->m_ptr = boxed_malloc (sizeof (struct link)); + return res; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/boxed-malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/boxed-malloc-1.c new file mode 100644 index 00000000000..5428f2baf49 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/boxed-malloc-1.c @@ -0,0 +1,476 @@ +/* Adapted from malloc-1.c, but wrapping the pointers in a struct. */ + +/* { dg-require-effective-target alloca } */ + +#include + +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 *. */ + +typedef struct boxed_ptr { void *value; } boxed_ptr; + +boxed_ptr +boxed_malloc (size_t sz) +{ + boxed_ptr result; + result.value = malloc (sz); + return result; +} + +boxed_ptr +boxed_free (boxed_ptr ptr) +{ + free (ptr.value); +} + +const boxed_ptr boxed_null = {NULL}; + +void test_1 (void) +{ + boxed_ptr ptr; + ptr.value = malloc (1024); + free (ptr.value); + free (ptr.value); /* { dg-warning "double-'free' of 'ptr.value'" } */ +} + +void test_2 (boxed_ptr ptr) +{ + free (ptr.value); + free (ptr.value); /* { dg-warning "double-'free' of 'ptr.value'" } */ +} + +boxed_ptr +test_3 (void) +{ + boxed_ptr ptr; + ptr.value = malloc (sizeof (int)); + *(int *)ptr.value = 42; /* { dg-warning "dereference of possibly-NULL 'ptr.value' \\\[CWE-690\\\]" } */ + return ptr; +} + +boxed_ptr +test_4 (void) +{ + boxed_ptr ptr; + ptr.value = malloc (sizeof (int)); + int *iptr = (int *)ptr.value; + if (iptr) + *iptr = 42; + else + *iptr = 43; /* { dg-warning "dereference of NULL 'iptr' \\\[CWE-476\\\]" } */ + return ptr; +} + +int test_5 (boxed_ptr ptr) +{ + free (ptr.value); + return *(int *)ptr.value; /* { dg-warning "use after 'free' of 'ptr.value'" } */ +} + +void test_6 (void *ptr) +{ + boxed_ptr q; + q.value = ptr; + free (ptr); + free (q.value); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_6a (boxed_ptr ptr) +{ + boxed_ptr q; + q = ptr; + boxed_free (ptr); + free (q.value); /* { dg-warning "double-'free' of 'ptr.value'" } */ +} + +void test_7 (void) +{ + boxed_ptr ptr = boxed_malloc(4096); + if (!ptr.value) + return; + __builtin_memset(ptr.value, 0, 4096); + boxed_free(ptr); +} + +boxed_ptr test_8 (void) +{ + boxed_ptr ptr = boxed_malloc(4096); + if (!ptr.value) + return boxed_null; + __builtin_memset(ptr.value, 0, 4096); + return ptr; +} + +void test_9 (void) +{ + boxed_ptr ptr = boxed_malloc (1024); + + int i; + for (i = 0; i < 1024; i++) + free (ptr.value); /* { dg-warning "double-'free' of 'ptr.value'" } */ +} + +void test_10 (void) +{ + boxed_ptr ptr = boxed_malloc (1024); + + int i; + for (i = 0; i < 1024; i++) + foo (); + + free (ptr.value); + free (ptr.value); /* { dg-warning "double-'free' of 'ptr.value'" } */ +} + +void test_11 (void) +{ + boxed_ptr ptr = boxed_malloc (1024); + + while (foo ()) + bar (); + + free (ptr.value); + free (ptr.value); /* { dg-warning "double-'free' of 'ptr.value'" } */ +} + +void test_12 (void) +{ + boxed_ptr ptr = boxed_malloc (1024); + + while (1) + { + free (ptr.value); + free (ptr.value); /* { dg-warning "double-'free' of 'ptr.value'" } */ + } +} + +void test_13 (void) +{ + boxed_ptr p = boxed_malloc (1024); + boxed_ptr q = boxed_malloc (1024); + + foo (); + if (!q.value) + { + boxed_free (q); + return; /* { dg-warning "leak of 'p.value'" } */ + } + bar (); + boxed_free (q); + boxed_free (p); +} + +void test_14 (void) +{ + boxed_ptr p, q; + p = boxed_malloc (1024); + if (!p.value) + return; + + q = boxed_malloc (1024); + if (!q.value) + { + boxed_free (p); + boxed_free (q); + /* oops: missing "return". */ + } + bar (); + boxed_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.value); /* { dg-warning "double-'free' of 'p.value'" } */ +} + +void test_15 (void) +{ + boxed_ptr p, q; + p.value = NULL; + q.value = NULL; + + p = boxed_malloc (1024); + if (!p.value) + goto fail; + + foo (); + + q = boxed_malloc (1024); + if (!q.value) + goto fail; + + bar (); + + fail: + boxed_free (q); + boxed_free (p); +} + +void test_16 (void) +{ + boxed_ptr p, q; /* { dg-message "region created on stack here" } */ + + p = boxed_malloc (1024); + if (!p.value) + goto fail; + + foo (); + + q = boxed_malloc (1024); + if (!q.value) + goto fail; + + bar (); + + fail: + boxed_free (q); /* { dg-warning "use of uninitialized value 'q'" } */ + boxed_free (p); +} + +void test_17 (void) +{ + boxed_ptr ptr = boxed_malloc (1024); +} /* { dg-warning "leak of 'ptr.value'" } */ + +void test_18 (void) +{ + boxed_ptr ptr = boxed_malloc (64); + ptr = boxed_null; /* { dg-warning "leak of 'ptr.value'" } */ +} + +void test_18a (void) +{ + boxed_ptr ptr = boxed_malloc (64); + ptr.value = NULL; /* { dg-warning "leak of 'ptr.value'" } */ +} + +void test_19 (void) +{ + boxed_ptr ptr = boxed_malloc (64); + free (ptr.value); + ptr.value = NULL; + free (ptr.value); +} + +boxed_ptr global_ptr_20; + +void test_20 (void) +{ + global_ptr_20 = boxed_malloc (1024); +} + +int *test_21 (int i) +{ + boxed_ptr ptr = boxed_malloc (sizeof (int)); + if (!ptr.value) + abort (); + *(int *)ptr.value = i; + return ptr.value; +} + +boxed_ptr test_21a (int i) +{ + boxed_ptr ptr = boxed_malloc (sizeof (int)); + if (!ptr.value) + abort (); + *(int *)ptr.value = i; + return ptr; +} + +void test_22 (void) +{ + boxed_ptr ptr = boxed_malloc (1024); + + int i; + for (i = 5; i < 10; i++) + foo (); + + free (ptr.value); + free (ptr.value); /* { dg-warning "double-'free' of 'ptr.value'" } */ +} + +int test_24 (void) +{ + boxed_ptr ptr; + ptr.value = __builtin_alloca (sizeof (int)); /* { dg-message "region created on stack here" } */ + free (ptr.value); /* { dg-warning "'free' of 'ptr.value' which points to memory on the stack \\\[CWE-590\\\]" } */ +} + +int test_25 (void) +{ + char tmp[100]; /* { dg-message "region created on stack here" } */ + boxed_ptr p; + p.value = tmp; + free (p.value); /* { dg-warning "'free' of '&tmp' which points to memory on the stack \\\[CWE-590\\\]" } */ +} + +char global_buffer[100]; /* { dg-message "region created here" } */ + +int test_26 (void) +{ + boxed_ptr p; + p.value = global_buffer; + free (p.value); /* { dg-warning "'free' of '&global_buffer' which points to memory not on the heap \\\[CWE-590\\\]" } */ +} + +struct coord { + float x; + float y; +}; + +boxed_ptr test_27 (void) +{ + boxed_ptr p = boxed_malloc (sizeof (struct coord)); + ((struct coord *)p.value)->x = 0.f; /* { dg-warning "dereference of possibly-NULL 'p.value' \\\[CWE-690\\\]" } */ + + /* Only the first such usage should be reported: */ + ((struct coord *)p.value)->y = 0.f; + + return p; +} + +struct link +{ + boxed_ptr m_ptr; +}; + +void test_31 (void) +{ + struct link tmp; + boxed_ptr ptr = boxed_malloc (sizeof (struct link)); + tmp.m_ptr = ptr; +} /* { dg-warning "leak" } */ + +void test_32 (void) +{ + boxed_ptr ptr = boxed_malloc (1024); + could_free (ptr.value); +} /* { dg-bogus "leak" } */ + +void test_33 (void) +{ + boxed_ptr ptr = boxed_malloc (1024); + cant_free (ptr.value); +} /* { dg-warning "leak of 'ptr.value'" } */ + +void test_34 (void) +{ + float *q; + boxed_ptr p = boxed_malloc (sizeof (struct coord)); + if (!p.value) + return; + ((struct coord *)p.value)->x = 0.0f; + q = &((struct coord *)p.value)->x; + boxed_free (p); + *q = 1.0f; /* { dg-warning "use after 'free' of 'q'" } */ +}; + +int test_35 (void) +{ + boxed_ptr ptr = boxed_malloc(4096); + if (!ptr.value) + return -1; + __builtin_memset(ptr.value, 0, 4096); + boxed_free(ptr); + return 0; +} + +void test_36 (void) +{ + boxed_ptr ptr = boxed_malloc(4096); + if (!ptr.value) + return; + __builtin_memset(ptr.value, 0, 4096); + boxed_free(ptr); +} + +boxed_ptr test_37a (void) +{ + boxed_ptr ptr = boxed_malloc(4096); + __builtin_memset(ptr.value, 0, 4096); /* { dg-warning "use of possibly-NULL 'ptr.value' where non-null expected \\\[CWE-690\\\]" } */ + return ptr; +} + +int test_37b (void) +{ + boxed_ptr p = boxed_malloc(4096); + boxed_ptr q = boxed_malloc(4096); + if (p.value) { + __builtin_memset(p.value, 0, 4096); /* Not a bug: checked */ + } else { + __builtin_memset(q.value, 0, 4096); /* { dg-warning "use of possibly-NULL 'q.value' where non-null expected \\\[CWE-690\\\]" } */ + } + boxed_free(p); + boxed_free(q); + return 0; +} + +extern void might_use_ptr (void *ptr); + +void test_38(int i) +{ + boxed_ptr p; + + p = boxed_malloc(1024); + if (p.value) { + boxed_free(p); + might_use_ptr(p.value); /* { dg-warning "use after 'free' of 'p.value'" "" { xfail *-*-* } } */ + // TODO: xfail + } +} + +boxed_ptr +test_39 (int i) +{ + boxed_ptr p = boxed_malloc(sizeof(int*)); + *(int *)p.value = i; /* { dg-warning "dereference of possibly-NULL 'p.value' \\\[CWE-690\\\]" } */ + return p; +} + +boxed_ptr +test_41 (int flag) +{ + boxed_ptr buffer; + + if (flag) { + buffer = boxed_malloc(4096); + } else { + buffer = boxed_null; + } + + ((char *)buffer.value)[0] = 'a'; /* { dg-warning "dereference of possibly-NULL 'buffer.value' \\\[CWE-690\\\]" "possibly-NULL" } */ + /* { dg-warning "dereference of NULL" "NULL" { target *-*-* } .-1 } */ + + return buffer; +} + +extern void might_take_ownership (boxed_ptr ptr); + +void test_45 (void) +{ + boxed_ptr p = boxed_malloc (1024); + might_take_ownership (p); +} + +/* Free of function, and of label within function. */ + +void test_50a (void) +{ +} + +void test_50b (void) +{ + boxed_ptr ptr; + ptr.value = test_50a; + free (ptr.value); /* { dg-warning "'free' of '&test_50a' which points to memory not on the heap \\\[CWE-590\\\]" } */ +} + +void test_50c (void) +{ + my_label: + boxed_ptr ptr; + ptr.value = &&my_label; + free (ptr.value); /* { dg-warning "'free' of '&my_label' which points to memory not on the heap \\\[CWE-590\\\]" } */ +} + +/* { dg-prune-output "\\\[-Wfree-nonheap-object" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-alloc-5.c b/gcc/testsuite/gcc.dg/analyzer/taint-alloc-5.c new file mode 100644 index 00000000000..9a159800c61 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-alloc-5.c @@ -0,0 +1,21 @@ +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +#include "analyzer-decls.h" + +struct foo +{ + int num; +}; + +/* malloc with tainted size from a field. */ + +void * __attribute__ ((tainted_args)) +test_1 (struct foo f) +{ + __analyzer_dump_state ("taint", f.num); /* { dg-warning "state: 'tainted'" } */ + __analyzer_dump_state ("taint", f.num * 16); /* { dg-warning "state: 'tainted'" } */ + + return __builtin_malloc (f.num * 16); /* { dg-warning "use of attacker-controlled value 'f\\.num \\* 16' as allocation size without upper-bounds checking" "warning" } */ + /* { dg-message "\\(\[0-9\]+\\) use of attacker-controlled value 'f\\.num \\* 16' as allocation size without upper-bounds checking" "final event with expr" { target *-*-* } .-1 } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/boxed-int-1.c b/gcc/testsuite/gcc.dg/analyzer/torture/boxed-int-1.c new file mode 100644 index 00000000000..94111e66a8b --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/boxed-int-1.c @@ -0,0 +1,170 @@ +/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */ + +#include "../analyzer-decls.h" + +typedef struct boxed_int { int value; } boxed_int; + +extern boxed_int boxed_int_add (boxed_int a, boxed_int b); +extern boxed_int boxed_int_mul (boxed_int a, boxed_int b); + +boxed_int __attribute__((noinline)) +noinline_boxed_int_add (boxed_int a, boxed_int b) +{ + boxed_int result; + result.value = a.value + b.value; + return result; +} + +static inline boxed_int +inline_boxed_int_add (boxed_int a, boxed_int b) +{ + boxed_int result; + result.value = a.value + b.value; + return result; +} + +boxed_int +test_1 (boxed_int a, boxed_int b) +{ + boxed_int result = boxed_int_add (boxed_int_mul (a, a), + boxed_int_mul (b, b)); + return result; +} + +void +test_2a (void) +{ + boxed_int arr[4]; + arr[0].value = 1; + arr[1].value = 2; + arr[2].value = 3; + arr[3].value = 4; + boxed_int sum; + sum.value = arr[0].value + arr[1].value + arr[2].value + arr[3].value; + __analyzer_eval (sum.value == 10); /* { dg-warning "TRUE" } */ +} + +void +test_2b (void) +{ + boxed_int a, b, c, d; + a.value = 1; + b.value = 2; + c.value = 3; + d.value = 4; + boxed_int sum; + sum.value = a.value + b.value + c.value + d.value; + __analyzer_eval (sum.value == 10); /* { dg-warning "TRUE" } */ +} + +void +test_2c (void) +{ + boxed_int a, b, c, d; + a.value = 1; + b.value = 2; + c.value = 3; + d.value = 4; + boxed_int sum = inline_boxed_int_add (inline_boxed_int_add (a, b), + inline_boxed_int_add (c, d)); + __analyzer_eval (sum.value == 10); /* { dg-warning "TRUE" } */ +} + +void +test_2d (void) +{ + boxed_int a, b, c, d; + a.value = 1; + b.value = 2; + c.value = 3; + d.value = 4; + boxed_int sum = noinline_boxed_int_add (noinline_boxed_int_add (a, b), + noinline_boxed_int_add (c, d)); + __analyzer_eval (sum.value == 10); /* { dg-warning "TRUE" } */ +} + +/* Pointer to a local. */ + +void test_4 (void) +{ + boxed_int i; + int *p = &i.value; + i.value = 1; + *p = 2; + __analyzer_eval (i.value == 2); /* { dg-warning "TRUE" } */ +} + +/* Local array. */ + +void test_5 (void) +{ + boxed_int a[10]; + a[3].value = 5; /* ARRAY_REF. */ + __analyzer_eval (a[3].value == 5); /* { dg-warning "TRUE" } */ +} + +/* Local array, but using an unknown index. */ + +void test_5a (int idx) +{ + boxed_int a[10]; + a[idx].value = 5; /* ARRAY_REF. */ + __analyzer_eval (a[idx].value == 5); /* { dg-warning "TRUE" } */ +} + +/* Array passed in as a param. */ + +void test_6 (boxed_int a[10]) +{ + /* POINTER_PLUS_EXPR then a MEM_REF. */ + __analyzer_eval (a[3].value == 42); /* { dg-warning "UNKNOWN" } */ + a[3].value = 42; + __analyzer_eval (a[3].value == 42); /* { dg-warning "TRUE" } */ +} + +/* Array passed in as a param ptr. */ + +void test_7 (boxed_int *a) +{ + __analyzer_eval (a[3].value == 42); /* { dg-warning "UNKNOWN" } */ + a[3].value = 42; + __analyzer_eval (a[3].value == 42); /* { dg-warning "TRUE" } */ +} + +/* Globals. */ + +boxed_int glob_a; + +void test_10 (void) +{ + __analyzer_eval (glob_a.value == 42); /* { dg-warning "UNKNOWN" } */ + glob_a.value = 42; + __analyzer_eval (glob_a.value == 42); /* { dg-warning "TRUE" } */ +} + +/* Use of uninit value. */ +int test_12a (void) +{ + boxed_int i; /* { dg-message "region created on stack here" } */ + return i.value; /* { dg-warning "use of uninitialized value 'i.value'" } */ +} + +/* Use of uninit value. */ +boxed_int test_12b (void) +{ + boxed_int i; /* { dg-message "region created on stack here" } */ + return i; /* { dg-warning "use of uninitialized value '\[^\n\r\]*'" } */ +} + +void test_loop (void) +{ + boxed_int i; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ + + for (i.value=0; i.value<256; i.value++) { + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */ + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 processed enode" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/torture/boxed-ptr-1.c b/gcc/testsuite/gcc.dg/analyzer/torture/boxed-ptr-1.c new file mode 100644 index 00000000000..8db93f109c7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/torture/boxed-ptr-1.c @@ -0,0 +1,82 @@ +/* { dg-skip-if "" { *-*-* } { "-fno-fat-lto-objects" } { "" } } */ + +#include +#include "../analyzer-decls.h" + +typedef struct boxed_ptr { void *value; } boxed_ptr; + +boxed_ptr __attribute__((noinline)) +boxed_malloc (size_t sz) +{ + boxed_ptr result; + result.value = malloc (sz); + return result; +} + +boxed_ptr __attribute__((noinline)) +boxed_free (boxed_ptr ptr) +{ + free (ptr.value); +} + +const boxed_ptr boxed_null = {NULL}; + +boxed_ptr test_1 (int flag) +{ + boxed_ptr ptr = boxed_malloc (sizeof (int)); + + if (flag) /* { dg-message "following 'false' branch" } */ + if (!ptr.value) + return boxed_null; + + *((int *)ptr.value) = 42; /* { dg-warning "dereference of possibly-NULL '\[^\n\r\]*'" } */ + + return ptr; +} + +void test_2 (int flag) +{ + boxed_ptr ptr; + + if (flag) + ptr = boxed_malloc (4096); + else + ptr = boxed_malloc (1024); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */ + + boxed_free (ptr); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 processed enodes" } */ + /* TODO: ideally we would have purged the state of "ptr", and there would be + just 1 enode here (PR analyzer/104943). */ +} + +void test_3 (int kind) +{ + boxed_ptr ptr; + + switch (kind) + { + default: + ptr = boxed_malloc (4096); + break; + case 0: + ptr = boxed_malloc (128); + break; + case 1: + ptr = boxed_malloc (1024); + break; + case 2: + ptr = boxed_malloc (65536); + break; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 processed enodes" } */ + + boxed_free (ptr); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 processed enodes" } */ + /* TODO: ideally we would have purged the state of "ptr", and there would be + just 1 enode here (PR analyzer/104943). */ +}