From nobody Sat Nov 1 22:27:55 2025 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=chromium.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527643743905615.7417926618398; Tue, 29 May 2018 18:29:03 -0700 (PDT) Received: from localhost ([::1]:35730 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fNpvC-0003WR-M5 for importer@patchew.org; Tue, 29 May 2018 21:29:02 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57951) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fNoIF-0002i8-N7 for qemu-devel@nongnu.org; Tue, 29 May 2018 19:44:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fNoIE-00066c-9f for qemu-devel@nongnu.org; Tue, 29 May 2018 19:44:43 -0400 Received: from mail-pg0-x242.google.com ([2607:f8b0:400e:c05::242]:37934) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1fNoIE-00065r-10 for qemu-devel@nongnu.org; Tue, 29 May 2018 19:44:42 -0400 Received: by mail-pg0-x242.google.com with SMTP id c9-v6so4446080pgf.5 for ; Tue, 29 May 2018 16:44:41 -0700 (PDT) Received: from evgreen2.mtv.corp.google.com ([2620:0:1000:1511:116f:8bf3:133b:f7fd]) by smtp.gmail.com with ESMTPSA id h65-v6sm79887083pfj.54.2018.05.29.16.44.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 29 May 2018 16:44:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id; bh=DJzcQTaLuxp584u/U6KIbahjH0DtAtdyzx7usUXxJG8=; b=a+zcak7Kp4VLf1n6sGVyLryD3eXGSx9yBjjwd4X4oXlVCOPa8Lp8cTcEVt0qVH8Gx0 kMSarLMIIzgmWkoJqkuj0XmV0ykd/l6sMzFqydkjxjTCf6Z8wjzAfu4Gka/y61fHbfVx /SSw9fhDb15aGiv42zWkmOVXznfrop6Zl0GBQ= 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; bh=DJzcQTaLuxp584u/U6KIbahjH0DtAtdyzx7usUXxJG8=; b=j+kkoYBjO4qL/39Rmnki7xUnKm7VxQywW3+8U9oHmqF9ZRILwsNXmJuANei4KcOGSO XjqZXI2tgx+8xLpcNtX4f0j5NLpd98A5RJ2DnbtR8QJb2fJIaqsRam/FZh4MLAP02yik t57GqhNQV5lRUXjmhqGcV0OmzC7wUg9M9F+wLAoZAGVw8s+LQBQgyUbBq43yXjtxGo2d zG2/jJLixN+yQ7vXJoHvwt5kdEchniwscYGjs17QWNHAfOJ2dxZsqpk5jSaW+dWnmTQ+ 3sbj9j2r9fatvijBBu+Lx5wc95ASx5nw5xC2YzjlgXlry9gByU7tW9DF+3u2CbN/FldR j59A== X-Gm-Message-State: ALKqPwf+jrOhgRa8TSqplA9FJ9nMHHTsFFESTK5m1RaVnVxrjVArg5rV k+6p4bg6QCg2xyRiRzeboZkd55xAXks= X-Google-Smtp-Source: ADUXVKKoNGnZYE6QrIEzGRPDz3zIdzhiPpKiEq531wOo9AW08g9WhPfzEDpZHY8Ly08kQRth5k4UUw== X-Received: by 2002:a65:490d:: with SMTP id p13-v6mr358188pgs.84.1527637480440; Tue, 29 May 2018 16:44:40 -0700 (PDT) From: Evan Green To: qemu-devel@nongnu.org Date: Tue, 29 May 2018 16:44:09 -0700 Message-Id: <20180529234409.27023-1-evgreen@chromium.org> X-Mailer: git-send-email 2.13.5 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:400e:c05::242 X-Mailman-Approved-At: Tue, 29 May 2018 21:26:27 -0400 Subject: [Qemu-devel] [PATCH] Fix hang with -L and symlink loop 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: Evan Green 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" When using -L with Linux app emulation, there is an issue in init_paths where Qemu will get lost exploring a directory tree with a symlink loop in it. This causes Qemu to hang, and eventually consume all memory in the system. Qemu's code for pre-exploring the entire directory tree is both error-prone and slow. Instead, this changes uses faccessat, which both avoids the symlink loop (since the entire directory space isn't being explored up front), and likely speeds things up a bit. Partial credit goes to Richard Henderson, as it was only after staring at his patch [1] that I wrote mine. [1] https://patchwork.kernel.org/patch/9512083/ Signed-off-by: Evan Green Reviewed-by: Richard Henderson --- Since the path() function returns a final path, I used a thread-local global to contain the glued-together path. From my examination of the callers of path(), this is sufficient. The thread-localness of it may not actually be necessary, but it seemed safer. I can remove the __thread attribute if desired. --- util/path.c | 159 ++++++++------------------------------------------------= ---- 1 file changed, 21 insertions(+), 138 deletions(-) diff --git a/util/path.c b/util/path.c index 7f9fc272fb..b9a0e8ef47 100644 --- a/util/path.c +++ b/util/path.c @@ -4,133 +4,13 @@ The assumption is that this area does not change. */ #include "qemu/osdep.h" -#include -#include +#include #include "qemu/cutils.h" #include "qemu/path.h" =20 -struct pathelem -{ - /* Name of this, eg. lib */ - char *name; - /* Full path name, eg. /usr/gnemul/x86-linux/lib. */ - char *pathname; - struct pathelem *parent; - /* Children */ - unsigned int num_entries; - struct pathelem *entries[0]; -}; - -static struct pathelem *base; - -/* First N chars of S1 match S2, and S2 is N chars long. */ -static int strneq(const char *s1, unsigned int n, const char *s2) -{ - unsigned int i; - - for (i =3D 0; i < n; i++) - if (s1[i] !=3D s2[i]) - return 0; - return s2[i] =3D=3D 0; -} - -static struct pathelem *add_entry(struct pathelem *root, const char *name, - unsigned type); - -static struct pathelem *new_entry(const char *root, - struct pathelem *parent, - const char *name) -{ - struct pathelem *new =3D g_malloc(sizeof(*new)); - new->name =3D g_strdup(name); - new->pathname =3D g_strdup_printf("%s/%s", root, name); - new->num_entries =3D 0; - return new; -} - -#define streq(a,b) (strcmp((a), (b)) =3D=3D 0) - -/* Not all systems provide this feature */ -#if defined(DT_DIR) && defined(DT_UNKNOWN) && defined(DT_LNK) -# define dirent_type(dirent) ((dirent)->d_type) -# define is_dir_maybe(type) \ - ((type) =3D=3D DT_DIR || (type) =3D=3D DT_UNKNOWN || (type) =3D=3D DT_= LNK) -#else -# define dirent_type(dirent) (1) -# define is_dir_maybe(type) (type) -#endif - -static struct pathelem *add_dir_maybe(struct pathelem *path) -{ - DIR *dir; - - if ((dir =3D opendir(path->pathname)) !=3D NULL) { - struct dirent *dirent; - - while ((dirent =3D readdir(dir)) !=3D NULL) { - if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){ - path =3D add_entry(path, dirent->d_name, dirent_type(diren= t)); - } - } - closedir(dir); - } - return path; -} - -static struct pathelem *add_entry(struct pathelem *root, const char *name, - unsigned type) -{ - struct pathelem **e; - - root->num_entries++; - - root =3D g_realloc(root, sizeof(*root) - + sizeof(root->entries[0])*root->num_entries); - e =3D &root->entries[root->num_entries-1]; - - *e =3D new_entry(root->pathname, root, name); - if (is_dir_maybe(type)) { - *e =3D add_dir_maybe(*e); - } - - return root; -} - -/* This needs to be done after tree is stabilized (ie. no more reallocs!).= */ -static void set_parents(struct pathelem *child, struct pathelem *parent) -{ - unsigned int i; - - child->parent =3D parent; - for (i =3D 0; i < child->num_entries; i++) - set_parents(child->entries[i], child); -} - -/* FIXME: Doesn't handle DIR/.. where DIR is not in emulated dir. */ -static const char * -follow_path(const struct pathelem *cursor, const char *name) -{ - unsigned int i, namelen; - - name +=3D strspn(name, "/"); - namelen =3D strcspn(name, "/"); - - if (namelen =3D=3D 0) - return cursor->pathname; - - if (strneq(name, namelen, "..")) - return follow_path(cursor->parent, name + namelen); - - if (strneq(name, namelen, ".")) - return follow_path(cursor, name + namelen); - - for (i =3D 0; i < cursor->num_entries; i++) - if (strneq(name, namelen, cursor->entries[i]->name)) - return follow_path(cursor->entries[i], name + namelen); - - /* Not found */ - return NULL; -} +static const char *pathprefix; +int pathprefixfd =3D -1; +__thread char gluedpath[PATH_MAX]; =20 void init_paths(const char *prefix) { @@ -150,28 +30,31 @@ void init_paths(const char *prefix) pstrcat(pref_buf, pref_buf_len, "/"); pstrcat(pref_buf, pref_buf_len, prefix); free(cwd); - } else - pstrcpy(pref_buf, sizeof(pref_buf), prefix + 1); - - base =3D new_entry("", NULL, pref_buf); - base =3D add_dir_maybe(base); - if (base->num_entries =3D=3D 0) { - g_free(base->pathname); - g_free(base->name); - g_free(base); - base =3D NULL; - } else { - set_parents(base, base); + prefix =3D strdup(pref_buf); + if (!prefix) { + abort(); + } } + + pathprefix =3D prefix; + pathprefixfd =3D open(pathprefix, O_RDONLY | O_DIRECTORY | O_CLOEXEC); } =20 /* Look for path in emulation dir, otherwise return name. */ const char *path(const char *name) { + const char *relname; /* Only do absolute paths: quick and dirty, but should mostly be OK. Could do relative by tracking cwd. */ - if (!base || !name || name[0] !=3D '/') + if ((pathprefixfd < 0) || !name || name[0] !=3D '/') { return name; + } + + relname =3D name + strspn(name, "/"); + if (faccessat(pathprefixfd, relname, R_OK, AT_EACCESS) =3D=3D 0) { + snprintf(gluedpath, sizeof(gluedpath), "%s%s", pathprefix, name); + return gluedpath; + } =20 - return follow_path(base, name) ?: name; + return name; } --=20 2.13.5