Is there a fixed virtual address that the system promises never to use for anything, so I can use it?

Raymond Chen

A customer was interfacing with a component that required that shared memory blocks have the same virtual address in all processes.¹ The customer picked an address and tried to allocate memory at that address at the very start of the process, but they found that it would fail occasionally because address space layout randomization (ASLR) happened to choose that address for a DLL or heap or stack or something.

The customer wanted to know if there was some reserved region of address space that the operating system promises never to use, so that applications can use it as a hard-coded address.

And since everything comes in waves,² about four weeks later, a different customer asked basically the same question: They have been experiencing an increase in problems caused by the inability to allocate shared memory at the same virtual address in all client processes because they store pointers in the shared memory block. The code is decades old, ported from an earlier mainframe system, and none of the original developers are still around. This customer fully acknowledges that their design is subject to the vagaries of the process address space, and that the proper solution is to store offsets in their shared memory block instead of raw pointers. They’re working on a complete rewrite of this module that will address the problem, but it’s not quite finished yet and are hoping for some ideas that could buy some time and let them limp along with the original architecture until the rewrite is finished.

So how about it. Is there some fixed address we can safely reserve across all processes?

No, there is no such reserved region.

First of all, reserving such a region would impair ASLR, since it reduces the space of addresses that can be used for randomization.

Second, it creates the “What if two programs did this?” problem.

Suppose you had two programs, both of which claim the reserved region for themselves. And then they try to communicate with each other. The first one tries to allocate the matching address in the second process, but finds that it has already been taken. So it already has to deal with the possibility that the address it wants is unavailable in all processes.

The problem could even occur within one process. Your process uses two components, and both of those components try to allocate memory at the “guaranteed available” address. The first one will succeed and the second will fail.

So you still have the problem where any component has to deal with the possibility that the address it wants to use is not available, and develop a fallback. You haven’t gained anything. You may as well just use the fallback all the time.

Bonus craziness: One idea is to create a DLL that consists of a large BSS section, at big as the size of the shared memory they intend to use. And then strip the relocations from it! Since the DLL is non-relocatable, it must load at its preferred base address, or it will never load at all. Have the primary executable contain a load-time link to this DLL, and that may help get the DLL loaded into memory ahead of other things that would normally consume the address space. If you mark the BSS section as shared, then you have a shared memory block at a fixed address. Of course, if that address is not available, then the process will fail to start. It’s “get the address or die.”

Note also that your shared section is now a security vulnerability, so this technique assumes that you can mitigate those security issues, say by running the program only under very controlled system configurations.

¹ My guess is that they store raw pointers directly in the shared memory block, and therefore also have the hidden requirement that all clients be the same bitness.

² Just like how two volcano disaster movies came out in 1997, or two animated insect movies came out in 1998, or two astronomical object threatens to destroy the planet movies came out in 1998.

7 comments

Discussion is closed. Login to edit/delete existing comments.

  • Joshua Hudson 0

    I generated a different idea. Create a kernel mode driver that maps the address out of the kernel mode address range. To avoid difficulties, don’t map the address as visible in kernel mode (requires kpti enabled).

    Note that I don’t recommend this. Use relative pointers please.

  • Ismo Salonen 0

    I once ported similar program ( from sco unix/hpux era). I mapped known file to some address ( some where predetermined) and relocated the pointers there. Then saved the file as as strem to the original file ( the relocation address as stream name, relocation was expensive operation). Loading process mapped one of those streams to correct address and if failed create new stream. Maximum 4 streams was ever generated ( in windows xp era). One could also disable ASLR for relevant processes, that might help.

  • Henke37 2

    For those wondering, you can use based pointers to make the compiler do the ugly math for you.

  • chris ryan 0

    In general applications should not be using platform specific mechanisms. However, you can have a module/component that does, acting as a Hardware Abstraction Layer that implements some platform specific IPC or shared memory mechanism.

    Since @Raymond Chen is referencing .DLLs thus windows, you can also use linker sections that are shared across all instances, using MSVC in a C or C++ sdk program.

    #pragma data_seg (".SharedMemory")
    int shared_count=0;
    char shared_buffer[1024]="";
    #pragma data_seg()
    #pragma comment(linker,"/section:.SharedMemory,rws")

    The variables in the section “.SharedMemory” (shared_count & shared_buffer) are read/write shared (rws) by all instances at load time by the linker/mapper.

    You can name the section & variables whatever you want. It is the “rws” in the linker directive “/section:(.sectionname),rws” that is the ‘magic’.

    learn.microsoft.com/en-us/cpp/build/reference/section-specify-section-attributes

  • Piotr Siódmak 0

    Couldn’t they just disable ASLR for these processes? Isn’t there an app verifier switch to do that?

    • Joshua Hudson 0

      Yes there is, but it’s not 100% effective. ntdll and kernel32.dll must be loaded at the same addresses in all processes (for compatability reasons…)

      If you want to know how the fight goes, look at cygwin’s fork. Cygwin hates aslr.

  • Nick 0

    As an addendum to your second footnote, there’s also two Mars movies that came six months apart in 2000.

    It seems like there was a run of these back-to-back movies for the later part of the 1990s. I’ve wondered if there was _someone_ in Hollywood that would see some new movie script getting shopped around and think “hey that’s a good idea” and get a second script written ASAP to try and compete with it, only to end up with both getting made.

Feedback usabilla icon