Dear developers, I hope it's fine for me to ask you a question, please forgive me if not. I just have a question about the implementation intention of obstack_free (an API in obstack, which is widely used in various gnu libraries, e.g.c Glibc, more details in https://gcc.gnu.org/onlinedocs/libiberty<https://gcc.gnu.org/onlinedocs/libiberty/>), and want to request your suggestions. Top (GNU libiberty)<https://gcc.gnu.org/onlinedocs/libiberty/> Introduction. The libiberty library is a collection of subroutines used by various GNU programs. It is available under the Library General Public License; for more information, see Library Copying. • Using: gcc.gnu.org
The question is about the intention of how does obstack_free free an address at the bottom of a chunk in the obstack. Here is a quick demonstration code: https://godbolt.org/z/arv4ha19b My point here is that the address "string_obstack->chunk" in obstrack_free (line 40) is a valid address from this chunk, and it should be freed normally as other pointers (execute this line will crash). However, it seems the current obstack_free function can not handle it and it will finally get an abort failure. (please refer to the gdb-log.txt in the attachment, as well as the testing code and the compiling script, for more details). I found this "issue" when I tested the library using the symbolic execution technique. Again, I am not sure whether it's an issue or not. If so, the possible fixing is just changing the if condition "__obj > (void *) __o->chunk" to "__obj >= (void *) __o->chunk". Or if not, is it the intention of the obstack implementation to do so? Or in what purpose does obstack not support free from that specific address? Since the obstack is widely used, I guess it's quite important to avoid any potential issues in the implementation code. obstack_free defined in "obstack.h" ``` # define obstack_free(OBSTACK, OBJ) \ __extension__ \ ({ struct obstack *__o = (OBSTACK); \ void *__obj = (OBJ); \ if (__obj > (void *) __o->chunk && __obj < (void *) __o->chunk_limit) \ __o->next_free = __o->object_base = (char *) __obj; \ else (__obstack_free) (__o, __obj); }) ``` Any suggestions or comments are welcome! Thank you very much for your time and waiting for your reply~ Best regards, Haoxin
GNU gdb (GDB) 10.0.50.20200724-git Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from obstack... (gdb) b obstack_free Breakpoint 1 at 0x1060 (gdb) r Starting program: /media/haoxin/SeagateData/haoxin-data/smu-research/exp/datasets/ram-paper/klee-mm-benchmarks/m4/build/src/obstack address of obstack = 0x555555559260, chunk_size = 4064 chunk = 0x5555555592c0, object_base = 0x5555555592d0 next_free = 0x5555555592d0 ======First chunk====== before free: ((void *) lp = 0x55555555a6c0 , (void *) (lp)->limit = 0x55555555b713 before free : prv = (nil) before free : address of s = 0x55555555a6d0 before free : address of ss = 0x55555555b730 ======Second chunk====== before free: ((void *) lp = 0x55555555b720 , (void *) (lp)->limit = 0x55555555c773 before free : prv = 0x55555555a6c0 ======Start to free====== before free : obj (string_obstack->chunk) = 0x55555555b720 before free : obj (t) = 0x55555555b721 Breakpoint 1, obstack_free (h=0x555555559260, obj=0x55555555b720) at obstack.c:346 346 { (gdb) n 350 lp = h->chunk; (gdb) l 345 __obstack_free (struct obstack *h, void *obj) 346 { 347 struct _obstack_chunk *lp; /* below addr of any objects in this chunk */ 348 struct _obstack_chunk *plp; /* point to previous chunk if any */ 349 350 lp = h->chunk; 351 /* We use >= because there cannot be an object at the beginning of a chunk. 352 But there can be an empty object at that address 353 at the end of another chunk. */ 354 while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) (gdb) n 354 while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) (gdb) p lp $1 = (struct _obstack_chunk *) 0x55555555b720 (gdb) p obj $2 = (void *) 0x55555555b720 (gdb) n 357 CALL_FREEFUN (h, lp); (gdb) n 356 plp = lp->prev; (gdb) 357 CALL_FREEFUN (h, lp); (gdb) 361 h->maybe_empty_object = 1; (gdb) 354 while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) (gdb) p lp $3 = (struct _obstack_chunk *) 0x55555555a6c0 (gdb) p obj $4 = (void *) 0x55555555b720 (gdb) n 357 CALL_FREEFUN (h, lp); (gdb) 356 plp = lp->prev; (gdb) 357 CALL_FREEFUN (h, lp); (gdb) 361 h->maybe_empty_object = 1; (gdb) 354 while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj)) (gdb) p lp $5 = (struct _obstack_chunk *) 0x0 (gdb) p obj $6 = (void *) 0x55555555b720 (gdb) n 369 else if (obj != 0) (gdb) n 371 abort (); (gdb) Program received signal SIGABRT, Aborted. __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51 51 } (gdb)
#include <obstack.h> #include <stdio.h> #include <stdlib.h> #define obstack_chunk_alloc malloc #define obstack_chunk_free free //struct obstack *string_obstack; int main(){ struct obstack *string_obstack = (struct obstack *) malloc (sizeof (struct obstack)); obstack_init (string_obstack); printf("address of obstack = %p, chunk_size = %d \n", string_obstack, string_obstack->chunk_size); printf("chunk = %p, object_base = %p next_free = %p \n\n", string_obstack->chunk, string_obstack->object_base, string_obstack->next_free); // Step 1: allocate the first chunk char *s = (char *) obstack_alloc (string_obstack, 4064); printf("======First chunk======\n"); printf("before free: ((void *) lp = %p , (void *) (lp)->limit = %p \n", string_obstack->chunk, (void *) (string_obstack)->chunk->limit); printf("before free : prv = %p\n\n", string_obstack->chunk->prev); // Step 2: allocate the second chunk char *ss = (char *) obstack_alloc (string_obstack, 4064); printf("before free : address of s = %p\n", s); printf("before free : address of ss = %p\n\n", ss); printf("======Second chunk======\n"); printf("before free: ((void *) lp = %p , (void *) (lp)->limit = %p \n", string_obstack->chunk, (void *) (string_obstack)->chunk->limit); printf("before free : prv = %p\n\n", string_obstack->chunk->prev); // Step 3: free the second chunk through obj printf("======Start to free======\n"); char *t = s+4177; printf("before free : obj (string_obstack->chunk) = %p \n", string_obstack->chunk); printf("before free : obj (t) = %p \n\n", t); obstack_free (string_obstack, string_obstack->chunk); //problematic obstack_free (string_obstack, t); printf("======Third chunk======\n"); printf("after free: ((void *) lp = %p , (void *) (lp)->limit = %p \n", (void *) (string_obstack)->chunk, (void *) (string_obstack)->chunk->limit); printf("after free : prv = %p\n", string_obstack->chunk->prev); // Step 4: allocate the third chunk in the location of chunk2 char *sss = (char *) obstack_alloc (string_obstack, 4064); printf("after free : address of s = %p\n", s); printf("after free : address of ss = %p\n", ss); printf("after free : address of sss = %p\n\n", sss); return 0; }
gcc -g obstack-test.c -o obstack -Wl,--rpath=/home/haoxin/haoxin-data/smu-research/onsite-env/klee-nme-aeg/glibc-2.27/glibc-2.27/build/ #-Wl,--dynamic-linker=/home/haoxin/haoxin-data/smu-research/onsite-env/klee-nme-aeg/glibc-2.27/glibc-2.27/build/elf/ld.so