Description of problem: readv()/writev() does not return -1, although the vector count is zero. Moreover, errno is not set appropriately. manual says ----------------------------------- RETURN VALUE On success, the readv() function returns the number of bytes read; the writev() function returns the number of bytes written. On error, -1 is returned, and errno is set appropriately. ERRORS The errors are as given for read(2) and write(2). Additionally the following error is defined. EINVAL The sum of the iov_len values overflows an ssize_t value. Or, the vector count count is zero or greater than MAX_IOVEC. ----------------------------------- If readv()/writev() is bug, we wish you modify it. Version-Release number of selected component (if applicable): How reproducible: always Steps to Reproduce: 1.do program called readv()/writev(). (set vector count is zero) 2. 3. Actual results: return value is zero, errno is zero. Expected results: return value is -1, errno is 22(EINVAL). Additional info: $ cat test_vio_0.c #include <stdio.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> main() { int s; struct iovec w_vector[10]; struct iovec r_vector[10]; char buf[100]; int i; /* now build up a two part writev and write it out */ for( i = 0; i < sizeof( buf ); ++i ) { buf[i] = 'x'; } for( i = 0; i < 10; ++i ) { w_vector[i].iov_base = buf; w_vector[i].iov_len = sizeof(buf); } for( i = 0; i < 10; ++i ) { r_vector[i].iov_base = buf; r_vector[i].iov_len = sizeof(buf); } ----------------------------------- $ gcc test_vio_0.c $ ./a.out i=0, errno=0:Success i=0, errno=0:Success ----------------------------------- $ strace ./a.out > straVec.log 2>&1 $ cat straVec.log execve("./a.out", ["./a.out"], [/* 24 vars */]) = 0 uname({sys="Linux", node="ipf13", ...}) = 0 brk(0) = 0x6000000000004000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=111244, ...}) = 0 mmap(NULL, 111244, PROT_READ, MAP_PRIVATE, 3, 0) = 0x2000000000044000 close(3) = 0 open("/lib/tls/libc.so.6.1", O_RDONLY) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0002\0\1\0\0\0\200\226"..., 640) = 640 fstat(3, {st_mode=S_IFREG|0755, st_size=2747008, ...}) = 0 mmap(NULL, 2488936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x2000000000060000 mprotect(0x20000000002a8000, 96872, PROT_NONE) = 0 mmap(0x20000000002b0000, 49152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x240000) = 0x20000000002b0000 mmap(0x20000000002bc000, 14952, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000002bc000 close(3) = 0 mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x20000000002c0000 mmap(NULL, 32768, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x20000000002c4000 munmap(0x2000000000044000, 111244) = 0 open("tmp.test_writev", O_RDWR|O_CREAT|O_TRUNC, 0777) = 3 writev(3, [], 0) = 0 fstat(1, {st_mode=S_IFREG|0664, st_size=1417, ...}) = 0 mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2000000000044000 lseek(3, 0, SEEK_SET) = 0 readv(3, [], 0) = 0 close(3) = 0 write(1, "i=0, errno=0:Success\ni=0, errno="..., 42i=0, errno=0:Success i=0, errno=0:Success ) = 42 munmap(0x2000000000044000, 65536) = 0 exit_group(0) = ?
Looks like the tail end of the example code got chopped off. Can you please repost the same code as an attachment?
Created attachment 110487 [details] this is strace log
Sorry, I have misunderstand on this completely. Since the example code is short, we post it. ----------------------------------------------- #include <stdio.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> main() { int s; struct iovec w_vector[10]; struct iovec r_vector[10]; char buf[100]; int i; /* now build up a two part writev and write it out */ for( i = 0; i < sizeof( buf ); ++i ) { buf[i] = 'x'; } for( i = 0; i < 10; ++i ) { w_vector[i].iov_base = buf; w_vector[i].iov_len = sizeof(buf); } for( i = 0; i < 10; ++i ) { r_vector[i].iov_base = buf; r_vector[i].iov_len = sizeof(buf); } /* testing writev() */ s = open( "tmp.test_writev",O_RDWR|O_CREAT|O_TRUNC ,0777 ); i = writev( s, &w_vector[0], 0 ); fprintf( stdout, "i=%d, errno=%d:%s\n", i, errno, strerror(errno) ); /* rewind, and test readv() */ lseek( s, 0, SEEK_SET ); i = readv( s, &r_vector[0], 0 ); fprintf( stdout, "i=%d, errno=%d:%s\n", i, errno, strerror(errno) ); close( s ); } -----------------------------------------------
This is not a bug. As mentioned in the kernel source for this function: /* * SuS says "The readv() function *may* fail if the iovcnt argument * was less than or equal to 0, or greater than {IOV_MAX}. Linux has * traditionally returned zero for zero segments, so... */ ret = 0; if (nr_segs == 0) goto out; And indeed, SUSv3 does have exactly that wording; the function is not required to fail on count==0. It's not even clear that the man page needs to be changed --- the above code is from the common vfs layer's readv code, but individual filesystems have the right to intercept readv calls and provide their own implementation. So 3rd-party modules, for example, might well provide implementations which fail with EINVAL on count==0. Using the word "may" to show this in the man page might make sense, but the kernel code does not need to be changed.