From nobody Tue Feb 10 13:17:41 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=linaro.org Return-Path: Received: from lists.gnu.org (208.118.235.17 [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527807240146784.9954876325764; Thu, 31 May 2018 15:54:00 -0700 (PDT) Received: from localhost ([::1]:46521 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOWS6-0000BU-HZ for importer@patchew.org; Thu, 31 May 2018 18:53:50 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38559) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOWNr-0005Gw-Uj for qemu-devel@nongnu.org; Thu, 31 May 2018 18:49:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOWNo-0000rO-LU for qemu-devel@nongnu.org; Thu, 31 May 2018 18:49:27 -0400 Received: from mail-pg0-x244.google.com ([2607:f8b0:400e:c05::244]:44340) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fOWNo-0000qf-B4 for qemu-devel@nongnu.org; Thu, 31 May 2018 18:49:24 -0400 Received: by mail-pg0-x244.google.com with SMTP id p21-v6so10350839pgd.11 for ; Thu, 31 May 2018 15:49:24 -0700 (PDT) Received: from cloudburst.twiddle.net (97-126-112-211.tukw.qwest.net. [97.126.112.211]) by smtp.gmail.com with ESMTPSA id t3-v6sm33385584pgs.91.2018.05.31.15.49.21 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 31 May 2018 15:49:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=7OSmq4hqL21PytEiWwZ62wkBZ8Vmh8b24upJeUdJdBg=; b=kFcSyzrF7C6LBF7LlbRzF78myjrbbahwkKM1+4VP1v24M+W2Fw6iOmEY5nsO/amv8m fIza4QHYBU1qKFX5tEOZmQj3auBXOtuH5aF68PRZhBOkrxfPvrAWBRhJ6IfynOEu80Fz xMT5d5+fWJoKFHetzVdNGV/y1n4d2a2v5xBSE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=7OSmq4hqL21PytEiWwZ62wkBZ8Vmh8b24upJeUdJdBg=; b=Uy5ye6xpYPyKvoScUtMblN/d00wFL9YiEIgZMNRVAs8ldIalSqUAAc4mBA4HHfxPj8 HtrVUunksnvVoWYm+1/sU/FzEcgRh+0QyRQ7Bb+Z4A3wCsLTfrH7HP64BFYB+RHt3Tlz r6vmqcJlwW8AvT1If/3MOcazG3YHsTQ5XmZqDvNz5RZlymamUPE6GNMtenTf9J/KI/Eb ZiNaihZ56L4f/Rvdq64AX7wdgDgIog7PXHZfBfDXky31366tUypb8EO8qBfQvsP7V2KZ k3c9JYDXtnz7JYc4368BzWSa1TU1i8cYUI3ymQymqqQqFHy9/xCizuxo/rhIFLT+7RGN /qTw== X-Gm-Message-State: ALKqPwevhDMV6NvBn7EUrE+DH8G/DpGXpUBrMulhjajIcBcWlmH4Bzkp 2O0SMmd7E5sBpcE8fbaMlyTa7uUgJsg= X-Google-Smtp-Source: ADUXVKL29ReXwPINHFsrGS0gj4CswfWahx3IBHBxuMpxSOF7w8XojmbI0DAn7y1VzdPQPZZGLuUARw== X-Received: by 2002:a65:4b49:: with SMTP id k9-v6mr6772862pgt.369.1527806962785; Thu, 31 May 2018 15:49:22 -0700 (PDT) From: Richard Henderson To: qemu-devel@nongnu.org Date: Thu, 31 May 2018 15:49:11 -0700 Message-Id: <20180531224911.23725-7-richard.henderson@linaro.org> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180531224911.23725-1-richard.henderson@linaro.org> References: <20180531224911.23725-1-richard.henderson@linaro.org> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400e:c05::244 Subject: [Qemu-devel] [PATCH 6/6] linux-user: Use *at functions to implement interp_prefix X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peter.maydell@linaro.org, laurent@vivier.eu, evgreen@chromium.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" If the interp_prefix is a complete chroot, it may have a *lot* of files. Setting up the cache for this is quite expensive. For the most part, we can use the *at versions of various syscalls to attempt the operation in the prefix. For the few cases that remain, attempt the operation in the prefix via concatenation and then retry if that fails. Signed-off-by: Richard Henderson --- linux-user/qemu.h | 37 ++++++++++ linux-user/elfload.c | 5 +- linux-user/main.c | 26 ++++++- linux-user/syscall.c | 163 +++++++++++++++++++++++++++++-------------- 4 files changed, 174 insertions(+), 57 deletions(-) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 33dafbe0e4..05a82a3628 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -471,7 +471,44 @@ void mmap_fork_start(void); void mmap_fork_end(int child); =20 /* main.c */ +extern int interp_dirfd; extern unsigned long guest_stack_size; +char *interp_prefix_path(const char *path); + +/* If PATH is absolute, attempt an operation first within interp_dirfd, + * using OPENAT_EXPR. If that fails with ENOENT, or if PATH is not + * absolute, only use NORMAL_EXPR. + */ +#define TRY_INTERP_FD(RET, PATH, OPENAT_EXPR, NORMAL_EXPR) \ + do { \ + if (interp_dirfd >=3D 0 && (PATH)[0] =3D=3D '/') { \ + RET =3D OPENAT_EXPR; \ + if (RET !=3D -1 || errno !=3D ENOENT) { \ + break; \ + } \ + } \ + RET =3D NORMAL_EXPR; \ + } while (0) + +/* If PATH is absolute, attempt an operation first with interp_prefix + * prefixed. If that fails with ENOENT, or if PATH is not absolute, + * only attempt with PATH. + */ +#define TRY_INTERP_PATH(RET, PATH, EXPR) \ + do { \ + char *new_##PATH =3D interp_prefix_path(PATH); \ + if (new_##PATH) { \ + __typeof(PATH) save_##PATH =3D PATH; \ + PATH =3D new_##PATH; \ + RET =3D EXPR; \ + free(new_##PATH); \ + PATH =3D save_##PATH; \ + if (RET !=3D -1 || errno !=3D ENOENT) { \ + break; \ + } \ + } \ + RET =3D EXPR; \ + } while (0) =20 /* user access */ =20 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 13bc78d0c8..abdf5bbf01 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -6,7 +6,6 @@ =20 #include "qemu.h" #include "disas/disas.h" -#include "qemu/path.h" =20 #ifdef _ARCH_PPC64 #undef ARCH_DLINFO @@ -2375,7 +2374,9 @@ static void load_elf_interp(const char *filename, str= uct image_info *info, { int fd, retval; =20 - fd =3D open(path(filename), O_RDONLY); + TRY_INTERP_FD(fd, filename, + openat(interp_dirfd, filename + 1, O_RDONLY), + open(filename, O_RDONLY)); if (fd < 0) { goto exit_perror; } diff --git a/linux-user/main.c b/linux-user/main.c index ee3f323c08..4e956fc00c 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -23,7 +23,6 @@ =20 #include "qapi/error.h" #include "qemu.h" -#include "qemu/path.h" #include "qemu/config-file.h" #include "qemu/cutils.h" #include "qemu/help_option.h" @@ -89,9 +88,27 @@ unsigned long reserved_va; =20 static void usage(int exitcode); =20 +int interp_dirfd; static const char *interp_prefix =3D CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; =20 +char *interp_prefix_path(const char *path) +{ + size_t i_len, p_len; + char *ret; + + if (interp_prefix =3D=3D NULL || path[0] !=3D '/') { + return NULL; + } + i_len =3D strlen(interp_prefix); + p_len =3D strlen(path) + 1; + ret =3D g_malloc(i_len + p_len); + + memcpy(ret, interp_prefix, i_len); + memcpy(ret + i_len, path, p_len); + return ret; +} + /* XXX: on x86 MAP_GROWSDOWN only works if ESP <=3D address + 32, so we allocate a bigger stack. Need a better solution, for example by remapping the process stack directly at the right place */ @@ -671,7 +688,12 @@ int main(int argc, char **argv, char **envp) memset(&bprm, 0, sizeof (bprm)); =20 /* Scan interp_prefix dir for replacement files. */ - init_paths(interp_prefix); + interp_dirfd =3D open(interp_prefix, O_CLOEXEC | O_DIRECTORY | O_PATH); + if (interp_dirfd >=3D 0) { + add_hostfd(interp_dirfd); + } else { + interp_prefix =3D NULL; + } =20 init_qemu_uname_release(); =20 diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d7513d5dac..b75dd9a5bc 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -19,7 +19,6 @@ #define _ATFILE_SOURCE #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "qemu/path.h" #include #include #include @@ -7401,7 +7400,10 @@ static abi_long do_name_to_handle_at(abi_long dirfd,= abi_long pathname, fh =3D g_malloc0(total_size); fh->handle_bytes =3D size; =20 - ret =3D get_errno(name_to_handle_at(dirfd, path(name), fh, &mid, flags= )); + TRY_INTERP_FD(ret, name, + name_to_handle_at(interp_dirfd, name + 1, fh, &mid, flag= s), + name_to_handle_at(dirfd, name, fh, &mid, flags)); + ret =3D get_errno(ret); unlock_user(name, pathname, 0); =20 /* man name_to_handle_at(2): @@ -7777,6 +7779,7 @@ static int do_openat(void *cpu_env, int dirfd, const = char *pathname, int flags, #endif { NULL, NULL, NULL } }; + int ret; =20 if (is_proc_myself(pathname, "exe")) { int execfd =3D qemu_getauxval(AT_EXECFD); @@ -7816,7 +7819,10 @@ static int do_openat(void *cpu_env, int dirfd, const= char *pathname, int flags, return fd; } =20 - return safe_openat(dirfd, path(pathname), flags, mode); + TRY_INTERP_FD(ret, pathname, + safe_openat(interp_dirfd, pathname + 1, flags, mode), + safe_openat(dirfd, pathname, flags, mode)); + return ret; } =20 #define TIMER_MAGIC 0x0caf0000 @@ -7969,6 +7975,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long = arg1, struct stat st; struct statfs stfs; void *p; + char *fn; =20 #if defined(DEBUG_ERESTARTSYS) /* Debug-only code for exercising the syscall-restart code paths @@ -8531,10 +8538,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, } else { tvp =3D NULL; } - if (!(p =3D lock_user_string(arg2))) + if (!(fn =3D lock_user_string(arg2))) { goto efault; - ret =3D get_errno(futimesat(arg1, path(p), tvp)); - unlock_user(p, arg2, 0); + } + TRY_INTERP_FD(ret, fn, + futimesat(interp_dirfd, fn + 1, tvp), + futimesat(arg1, fn, tvp)); + ret =3D get_errno(ret); + unlock_user(fn, arg2, 0); } break; #endif @@ -8548,10 +8559,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, #endif #ifdef TARGET_NR_access case TARGET_NR_access: - if (!(p =3D lock_user_string(arg1))) + if (!(fn =3D lock_user_string(arg1))) { goto efault; - ret =3D get_errno(access(path(p), arg2)); - unlock_user(p, arg1, 0); + } + TRY_INTERP_FD(ret, fn, + faccessat(interp_dirfd, fn + 1, arg2, 0), + access(fn, arg2)); + ret =3D get_errno(ret); + unlock_user(fn, arg1, 0); break; #endif #if defined(TARGET_NR_faccessat) && defined(__NR_faccessat) @@ -8559,10 +8574,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, if (is_hostfd(arg1)) { goto ebadf; } - if (!(p =3D lock_user_string(arg2))) + if (!(fn =3D lock_user_string(arg2))) { goto efault; - ret =3D get_errno(faccessat(arg1, p, arg3, 0)); - unlock_user(p, arg2, 0); + } + TRY_INTERP_FD(ret, fn, + faccessat(interp_dirfd, fn + 1, arg3, 0), + faccessat(arg1, fn, arg3, 0)); + ret =3D get_errno(ret); + unlock_user(fn, arg2, 0); break; #endif #ifdef TARGET_NR_nice /* not on alpha */ @@ -8713,10 +8732,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, if (arg1 =3D=3D 0) { ret =3D get_errno(acct(NULL)); } else { - if (!(p =3D lock_user_string(arg1))) + if (!(fn =3D lock_user_string(arg1))) { goto efault; - ret =3D get_errno(acct(path(p))); - unlock_user(p, arg1, 0); + } + TRY_INTERP_PATH(ret, fn, acct(fn)); + ret =3D get_errno(ret); + unlock_user(fn, arg1, 0); } break; #ifdef TARGET_NR_umount2 @@ -9507,14 +9528,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, case TARGET_NR_readlink: { void *p2; - p =3D lock_user_string(arg1); + fn =3D lock_user_string(arg1); p2 =3D lock_user(VERIFY_WRITE, arg2, arg3, 0); - if (!p || !p2) { + if (!fn || !p2) { ret =3D -TARGET_EFAULT; } else if (!arg3) { /* Short circuit this for the magic exe check. */ ret =3D -TARGET_EINVAL; - } else if (is_proc_myself((const char *)p, "exe")) { + } else if (is_proc_myself(fn, "exe")) { char real[PATH_MAX], *temp; temp =3D realpath(exec_path, real); /* Return value is # of bytes that we wrote to the buffer.= */ @@ -9528,10 +9549,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, memcpy(p2, real, ret); } } else { - ret =3D get_errno(readlink(path(p), p2, arg3)); + TRY_INTERP_FD(ret, fn, + readlinkat(interp_dirfd, fn + 1, p2, arg3), + readlink(fn, p2, arg3)); + ret =3D get_errno(ret); } unlock_user(p2, arg2, ret); - unlock_user(p, arg1, 0); + unlock_user(fn, arg1, 0); } break; #endif @@ -9541,20 +9565,23 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, goto ebadf; } else { void *p2; - p =3D lock_user_string(arg2); + fn =3D lock_user_string(arg2); p2 =3D lock_user(VERIFY_WRITE, arg3, arg4, 0); - if (!p || !p2) { + if (!fn || !p2) { ret =3D -TARGET_EFAULT; - } else if (is_proc_myself((const char *)p, "exe")) { + } else if (is_proc_myself(fn, "exe")) { char real[PATH_MAX], *temp; temp =3D realpath(exec_path, real); ret =3D temp =3D=3D NULL ? get_errno(-1) : strlen(real) ; snprintf((char *)p2, arg4, "%s", real); } else { - ret =3D get_errno(readlinkat(arg1, path(p), p2, arg4)); + TRY_INTERP_FD(ret, fn, + readlinkat(interp_dirfd, fn + 1, p2, arg4), + readlinkat(arg1, fn, p2, arg4)); + ret =3D get_errno(ret); } unlock_user(p2, arg3, ret); - unlock_user(p, arg2, 0); + unlock_user(fn, arg2, 0); } break; #endif @@ -9739,10 +9766,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, goto unimplemented; #endif case TARGET_NR_statfs: - if (!(p =3D lock_user_string(arg1))) + if (!(fn =3D lock_user_string(arg1))) { goto efault; - ret =3D get_errno(statfs(path(p), &stfs)); - unlock_user(p, arg1, 0); + } + TRY_INTERP_PATH(ret, fn, statfs(fn, &stfs)); + ret =3D get_errno(ret); + unlock_user(fn, arg1, 0); convert_statfs: if (!is_error(ret)) { struct target_statfs *target_stfs; @@ -9777,10 +9806,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_lon= g arg1, goto convert_statfs; #ifdef TARGET_NR_statfs64 case TARGET_NR_statfs64: - if (!(p =3D lock_user_string(arg1))) + if (!(fn =3D lock_user_string(arg1))) { goto efault; - ret =3D get_errno(statfs(path(p), &stfs)); - unlock_user(p, arg1, 0); + } + TRY_INTERP_PATH(ret, fn, statfs(fn, &stfs)); + ret =3D get_errno(ret); + unlock_user(fn, arg1, 0); convert_statfs64: if (!is_error(ret)) { struct target_statfs64 *target_stfs; @@ -10065,18 +10096,26 @@ abi_long do_syscall(void *cpu_env, int num, abi_l= ong arg1, break; #ifdef TARGET_NR_stat case TARGET_NR_stat: - if (!(p =3D lock_user_string(arg1))) + if (!(fn =3D lock_user_string(arg1))) { goto efault; - ret =3D get_errno(stat(path(p), &st)); - unlock_user(p, arg1, 0); + } + TRY_INTERP_FD(ret, fn, + fstatat(interp_dirfd, fn + 1, &st, 0), + stat(fn, &st)); + ret =3D get_errno(ret); + unlock_user(fn, arg1, 0); goto do_stat; #endif #ifdef TARGET_NR_lstat case TARGET_NR_lstat: - if (!(p =3D lock_user_string(arg1))) + if (!(fn =3D lock_user_string(arg1))) { goto efault; - ret =3D get_errno(lstat(path(p), &st)); - unlock_user(p, arg1, 0); + } + TRY_INTERP_FD(ret, fn, + fstatat(interp_dirfd, fn + 1, &st, AT_SYMLINK_NOFOLL= OW), + lstat(fn, &st)); + ret =3D get_errno(ret); + unlock_user(fn, arg1, 0); goto do_stat; #endif case TARGET_NR_fstat: @@ -11261,20 +11300,28 @@ abi_long do_syscall(void *cpu_env, int num, abi_l= ong arg1, #endif #ifdef TARGET_NR_stat64 case TARGET_NR_stat64: - if (!(p =3D lock_user_string(arg1))) + if (!(fn =3D lock_user_string(arg1))) { goto efault; - ret =3D get_errno(stat(path(p), &st)); - unlock_user(p, arg1, 0); + } + TRY_INTERP_FD(ret, fn, + fstatat(interp_dirfd, fn + 1, &st, 0), + stat(fn, &st)); + ret =3D get_errno(ret); + unlock_user(fn, arg1, 0); if (!is_error(ret)) ret =3D host_to_target_stat64(cpu_env, arg2, &st); break; #endif #ifdef TARGET_NR_lstat64 case TARGET_NR_lstat64: - if (!(p =3D lock_user_string(arg1))) + if (!(fn =3D lock_user_string(arg1))) { goto efault; - ret =3D get_errno(lstat(path(p), &st)); - unlock_user(p, arg1, 0); + } + TRY_INTERP_FD(ret, fn, + fstatat(interp_dirfd, fn + 1, &st, AT_SYMLINK_NOFOLL= OW), + lstat(fn, &st)); + ret =3D get_errno(ret); + unlock_user(fn, arg1, 0); if (!is_error(ret)) ret =3D host_to_target_stat64(cpu_env, arg2, &st); break; @@ -11299,9 +11346,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_lo= ng arg1, if (is_hostfd(arg1)) { goto ebadf; } - if (!(p =3D lock_user_string(arg2))) + if (!(fn =3D lock_user_string(arg2))) { goto efault; - ret =3D get_errno(fstatat(arg1, path(p), &st, arg4)); + } + TRY_INTERP_FD(ret, fn, + fstatat(interp_dirfd, fn + 1, &st, arg4), + fstatat(arg1, fn, &st, arg4)); + ret =3D get_errno(ret); + unlock_user(fn, arg2, 0); if (!is_error(ret)) ret =3D host_to_target_stat64(cpu_env, arg3, &st); break; @@ -12339,12 +12391,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_l= ong arg1, if (!arg2) ret =3D get_errno(sys_utimensat(arg1, NULL, tsp, arg4)); else { - if (!(p =3D lock_user_string(arg2))) { - ret =3D -TARGET_EFAULT; - goto fail; + if (!(fn =3D lock_user_string(arg2))) { + goto efault; } - ret =3D get_errno(sys_utimensat(arg1, path(p), tsp, arg4)); - unlock_user(p, arg2, 0); + TRY_INTERP_FD(ret, fn, + sys_utimensat(interp_dirfd, fn + 1, tsp, arg= 4), + sys_utimensat(arg1, fn, tsp, arg4)); + ret =3D get_errno(ret); + unlock_user(fn, arg2, 0); } } break; @@ -12376,9 +12430,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_lo= ng arg1, if (is_hostfd(arg1)) { goto ebadf; } - p =3D lock_user_string(arg2); - ret =3D get_errno(sys_inotify_add_watch(arg1, path(p), arg3)); - unlock_user(p, arg2, 0); + if (!(fn =3D lock_user_string(arg2))) { + goto efault; + } + TRY_INTERP_PATH(ret, fn, sys_inotify_add_watch(arg1, fn, arg3)); + ret =3D get_errno(ret); + unlock_user(fn, arg2, 0); break; #endif #if defined(TARGET_NR_inotify_rm_watch) && defined(__NR_inotify_rm_watch) --=20 2.17.0