/* Example of a multilevel wrapper around malloc/free, with a double-'free'. */ /* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fdiagnostics-show-caret" } */ /* { dg-enable-nn-line-numbers "" } */ #include 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-4 | | NN | make_boxed_int (int i) | | ^~~~~~~~~~~~~~ | | | | | (3) entry to 'make_boxed_int' | NN | { | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int)); | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (4) calling 'wrapped_malloc' from 'make_boxed_int' | +--> 'wrapped_malloc': events 5-6 | | NN | void *wrapped_malloc (size_t size) | | ^~~~~~~~~~~~~~ | | | | | (5) entry to 'wrapped_malloc' | NN | { | NN | return malloc (size); | | ~~~~~~~~~~~~~ | | | | | (6) allocated here | <------+ | 'make_boxed_int': events 7-10 | | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int)); | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | | | | (7) returning to 'make_boxed_int' from 'wrapped_malloc' | NN | if (!result) | | ~ | | | | | (8) assuming 'result' is non-NULL | | (9) following 'false' branch (when 'result' is non-NULL)... | NN | abort (); | NN | result->i = i; | | ~~~~~~~~~~~~~ | | | | | (10) ...to here | <------+ | 'test': events 11-12 | | NN | boxed_int *obj = make_boxed_int (i); | | ^~~~~~~~~~~~~~~~~~ | | | | | (11) returning to 'test' from 'make_boxed_int' | NN | | NN | free_boxed_int (obj); | | ~~~~~~~~~~~~~~~~~~~~ | | | | | (12) calling 'free_boxed_int' from 'test' | +--> 'free_boxed_int': events 13-14 | | NN | free_boxed_int (boxed_int *bi) | | ^~~~~~~~~~~~~~ | | | | | (13) entry to 'free_boxed_int' | NN | { | NN | wrapped_free (bi); | | ~~~~~~~~~~~~~~~~~ | | | | | (14) calling 'wrapped_free' from 'free_boxed_int' | +--> 'wrapped_free': events 15-16 | | NN | void wrapped_free (void *ptr) | | ^~~~~~~~~~~~ | | | | | (15) entry to 'wrapped_free' | NN | { | NN | free (ptr); | | ~~~~~~~~~~ | | | | | (16) first 'free' here | <------+ | 'free_boxed_int': event 17 | | NN | wrapped_free (bi); | | ^~~~~~~~~~~~~~~~~ | | | | | (17) returning to 'free_boxed_int' from 'wrapped_free' | <------+ | 'test': events 18-19 | | NN | free_boxed_int (obj); | | ^~~~~~~~~~~~~~~~~~~~ | | | | | (18) returning to 'test' from 'free_boxed_int' | NN | | NN | free_boxed_int (obj); | | ~~~~~~~~~~~~~~~~~~~~ | | | | | (19) passing freed pointer 'obj' in call to 'free_boxed_int' from 'test' | +--> 'free_boxed_int': events 20-21 | | NN | free_boxed_int (boxed_int *bi) | | ^~~~~~~~~~~~~~ | | | | | (20) entry to 'free_boxed_int' | NN | { | NN | wrapped_free (bi); | | ~~~~~~~~~~~~~~~~~ | | | | | (21) passing freed pointer 'bi' in call to 'wrapped_free' from 'free_boxed_int' | +--> 'wrapped_free': events 22-23 | | NN | void wrapped_free (void *ptr) | | ^~~~~~~~~~~~ | | | | | (22) entry to 'wrapped_free' | NN | { | NN | free (ptr); | | ~~~~~~~~~~ | | | | | (23) second 'free' here; first 'free' was at (16) | { dg-end-multiline-output "" } */ /* TODO: the event describing the allocation is uninteresting and probably should be purged. */