1  /* Example of a multilevel wrapper around malloc/free, with a double-'free'.  */
       2  
       3  /* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fdiagnostics-show-caret" } */
       4  /* { dg-enable-nn-line-numbers "" } */
       5  
       6  #include <stdlib.h>
       7  
       8  void *wrapped_malloc (size_t size)
       9  {
      10    return malloc (size);
      11  }
      12  
      13  void wrapped_free (void *ptr)
      14  {
      15    free (ptr); /* { dg-warning "double-'free' of 'ptr' \\\[CWE-415\\\]" } */
      16  }
      17  
      18  typedef struct boxed_int
      19  {
      20    int i;
      21  } boxed_int;
      22  
      23  boxed_int *
      24  make_boxed_int (int i)
      25  {
      26    boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
      27    if (!result)
      28      abort ();
      29    result->i = i;
      30    return result;
      31  }
      32  
      33  void
      34  free_boxed_int (boxed_int *bi)
      35  {
      36    wrapped_free (bi);
      37  }
      38  
      39  void test (int i)
      40  {
      41    boxed_int *obj = make_boxed_int (i);
      42  
      43    free_boxed_int (obj);
      44  
      45    free_boxed_int (obj);
      46  }
      47  
      48  /* double-'free'.  */
      49  /* { dg-begin-multiline-output "" }
      50     NN |   free (ptr);
      51        |   ^~~~~~~~~~
      52    'test': events 1-2
      53      |
      54      |   NN | void test (int i)
      55      |      |      ^~~~
      56      |      |      |
      57      |      |      (1) entry to 'test'
      58      |   NN | {
      59      |   NN |   boxed_int *obj = make_boxed_int (i);
      60      |      |                    ~~~~~~~~~~~~~~~~~~
      61      |      |                    |
      62      |      |                    (2) calling 'make_boxed_int' from 'test'
      63      |
      64      +--> 'make_boxed_int': events 3-4
      65             |
      66             |   NN | make_boxed_int (int i)
      67             |      | ^~~~~~~~~~~~~~
      68             |      | |
      69             |      | (3) entry to 'make_boxed_int'
      70             |   NN | {
      71             |   NN |   boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
      72             |      |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      73             |      |                                    |
      74             |      |                                    (4) calling 'wrapped_malloc' from 'make_boxed_int'
      75             |
      76             +--> 'wrapped_malloc': events 5-6
      77                    |
      78                    |   NN | void *wrapped_malloc (size_t size)
      79                    |      |       ^~~~~~~~~~~~~~
      80                    |      |       |
      81                    |      |       (5) entry to 'wrapped_malloc'
      82                    |   NN | {
      83                    |   NN |   return malloc (size);
      84                    |      |          ~~~~~~~~~~~~~
      85                    |      |          |
      86                    |      |          (6) allocated here
      87                    |
      88             <------+
      89             |
      90           'make_boxed_int': events 7-10
      91             |
      92             |   NN |   boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int));
      93             |      |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      94             |      |                                    |
      95             |      |                                    (7) returning to 'make_boxed_int' from 'wrapped_malloc'
      96             |   NN |   if (!result)
      97             |      |      ~                              
      98             |      |      |
      99             |      |      (8) assuming 'result' is non-NULL
     100             |      |      (9) following 'false' branch (when 'result' is non-NULL)...
     101             |   NN |     abort ();
     102             |   NN |   result->i = i;
     103             |      |   ~~~~~~~~~~~~~                     
     104             |      |             |
     105             |      |             (10) ...to here
     106             |
     107      <------+
     108      |
     109    'test': events 11-12
     110      |
     111      |   NN |   boxed_int *obj = make_boxed_int (i);
     112      |      |                    ^~~~~~~~~~~~~~~~~~
     113      |      |                    |
     114      |      |                    (11) returning to 'test' from 'make_boxed_int'
     115      |   NN | 
     116      |   NN |   free_boxed_int (obj);
     117      |      |   ~~~~~~~~~~~~~~~~~~~~
     118      |      |   |
     119      |      |   (12) calling 'free_boxed_int' from 'test'
     120      |
     121      +--> 'free_boxed_int': events 13-14
     122             |
     123             |   NN | free_boxed_int (boxed_int *bi)
     124             |      | ^~~~~~~~~~~~~~
     125             |      | |
     126             |      | (13) entry to 'free_boxed_int'
     127             |   NN | {
     128             |   NN |   wrapped_free (bi);
     129             |      |   ~~~~~~~~~~~~~~~~~
     130             |      |   |
     131             |      |   (14) calling 'wrapped_free' from 'free_boxed_int'
     132             |
     133             +--> 'wrapped_free': events 15-16
     134                    |
     135                    |   NN | void wrapped_free (void *ptr)
     136                    |      |      ^~~~~~~~~~~~
     137                    |      |      |
     138                    |      |      (15) entry to 'wrapped_free'
     139                    |   NN | {
     140                    |   NN |   free (ptr);
     141                    |      |   ~~~~~~~~~~
     142                    |      |   |
     143                    |      |   (16) first 'free' here
     144                    |
     145             <------+
     146             |
     147           'free_boxed_int': event 17
     148             |
     149             |   NN |   wrapped_free (bi);
     150             |      |   ^~~~~~~~~~~~~~~~~
     151             |      |   |
     152             |      |   (17) returning to 'free_boxed_int' from 'wrapped_free'
     153             |
     154      <------+
     155      |
     156    'test': events 18-19
     157      |
     158      |   NN |   free_boxed_int (obj);
     159      |      |   ^~~~~~~~~~~~~~~~~~~~
     160      |      |   |
     161      |      |   (18) returning to 'test' from 'free_boxed_int'
     162      |   NN | 
     163      |   NN |   free_boxed_int (obj);
     164      |      |   ~~~~~~~~~~~~~~~~~~~~
     165      |      |   |
     166      |      |   (19) passing freed pointer 'obj' in call to 'free_boxed_int' from 'test'
     167      |
     168      +--> 'free_boxed_int': events 20-21
     169             |
     170             |   NN | free_boxed_int (boxed_int *bi)
     171             |      | ^~~~~~~~~~~~~~
     172             |      | |
     173             |      | (20) entry to 'free_boxed_int'
     174             |   NN | {
     175             |   NN |   wrapped_free (bi);
     176             |      |   ~~~~~~~~~~~~~~~~~
     177             |      |   |
     178             |      |   (21) passing freed pointer 'bi' in call to 'wrapped_free' from 'free_boxed_int'
     179             |
     180             +--> 'wrapped_free': events 22-23
     181                    |
     182                    |   NN | void wrapped_free (void *ptr)
     183                    |      |      ^~~~~~~~~~~~
     184                    |      |      |
     185                    |      |      (22) entry to 'wrapped_free'
     186                    |   NN | {
     187                    |   NN |   free (ptr);
     188                    |      |   ~~~~~~~~~~
     189                    |      |   |
     190                    |      |   (23) second 'free' here; first 'free' was at (16)
     191                    |
     192     { dg-end-multiline-output "" } */
     193  
     194  /* TODO: the event describing the allocation is uninteresting and probably
     195     should be purged.  */