alloca()
functionsThere 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.