[PATCH] nfs: clear_commit_release incorrectly handle truncated page

From: Dmitry Monakhov
Date: Tue Feb 02 2010 - 06:13:14 EST



After page was truncated it lost it's mapping, this result in null
pointer dereference on bdi_stat update. In fact we have to decrement
bdi_stat even for truncated pages, so let's pass correct mapping in
function arguments. Patch against linux-2.6
##TEST_CASE
/*
Tast case for bug in nfs_clear_request_commit()
caused by null pointer dereference in case of truncated page.
It takes less than 10 minutes to reproduce the bug.
### start script
#! /bin/bash -x
# mount my-host:/my-share /mnt
mkdir /mnt/T
cd /mnt/T || exit 1
while true ;
do for ((i=0;i<3;i++)); do
/tmp/mmap file3 200000000 $i & done;
sleep 2 ;
killall -9 mmap ;
done
done
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(int argc, char *argv[])
{
char *addr;
unsigned int size;
off_t off;
int i = 0;
int fd, result;
if (argc != 4) {
perror("Wrong args:\n Usage: %s [file] [size] [offset]\n");
exit(EXIT_FAILURE);
}
size = atoi(argv[2]);
size &= ~(4096-1);
off = atol(argv[3]);
off = ((off_t)size)*off;
fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
if (fd == -1) {
perror("Error opening file for writing");
exit(EXIT_FAILURE);
}
result = lseek(fd, off + (off_t)size, SEEK_SET);
if (result == -1) {
close(fd);
perror("Error calling lseek() to 'stretch' the file");
exit(EXIT_FAILURE);
}
write(fd, "a", 1);
addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, off);
if (addr == MAP_FAILED) {
close(fd);
perror("Error mmapping the file");
exit(EXIT_FAILURE);
}
/* Now write int's to the file as if it were memory (an array of ints).
*/
while (1) {
memset(addr, i++, size);
}
}