linux-user: fix preadv/pwritev offsets
PATCH 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.
Signed-off-by Max Filippov <[email protected]>
--- 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 )(thigh >>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 )(thigh >>HOST_LONG_BITS ); +#endif
This third clause does not look right.
It might have been thigh >> HOST_LONG_BITS, which is good for the specific cases (32, 64) that we will always have. But you'd certainly want to not pretend otherwise by only checking relative sizes.
r~