Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Nov 2013 13:59:58 +0200
From:      Andriy Gapon <avg@FreeBSD.org>
To:        Steven Hartland <smh@FreeBSD.org>, hartzell@alerce.com, freebsd-stable@FreeBSD.org
Cc:        Richard Todd <rmtodd@servalan.servalan.com>
Subject:   Re: Help with filing a [maybe] ZFS/mmap bug.
Message-ID:  <5284BB3E.7090802@FreeBSD.org>
In-Reply-To: <5284B8A5.8040604@FreeBSD.org>
References:  <20967.760.95825.310085@gargle.gargle.HOWL><51E80B30.1090004@FreeBSD.org><20968.10645.880772.30501@gargle.gargle.HOWL><520202E5.30300@FreeBSD.org><20994.55913.93606.436124@gargle.gargle.HOWL><FEE7BDCF7F494EE1BA0BE9424275AA91@multiplay.co.uk> <21111.12085.958991.356982@gargle.gargle.HOWL> <4EB902F80CE84DD2BF36C85EF4CE8EF8@multiplay.co.uk> <5284B8A5.8040604@FreeBSD.org>

next in thread | previous in thread | raw e-mail | index | archive | help
on 14/11/2013 13:48 Andriy Gapon said the following:
> HOWEVER.  I think that there is a bug that I introduced in r246293.
> Specifically I changed
> 	vm_page_undirty(pp);
> to
> 	pmap_remove_write(pp);
> 	vm_page_clear_dirty(pp, off, nbytes);
> 
> vm_page_undirty() would be a very serious (and probably obvious) bug, if it were
> not a NOP in effect.  The details are explained in the commit message.
> But when I used vm_page_clear_dirty I completely missed the fact that *extends*
> the range to DEV_BSIZE aligned boundaries[*].  So, given the described behavior
> and that pmap_remove_write clears the page modified bit, it is possible that the
> data dirty data in the extended areas will be marked as clean.

I should also add that the above information is consistent with the corruption
that George observed and analyzed (thanks!) -- a few bytes at the end of a page.

In fact, I am able to reproduce the bug with the following program.

#include <sys/param.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

static const off_t len = PAGE_SIZE;
static const off_t len2 = PAGE_SIZE - DEV_BSIZE + 1;

int main (int argc, char *argv[])
{
	char dummy[len2];
        char *p;
        off_t i;
        int fd;

        if (argc < 2) {
                fprintf (stderr, "usage: %s <file>\n", argv[0]);
                return (1);
        }

        fd = open(argv[1], O_CREAT | O_EXCL | O_RDWR, 0660);
        if (fd == -1) {
                perror ("open");
                return (1);
        }

        if (ftruncate(fd, len) == -1) {
                perror ("ftruncate");
                return (1);
        }

        p = mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (p == MAP_FAILED) {
                perror ("mmap");
                return (1);
        }

        for (i = 0; i < len; i++)
                p[i] = '0';

	if (msync(p, len, MS_SYNC) != 0) {
                perror ("msync");
                return (1);
	}

	printf("file filled with 0s and synced\n");

        for (i = 0; i < len; i++)
                p[i] = '1';

	printf("file filled with 1s\n");

        for (i = 0; i < len2; i++)
		dummy[i] = 'x';

	if (write(fd, dummy, len2) != len2) {
		perror ("write");
		return (1);
	}

	printf("first %ju bytes are overwritten with 'x'\n",
	    (uintmax_t)len2);

        if (munmap(p, len) == -1) {
                perror ("munmap");
                return (1);
        }

        if (close(fd) == -1) {
                perror ("close");
                return (1);
        }

	printf("file is unmapped and closed\n");
	printf("please unmount and remount filesystem and check file content\n");

        return (0);
}


-- 
Andriy Gapon



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?5284BB3E.7090802>