[PATCH] Pass 64-bit off_t as one 64-bit argument on 64-bit platforms

From: H.J. Lu
Date: Fri Jul 15 2016 - 11:13:26 EST


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.


--
H.J.
From 8e00ed418e4c4a7d575ad5ac197dd03857bc9cad Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@xxxxxxxxx>
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 <hjl.tools@xxxxxxxxx>
---
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