diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index 1940910a73..ad84380738 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -1,7 +1,8 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ elfload.o linuxload.o uaccess.o uname.o \ - safe-syscall.o $(TARGET_ABI_DIR)/signal.o \ - $(TARGET_ABI_DIR)/cpu_loop.o exit.o fd-trans.o + safe-syscall.o syscall_proc.o \ + $(TARGET_ABI_DIR)/cpu_loop.o $(TARGET_ABI_DIR)/signal.o \ + exit.o fd-trans.o obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 05f03919ff..d6b02224a9 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -122,6 +122,7 @@ #include "qapi/error.h" #include "fd-trans.h" #include "tcg/tcg.h" +#include "syscall_proc.h" #ifndef CLONE_IO #define CLONE_IO 0x80000000 /* Clone io context */ @@ -1104,7 +1105,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) { abi_ulong target_rlim_swap; rlim_t result; - + target_rlim_swap = tswapal(target_rlim); if (target_rlim_swap == TARGET_RLIM_INFINITY) return RLIM_INFINITY; @@ -1112,7 +1113,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) result = target_rlim_swap; if (target_rlim_swap != (rlim_t)result) return RLIM_INFINITY; - + return result; } #endif @@ -1122,13 +1123,13 @@ static inline abi_ulong host_to_target_rlim(rlim_t rlim) { abi_ulong target_rlim_swap; abi_ulong result; - + if (rlim == RLIM_INFINITY || rlim != (abi_long)rlim) target_rlim_swap = TARGET_RLIM_INFINITY; else target_rlim_swap = rlim; result = tswapal(target_rlim_swap); - + return result; } #endif @@ -1615,9 +1616,9 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, abi_ulong target_cmsg_addr; struct target_cmsghdr *target_cmsg, *target_cmsg_start; socklen_t space = 0; - + msg_controllen = tswapal(target_msgh->msg_controllen); - if (msg_controllen < sizeof (struct target_cmsghdr)) + if (msg_controllen < sizeof (struct target_cmsghdr)) goto the_end; target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1); @@ -1703,7 +1704,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, socklen_t space = 0; msg_controllen = tswapal(target_msgh->msg_controllen); - if (msg_controllen < sizeof (struct target_cmsghdr)) + if (msg_controllen < sizeof (struct target_cmsghdr)) goto the_end; target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0); @@ -5750,7 +5751,7 @@ abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr) } unlock_user_struct(target_ldt_info, ptr, 1); - if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN || + if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN || ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX) return -TARGET_EINVAL; seg_32bit = ldt_info.flags & 1; @@ -5828,7 +5829,7 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) lp = (uint32_t *)(gdt_table + idx); entry_1 = tswap32(lp[0]); entry_2 = tswap32(lp[1]); - + read_exec_only = ((entry_2 >> 9) & 1) ^ 1; contents = (entry_2 >> 10) & 3; seg_not_present = ((entry_2 >> 15) & 1) ^ 1; @@ -5844,8 +5845,8 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) (read_exec_only << 3) | (limit_in_pages << 4) | (seg_not_present << 5) | (useable << 6) | (lm << 7); limit = (entry_1 & 0xffff) | (entry_2 & 0xf0000); - base_addr = (entry_1 >> 16) | - (entry_2 & 0xff000000) | + base_addr = (entry_1 >> 16) | + (entry_2 & 0xff000000) | ((entry_2 & 0xff) << 16); target_ldt_info->base_addr = tswapal(base_addr); target_ldt_info->limit = tswap32(limit); @@ -7353,38 +7354,6 @@ static int open_self_auxv(void *cpu_env, int fd) return 0; } -static int is_proc_myself(const char *filename, const char *entry) -{ - if (!strncmp(filename, "/proc/", strlen("/proc/"))) { - filename += strlen("/proc/"); - if (!strncmp(filename, "self/", strlen("self/"))) { - filename += strlen("self/"); - } else if (*filename >= '1' && *filename <= '9') { - char myself[80]; - snprintf(myself, sizeof(myself), "%d/", getpid()); - if (!strncmp(filename, myself, strlen(myself))) { - filename += strlen(myself); - } else { - return 0; - } - } else { - return 0; - } - if (!strcmp(filename, entry)) { - return 1; - } - } - return 0; -} - -#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) || \ - defined(TARGET_SPARC) || defined(TARGET_M68K) -static int is_proc(const char *filename, const char *entry) -{ - return strcmp(filename, entry) == 0; -} -#endif - #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) static int open_net_route(void *cpu_env, int fd) { @@ -7460,18 +7429,18 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, { "auxv", open_self_auxv, is_proc_myself }, { "cmdline", open_self_cmdline, is_proc_myself }, #if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN) - { "/proc/net/route", open_net_route, is_proc }, + { "net/route", open_net_route, is_proc }, #endif #if defined(TARGET_SPARC) - { "/proc/cpuinfo", open_cpuinfo, is_proc }, + { "cpuinfo", open_cpuinfo, is_proc }, #endif #if defined(TARGET_M68K) - { "/proc/hardware", open_hardware, is_proc }, + { "hardware", open_hardware, is_proc }, #endif { NULL, NULL, NULL } }; - if (is_proc_myself(pathname, "exe")) { + if (is_proc_myself_exe(pathname)) { int execfd = qemu_getauxval(AT_EXECFD); return execfd ? execfd : safe_openat(dirfd, exec_path, flags, mode); } @@ -9031,7 +9000,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } else if (!arg3) { /* Short circuit this for the magic exe check. */ ret = -TARGET_EINVAL; - } else if (is_proc_myself((const char *)p, "exe")) { + } else if (is_proc_myself_exe((const char *)p)) { char real[PATH_MAX], *temp; temp = realpath(exec_path, real); /* Return value is # of bytes that we wrote to the buffer. */ @@ -9060,7 +9029,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, p2 = lock_user(VERIFY_WRITE, arg3, arg4, 0); if (!p || !p2) { ret = -TARGET_EFAULT; - } else if (is_proc_myself((const char *)p, "exe")) { + } else if (is_proc_myself_exe((const char *)p)) { char real[PATH_MAX], *temp; temp = realpath(exec_path, real); ret = temp == NULL ? get_errno(-1) : strlen(real) ; @@ -10847,7 +10816,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3))); #if defined(TARGET_NR_fchownat) case TARGET_NR_fchownat: - if (!(p = lock_user_string(arg2))) + if (!(p = lock_user_string(arg2))) return -TARGET_EFAULT; ret = get_errno(fchownat(arg1, p, low2highuid(arg3), low2highgid(arg4), arg5)); diff --git a/linux-user/syscall_proc.c b/linux-user/syscall_proc.c new file mode 100644 index 0000000000..869102f3fe --- /dev/null +++ b/linux-user/syscall_proc.c @@ -0,0 +1,95 @@ +#include "syscall_proc.h" +#include "qemu/osdep.h" +#include "elf.h" + +#define PROC "/proc/" +#define SELF "self/" + +#define STARTS_WITH(path, CONSTANT) \ + strlen(path) >= strlen(CONSTANT) && strncmp(path, CONSTANT, strlen(CONSTANT)) == 0 + +static inline char *scope_to_proc(const char *path) +{ + if (STARTS_WITH(path, PROC)) { + return (char *)path + strlen(PROC); + } + + return NULL; +} + +static inline char *scope_to_proc_myself(const char *path) +{ + char *scope_path = scope_to_proc(path); + if (scope_path == NULL) { + return NULL; + } + + if (STARTS_WITH(scope_path, SELF)) { + return scope_path + strlen(SELF); + } + + if (strlen(scope_path) >= 1 && *scope_path >= '1' && *scope_path <= '9') { + char pid_path[80]; + snprintf(pid_path, sizeof(pid_path), "%d/", getpid()); + if (STARTS_WITH(scope_path, pid_path)) { + return scope_path + strlen(pid_path); + } + } + + return NULL; +} + +int is_proc(const char *path, const char *entry) +{ + char *scope_path = scope_to_proc(path); + if (scope_path == NULL) { + return 0; + } + + return strcmp(scope_path, entry) == 0; +} + +int is_proc_myself(const char *path, const char *entry) +{ + char *scope_path = scope_to_proc_myself(path); + if (scope_path == NULL) { + return 0; + } + + return strcmp(scope_path, entry) == 0; +} + +int is_proc_myself_exe(const char *path) +{ + char *scope_path = scope_to_proc_myself(path); + if (scope_path == NULL) { + return 0; + } + + // Kernel creates "fd/#{number}" link after opening "exe" link. + // Both "exe" and "fd/#{number}" can be used by application. + + // Kernel can provide infinite amount of fd numbers. + // Qemu is going to always return single global execfd. + + // So we need to check "exe" and "fd/#{global_execfd}" only. + + if (strcmp(scope_path, "exe") == 0) { + return 1; + } + + if (STARTS_WITH(scope_path, "fd/")) { + scope_path += strlen("fd/"); + + int execfd = qemu_getauxval(AT_EXECFD); + if (strlen(scope_path) >= 1 && *scope_path >= '1' && *scope_path <= '9' && execfd) { + char execfd_path[80]; + snprintf(execfd_path, sizeof(execfd_path), "%d", execfd); + if (strcmp(scope_path, execfd_path) == 0) { + return 1; + } + } + } + + return 0; +} diff --git a/linux-user/syscall_proc.h b/linux-user/syscall_proc.h new file mode 100644 index 0000000000..f0e59c0e96 --- /dev/null +++ b/linux-user/syscall_proc.h @@ -0,0 +1,8 @@ +#ifndef SYSCALL_PROC_H +#define SYSCALL_PROC_H + +int is_proc(const char *path, const char *entry); +int is_proc_myself(const char *path, const char *entry); +int is_proc_myself_exe(const char *path); + +#endif