Subject: kern/18855: mmap offsets fail for >= 4G if block allocation needed
To: None <gnats-bugs@gnats.netbsd.org>
From: None <thorpej@shagadelic.org>
List: netbsd-bugs
Date: 10/29/2002 14:38:06
>Number:         18855
>Category:       kern
>Synopsis:       mmap offsets fail for >= 4G if block allocation needed
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Oct 29 14:39:01 PST 2002
>Closed-Date:
>Last-Modified:
>Originator:     Jason R Thorpe
>Release:        NetBSD 1.6I
>Organization:
Wasabi Systems, Inc.
>Environment:
	
	
System: NetBSD e7500.fast-100.shagadelic.org 1.6I NetBSD 1.6I (GENERIC) #1: Tue Oct 22 23:31:32 PDT 2002     thorpej@yeah-baby.shagadelic.org:/u1/hack/nathanw_sa/src/sys/arch/i386/compile/GENERIC i386
Architecture: i386
Machine: i386
>Description:

	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
	   test program.)

	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
	offset 4G.

>How-To-Repeat:
	
	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.

#include <sys/types.h>
#include <sys/mman.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

#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)

int
main(int argc, char *argv[])
{
	char buf[TESTPAT_SIZE];
	char *ptr;
	int fd;

	fd = open(TESTFILE, O_RDWR | O_CREAT, 0666);
	if (fd == -1)
		err(1, "open");

	if (pwrite(fd, CONTROLPAT, CONTROLPAT_SIZE, 0) != CONTROLPAT_SIZE)
		err(1, "pwrite");

	if (pwrite(fd, NULLPAT, TESTPAT_SIZE, 0x100010000ULL) != TESTPAT_SIZE)
		err(1, "pwrite");

	ptr = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE,
	    MAP_FILE|MAP_SHARED, fd, 0x100000000ULL);
	if (ptr == MAP_FAILED)
		err(1, "mmap");

	memcpy(ptr, TESTPAT, TESTPAT_SIZE);

	msync(ptr, getpagesize(), MS_SYNC);

	munmap(ptr, getpagesize());

	if (pread(fd, buf, TESTPAT_SIZE, 0x100000000ULL) != TESTPAT_SIZE)
		err(1, "pread");

	if (memcmp(buf, TESTPAT, TESTPAT_SIZE) != 0)
		printf("failed\n");
	else
		printf("passed\n");

	exit(0);
}

>Fix:
	Not provided.

	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.)
>Release-Note:
>Audit-Trail:
>Unformatted: