Fix 64-bit syscall argument fetching in 32-bit Linux syscall handlers.

The Linux32 system call argument fetcher places each argument (passed in
registers in the Linux x86 system call convention) into an entry in the
generic system call args array.  Each member of this array is 8 bytes
wide, so this approach is broken for system calls that take off_t
arguments.

Fix the problem by splitting l_loff_t arguments in the 32-bit system
call descriptions, the same as we do for FreeBSD32.  Change entry points
to handle this using the PAIR32TO64 macro.

Move linux_ftruncate64() into compat/linux.

PR:		243155
Reported by:	Alex S <iwtcex@gmail.com>
Reviewed by:	kib (previous version)
MFC after:	2 weeks
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D23210
This commit is contained in:
markj 2020-01-21 17:28:22 +00:00
parent 05386e4d1f
commit 293becab6b
4 changed files with 93 additions and 40 deletions

View File

@ -592,13 +592,6 @@ linux_sigaltstack(struct thread *td, struct linux_sigaltstack_args *uap)
return (error);
}
int
linux_ftruncate64(struct thread *td, struct linux_ftruncate64_args *args)
{
return (kern_ftruncate(td, args->fd, args->length));
}
int
linux_gettimeofday(struct thread *td, struct linux_gettimeofday_args *uap)
{

View File

@ -325,9 +325,9 @@
l_sigset_t *newset, \
l_size_t sigsetsize); }
180 AUE_PREAD STD { int linux_pread(l_uint fd, char *buf, \
l_size_t nbyte, l_loff_t offset); }
l_size_t nbyte, uint32_t offset1, uint32_t offset2); }
181 AUE_PWRITE STD { int linux_pwrite(l_uint fd, char *buf, \
l_size_t nbyte, l_loff_t offset); }
l_size_t nbyte, uint32_t offset1, uint32_t offset2); }
182 AUE_CHOWN STD { int linux_chown16(char *path, \
l_uid16_t uid, l_gid16_t gid); }
183 AUE_GETCWD STD { int linux_getcwd(char *buf, \
@ -349,9 +349,9 @@
l_ulong prot, l_ulong flags, l_ulong fd, \
l_ulong pgoff); }
193 AUE_TRUNCATE STD { int linux_truncate64(char *path, \
l_loff_t length); }
uint32_t length1, uint32_t length2); }
194 AUE_FTRUNCATE STD { int linux_ftruncate64(l_uint fd, \
l_loff_t length); }
uint32_t length1, uint32_t length2); }
195 AUE_STAT STD { int linux_stat64(const char *filename, \
struct l_stat64 *statbuf); }
196 AUE_LSTAT STD { int linux_lstat64(const char *filename, \
@ -426,7 +426,7 @@
247 AUE_NULL UNIMPL linux_io_getevents
248 AUE_NULL UNIMPL linux_io_submit
249 AUE_NULL UNIMPL linux_io_cancel
250 AUE_NULL STD { int linux_fadvise64(int fd, l_loff_t offset, \
250 AUE_NULL STD { int linux_fadvise64(int fd, uint32_t offset1, uint32_t offset2, \
l_size_t len, int advice); }
251 AUE_NULL UNIMPL
252 AUE_EXIT STD { int linux_exit_group(int error_code); }
@ -456,7 +456,8 @@
271 AUE_UTIMES STD { int linux_utimes(char *fname, \
struct l_timeval *tptr); }
272 AUE_NULL STD { int linux_fadvise64_64(int fd, \
l_loff_t offset, l_loff_t len, \
uint32_t offset1, uint32_t offset2, \
uint32_t len1, uint32_t len2, \
int advice); }
273 AUE_NULL UNIMPL vserver
274 AUE_NULL STD { int linux_mbind(void); }
@ -524,8 +525,9 @@
312 AUE_NULL STD { int linux_get_robust_list(l_int pid, \
struct linux_robust_list_head **head, l_size_t *len); }
313 AUE_NULL STD { int linux_splice(void); }
314 AUE_NULL STD { int linux_sync_file_range(l_int fd, l_loff_t offset,
l_loff_t nbytes, unsigned int flags); }
314 AUE_NULL STD { int linux_sync_file_range(l_int fd, uint32_t offset1,
uint32_t offset2, uint32_t nbytes1, uint32_t nbytes2,
unsigned int flags); }
315 AUE_NULL STD { int linux_tee(void); }
316 AUE_NULL STD { int linux_vmsplice(void); }
; Linux 2.6.18:
@ -544,7 +546,8 @@
323 AUE_NULL STD { int linux_eventfd(l_uint initval); }
; Linux 2.6.23:
324 AUE_NULL STD { int linux_fallocate(l_int fd, l_int mode, \
l_loff_t offset, l_loff_t len); }
uint32_t offset1, uint32_t offset2, uint32_t len1,
uint32_t len2); }
; Linux 2.6.25:
325 AUE_NULL STD { int linux_timerfd_settime(l_int fd, l_int flags, \
const struct l_itimerspec *new_value, \

View File

@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include <sys/vnode.h>
#ifdef COMPAT_LINUX32
#include <compat/freebsd32/freebsd32_misc.h>
#include <machine/../linux32/linux.h>
#include <machine/../linux32/linux32_proto.h>
#else
@ -67,7 +68,6 @@ __FBSDID("$FreeBSD$");
static int linux_common_open(struct thread *, int, char *, int, int);
static int linux_getdents_error(struct thread *, int, int);
#ifdef LINUX_LEGACY_SYSCALLS
int
linux_creat(struct thread *td, struct linux_creat_args *args)
@ -820,7 +820,6 @@ linux_truncate(struct thread *td, struct linux_truncate_args *args)
int error;
LCONVPATHEXIST(td, args->path, &path);
error = kern_truncate(td, path, UIO_SYSSPACE, args->length);
LFREEPATH(path);
return (error);
@ -831,11 +830,17 @@ int
linux_truncate64(struct thread *td, struct linux_truncate64_args *args)
{
char *path;
off_t length;
int error;
LCONVPATHEXIST(td, args->path, &path);
#if defined(__amd64__) && defined(COMPAT_LINUX32)
length = PAIR32TO64(off_t, args->length);
#else
length = args->length;
#endif
error = kern_truncate(td, path, UIO_SYSSPACE, args->length);
LCONVPATHEXIST(td, args->path, &path);
error = kern_truncate(td, path, UIO_SYSSPACE, length);
LFREEPATH(path);
return (error);
}
@ -848,6 +853,22 @@ linux_ftruncate(struct thread *td, struct linux_ftruncate_args *args)
return (kern_ftruncate(td, args->fd, args->length));
}
#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
int
linux_ftruncate64(struct thread *td, struct linux_ftruncate64_args *args)
{
off_t length;
#if defined(__amd64__) && defined(COMPAT_LINUX32)
length = PAIR32TO64(off_t, args->length);
#else
length = args->length;
#endif
return (kern_ftruncate(td, args->fd, length));
}
#endif
#ifdef LINUX_LEGACY_SYSCALLS
int
linux_link(struct thread *td, struct linux_link_args *args)
@ -908,8 +929,17 @@ linux_fdatasync(struct thread *td, struct linux_fdatasync_args *uap)
int
linux_sync_file_range(struct thread *td, struct linux_sync_file_range_args *uap)
{
off_t nbytes, offset;
if (uap->offset < 0 || uap->nbytes < 0 ||
#if defined(__amd64__) && defined(COMPAT_LINUX32)
nbytes = PAIR32TO64(off_t, uap->nbytes);
offset = PAIR32TO64(off_t, uap->offset);
#else
nbytes = uap->nbytes;
offset = uap->offset;
#endif
if (offset < 0 || nbytes < 0 ||
(uap->flags & ~(LINUX_SYNC_FILE_RANGE_WAIT_BEFORE |
LINUX_SYNC_FILE_RANGE_WRITE |
LINUX_SYNC_FILE_RANGE_WAIT_AFTER)) != 0) {
@ -923,18 +953,23 @@ int
linux_pread(struct thread *td, struct linux_pread_args *uap)
{
struct vnode *vp;
off_t offset;
int error;
error = kern_pread(td, uap->fd, uap->buf, uap->nbyte, uap->offset);
#if defined(__amd64__) && defined(COMPAT_LINUX32)
offset = PAIR32TO64(off_t, uap->offset);
#else
offset = uap->offset;
#endif
error = kern_pread(td, uap->fd, uap->buf, uap->nbyte, offset);
if (error == 0) {
/* This seems to violate POSIX but Linux does it. */
error = fgetvp(td, uap->fd, &cap_pread_rights, &vp);
if (error != 0)
return (error);
if (vp->v_type == VDIR) {
vrele(vp);
return (EISDIR);
}
if (vp->v_type == VDIR)
error = EISDIR;
vrele(vp);
}
return (error);
@ -943,8 +978,15 @@ linux_pread(struct thread *td, struct linux_pread_args *uap)
int
linux_pwrite(struct thread *td, struct linux_pwrite_args *uap)
{
off_t offset;
return (kern_pwrite(td, uap->fd, uap->buf, uap->nbyte, uap->offset));
#if defined(__amd64__) && defined(COMPAT_LINUX32)
offset = PAIR32TO64(off_t, uap->offset);
#else
offset = uap->offset;
#endif
return (kern_pwrite(td, uap->fd, uap->buf, uap->nbyte, offset));
}
int
@ -1466,26 +1508,40 @@ convert_fadvice(int advice)
int
linux_fadvise64(struct thread *td, struct linux_fadvise64_args *args)
{
off_t offset;
int advice;
#if defined(__amd64__) && defined(COMPAT_LINUX32)
offset = PAIR32TO64(off_t, args->offset);
#else
offset = args->offset;
#endif
advice = convert_fadvice(args->advice);
if (advice == -1)
return (EINVAL);
return (kern_posix_fadvise(td, args->fd, args->offset, args->len,
advice));
return (kern_posix_fadvise(td, args->fd, offset, args->len, advice));
}
#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
int
linux_fadvise64_64(struct thread *td, struct linux_fadvise64_64_args *args)
{
off_t len, offset;
int advice;
#if defined(__amd64__) && defined(COMPAT_LINUX32)
len = PAIR32TO64(off_t, args->len);
offset = PAIR32TO64(off_t, args->offset);
#else
len = args->len;
offset = args->offset;
#endif
advice = convert_fadvice(args->advice);
if (advice == -1)
return (EINVAL);
return (kern_posix_fadvise(td, args->fd, args->offset, args->len,
advice));
return (kern_posix_fadvise(td, args->fd, offset, len, advice));
}
#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
@ -1559,6 +1615,7 @@ linux_dup3(struct thread *td, struct linux_dup3_args *args)
int
linux_fallocate(struct thread *td, struct linux_fallocate_args *args)
{
off_t len, offset;
/*
* We emulate only posix_fallocate system call for which
@ -1567,8 +1624,15 @@ linux_fallocate(struct thread *td, struct linux_fallocate_args *args)
if (args->mode != 0)
return (ENOSYS);
return (kern_posix_fallocate(td, args->fd, args->offset,
args->len));
#if defined(__amd64__) && defined(COMPAT_LINUX32)
len = PAIR32TO64(off_t, args->len);
offset = PAIR32TO64(off_t, args->offset);
#else
len = args->len;
offset = args->offset;
#endif
return (kern_posix_fallocate(td, args->fd, offset, len));
}
int

View File

@ -552,13 +552,6 @@ linux_sigaltstack(struct thread *td, struct linux_sigaltstack_args *uap)
return (error);
}
int
linux_ftruncate64(struct thread *td, struct linux_ftruncate64_args *args)
{
return (kern_ftruncate(td, args->fd, args->length));
}
int
linux_set_thread_area(struct thread *td, struct linux_set_thread_area_args *args)
{