From c42e77a90d9244c8caf76fe0e54f84200430a4e1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Aug 2023 09:44:18 -0700 Subject: [PATCH 1/6] qemu/osdep: Remove fallback for MAP_FIXED_NOREPLACE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order for our emulation of MAP_FIXED_NOREPLACE to succeed within linux-user target_mmap, we require a non-zero value. This does not require host kernel support, merely the bit being defined. MAP_FIXED_NOREPLACE was added with glibc 2.28. From repology.org: Fedora 36: 2.35 CentOS 8 (RHEL-8): 2.28 Debian 11: 2.31 OpenSUSE Leap 15.4: 2.31 Ubuntu LTS 20.04: 2.31 Reported-by: Akihiko Odaki Reviewed-by: Akihiko Odaki Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson Message-Id: <20230808164418.69989-1-richard.henderson@linaro.org> --- include/qemu/osdep.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index cc61b00ba9..21ef8f1699 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -289,9 +289,6 @@ void QEMU_ERROR("code path is reachable") #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif -#ifndef MAP_FIXED_NOREPLACE -#define MAP_FIXED_NOREPLACE 0 -#endif #ifndef MAP_NORESERVE #define MAP_NORESERVE 0 #endif From 9ab8d0714964abce6f5b8bcac49d6239c548ed12 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 7 Aug 2023 18:22:35 -0700 Subject: [PATCH 2/6] linux-user: Split out do_mmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New function that rejects unsupported map types and flags. In 4b840f96 we should not have accepted MAP_SHARED_VALIDATE without actually validating the rest of the flags. Fixes: 4b840f96 ("linux-user: Populate more bits in mmap_flags_tbl") Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- linux-user/syscall.c | 90 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 17 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index a15bce2be2..34deff0723 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5985,10 +5985,6 @@ static const StructEntry struct_termios_def = { #endif static const bitmask_transtbl mmap_flags_tbl[] = { - { TARGET_MAP_TYPE, TARGET_MAP_SHARED, MAP_TYPE, MAP_SHARED }, - { TARGET_MAP_TYPE, TARGET_MAP_PRIVATE, MAP_TYPE, MAP_PRIVATE }, - { TARGET_MAP_TYPE, TARGET_MAP_SHARED_VALIDATE, - MAP_TYPE, MAP_SHARED_VALIDATE }, { TARGET_MAP_FIXED, TARGET_MAP_FIXED, MAP_FIXED, MAP_FIXED }, { TARGET_MAP_ANONYMOUS, TARGET_MAP_ANONYMOUS, MAP_ANONYMOUS, MAP_ANONYMOUS }, @@ -6006,7 +6002,6 @@ static const bitmask_transtbl mmap_flags_tbl[] = { Recognize it for the target insofar as we do not want to pass it through to the host. */ { TARGET_MAP_STACK, TARGET_MAP_STACK, 0, 0 }, - { TARGET_MAP_SYNC, TARGET_MAP_SYNC, MAP_SYNC, MAP_SYNC }, { TARGET_MAP_NONBLOCK, TARGET_MAP_NONBLOCK, MAP_NONBLOCK, MAP_NONBLOCK }, { TARGET_MAP_POPULATE, TARGET_MAP_POPULATE, MAP_POPULATE, MAP_POPULATE }, { TARGET_MAP_FIXED_NOREPLACE, TARGET_MAP_FIXED_NOREPLACE, @@ -6016,6 +6011,75 @@ static const bitmask_transtbl mmap_flags_tbl[] = { { 0, 0, 0, 0 } }; +/* + * Arrange for legacy / undefined architecture specific flags to be + * ignored by mmap handling code. + */ +#ifndef TARGET_MAP_32BIT +#define TARGET_MAP_32BIT 0 +#endif +#ifndef TARGET_MAP_HUGE_2MB +#define TARGET_MAP_HUGE_2MB 0 +#endif +#ifndef TARGET_MAP_HUGE_1GB +#define TARGET_MAP_HUGE_1GB 0 +#endif + +static abi_long do_mmap(abi_ulong addr, abi_ulong len, int prot, + int target_flags, int fd, off_t offset) +{ + /* + * The historical set of flags that all mmap types implicitly support. + */ + enum { + TARGET_LEGACY_MAP_MASK = TARGET_MAP_SHARED + | TARGET_MAP_PRIVATE + | TARGET_MAP_FIXED + | TARGET_MAP_ANONYMOUS + | TARGET_MAP_DENYWRITE + | TARGET_MAP_EXECUTABLE + | TARGET_MAP_UNINITIALIZED + | TARGET_MAP_GROWSDOWN + | TARGET_MAP_LOCKED + | TARGET_MAP_NORESERVE + | TARGET_MAP_POPULATE + | TARGET_MAP_NONBLOCK + | TARGET_MAP_STACK + | TARGET_MAP_HUGETLB + | TARGET_MAP_32BIT + | TARGET_MAP_HUGE_2MB + | TARGET_MAP_HUGE_1GB + }; + int host_flags; + + switch (target_flags & TARGET_MAP_TYPE) { + case TARGET_MAP_PRIVATE: + host_flags = MAP_PRIVATE; + break; + case TARGET_MAP_SHARED: + host_flags = MAP_SHARED; + break; + case TARGET_MAP_SHARED_VALIDATE: + /* + * MAP_SYNC is only supported for MAP_SHARED_VALIDATE, and is + * therefore omitted from mmap_flags_tbl and TARGET_LEGACY_MAP_MASK. + */ + if (target_flags & ~(TARGET_LEGACY_MAP_MASK | TARGET_MAP_SYNC)) { + return -TARGET_EOPNOTSUPP; + } + host_flags = MAP_SHARED_VALIDATE; + if (target_flags & TARGET_MAP_SYNC) { + host_flags |= MAP_SYNC; + } + break; + default: + return -TARGET_EINVAL; + } + host_flags |= target_to_host_bitmask(target_flags, mmap_flags_tbl); + + return get_errno(target_mmap(addr, len, prot, host_flags, fd, offset)); +} + /* * NOTE: TARGET_ABI32 is defined for TARGET_I386 (but not for TARGET_X86_64) * TARGET_I386 is defined if TARGET_X86_64 is defined @@ -10536,28 +10600,20 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, v5 = tswapal(v[4]); v6 = tswapal(v[5]); unlock_user(v, arg1, 0); - ret = get_errno(target_mmap(v1, v2, v3, - target_to_host_bitmask(v4, mmap_flags_tbl), - v5, v6)); + return do_mmap(v1, v2, v3, v4, v5, v6); } #else /* mmap pointers are always untagged */ - ret = get_errno(target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, - arg6)); + return do_mmap(arg1, arg2, arg3, arg4, arg5, arg6); #endif - return ret; #endif #ifdef TARGET_NR_mmap2 case TARGET_NR_mmap2: #ifndef MMAP_SHIFT #define MMAP_SHIFT 12 #endif - ret = target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, (off_t)(abi_ulong)arg6 << MMAP_SHIFT); - return get_errno(ret); + return do_mmap(arg1, arg2, arg3, arg4, arg5, + (off_t)(abi_ulong)arg6 << MMAP_SHIFT); #endif case TARGET_NR_munmap: arg1 = cpu_untagged_addr(cpu, arg1); From a05cee93f4052179c036789c5a246948dcc362f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 7 Aug 2023 18:26:54 -0700 Subject: [PATCH 3/6] linux-user: Use ARRAY_SIZE with bitmask_transtbl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than using a zero tuple to end the table, use a macro to apply ARRAY_SIZE and pass that on to the convert functions. This fixes two bugs in which the conversion functions required that both the target and host masks be non-zero in order to continue, rather than require both target and host masks be zero in order to terminate. This affected mmap_flags_tbl when the host does not support all of the flags we wish to convert (e.g. MAP_UNINITIALIZED). Mapping these flags to zero is good enough, and matches how the kernel ignores bits that are unknown. Fixes: 4b840f96 ("linux-user: Populate more bits in mmap_flags_tbl") Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- bsd-user/syscall_defs.h | 2 ++ include/exec/user/thunk.h | 15 +++++++++++---- linux-user/syscall.c | 6 ------ linux-user/thunk.c | 24 ++++++++++++------------ 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index b6d113d24a..aedfbf2d7d 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -227,7 +227,9 @@ type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ } /* So far all target and host bitmasks are the same */ +#undef target_to_host_bitmask #define target_to_host_bitmask(x, tbl) (x) +#undef host_to_target_bitmask #define host_to_target_bitmask(x, tbl) (x) #endif /* SYSCALL_DEFS_H */ diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index 300a840d58..6eedef48d8 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -193,10 +193,17 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host) } } -unsigned int target_to_host_bitmask(unsigned int target_mask, - const bitmask_transtbl * trans_tbl); -unsigned int host_to_target_bitmask(unsigned int host_mask, - const bitmask_transtbl * trans_tbl); +unsigned int target_to_host_bitmask_len(unsigned int target_mask, + const bitmask_transtbl *trans_tbl, + size_t trans_len); +unsigned int host_to_target_bitmask_len(unsigned int host_mask, + const bitmask_transtbl * trans_tbl, + size_t trans_len); + +#define target_to_host_bitmask(M, T) \ + target_to_host_bitmask_len(M, T, ARRAY_SIZE(T)) +#define host_to_target_bitmask(M, T) \ + host_to_target_bitmask_len(M, T, ARRAY_SIZE(T)) void thunk_init(unsigned int max_structs); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 34deff0723..12ebc70df5 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -455,7 +455,6 @@ static const bitmask_transtbl fcntl_flags_tbl[] = { #if TARGET_O_LARGEFILE != 0 || O_LARGEFILE != 0 { TARGET_O_LARGEFILE, TARGET_O_LARGEFILE, O_LARGEFILE, O_LARGEFILE, }, #endif - { 0, 0, 0, 0 } }; _syscall2(int, sys_getcwd1, char *, buf, size_t, size) @@ -5813,7 +5812,6 @@ static const bitmask_transtbl iflag_tbl[] = { { TARGET_IXOFF, TARGET_IXOFF, IXOFF, IXOFF }, { TARGET_IMAXBEL, TARGET_IMAXBEL, IMAXBEL, IMAXBEL }, { TARGET_IUTF8, TARGET_IUTF8, IUTF8, IUTF8}, - { 0, 0, 0, 0 } }; static const bitmask_transtbl oflag_tbl[] = { @@ -5841,7 +5839,6 @@ static const bitmask_transtbl oflag_tbl[] = { { TARGET_VTDLY, TARGET_VT1, VTDLY, VT1 }, { TARGET_FFDLY, TARGET_FF0, FFDLY, FF0 }, { TARGET_FFDLY, TARGET_FF1, FFDLY, FF1 }, - { 0, 0, 0, 0 } }; static const bitmask_transtbl cflag_tbl[] = { @@ -5876,7 +5873,6 @@ static const bitmask_transtbl cflag_tbl[] = { { TARGET_HUPCL, TARGET_HUPCL, HUPCL, HUPCL }, { TARGET_CLOCAL, TARGET_CLOCAL, CLOCAL, CLOCAL }, { TARGET_CRTSCTS, TARGET_CRTSCTS, CRTSCTS, CRTSCTS }, - { 0, 0, 0, 0 } }; static const bitmask_transtbl lflag_tbl[] = { @@ -5896,7 +5892,6 @@ static const bitmask_transtbl lflag_tbl[] = { { TARGET_PENDIN, TARGET_PENDIN, PENDIN, PENDIN }, { TARGET_IEXTEN, TARGET_IEXTEN, IEXTEN, IEXTEN }, { TARGET_EXTPROC, TARGET_EXTPROC, EXTPROC, EXTPROC}, - { 0, 0, 0, 0 } }; static void target_to_host_termios (void *dst, const void *src) @@ -6008,7 +6003,6 @@ static const bitmask_transtbl mmap_flags_tbl[] = { MAP_FIXED_NOREPLACE, MAP_FIXED_NOREPLACE }, { TARGET_MAP_UNINITIALIZED, TARGET_MAP_UNINITIALIZED, MAP_UNINITIALIZED, MAP_UNINITIALIZED }, - { 0, 0, 0, 0 } }; /* diff --git a/linux-user/thunk.c b/linux-user/thunk.c index dac4bf11c6..071aad4b5f 100644 --- a/linux-user/thunk.c +++ b/linux-user/thunk.c @@ -436,29 +436,29 @@ const argtype *thunk_print(void *arg, const argtype *type_ptr) /* Utility function: Table-driven functions to translate bitmasks * between host and target formats */ -unsigned int target_to_host_bitmask(unsigned int target_mask, - const bitmask_transtbl * trans_tbl) +unsigned int target_to_host_bitmask_len(unsigned int target_mask, + const bitmask_transtbl *tbl, + size_t len) { - const bitmask_transtbl *btp; unsigned int host_mask = 0; - for (btp = trans_tbl; btp->target_mask && btp->host_mask; btp++) { - if ((target_mask & btp->target_mask) == btp->target_bits) { - host_mask |= btp->host_bits; + for (size_t i = 0; i < len; ++i) { + if ((target_mask & tbl[i].target_mask) == tbl[i].target_bits) { + host_mask |= tbl[i].host_bits; } } return host_mask; } -unsigned int host_to_target_bitmask(unsigned int host_mask, - const bitmask_transtbl * trans_tbl) +unsigned int host_to_target_bitmask_len(unsigned int host_mask, + const bitmask_transtbl *tbl, + size_t len) { - const bitmask_transtbl *btp; unsigned int target_mask = 0; - for (btp = trans_tbl; btp->target_mask && btp->host_mask; btp++) { - if ((host_mask & btp->host_mask) == btp->host_bits) { - target_mask |= btp->target_bits; + for (size_t i = 0; i < len; ++i) { + if ((host_mask & tbl[i].host_mask) == tbl[i].host_bits) { + target_mask |= tbl[i].target_bits; } } return target_mask; From 1b65895ddd9bb3898458ec622f5550173f9a8550 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Aug 2023 23:41:20 +0000 Subject: [PATCH 4/6] tests/tcg: Disable filename test for info proc mappings This test fails when host page size != guest page size, because qemu may not be able to directly map the file. Fixes: a6341482695 ("tests/tcg: Add a test for info proc mappings") Acked-by: Ilya Leoshkevich Signed-off-by: Richard Henderson --- tests/tcg/multiarch/gdbstub/test-proc-mappings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py index 7b596ac21b..5e3e5a2fb7 100644 --- a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py +++ b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py @@ -33,7 +33,8 @@ def run_test(): return raise report(isinstance(mappings, str), "Fetched the mappings from the inferior") - report("/sha1" in mappings, "Found the test binary name in the mappings") + # Broken with host page size > guest page size + # report("/sha1" in mappings, "Found the test binary name in the mappings") def main(): From 47d1e982318a0d97c557ce349ea9e2b601554055 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 9 Aug 2023 17:11:39 +0200 Subject: [PATCH 5/6] util/interval-tree: Check root for null in interval_tree_iter_first Fix a crash in qemu-user when running cat /proc/self/maps in a chroot, where /proc isn't mounted. The problem was introduced by commit 3ce3dd8ca965 ("util/selfmap: Rewrite using qemu/interval-tree.h") where in open_self_maps_1() the function read_self_maps() is called and which returns NULL if it can't read the hosts /proc/self/maps file. Afterwards that NULL is fed into interval_tree_iter_first() which doesn't check if the root node is NULL. Fix it by adding a check if root is NULL and return NULL in that case. Signed-off-by: Helge Deller Fixes: 3ce3dd8ca965 ("util/selfmap: Rewrite using qemu/interval-tree.h") Message-Id: Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- util/interval-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/interval-tree.c b/util/interval-tree.c index f2866aa7d3..53465182e6 100644 --- a/util/interval-tree.c +++ b/util/interval-tree.c @@ -797,7 +797,7 @@ IntervalTreeNode *interval_tree_iter_first(IntervalTreeRoot *root, { IntervalTreeNode *node, *leftmost; - if (!root->rb_root.rb_node) { + if (!root || !root->rb_root.rb_node) { return NULL; } From b8002058c45a50d893c51cf62ec96c70128fc1eb Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 3 Aug 2023 23:44:47 +0200 Subject: [PATCH 6/6] linux-user: Fix openat() emulation to correctly detect accesses to /proc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In qemu we catch accesses to files like /proc/cpuinfo or /proc/net/route and return to the guest contents which would be visible on a real system (instead what the host would show). This patch fixes a bug, where for example the accesses cat /proc////cpuinfo or cd /proc && cat cpuinfo will not be recognized by qemu and where qemu will wrongly show the contents of the host's /proc/cpuinfo file. Signed-off-by: Helge Deller Reviewed-by: Daniel P. Berrangé Message-Id: <20230803214450.647040-2-deller@gmx.de> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- linux-user/syscall.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 12ebc70df5..9353268cc1 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8557,9 +8557,12 @@ static int open_hardware(CPUArchState *cpu_env, int fd) } #endif -int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, + +int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname, int flags, mode_t mode, bool safe) { + g_autofree char *proc_name = NULL; + const char *pathname; struct fake_open { const char *filename; int (*fill)(CPUArchState *cpu_env, int fd); @@ -8585,6 +8588,14 @@ int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, { NULL, NULL, NULL } }; + /* if this is a file from /proc/ filesystem, expand full name */ + proc_name = realpath(fname, NULL); + if (proc_name && strncmp(proc_name, "/proc/", 6) == 0) { + pathname = proc_name; + } else { + pathname = fname; + } + if (is_proc_myself(pathname, "exe")) { if (safe) { return safe_openat(dirfd, exec_path, flags, mode);