From 8e00ed418e4c4a7d575ad5ac197dd03857bc9cad Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Fri, 15 Jul 2016 07:56:03 -0700 Subject: [PATCH] Pass 64-bit off_t as one 64-bit argument on 64-bit platforms Currently 64-bit off_t is passed in 2 64-bit arguments on 64-bit platforms with the second argument ignored by static inline loff_t pos_from_hilo(unsigned long high, unsigned long low) { return (((loff_t)high << HALF_LONG_BITS) << HALF_LONG_BITS) | low; } SYSCALL_DEFINE5(preadv, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h) { loff_t pos = pos_from_hilo(pos_h, pos_l); return do_preadv(fd, vec, vlen, pos, 0); } On x86-64, only 4 arguments are passed to preadv. Since off_t is the last argument, garbage in the 5th argument isn't used by kernel. Since off_t isn't the last argument for preadv2 nor pwritev2, 64-bit off_t has be be passed in 2 64-bit arguments. This patch changes preadv, pwritev, preadv2 and pwritev2 to pass 64-bit off_t in one 64-bit argument on 64-bit platforms. preadv and pwritev are compatible with passing off_t in one or two arguments as the 2nd argument is ignored by kernel. But preadv2 and pwritev2 are incompatible with the old ones. Signed-off-by: H.J. Lu --- fs/read_write.c | 28 ++++++++++++++++++++++++++++ include/linux/syscalls.h | 11 +++++++++++ 2 files changed, 39 insertions(+) diff --git a/fs/read_write.c b/fs/read_write.c index 66215a7..779461a 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -924,11 +924,13 @@ static ssize_t do_writev(unsigned long fd, const struct iovec __user *vec, return ret; } +#if BITS_PER_LONG == 32 static inline loff_t pos_from_hilo(unsigned long high, unsigned long low) { #define HALF_LONG_BITS (BITS_PER_LONG / 2) return (((loff_t)high << HALF_LONG_BITS) << HALF_LONG_BITS) | low; } +#endif static ssize_t do_preadv(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, loff_t pos, int flags) @@ -988,6 +990,7 @@ SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec, return do_writev(fd, vec, vlen, 0); } +#if BITS_PER_LONG == 32 SYSCALL_DEFINE5(preadv, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h) { @@ -1027,6 +1030,31 @@ SYSCALL_DEFINE6(pwritev2, unsigned long, fd, const struct iovec __user *, vec, return do_pwritev(fd, vec, vlen, pos, flags); } +#else +SYSCALL_DEFINE4(preadv, unsigned long, fd, const struct iovec __user *, vec, + unsigned long, vlen, loff_t, pos) +{ + return do_preadv(fd, vec, vlen, pos, 0); +} + +SYSCALL_DEFINE5(preadv2, unsigned long, fd, const struct iovec __user *, vec, + unsigned long, vlen, loff_t, pos, int, flags) +{ + return do_preadv(fd, vec, vlen, pos, flags); +} + +SYSCALL_DEFINE4(pwritev, unsigned long, fd, const struct iovec __user *, vec, + unsigned long, vlen, loff_t, pos) +{ + return do_pwritev(fd, vec, vlen, pos, 0); +} + +SYSCALL_DEFINE5(pwritev2, unsigned long, fd, const struct iovec __user *, vec, + unsigned long, vlen, loff_t, pos, int, flags) +{ + return do_pwritev(fd, vec, vlen, pos, flags); +} +#endif #ifdef CONFIG_COMPAT diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index d022390..e61066f 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -573,6 +573,7 @@ asmlinkage long sys_pread64(unsigned int fd, char __user *buf, size_t count, loff_t pos); asmlinkage long sys_pwrite64(unsigned int fd, const char __user *buf, size_t count, loff_t pos); +#if BITS_PER_LONG == 32 asmlinkage long sys_preadv(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h); asmlinkage long sys_preadv2(unsigned long fd, const struct iovec __user *vec, @@ -583,6 +584,16 @@ asmlinkage long sys_pwritev(unsigned long fd, const struct iovec __user *vec, asmlinkage long sys_pwritev2(unsigned long fd, const struct iovec __user *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h, int flags); +#else +asmlinkage long sys_preadv(unsigned long fd, const struct iovec __user *vec, + unsigned long vlen, loff_t pos); +asmlinkage long sys_preadv2(unsigned long fd, const struct iovec __user *vec, + unsigned long vlen, loff_t pos, int flags); +asmlinkage long sys_pwritev(unsigned long fd, const struct iovec __user *vec, + unsigned long vlen, loff_t pos); +asmlinkage long sys_pwritev2(unsigned long fd, const struct iovec __user *vec, + unsigned long vlen, loff_t pos, int flags); +#endif asmlinkage long sys_getcwd(char __user *buf, unsigned long size); asmlinkage long sys_mkdir(const char __user *pathname, umode_t mode); asmlinkage long sys_chdir(const char __user *filename); -- 2.7.4