Subject: kern/33777: ftruncate broken on extend on ffs with large page size
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: Simon Burge <simonb@netbsd.org>
List: netbsd-bugs
Date: 06/20/2006 16:30:00
	Note: There was a bad value `non-critical | serious | critical ] (one line)>' for the field `Severity'.
	It was set to the default value of `serious'.

>Number:         33777
>Category:       kern
>Synopsis:       ftruncate broken on extend on ffs with large page size
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Jun 20 16:30:00 +0000 2006
>Originator:     Simon Burge <simonb@netbsd.org>
>Release:        All?  Observerd on alpha with 1.6ZG and walnut with 3.99.21
>Organization:
>Environment:
>Description:
        ftruncate has a problem where it doesn't zero-fill data when a
        file is extended under some circumstances.  It appears to be if
        you extend a file to a page sized boundary on a filesystem where
        the ffs block size is half the page size.  I've observed this
        on a Walnut (16kB page size on ffs with 8kB block size) running
        NetBSD 3.99.21 and an Alpha (8kB page size on an ffs with 4kB
        block size) running NetBSD 1.6ZG.

>How-To-Repeat:

        Run the following program on a system where the pagesize is
        double the size of the ffs block size, and observe output of
        non-zeros.

	#include <err.h>
	#include <fcntl.h>
	#include <string.h>
	#include <unistd.h>

	#define	BUFLEN	0x10

	main(int argc, char **argv)
	{
		int fd, i, pgsize;
		char buf[BUFLEN];

		pgsize = getpagesize();

		if (argc < 2)
			errx(1, "usage");

		unlink(argv[1]);

		fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0644);
		if (fd < 0)
			err(1, "open: %s", argv[1]);

		for (i = 0; i < sizeof(buf); i++)
			buf[i] = i + 'A';

		pwrite(fd, buf, sizeof(buf), pgsize - 0x10);
		ftruncate(fd, pgsize / 2);
		ftruncate(fd, pgsize);
		pread(fd, buf, sizeof(buf), pgsize - 0x10);
		printf("buf =");
		for (i = 0; i < BUFLEN; i++)
			printf(" %02x", buf[i]);
		printf("\n");

		close(fd);
		exit(0);
	}

>Fix:
        None given...

        ffs_truncate() calls ufs_balloc_range() then basically returns.

        The comment above ufs_balloc_range() says

 * after this function returns, any page entirely contained within the range
 * will map to invalid data and thus must be overwritten before it is made
 * accessible to others.

        which seems to indicate we should be zero'ing something
        somewhere.  I've no idea why this problem is showing up only
        with certain specific page size and block size combinations.