Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 67 additions & 3 deletions linux-user/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -10281,7 +10281,38 @@ static inline abi_long copy_to_user_flock64(abi_ulong target_flock_addr,
return 0;
}

static abi_long do_fcntl(int fd, int cmd, abi_ulong arg)
#ifdef TARGET_I386
/*
* The executable identity fd belongs to the translator, not to the guest fd
* table. If a guest operation would mutate or destroy that descriptor,
* move the identity reference first and let the operation keep its normal
* result on the old descriptor number.
*/
static abi_long guest_exe_identity_relocate(CPUArchState *env, int fd)
{
TaskState *ts = env_cpu(env)->opaque;
int new_fd = -1;
abi_long ret = 0;

mmap_lock();
if (ts->info->prctl_mm_exe_fd == fd) {
new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
if (new_fd < 0) {
ret = get_errno(-1);
} else {
ts->info->prctl_mm_exe_fd = new_fd;
}
}
mmap_unlock();

if (new_fd >= 0) {
fd_trans_unregister(new_fd);
}
return ret;
}
#endif

static abi_long do_fcntl(CPUArchState *env, int fd, int cmd, abi_ulong arg)
{
struct flock64 fl64;
#ifdef F_GETOWN_EX
Expand All @@ -10294,6 +10325,17 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg)
if (host_cmd == -TARGET_EINVAL)
return host_cmd;

#ifdef TARGET_I386
/* The identity copy is already close-on-exec. Setting that same bit is
* harmless; only clearing it would mutate the translator's reference. */
if (cmd == TARGET_F_SETFD && !(arg & FD_CLOEXEC)) {
ret = guest_exe_identity_relocate(env, fd);
if (ret) {
return ret;
}
}
#endif

switch(cmd) {
case TARGET_F_GETLK:
ret = copy_from_user_flock(&fl64, arg);
Expand Down Expand Up @@ -14408,6 +14450,12 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
return ret;
#endif
case TARGET_NR_close:
#ifdef TARGET_I386
ret = guest_exe_identity_relocate(cpu_env, arg1);
if (ret) {
return ret;
}
#endif
fd_trans_unregister(arg1);
return get_errno(close(arg1));

Expand Down Expand Up @@ -15575,7 +15623,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
return ret;
#ifdef TARGET_NR_fcntl
case TARGET_NR_fcntl:
return do_fcntl(arg1, arg2, arg3);
return do_fcntl(cpu_env, arg1, arg2, arg3);
#endif
case TARGET_NR_setpgid:
return get_errno(setpgid(arg1, arg2));
Expand All @@ -15589,6 +15637,14 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
return ret;
#ifdef TARGET_NR_dup2
case TARGET_NR_dup2:
#ifdef TARGET_I386
if (arg1 != arg2) {
ret = guest_exe_identity_relocate(cpu_env, arg2);
if (ret) {
return ret;
}
}
#endif
ret = get_errno(dup2(arg1, arg2));
if (ret >= 0) {
fd_trans_dup(arg1, arg2);
Expand All @@ -15604,6 +15660,14 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
return -EINVAL;
}
host_flags = target_to_host_bitmask(arg3, fcntl_flags_tbl);
#ifdef TARGET_I386
if (arg1 != arg2) {
ret = guest_exe_identity_relocate(cpu_env, arg2);
if (ret) {
return ret;
}
}
#endif
ret = get_errno(dup3(arg1, arg2, host_flags));
if (ret >= 0) {
fd_trans_dup(arg1, arg2);
Expand Down Expand Up @@ -19273,7 +19337,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
ret = get_errno(safe_fcntl(arg1, cmd, &fl));
break;
default:
ret = do_fcntl(arg1, arg2, arg3);
ret = do_fcntl(cpu_env, arg1, arg2, arg3);
break;
}
return ret;
Expand Down
114 changes: 114 additions & 0 deletions tests/integration/prctl-x86-semantics.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ typedef long guest_slong_t;
#define __NR_read 0
#define __NR_write 1
#define __NR_close 3
#define __NR_dup2 33
#define __NR_execve 59
#define __NR_fcntl 72
#define __NR_mmap 9
#define __NR_mprotect 10
#define __NR_munmap 11
Expand All @@ -33,6 +35,7 @@ typedef long guest_slong_t;
#define __NR_openat 257
#define __NR_memfd_create 319
#define __NR_readlinkat 267
#define __NR_dup3 292
#define __NR_execveat 322
#define __NR_readlink 89
#define __NR_rename 82
Expand Down Expand Up @@ -66,6 +69,10 @@ typedef long guest_slong_t;
#define AT_EMPTY_PATH 0x1000
#define O_DIRECTORY 00200000

#define F_GETFD 1
#define F_SETFD 2
#define FD_CLOEXEC 1

#define IPC_PRIVATE 0
#define IPC_CREAT 01000
#define IPC_RMID 0
Expand Down Expand Up @@ -282,6 +289,34 @@ static guest_ulong_t string_length(const char *value)
return len;
}

static int proc_fd_path(char *buffer, guest_ulong_t size, unsigned int fd)
{
static const char prefix[] = "/proc/self/fd/";
char digits[16];
guest_ulong_t pos = 0;
unsigned int count = 0;

if (size < sizeof(prefix) + 1) {
return 0;
}
while (pos < sizeof(prefix) - 1) {
buffer[pos] = prefix[pos];
pos++;
}
do {
digits[count++] = '0' + fd % 10;
fd /= 10;
} while (fd && count < sizeof(digits));
if (pos + count >= size) {
return 0;
}
while (count) {
buffer[pos++] = digits[--count];
}
buffer[pos] = 0;
return 1;
}

static int contains(const char *buf, guest_ulong_t len, const char *needle,
guest_ulong_t needle_len)
{
Expand Down Expand Up @@ -1548,6 +1583,81 @@ static int test_exec_race(const char *target_path, int use_execveat)
return 135;
}

static int find_exe_identity_fd(const char *self_path)
{
char fd_path[32];
char link_path[4096];
guest_ulong_t self_len = string_length(self_path);
unsigned int fd;

for (fd = 3; fd < 1024; fd++) {
guest_slong_t flags;
guest_slong_t len;

if (!proc_fd_path(fd_path, sizeof(fd_path), fd)) {
return -1;
}
len = syscall3(__NR_readlink, (guest_slong_t)fd_path,
(guest_slong_t)link_path, sizeof(link_path));
if (len != (guest_slong_t)self_len ||
!bytes_equal(link_path, self_path, self_len)) {
continue;
}
flags = syscall3(__NR_fcntl, fd, F_GETFD, 0);
if (flags >= 0 && (flags & FD_CLOEXEC)) {
return fd;
}
}
return -1;
}

static int test_exe_identity_mutation(const char *self_path, char mode,
int child)
{
static char exe_path[] = "/proc/self/exe";
static char null_path[] = "/dev/null";
static char child_mode[] = "child";
char mode_arg[] = { mode, 0 };
char *argv[] = { exe_path, mode_arg, child_mode, 0 };
guest_slong_t fd;
guest_slong_t null_fd;
guest_slong_t ret;

if (child) {
return 0;
}
if (!self_path) {
return 136;
}
fd = find_exe_identity_fd(self_path);
if (fd < 0) {
return 136;
}
if (mode == 'q') {
ret = syscall1(__NR_close, fd);
} else if (mode == 'o') {
ret = syscall3(__NR_fcntl, fd, F_SETFD, 0);
} else {
null_fd = syscall4(__NR_openat, AT_FDCWD,
(guest_slong_t)null_path, 0, 0);
if (null_fd < 0) {
return 137;
}
if (mode == 'n') {
ret = syscall2(__NR_dup2, null_fd, fd);
} else {
ret = syscall3(__NR_dup3, null_fd, fd, 0);
}
syscall1(__NR_close, null_fd);
}
if (ret < 0) {
return 137;
}
syscall3(__NR_execve, (guest_slong_t)exe_path,
(guest_slong_t)argv, 0);
return 138;
}

int test_main(guest_ulong_t *stack)
{
guest_ulong_t argc = stack[0];
Expand Down Expand Up @@ -1609,6 +1719,10 @@ int test_main(guest_ulong_t *stack)
if (mode == 'u') {
return test_exec_race(argc == 3 ? argv[2] : 0, 1);
}
if (mode == 'q' || mode == 'o' || mode == 'n' || mode == 'j') {
return test_exe_identity_mutation(
argv[0], mode, argc == 3 && argv[2][0] == 'c');
}
return 101;
}

Expand Down
4 changes: 4 additions & 0 deletions tests/integration/test-prctl-x86-semantics.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ run_case i
run_case e "$workdir/prctl-native-env-helper"
run_case a "$workdir/prctl-native-env-helper"
run_case d "$workdir/relative"
run_case q
run_case o
run_case n
run_case j