Ubuntu 16.04 - implementation of malloc. Where is the pointer to the next fragment?

I am trying to understand how the malloc implementation in glibc works. According to the malloc source code (malloc.c in glibc 2.23), free chunks of memory have the following structure.

chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space (may be 0 bytes long) . . . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

Usually we should also see this structure in the gnu (gdb) debugger. So I wrote the following program. The program allocates 6 memory blocks of 64 bytes in size. Each piece is filled with a memset, so we can easily see the corresponding piece in gdb later. As pieces 1,3 and 6 are freed, they must have the above structure. Since chunks are allocated between them, the freed chunks cannot be consolidated, and as a result, they are organized into a doubled linked list through pointers in each fragment.

 #include <stdio.h> #include <stdlib.h> #include <string.h> void to_jump(); int main(int argc, char **argv){ char *b1, *b2, *b3, *b4, *b5, *b6; //allocate 6 chunks of memory b1 = malloc(64); b2 = malloc(64); b3 = malloc(64); b4 = malloc(64); b5 = malloc(64); b6 = malloc(64); memset(b1, 'B', 64); memset(b2, 'C', 64); memset(b3, 'D', 64); memset(b5, 'E', 64); memset(b6, 'F', 64); //free chunks 1,3 and 6 free(b1); free(b3); free(b6); strcpy(b4, argv[1]); // <-- set breakpoint here //exploit this line free(b4); free(b5); free(b2); } void to_jump(){ printf("Exploited"); } 

When I run the program in gdb and set a breakpoint on the strcpy(b4, argv[1]); line strcpy(b4, argv[1]); we should see that the freed chunks are organized on a double linked list. However, the output of gdb is as follows:

 gdb-peda$ p b1 $11 = 0x602010 "" gdb-peda$ x/62xg 0x602000 0x602000: 0x0000000000000000 0x0000000000000051 0x602010: 0x0000000000000000 0x4242424242424242 | 0x602020: 0x4242424242424242 0x4242424242424242 | b1 (freed) 0x602030: 0x4242424242424242 0x4242424242424242 | 0x602040: 0x4242424242424242 0x4242424242424242 | 0x602050: 0x0000000000000000 0x0000000000000051 0x602060: 0x4343434343434343 0x4343434343434343 | 0x602070: 0x4343434343434343 0x4343434343434343 | b2 (allocated) 0x602080: 0x4343434343434343 0x4343434343434343 | 0x602090: 0x4343434343434343 0x4343434343434343 | 0x6020a0: 0x0000000000000000 0x0000000000000051 0x6020b0: 0x0000000000602000 0x4444444444444444 | 0x602000 is pointing to b1 (previous freed block) 0x6020c0: 0x4444444444444444 0x4444444444444444 | b3 (freed) 0x6020d0: 0x4444444444444444 0x4444444444444444 | 0x6020e0: 0x4444444444444444 0x4444444444444444 | 0x6020f0: 0x0000000000000000 0x0000000000000051 0x602100: 0x0000000000000000 0x0000000000000000 | 0x602110: 0x0000000000000000 0x0000000000000000 | b4 (will be filled trough strcpy(b4, argv[1]); 0x602120: 0x0000000000000000 0x0000000000000000 | 0x602130: 0x0000000000000000 0x0000000000000000 | 0x602140: 0x0000000000000000 0x0000000000000051 0x602150: 0x4545454545454545 0x4545454545454545 | 0x602160: 0x4545454545454545 0x4545454545454545 | b5 (allocated) 0x602170: 0x4545454545454545 0x4545454545454545 | 0x602180: 0x4545454545454545 0x4545454545454545 | 0x602190: 0x0000000000000000 0x0000000000000051 0x6021a0: 0x00000000006020a0 0x4646464646464646 | 0x6020a0 is pointing to b3 (previous freed block) 0x6021b0: 0x4646464646464646 0x4646464646464646 | b6 (freed) 0x6021c0: 0x4646464646464646 0x4646464646464646 | 0x6021d0: 0x4646464646464646 0x4646464646464646 | 0x6021e0: 0x0000000000000000 0x0000000000020e21 

In this output, we can see the freed chunks and a backward pointer to the previous freed fragment (see comments to the right of the output). But where are the pointers and size of the previous fragment?

+5
source share
1 answer

Crossroads with security.stackexchange

Depending on the size of the fragment to be freed, the pieces are stored in different types of cells (linked lists):

  • Unsorted bins
  • Small baskets
  • Big boxes

You are encouraged to look at the source code if you are interested in knowing how these silos are supported. But something common among all these bunkers is that lists have a double link. So you were right in assuming that you should have found both the forward and backward pointers in the freed pieces (and also, possibly, a field of the previous size)

However, there is another type of special bunker known as fastbin. These fastbins store chunks of a very small size (usually from 16 to 80 bytes, but this may vary slightly in different versions). Unlike your regular boxes, they are connected to each other. They are stored in the appropriate fastbin based on their size (each box containing pieces of the same size). Instead of navigating through the list, pieces can be added and removed in LIFO order, speeding up performance. In addition, unlike regular chunks, adjacent fastbin chunks are not consolidated (which, of course, leads to fragmentation, but does it faster). It also means that you do not need the size of the previous fragment.

Chunks in your program are also probably part of one of these fastbins. Therefore, to see what you expect, try allocating and freeing a larger memory.

+3
source

Source: https://habr.com/ru/post/1261457/


All Articles