Cryptomancer
Cryptomancer

Reputation: 31

Why does this allocate on the stack instead of the heap?

Edit: My original question is no longer accurate, so I rewrote it.

So I'm writing a program to demonstrate a heap spray in C (which I know is unusual). I'm allocating a huge buffer (100MB) using malloc, but it allocates into a segment marked mapped rather than [heap] and I can't figure out why.

My code:

int NOPSize = 960;
int ShellcodeSize = 64;
int PayloadSize = NOPSize + ShellcodeSize;
char Payload[PayloadSize];
char* HeapBuf = (char*)malloc(0x6400000);   //104,857,600 (100MB)

for (int x = 0; x < PayloadSize; x++)
    Payload[x] = (x <= NOPSize) ? '\x90' : '\x41';

for (int x = 0; x < 0x6400000; x++)
    HeapBuf[x] = Payload[x % PayloadSize];

printf("%x\n", HeapBuf);
free(HeapBuf);

Output of vmmap using GDB-PEDA taken from a breakpoint set at the call to free :

0x80000000 0x80001000 r-xp  /root/Documents/a.out
0x80001000 0x80002000 r--p  /root/Documents/a.out
0x80002000 0x80003000 rw-p  /root/Documents/a.out
0x80003000 0x80024000 rw-p  [heap]
0xb19fd000 0xb7dfe000 rw-p  mapped
0xb7dfe000 0xb7faf000 r-xp  /lib/i386-linux-gnu/libc-2.24.so
0xb7faf000 0xb7fb1000 r--p  /lib/i386-linux-gnu/libc-2.24.so
0xb7fb1000 0xb7fb2000 rw-p  /lib/i386-linux-gnu/libc-2.24.so
0xb7fb2000 0xb7fb5000 rw-p  mapped
0xb7fd4000 0xb7fd7000 rw-p  mapped
0xb7fd7000 0xb7fd9000 r--p  [vvar]
0xb7fd9000 0xb7fdb000 r-xp  [vdso]
0xb7fdb000 0xb7ffd000 r-xp  /lib/i386-linux-gnu/ld-2.24.so
0xb7ffe000 0xb7fff000 r--p  /lib/i386-linux-gnu/ld-2.24.so
0xb7fff000 0xb8000000 rw-p  /lib/i386-linux-gnu/ld-2.24.so
0xbffdf000 0xc0000000 rw-p  [stack]

printf outputs 0xb19fd008 which is in the top mapped segment.

Interestingly enough, the [heap] segment disappears if I remove the call to printf. So my questions, why is the presence of the [heap] segment dependent on the call to printf and why is the printed address in the mapped segment and not in the [heap] segment? Thanks.

Upvotes: 0

Views: 344

Answers (3)

zwol
zwol

Reputation: 140445

There's two things going on here, and before I get into them, I have to warn you that based on the presence of the string /lib/i386-linux-gnu/libc-2.24.so in your map output, I am deducing that the operating system you're running your program on uses the Linux kernel and the GNU C Library ('glibc'). Some of what I'm about to tell you is very particular to those components.

First: for historical reasons, there are two different ways to allocate memory on operating systems that qualify as "Unix" (to first order: everything anyone still uses, except Windows): the system calls sbrk and mmap. sbrk is much more limited; it can only manage the size of a single region of memory at a predetermined location, whereas mmap (and its counterpart munmap) can allocate and deallocate independent blocks of memory anywhere in the address space. Linux's /proc/PID/maps file uses the [heap] label for the memory region managed by sbrk. Independent blocks of memory allocated with mmap are labeled "mapped".

Second: glibc's implementation of malloc uses sbrk for small allocations and mmap for large allocations (see the discussion of M_MMAP_THRESHOLD in the mallopt manpage). The default threshold between "small" and "large" is 128 kilobytes; you allocated 0x6400000 bytes = 100 megabytes, which is, obviously, much larger. The line of your mappings dump reading

0xb19fd000 0xb7dfe000 rw-p  mapped

is exactly one page larger than what you asked for - the discrepancy is due to malloc taking some extra space for its own bookkeeping information.

The [heap] segment appears when you call printf because - again, this is a quirk of glibc - the very first time you do anything with stdout, some memory gets allocated internally, using malloc. That allocation is smaller than 128kB so it gets put in the sbrk region and there you are.

(Depending on what kind of "heap spray" attack you are trying to simulate, either you don't care that your 100MB wound up in its own mmap allocation because you're just trying to cover enough of the overall address space that a wild pointer has a decent chance of pointing in there, or you need to allocate a large number of small objects, each the same size as some key piece of data in the vulnerable application, so that you have a decent chance of getting your allocations mixed up with the application's.)

Upvotes: 1

Nominal Animal
Nominal Animal

Reputation: 39298

I cannot duplicate OP's results. However, I'm running on an x86-64 kernel and 64-bit binaries, unlike OP.

Here is a small test program, test.c:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

void self_maps(void)
{
    FILE *in;
    int   c;

    in = fopen("/proc/self/maps", "r");
    if (!in)
        return;

    /* Let the C library handle the buffering. */
    while ((c = getc(in)) != EOF)
        putchar(c);

    fclose(in);
}

int main(void)
{
    size_t NOPSize = 960;
    size_t ShellcodeSize = 64;
    size_t PayloadSize = NOPSize + ShellcodeSize;
    size_t BufferSize = 0x6400000;
    size_t i;

    char Payload[PayloadSize];
    char *HeapBuf;

    HeapBuf = malloc(BufferSize);
    if (!HeapBuf) {
        fprintf(stderr, "Out of memory.\n");
        return EXIT_FAILURE;
    }

    printf("Payload = %p\n", (void *)Payload);
    printf("HeapBuf = %p\n", (void *)HeapBuf);

    for (i = 0; i < NOPSize; i++)
        Payload[i] = '\x90';

    for (i = NOPSize; i < PayloadSize; i++)
        Payload[i] = '\x41';

    for (i = 0; i < BufferSize; i++)
        HeapBuf[i] = Payload[i % PayloadSize];

    printf("\n");
    self_maps();

    free(HeapBuf);

    return EXIT_SUCCESS;
}

If I compile and run it using

gcc -Wall -O2 test.c -o test

./test

I get output

Payload = 0x7fff3122b6b0
HeapBuf = 0x7f378806a010

00400000-00401000 r-xp 00000000 08:07 4587700                /.../test
00600000-00601000 r--p 00000000 08:07 4587700                /.../test
00601000-00602000 rw-p 00001000 08:07 4587700                /.../test
022c3000-022e5000 rw-p 00000000 00:00 0                      [heap]
7f378806a000-7f378e46b000 rw-p 00000000 00:00 0 
7f378e46b000-7f378e62a000 r-xp 00000000 08:05 1966262        /lib/x86_64-linux-gnu/libc-2.23.so
7f378e62a000-7f378e82a000 ---p 001bf000 08:05 1966262        /lib/x86_64-linux-gnu/libc-2.23.so
7f378e82a000-7f378e82e000 r--p 001bf000 08:05 1966262        /lib/x86_64-linux-gnu/libc-2.23.so
7f378e82e000-7f378e830000 rw-p 001c3000 08:05 1966262        /lib/x86_64-linux-gnu/libc-2.23.so
7f378e830000-7f378e834000 rw-p 00000000 00:00 0 
7f378e834000-7f378e85a000 r-xp 00000000 08:05 1966245        /lib/x86_64-linux-gnu/ld-2.23.so
7f378ea2c000-7f378ea2f000 rw-p 00000000 00:00 0 
7f378ea57000-7f378ea59000 rw-p 00000000 00:00 0 
7f378ea59000-7f378ea5a000 r--p 00025000 08:05 1966245        /lib/x86_64-linux-gnu/ld-2.23.so
7f378ea5a000-7f378ea5b000 rw-p 00026000 08:05 1966245        /lib/x86_64-linux-gnu/ld-2.23.so
7f378ea5b000-7f378ea5c000 rw-p 00000000 00:00 0 
7fff3120c000-7fff3122d000 rw-p 00000000 00:00 0              [stack]
7fff3126c000-7fff3126e000 r--p 00000000 00:00 0              [vvar]
7fff3126e000-7fff31270000 r-xp 00000000 00:00 0              [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0      [vsyscall]

Because the version of the GNU C library I'm using uses memory mapping for allocations larger than 130 kB or so, HeapBuf is in a mapping of its own:

7f378806a000-7f378e46b000 rw-p 00000000 00:00 0 

Upvotes: 1

ForceBru
ForceBru

Reputation: 44828

HeapBuf resides on stack just like any other ints, longs, chars you have in your code because the pointer itself is merely a number. But the memory it points to is indeed allocated on the heap.

Upvotes: 2

Related Questions