From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Shu-Chun Weng <scw@google.com>
Cc: qemu-devel@nongnu.org, Laurent Vivier <laurent@vivier.eu>,
Richard Henderson <richard.henderson@linaro.org>
Subject: Re: [PATCH 2/2] linux-user: Fix openat() emulation to not modify atime
Date: Mon, 4 Dec 2023 16:58:34 +0000 [thread overview]
Message-ID: <ZW4FOs3LwSyVD7Xf@redhat.com> (raw)
In-Reply-To: <20231201032140.2470599-3-scw@google.com>
On Thu, Nov 30, 2023 at 07:21:40PM -0800, Shu-Chun Weng wrote:
> Commit b8002058 strengthened openat()'s /proc detection by calling
> realpath(3) on the given path, which allows various paths and symlinks
> that points to the /proc file system to be intercepted correctly.
>
> Using realpath(3), though, has a side effect that it reads the symlinks
> along the way, and thus changes their atime. The results in the
> following code snippet already get ~now instead of the real atime:
>
> int fd = open("/path/to/a/symlink", O_PATH | O_NOFOLLOW);
> struct stat st;
> fstat(fd, st);
> return st.st_atime;
>
> This change opens a path that doesn't appear to be part of /proc
> directly and checks the destination of /proc/self/fd/n to determine if
> it actually refers to a file in /proc.
>
> Neither this nor the existing code works with symlinks or indirect paths
> (e.g. /tmp/../proc/self/exe) that points to /proc/self/exe because it
> is itself a symlink, and both realpath(3) and /proc/self/fd/n will
> resolve into the location of QEMU.
I wonder if we can detect that by opening with O_NOFOLLOW, then
calling fstatfs() on the FD, and checking f_type == PROCFS_SUPER_MAGIC
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index e384e14248..25e2cda10a 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -8308,8 +8308,6 @@ static int open_net_route(CPUArchState *cpu_env, int fd)
> 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);
> @@ -8333,13 +8331,39 @@ int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname,
> #endif
> { NULL, NULL, NULL }
> };
> + char pathname[PATH_MAX];
>
> - /* 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;
> + if (strncmp(fname, "/proc/", 6) == 0) {
> + pstrcpy(pathname, sizeof(pathname), fname);
> } else {
> - pathname = fname;
> + char procpath[PATH_MAX];
> + int fd, n;
> +
> + if (safe) {
> + fd = safe_openat(dirfd, path(fname), flags, mode);
> + } else {
> + fd = openat(dirfd, path(fname), flags, mode);
> + }
> + if (fd < 0) {
> + return fd;
> + }
> +
> + /*
> + * Try to get the real path of the file we just opened. We avoid calling
> + * `realpath(3)` because it calls `readlink(2)` on symlinks which
> + * changes their atime. Note that since `/proc/self/exe` is a symlink,
> + * `pathname` will never resolves to it (neither will `realpath(3)`).
> + * That's why we check `fname` against the "/proc/" prefix first.
> + */
> + snprintf(procpath, sizeof(procpath), "/proc/self/fd/%d", fd);
g_strdup_printf() + g_autofree to avoid this PATH_MAX buffer
> + n = readlink(procpath, pathname, sizeof(pathname));
> + pathname[n < sizeof(pathname) ? n : sizeof(pathname)] = '\0';
If you call lstat() then sb_size will tell you how big the buffer
needs to be for a subsequent readlink(), whcih can be allocated
on the heap and released with g_autofree, avoiding the othuer PATH_MAX
buffer
> +
> + /* if this is not a file from /proc/ filesystem, the fd is good as-is */
> + if (strncmp(pathname, "/proc/", 6) != 0) {
> + return fd;
> + }
> + close(fd);
> }
>
> if (is_proc_myself(pathname, "exe")) {
> @@ -8390,9 +8414,9 @@ int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *fname,
> }
>
> if (safe) {
> - return safe_openat(dirfd, path(pathname), flags, mode);
> + return safe_openat(dirfd, pathname, flags, mode);
> } else {
> - return openat(dirfd, path(pathname), flags, mode);
> + return openat(dirfd, pathname, flags, mode);
> }
> }
>
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
next prev parent reply other threads:[~2023-12-04 16:59 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-01 3:21 [PATCH 0/2] linux-user: openat() fixes Shu-Chun Weng
2023-12-01 3:21 ` [PATCH 1/2] linux-user: Define TARGET_O_LARGEFILE for aarch64 Shu-Chun Weng
2023-12-01 12:38 ` [PATCH-for-8.2? " Philippe Mathieu-Daudé
2023-12-03 13:28 ` [PATCH " Laurent Vivier
2023-12-01 3:21 ` [PATCH 2/2] linux-user: Fix openat() emulation to not modify atime Shu-Chun Weng
2023-12-01 12:42 ` Philippe Mathieu-Daudé
2023-12-01 18:51 ` Shu-Chun Weng
2023-12-04 13:39 ` Philippe Mathieu-Daudé
2023-12-04 15:34 ` Stefan Hajnoczi
2023-12-01 17:09 ` Helge Deller
2023-12-04 16:58 ` Daniel P. Berrangé [this message]
2023-12-08 20:52 ` Shu-Chun Weng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZW4FOs3LwSyVD7Xf@redhat.com \
--to=berrange@redhat.com \
--cc=laurent@vivier.eu \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=scw@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.