linux-user: fix preadv/pwritev offsets
PATCH v2 1/1 linux-user: fix preadv/pwritev offsets
preadv/pwritev accept low and high parts of file offset in two separate parameters. When host bitness doesn't match guest bitness these parts must be appropriately recombined.
Introduce target_low_high_to_host_low_high that does this recombination and use it in preadv/pwritev syscalls.
This fixes glibc testsuite test misc/tst-preadvwritev64.
---
Changes v1->v2:
- fix host high computation in TARGET_LONG_BITS > HOST_LONG_BITS case
linux-user/syscall.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-)
linux-user/syscall.c +25 -2
@@ -3386,6 +3386,23 @@ static abi_long do_getsockopt(int sockfd, int level, int optname,return ret; } +static void target_low_high_to_host_low_high (abi_ulong tlow, +abi_ulong thigh, +unsigned long *hlow, +unsigned long *hhigh) +{ +#if TARGET_LONG_BITS ==HOST_LONG_BITS + *hlow = tlow; + *hhigh = thigh; +#elif TARGET_LONG_BITS <HOST_LONG_BITS + *hlow = tlow | (unsigned long )thigh <<TARGET_LONG_BITS ; + *hhigh =0 ; +#else + *hlow = (unsigned long )tlow; + *hhigh = (unsigned long )(tlow >>HOST_LONG_BITS ); +#endif +} +static struct iovec *lock_iovec (int type,abi_ulong target_addr,abi_ulong count,int copy) {
@@ -10449,7 +10466,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, {struct iovec *vec =lock_iovec (VERIFY_WRITE , arg2, arg3,0 );if (vec !=NULL ) { - ret =get_errno (safe_preadv (arg1, vec, arg3, arg4, arg5)); +unsigned long low, high; + +target_low_high_to_host_low_high (arg4, arg5, &low, &high); + ret =get_errno (safe_preadv (arg1, vec, arg3, low, high));unlock_iovec (vec, arg2, arg3,1 ); }else { ret = -host_to_target_errno (errno);
@@ -10462,7 +10482,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, {struct iovec *vec =lock_iovec (VERIFY_READ , arg2, arg3,1 );if (vec !=NULL ) { - ret =get_errno (safe_pwritev (arg1, vec, arg3, arg4, arg5)); +unsigned long low, high; + +target_low_high_to_host_low_high (arg4, arg5, &low, &high); + ret =get_errno (safe_pwritev (arg1, vec, arg3, low, high));unlock_iovec (vec, arg2, arg3,0 ); }else { ret = -host_to_target_errno (errno);
Signature · 1 line
2.11.0
Quoted 5 lines · Max Filippov
linux-user/syscall.c +5
-0+static void target_low_high_to_host_low_high (abi_ulong tlow, +abi_ulong thigh, +unsigned long *hlow, +unsigned long *hhigh) +{
linux-user/syscall.c static abi_long do_getsockopt(int sockfd, int level, int optname, · line 3403
Quoted 10 lines
linux-user/syscall.c +10
-0+#if TARGET_LONG_BITS ==HOST_LONG_BITS + *hlow = tlow; + *hhigh = thigh; +#elif TARGET_LONG_BITS <HOST_LONG_BITS + *hlow = tlow | (unsigned long )thigh <<TARGET_LONG_BITS ; + *hhigh =0 ; +#else + *hlow = (unsigned long )tlow; + *hhigh = (unsigned long )(tlow >>HOST_LONG_BITS ); +#endif
It might still be worth a check for HOST_LONG_BITS >= 2 * TARGET_LONG_BITS and #error otherwise. Or explicit checks vs 32 & 64 if you like.
r~
Quoted 11 lines · Richard Henderson
Quoted 10 lines · Max Filippov
linux-user/syscall.c +10
-0+static void target_low_high_to_host_low_high (abi_ulong tlow, +abi_ulong thigh, +unsigned long *hlow, +unsigned long *hhigh) +{ +#if TARGET_LONG_BITS ==HOST_LONG_BITS + *hlow = tlow; + *hhigh = thigh; +#elif TARGET_LONG_BITS <HOST_LONG_BITS + *hlow = tlow | (unsigned long )thigh <<TARGET_LONG_BITS ;
Quoted 8 lines
linux-user/syscall.c static abi_long do_getsockopt(int sockfd, int level, int optname, · line 3403
Quoted 5 lines
linux-user/syscall.c +5
-0+ *hhigh =0 ; +#else + *hlow = (unsigned long )tlow; + *hhigh = (unsigned long )(tlow >>HOST_LONG_BITS ); +#endif It might still be worth a check for
HOST_LONG_BITS>= 2 *TARGET_LONG_BITSand #error otherwise. Or explicit checks vs 32 & 64 if you like.
Ok, will do in v3.
Signature · 2 lines
Thanks. -- Max
Le 05/04/2018 à 03:41, Max Filippov a écrit :
Quoted 59 lines
preadv/pwritev accept low and high parts of file offset in two separate parameters. When host bitness doesn't match guest bitness these parts must be appropriately recombined.
Introducetarget_low_high_to_host_low_highthat does this recombination and use it in preadv/pwritev syscalls.This fixes glibc testsuite test misc/tst-preadvwritev64.
Signed-off-by Max Filippov <[email protected]>---
Changesv1->v2:
- fix host high computation inTARGET_LONG_BITS>HOST_LONG_BITScaselinux-user/syscall.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-)linux-user/syscall.c +21
-1@@ -3386,6 +3386,23 @@ static abi_long do_getsockopt(int sockfd, int level, int optname,return ret; } +static void target_low_high_to_host_low_high (abi_ulong tlow, +abi_ulong thigh, +unsigned long *hlow, +unsigned long *hhigh) +{ +#if TARGET_LONG_BITS ==HOST_LONG_BITS + *hlow = tlow; + *hhigh = thigh; +#elif TARGET_LONG_BITS <HOST_LONG_BITS + *hlow = tlow | (unsigned long )thigh <<TARGET_LONG_BITS ; + *hhigh =0 ; +#else + *hlow = (unsigned long )tlow; + *hhigh = (unsigned long )(tlow >>HOST_LONG_BITS ); +#endif +} +static struct iovec *lock_iovec (int type,abi_ulong target_addr,abi_ulong count,int copy) {@@ -10449,7 +10466,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, {struct iovec *vec =lock_iovec (VERIFY_WRITE , arg2, arg3,0 );if (vec !=NULL ) { - ret =get_errno (safe_preadv (arg1, vec, arg3, arg4, arg5)); +unsigned long low, high; + +target_low_high_to_host_low_high (arg4, arg5, &low, &high); + ret =get_errno (safe_preadv (arg1, vec, arg3, low, high));unlock_iovec (vec, arg2, arg3,1 ); }else { ret = -host_to_target_errno (errno);@@ -10462,7 +10482,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, {
linux-user/syscall.c abi_long do_syscall(void *cpu_env, int num, abi_long arg1, · line 10491
Quoted 10 lines
linux-user/syscall.c +4
-1struct iovec *vec =lock_iovec (VERIFY_READ , arg2, arg3,1 );if (vec !=NULL ) { - ret =get_errno (safe_pwritev (arg1, vec, arg3, arg4, arg5)); +unsigned long low, high; + +target_low_high_to_host_low_high (arg4, arg5, &low, &high); + ret =get_errno (safe_pwritev (arg1, vec, arg3, low, high));unlock_iovec (vec, arg2, arg3,0 ); }else { ret = -host_to_target_errno (errno);
Did you try to use the regpairs_aligned() and target_offset64() functions as it is done for pread64(), pwrite64(), fadvise64(),... ?
Thanks,
Laurent
Quoted 2 lines · Laurent Vivier
Did you try to use the
regpairs_aligned()andtarget_offset64()functions as it is done forpread64(),pwrite64(),fadvise64(),... ?
My understanding is that if the syscalls had single parameter that took two registers then it'd be a place to use regpairs_aligned. By the syscalls are defined as follows:
SYSCALL_DEFINE5(preadv, unsigned long, fd, const struct iovec __user *, vec,
unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h)
SYSCALL_DEFINE5(pwritev, unsigned long, fd, const struct iovec __user *, vec,
unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h)
so these are normal unsigned long arguments, low part, then the high part. target_offset64 treats arguments differently depending on the target endianness, which is not needed for preadv/pwritev.
Signature · 2 lines
Thanks. -- Max