Next: , Previous: Example 7, Up: Examples


16.8 The alloca() functions

There are two examples of using alloca() and its related functions in tests/pass/test8.c and tests/fail/test16.c. Both rely on mpatrol having full call stack traceback support, although they will work (albeit with slightly different results) on systems that do not.

The first test simply illustrates the use of alloca() and how its memory allocations are freed when they are no longer in use.

      23  /*
      24   * Tests alloca() and related functions via nested function calls.
      25   * The final output should be a horizontal pyramid of plus signs
      26   * followed by a horizontal pyramid of minus signs.
      27   */
     
     
      30  #include "mpatrol.h"
      31  #include <stdio.h>
     
     
      34  char *f1(char *s)
      35  {
      36      char *t;
      37      size_t l;
     
      39      l = strlen(s) + 1;
      40      if ((t = (char *) alloca(l + 1)) == NULL)
      41          return NULL;
      42      memcpy(t, s, l);
      43      t[l - 1] = t[l - 2];
      44      t[l] = '\0';
      45      return strdup(t);
      46  }
     
     
      49  char *f2(char *s)
      50  {
      51      char *t;
      52      size_t l;
     
      54      l = strlen(s) - 1;
      55      if ((t = (char *) alloca(l + 1)) == NULL)
      56          return NULL;
      57      memcpy(t, s, l + 1);
      58      t[l] = '\0';
      59      return strdup(t);
      60  }
     
     
      63  int f(char *s, size_t l)
      64  {
      65      char *t;
      66      size_t i;
     
      68      puts(s);
      69      for (i = 0; i < l; i++)
      70      {
      71          if (((t = f1(s)) == NULL) ||
      72              ((s = (char *) alloca(strlen(t) + 1)) == NULL))
      73              return 0;
      74          strcpy(s, t);
      75          free(t);
      76          puts(s);
      77      }
      78      for (i = 0; i < l; i++)
      79      {
      80          if (((t = f2(s)) == NULL) ||
      81              ((s = (char *) alloca(strlen(t) + 1)) == NULL))
      82              return 0;
      83          strcpy(s, t);
      84          free(t);
      85          puts(s);
      86      }
      87      return 1;
      88  }
     
     
      91  int main(void)
      92  {
      93      char *s;
     
      95      s = strdupa("+");
      96      if (!f(s, 4))
      97          exit(EXIT_FAILURE);
      98      dealloca(s);
      99      s = strdupa("-");
     100      if (!f(s, 4))
     101          exit(EXIT_FAILURE);
     102      dealloca(s);
     103      return EXIT_SUCCESS;
     104  }

When compiled and run, you should get the following output.

     +
     ++
     +++
     ++++
     +++++
     ++++
     +++
     ++
     +
     -
     --
     ---
     ----
     -----
     ----
     ---
     --
     -

If you run it again, this time with the MPATROL_OPTIONS environment variable set to LOGALLOCS and LOGFREES, you should see the following in the newly-generated mpatrol.log file. Note that the `...' marks text that has been removed.

     ALLOC: strdupa (1, 2 bytes, 1 byte) [main|test8.c|95] (char x 2)
             0x000138F0 main+52
             0x00013350 _start+100
     
     returns 0x0008C000
     
     ALLOC: alloca (2, 3 bytes, 8 bytes) [f1|test8.c|40]
             0x000134CC f1+76
             0x000136D8 f+68
             0x00013904 main+72
             0x00013350 _start+100
     
     returns 0x0008C008
     
     ALLOC: strdup (3, 3 bytes, 1 byte) [f1|test8.c|45] (char x 3)
             0x00013584 f1+260
             0x000136D8 f+68
             0x00013904 main+72
             0x00013350 _start+100
     
     returns 0x0008C002
     
     FREE: alloca (0x0008C008) [f|test8.c|72]
             0x00013728 f+148
             0x00013904 main+72
             0x00013350 _start+100
     
         0x0008C008 (3 bytes) {alloca:2:0} [f1|test8.c|40]
             0x000134CC f1+76
             0x000136D8 f+68
             0x00013904 main+72
             0x00013350 _start+100
     
     ALLOC: alloca (4, 3 bytes, 8 bytes) [f|test8.c|72]
             0x00013728 f+148
             0x00013904 main+72
             0x00013350 _start+100
     
     returns 0x0008C008
     
     ...
     
     FREE: alloca (0x0008C040) [main|test8.c|102]
             0x000139C8 main+268
             0x00013350 _start+100
     
         0x0008C040 (2 bytes) {alloca:50:0} [f|test8.c|81]
             0x00013828 f+404
             0x00013988 main+204
             0x00013350 _start+100
     
     FREE: alloca (0x0008C038) [main|test8.c|102]
             0x000139C8 main+268
             0x00013350 _start+100
     
         0x0008C038 (3 bytes) {alloca:47:0} [f|test8.c|81]
             0x00013828 f+404
             0x00013988 main+204
             0x00013350 _start+100
     
     ...
     
     FREE: alloca (0x0008C010) [main|test8.c|102]
             0x000139C8 main+268
             0x00013350 _start+100
     
         0x0008C010 (4 bytes) {alloca:32:0} [f|test8.c|72]
             0x00013728 f+148
             0x00013988 main+204
             0x00013350 _start+100
     
     FREE: alloca (0x0008C008) [main|test8.c|102]
             0x000139C8 main+268
             0x00013350 _start+100
     
         0x0008C008 (3 bytes) {alloca:29:0} [f|test8.c|72]
             0x00013728 f+148
             0x00013988 main+204
             0x00013350 _start+100
     
     FREE: dealloca (0x0008C000) [main|test8.c|102]
             0x000139C8 main+268
             0x00013350 _start+100
     
         0x0008C000 (2 bytes) {strdupa:26:0} [main|test8.c|99] (char x 2)
             0x00013974 main+184
             0x00013350 _start+100

After the first call to strdupa(), there is a call to alloca() followed by a call to strdup(). Because the memory allocation made by strdupa() is at the top level of the program it cannot automatically be freed until main() returns. However, at the next call to alloca() in f(), the mpatrol library notices that the memory allocation that was made by alloca() in f1() can be freed since f1() has returned. The relevant allocation is then freed before making the next memory allocation. You can see how it makes its decision by examining the call stack at the point of deallocation.

However, all of the memory allocations made by alloca() in f() cannot be freed until f() returns. This can be seen in the two sets of eight consecutive deallocations in the log file, each set followed by a call to dealloca(). The dealloca() function explicitly frees a memory allocation that was made by the alloca() family of functions, but these calls are not really necessary as all of these memory allocations would be freed anyway when main() returns. The call to dealloca() is really only necessary to force a deallocation for a specific purpose at a certain point in the program. Note that implicit deallocations are marked as being done by alloca() while explicit deallocations are marked as being done by dealloca().

The second test illustrates how the mpatrol library can help debug alloca()-related problems by treating such memory allocations as normal heap allocations.

     23  /*
     24   * Duplicates a string using alloca() and then returns the address
     25   * of the allocation.  This is illegal since the memory allocated
     26   * by alloca() will be freed when the function returns.  The call
     27   * to memcpy() will then corrupt free memory and the call to free()
     28   * will attempt to free an invalid pointer.
     29   */
     
     
     32  #include "mpatrol.h"
     33  #include <stdio.h>
     
     
     36  char *f(size_t l)
     37  {
     38      return (char *) alloca(l);
     39  }
     
     
     42  char *g(char *s)
     43  {
     44      char *t;
     45      size_t l;
     
     47      l = strlen(s) + 1;
     48      if (t = f(l))
     49          memcpy(t, s, l);
     50      return t;
     51  }
     
     
     54  int main(void)
     55  {
     56      char *s;
     
     58      s = g("test");
     59      free(s);
     60      return EXIT_SUCCESS;
     61  }

If you compile and run this example with the MPATROL_OPTIONS environment variable containing the options LOGALL and NOFREE=1 you should see the following in mpatrol.log.

     ALLOC: alloca (1, 5 bytes, 8 bytes) [f|test16.c|38]
             0x0001346C f+52
             0x000134A8 g+40
             0x00013524 main+20
             0x00013308 _start+100
     
     returns 0x0008C000
     
     FREE: alloca (0x0008C000) [g|test16.c|49]
             0x000134F8 g+120
             0x00013524 main+20
             0x00013308 _start+100
     
         0x0008C000 (5 bytes) {alloca:1:0} [f|test16.c|38]
             0x0001346C f+52
             0x000134A8 g+40
             0x00013524 main+20
             0x00013308 _start+100
     
     MEMCOPY: memcpy (0x0001F760, 0x0008C000, 5 bytes, 0x00) [g|test16.c|49]
             0x000134F8 g+120
             0x00013524 main+20
             0x00013308 _start+100
     
     ERROR: [FRDOPN]: memcpy: attempt to perform operation on freed memory
         0x0008C000 (5 bytes) {alloca:1:0} [g|test16.c|49]
             0x000134F8 g+120
             0x00013524 main+20
             0x00013308 _start+100
     
     returns 0x0008C000
     
     FREE: free (0x0008C000) [main|test16.c|59]
             0x00013550 main+64
             0x00013308 _start+100
     
     ERROR: [PRVFRD]: free: 0x0008C000 was freed with alloca
         0x0008C000 (5 bytes) {alloca:1:0} [g|test16.c|49]
             0x000134F8 g+120
             0x00013524 main+20
             0x00013308 _start+100

As you can see, memory allocations made by alloca() are treated in almost exactly the same way as normal memory allocations, with the result that errors similar to those above can be detected by the mpatrol library. The only real difference between the two types of memory allocations is that allocations made by the alloca() family of functions will never show up in the list of unfreed memory allocations.