bug 14360: dosmem forgot MAP_FIXED

John Reiser jreiser at BitWagon.com
Tue Jul 8 15:24:41 CDT 2008

Alexandre Julliard wrote:
> John Reiser <jreiser at BitWagon.com> writes:
>>Some environments strictly enforce the semantics of mmap(), namely a successful
>>return value need not equal the requested address unless MAP_FIXED,
>>even if the requested address is unmapped and otherwise available.
>>Unless MAP_FIXED, then it is legal to return any page range that is large enough.
>>Callers of the patched code were relying on getting actual fixed addresses
>>in the interval [0, 0x110000).  In particular, this did not work under valgrind.
> You can't use MAP_FIXED on an address that you are not sure is
> available. 

I call mmap(0,,,MAP_FIXED,,) often, and I get what I expect [*],
namely a ["new"] mapping at 0, replacing any previous mapping at 0,
regardless of the state of existing mappings.  See testcase below.

I do this when I want to map a page at address 0.  The typical reason
_I_ want this, is to mmap an entire file into memory, such that the byte offset
in the file equals the process virtual address in memory.  This is exceedingly
useful when examining random files using gdb.

The typical reason that _wine_ might want mmap(0,,,MAP_FIXED,,) is because
some DOS apps "know" about interrupt vector at 0 and BIOS low memory in [0, 0x1000),
or because some Win32 apps assume that there is an identity map from DOS addresses
0:0 through 0xffff:0xffff to process virtual addresses 0 through 0x10ffef.
Both of these are reasonable assumptions in some environments.  Supporting
those assumptions requires MAP_FIXED in the two calls from dosmem.c to mmap().

-----  demo that mmap(,,,MAP_FIXED,,) replaces any existing mapping
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>

	/* accommodate Linux >= 2.6.24 whose default policy prohibits mapping in first 64KB */
        char *const addr = (char *)0x100000;
        errno = 0;
        void *rv1 = mmap(addr, 4096, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
        printf("rv1=%x  errno=%d\n", rv1, errno);
        *addr = 5;
        printf("first byte = %d\n", *addr);
        errno = 0;
        void *rv2 = mmap(addr, 4096, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
        printf("rv2=%x  errno=%d\n", rv2, errno);
	*addr = 7;
        printf("first byte = %d\n", *addr);
[*] Linux >= 2.6.24 has a default *policy* of not allowing mmap in the first 64KB,
as a protection against privilege escalation that is enabled by
a kernel bug involving a NULL pointer.  So Linux >= 2.6.24 gives EINVAL
unless the default policy has been changed (which can be done without reboot.)

> If it wasn't reserved first, the best we can do is request
> the address, and refuse to support DOS apps if we don't get it. If you
> want to make this work in all cases you have to fix valgrind to allow
> the preloader to do its job.

_I_ don't care about those features of DOS and BIOS interface which require
fixed addresses.  What I want is that (* NULL) and (NULL->member) always
give SIGSEGV (at least for structures <= 64KB in size.)  This is a property
that helps to make general apps more portable between Win32 and wine+Linux.

One of the other patches that I submitted introduces a new environment variable
WINE_NO_DOSMEM_64KB which inhibits any attempt by wine itself to map [0, 0x10000),
even though the operating system might allow it.  The purpose of that patch
is to allow a wine that is run on Linux < 2.6.24 (such as Ubuntu 7.10)
to behave the same as it behaves on Linux >=2.6.24, and also to make
(* NULL) always deliver SIGSEGV.

John Reiser, jreiser at BitWagon.com

More information about the wine-devel mailing list