Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

afs: Fix StoreData op marshalling

The marshalling of AFS.StoreData, AFS.StoreData64 and YFS.StoreData64 calls
generated by ->setattr() ops for the purpose of expanding a file is
incorrect due to older documentation incorrectly describing the way the RPC
'FileLength' parameter is meant to work.

The older documentation says that this is the length the file is meant to
end up at the end of the operation; however, it was never implemented this
way in any of the servers, but rather the file is truncated down to this
before the write operation is effected, and never expanded to it (and,
indeed, it was renamed to 'TruncPos' in 2014).

Fix this by setting the position parameter to the new file length and doing
a zero-lengh write there.

The bug causes Xwayland to SIGBUS due to unexpected non-expansion of a file
it then mmaps. This can be tested by giving the following test program a
filename in an AFS directory:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
int main(int argc, char *argv[])
{
char *p;
int fd;
if (argc != 2) {
fprintf(stderr,
"Format: test-trunc-mmap <file>\n");
exit(2);
}
fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC);
if (fd < 0) {
perror(argv[1]);
exit(1);
}
if (ftruncate(fd, 0x140008) == -1) {
perror("ftruncate");
exit(1);
}
p = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
perror("mmap");
exit(1);
}
p[0] = 'a';
if (munmap(p, 4096) < 0) {
perror("munmap");
exit(1);
}
if (close(fd) < 0) {
perror("close");
exit(1);
}
exit(0);
}

Fixes: 31143d5d515e ("AFS: implement basic file write support")
Reported-by: Jonathan Billings <jsbillin@umich.edu>
Tested-by: Jonathan Billings <jsbillin@umich.edu>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

David Howells and committed by
Linus Torvalds
8c7ae38d 7d6ab823

+4 -4
+3 -3
fs/afs/fsclient.c
··· 1515 1515 1516 1516 xdr_encode_AFS_StoreStatus(&bp, attr); 1517 1517 1518 - *bp++ = 0; /* position of start of write */ 1519 - *bp++ = 0; 1518 + *bp++ = htonl(attr->ia_size >> 32); /* position of start of write */ 1519 + *bp++ = htonl((u32) attr->ia_size); 1520 1520 *bp++ = 0; /* size of write */ 1521 1521 *bp++ = 0; 1522 1522 *bp++ = htonl(attr->ia_size >> 32); /* new file length */ ··· 1564 1564 1565 1565 xdr_encode_AFS_StoreStatus(&bp, attr); 1566 1566 1567 - *bp++ = 0; /* position of start of write */ 1567 + *bp++ = htonl(attr->ia_size); /* position of start of write */ 1568 1568 *bp++ = 0; /* size of write */ 1569 1569 *bp++ = htonl(attr->ia_size); /* new file length */ 1570 1570
+1 -1
fs/afs/yfsclient.c
··· 1514 1514 bp = xdr_encode_u32(bp, 0); /* RPC flags */ 1515 1515 bp = xdr_encode_YFSFid(bp, &vnode->fid); 1516 1516 bp = xdr_encode_YFS_StoreStatus(bp, attr); 1517 - bp = xdr_encode_u64(bp, 0); /* position of start of write */ 1517 + bp = xdr_encode_u64(bp, attr->ia_size); /* position of start of write */ 1518 1518 bp = xdr_encode_u64(bp, 0); /* size of write */ 1519 1519 bp = xdr_encode_u64(bp, attr->ia_size); /* new file length */ 1520 1520 yfs_check_req(call, bp);