2

In order to test a software in limit conditions, I'm trying to create a test case where the provided user buffer is allocated at some very low memory address. Something very close to NULL, like for example 0x1000h.

This is proving a tough condition to create. Actually, I'm unable to generate that with malloc() on Linux, BSD, Windows, or OS-X. I'm convinced this situation can happen on other types of devices, but I need a reproducible test case that can be inserted into a CI test suite.

Is there any known method with moderate complexity (and dependencies) to generate such conditions ?

Edit : Selected the solution proposed by Eric Postpischil, using mmap(). Note that, as underlined by R., it's first necessary to lower the lowest address limit, readable at /proc/sys/vm/mmap_min_addr (on Linux).

sudo sysctl -w vm.mmap_min_addr="4096"

Then the example code :

#include <stdio.h>      /* printf */
#include <sys/mman.h>   /* mmap */

int main(int argc, const char** argv)
{
    void* lowBuff = mmap((void*)(0x1000), 64<<10,
                    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
                   -1, 0);
    printf("lowBuff starts at %p \n", lowBuff);
}

And the result :

lowBuff starts at 0x1000
Cyan
  • 13,248
  • 8
  • 43
  • 78
  • That's not how virtual memory works. The physical address and the virtual address in your program have no connections. Heck, the memory might even be paged to disk and not in actual RAM at all. – Some programmer dude Feb 05 '18 at 20:50
  • As for testing in low-memory situations, there are tools around to help you with that (but asking for them is off-topic here on SO). What happens when you run low depends though. Your process could be terminated, some other random process could be terminated, or `malloc` could simply return a null pointer (which you should *always* check for and handle). – Some programmer dude Feb 05 '18 at 20:54
  • why do you need to test about low value memory pointer? if you can store a pointer you can store any value into it. – Jean-François Fabre Feb 05 '18 at 20:55
  • 2
    @Someprogrammerdude: The question asks about low-value addresses, not about low memory. – Eric Postpischil Feb 05 '18 at 20:57
  • @Jean-FrançoisFabre: Your question is answered in the question’s first sentence. The OP wants to test some software. That software might not be written in C, and there may be reason to believe it could be susceptible to bugs that are sensitive to the values of the addresses. – Eric Postpischil Feb 05 '18 at 21:05
  • 3
    @Cyan You might have an [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Your problem is you want to test a program that might have a bug if given a low memory address. Your solution is to try and actually allocate that low memory address. You're asking about your solution. Perhaps you should ask about your problem: ***how do I test this situation?*** Or even better: ***how do I eliminate this class of problems?*** – Schwern Feb 05 '18 at 21:10
  • Have a word with your friendly, neighbourhood linker. – Martin James Feb 05 '18 at 21:19
  • 1
    @Someprogrammerdude: The question does not mention physical or virtual addressing. What makes you think OP wants a low physical address? For testing software for bugs in handling low addresses, the virtual address would be relevant, not the physical address – Eric Postpischil Feb 05 '18 at 21:53
  • Can you allocate arbitrary memory, subtract its base address from all addresses, pass them to the code being tested, let it do calculations (but not access the memory), take its results, add the base address back to the results, and then finish the tests? – Eric Postpischil Feb 05 '18 at 22:23
  • That's a good suggestion @Eric, but unfortunately, it doesn't work for this case. The code to test manipulate pointers directly, and that's this manipulation I want to stress. – Cyan Feb 05 '18 at 22:27

3 Answers3

3

On a POSIX/Unix system, you can use mmap to request memory at specific page-aligned addresses. The lowest you can get will depend on your particular system and circumstances.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 2
    Note: This is *very* dangerous; `mmap` with `MAP_FIXED` doesn't care if the addresses in question were already mapped, it happily discards the existing mappings. This can be useful if you need to reserve a contiguous block of addresses (allocate a huge block with `MAP_NORESERVE` and `PROT_NONE`, but without `MAP_FIXED`), and lazily allocate pieces of it (with normal protections, at specific offsets within the reserved block using `MAP_FIXED`), but when you didn't allocate the block, using `MAP_FIXED` risks throwing away mappings in use by other parts of the program. – ShadowRanger Feb 05 '18 at 21:13
  • @ShadowRanger: Yes. I am on mobile now and cannot write up a detailed answer for some time. If somebody else wants to do that, that could be good. You can request a starting address without `MAP_FIXED`, and `mmap` should start searching for an opening at that address. – Eric Postpischil Feb 05 '18 at 21:52
  • @ShadowRanger's comment is exactly right. The correct way to request an address, rather than "mapping over" something already in a known place, is `mmap` without `MAP_FIXED` but with a requested address instead of 0 as the address argument. Do be aware though that grsec kernels *intentionally break this functionality* with no justification. – R.. GitHub STOP HELPING ICE Feb 05 '18 at 21:58
  • [The POSIX spec isn't quite so specific](https://www.unix.com/man-page/posix/3P/mmap/); it just says "the implementation uses addr in an implementation-defined manner to arrive at" the return value. That said, I suspect you're correct that most systems would try to return a nearby unallocated block if possible, as it's the simplest way to interpret the hint. – ShadowRanger Feb 05 '18 at 21:59
  • 2
    Also note that on Linux, `/proc/sys/vm/mmap_min_addr` controlls the lowest address you can map. The default is usually 64k or so; if you want to map `0x1000` you'll have to lower it. – R.. GitHub STOP HELPING ICE Feb 05 '18 at 22:00
  • Thanks for these elements Eric and R. I'm going to try this suggestion on a Linux VM. I specifically need an address < 64K. The lower, the better, though something like 4K or even 16K would probably be good enough. – Cyan Feb 05 '18 at 22:08
  • @Cyan: I am glad it worked for you, but this answer is little more than a placeholder while folks work out more details. I will be happy to delete it in favor of somebody posting something more thorough. – Eric Postpischil Feb 05 '18 at 22:59
  • Um, folks, you can stop voting this up. I mostly did not want OP to be shut down with “You can’t do that,” so I pointed out `mmap`. I have much more impressive answers elsewhere you can vote up. – Eric Postpischil Feb 05 '18 at 23:33
0

You can only allocate virtual memory in C (malloc is a wrapper around VirtualAlloc on Windows as far as I know), and virtual memory is managed by your operating system, so it is pretty much impossible to predict what address you will get. It will likely never be close to 0x00 though. Unless you're writing a custom a memory allocation system, you shouldn't really have to deal with that and even then it shouldn't care about the provided address.

MattMatt2000
  • 622
  • 6
  • 15
0

On Windows, the lowest part of memory (64KB?) is reserved by the system specifically to catch accesses through invalid pointers. For example, if you try to dereference a null pointer, perhaps with an offset, you'll trigger an access violation. So 0x1000 is off the table because it's within the first 64KB.

Beyond that initial range, you can try to reserve and commit memory with VirtualAlloc, specifying a base address (the parameter is named lpAddress). The base address will be rounded down to the allocation granularity (which I think is also 64KB). If that memory is available, you're all set, but it might not be.

LPVOID desired_address = (LPVOID) 0x00010000;
LPVOID actual_address =
    VirtualAlloc(desired_address, desired_size,
                 MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (actual_address == NULL) {
    // we couldn't get the address we wanted.
} else if (actual_address != desired_address) {
    // something very weird happened
} else {
    // we got the low memory we wanted
}

Historically, typical 32-bit user processes has their executable code loaded starting at 0x00400000 and DLLs generally started higher (like 0x10000000), so you might get lucky shooting for something in that second block of 64KB. But address space layout randomization (ASLR) changes those defaults, and who knows what strange addresses other libraries are going to grab before you get a chance.

So you can try, but you have to be prepared for the possibility that trying to grab low memory with VirtualAlloc fails.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175