Subject: kern/18855: mmap offsets fail for >= 4G if block allocation needed
To: None <firstname.lastname@example.org>
From: None <email@example.com>
Date: 10/29/2002 14:38:06
>Synopsis: mmap offsets fail for >= 4G if block allocation needed
>Arrival-Date: Tue Oct 29 14:39:01 PST 2002
>Originator: Jason R Thorpe
>Release: NetBSD 1.6I
Wasabi Systems, Inc.
System: NetBSD e7500.fast-100.shagadelic.org 1.6I NetBSD 1.6I (GENERIC) #1: Tue Oct 22 23:31:32 PDT 2002 firstname.lastname@example.org:/u1/hack/nathanw_sa/src/sys/arch/i386/compile/GENERIC i386
mmap of a sparse file with an offset >= 4G does not work
properly if access of the mapped region causes file system
block allocation to be performed.
This bug was noticed when using a large multi-threaded virtual
volume manager. Many thanks to Bill Studenmund for helping
me determine the failure mode and write the testcase.
Below is a test program which does the following:
1. Creates a test file, writing a control pattern to the
first few bytes of the file.
2. Seeks to 4G+64K and writes a test pattern. (64K is chosen
because it is larger than the file system block size, which
is 8K, and likely to be as large or larger than a file
system block size in use by most people who might run the
3. mmaps (MAP_SHARED) one page of the file at offset of 4G. Note
that because of the seek in step in #2, there will be no file
system block allocated at this offset.
4. Copies a test pattern into the mapped region. This should cause
file system block allocation to occur.
5. msyncs and munmaps the region.
6. Reads back the test pattern from offset 4G, and checks to see if
it matches. Reports success or failure.
Subsequent "hexdump -C" of the file will show that the test
pattern was in fact written at offset 0, rather than at
Run the following test program. It will attempt to
create the file "foo" in the current directory and
perform the above operations.
Make sure the "foo" file does not already exist.
Note the test does not fail if you fully populate the
file before running the test.
#define TESTFILE "foo"
#define CONTROLPAT "control-pattern"
#define CONTROLPAT_SIZE (sizeof(CONTROLPAT) - 1)
#define TESTPAT "test-pattern"
#define NULLPAT " "
#define TESTPAT_SIZE (sizeof(TESTPAT) - 1)
main(int argc, char *argv)
fd = open(TESTFILE, O_RDWR | O_CREAT, 0666);
if (fd == -1)
if (pwrite(fd, CONTROLPAT, CONTROLPAT_SIZE, 0) != CONTROLPAT_SIZE)
if (pwrite(fd, NULLPAT, TESTPAT_SIZE, 0x100010000ULL) != TESTPAT_SIZE)
ptr = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE,
MAP_FILE|MAP_SHARED, fd, 0x100000000ULL);
if (ptr == MAP_FAILED)
memcpy(ptr, TESTPAT, TESTPAT_SIZE);
msync(ptr, getpagesize(), MS_SYNC);
if (pread(fd, buf, TESTPAT_SIZE, 0x100000000ULL) != TESTPAT_SIZE)
if (memcmp(buf, TESTPAT, TESTPAT_SIZE) != 0)
The main difference, of course, is that mmap faults don't go
through the UBC fault path. The bug likely exists in uvm_fault()
itself (UBC uses a custom fault handler, and bypasses almost all
of uvm_fault() completely.)