Bug 195697

Summary: reading 0 bytes first from /proc files causes corrupt reads later
Product: File System Reporter: Szabolcs Nagy (nsz)
Component: OtherAssignee: fs_other
Status: NEW ---    
Severity: normal CC: nsz
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: 4.10 Tree: Mainline
Regression: Yes

Description Szabolcs Nagy 2017-05-09 11:49:11 UTC
e.g the following test program fails for me on 4.10, but works on 4.9:

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

int main(int argc, char* argv[])
{
	char buf1[512];
	char buf2[512];
	int fd;

	fd = open("/proc/mounts", O_RDONLY);
	if (read(fd, buf1, 0) < 0) return 1;
	if (read(fd, buf1, sizeof buf1) < 0) return 1;
	lseek(fd, 0, SEEK_SET);
	if (read(fd, buf2, sizeof buf2) < 0) return 1;

	return strcmp(buf1, buf2) != 0;
}

strace shows that the read after a 0 read repeats the first line

$ strace -s 222 ./a.out 
execve("./a.out", ["./a.out"], [/* 33 vars */]) = 0
set_tid_address(0x4110a8)               = 5025
openat(AT_FDCWD, "/proc/mounts", O_RDONLY|0x20000) = 3
read(3, "", 0)                          = 0
read(3, "sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\nsysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\nproc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\nudev /dev devtmpfs rw,nosuid,relatime,size=16435904k,nr_inodes=4"..., 512) = 512
lseek(3, 0, SEEK_SET)                   = 0
read(3, "sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\nproc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\nudev /dev devtmpfs rw,nosuid,relatime,size=16435904k,nr_inodes=4108976,mode=755 0 0\ndevpts /dev/pts devpts rw,nosuid,"..., 512) = 512
exit_group(1)                           = ?
+++ exited with 1 +++


i suspect this commit:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e522751d605d99a81508e58390a8f51ee96fb662

but i cannot rebuild the kernel here to verify.
Comment 1 Szabolcs Nagy 2017-05-09 12:01:47 UTC
strcmp in the test should be memcmp(buf1,buf2,300) or similar, my bad.

forgot to mention that this affects musl libc based userspace as it uses readv in its stdio where the first iov buf can have 0 length which exhibit the same broken behaviour.