Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 0 additions & 32 deletions src/runtime/procemu.c
Original file line number Diff line number Diff line change
Expand Up @@ -3436,38 +3436,6 @@ int proc_intercept_open(const guest_t *g,
return PROC_NOT_INTERCEPTED;
}

int proc_intercept_statfs(const char *path, struct statfs *out)
{
/* /proc/<our_pid>[/...] -> /proc/self[...], same alias as
* proc_intercept_open/proc_intercept_stat.
*/
char alias[LINUX_PATH_MAX];
int aliased = proc_alias_self(path, alias, sizeof(alias));
if (aliased < 0)
return -1;
if (aliased > 0)
return proc_intercept_statfs(alias, out);

/* /proc/self/fd and /proc/self/fdinfo are the only /proc nodes whose open
* path allocates per-call scratch state: proc_open_fd_scratch mkdtemp's a
* fresh directory and creates one placeholder file per live guest fd on
* every single open, purely so getdents has something to enumerate. A
* plain statfs() never enumerates the directory, so paying for that
* allocation just to fstatfs() the result and immediately discard it is
* wasted work -- and once PROC_SCRATCH_DIRS_MAX untracked opens
* accumulate, the atexit cleanup no longer knows about further dirs and
* they leak in /tmp permanently.
*
* Every scratch dir (like every other synthetic /proc node) is created
* under /tmp, so statfs("/tmp") reports identical filesystem info without
* allocating anything.
*/
if (strcmp(path, "/proc/self/fd") && strcmp(path, "/proc/self/fd/") &&
strcmp(path, "/proc/self/fdinfo") && strcmp(path, "/proc/self/fdinfo/"))
return PROC_NOT_INTERCEPTED;

return statfs("/tmp", out) < 0 ? -1 : 0;
}

int proc_intercept_stat(const char *path, struct stat *st)
{
Expand Down
10 changes: 0 additions & 10 deletions src/runtime/procemu.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,6 @@ int proc_intercept_readlink(const char *path, char *buf, size_t bufsiz);
*/
int proc_intercept_stat(const char *path, struct stat *mac_st);

/* Intercept statfs for /proc paths that would otherwise route through
* proc_intercept_open purely to obtain filesystem-level info, forcing
* allocation of scratch state (e.g. /proc/self/fd's per-open directory of
* placeholder files, one per live guest fd) that statfs() has no use for.
* Returns 0 if the filesystem info was synthesized (mac_st filled), or -2
* (PROC_NOT_INTERCEPTED) if the path is not intercepted (fall through to the
* normal open/fstatfs/close path).
*/
int proc_intercept_statfs(const char *path, struct statfs *mac_st);

/* Intercept writes to synthetic proc files that need stateful behavior.
* Returns 1 if handled (with *written_out set), 0 if not intercepted, or -1 on
* error with errno set.
Expand Down
55 changes: 46 additions & 9 deletions src/syscall/fs-stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,13 @@ static void fill_proc_statfs(linux_statfs_t *lin)
lin->f_frsize = 4096;
}

int64_t sys_statfs(guest_t *g, uint64_t path_gva, uint64_t buf_gva)
static int64_t sys_statfs_impl(guest_t *g,
const char *path,
uint64_t buf_gva,
int depth)
{
char path[LINUX_PATH_MAX];
if (guest_read_str_small(g, path_gva, path, sizeof(path)) < 0)
return -LINUX_EFAULT;
if (depth > 40)
return -LINUX_ELOOP;

path_translation_t tx;
if (path_translate_at(LINUX_AT_FDCWD, path, PATH_TR_NONE, &tx) < 0)
Expand All @@ -394,11 +396,37 @@ int64_t sys_statfs(guest_t *g, uint64_t path_gva, uint64_t buf_gva)
return -LINUX_ENOSYS;

if (statfs_path_is_proc(tx.intercept_path)) {
linux_statfs_t lin_st;
fill_proc_statfs(&lin_st);
if (guest_write_small(g, buf_gva, &lin_st, sizeof(lin_st)) < 0)
return -LINUX_EFAULT;
return 0;
if (proc_path_is_symlink(tx.intercept_path)) {
char link[LINUX_PATH_MAX];
int len = proc_intercept_readlink(tx.intercept_path, link,
sizeof(link) - 1);
if (len < 0)
return linux_errno();
link[len] = '\0';
return sys_statfs_impl(g, link, buf_gva, depth + 1);
}

struct stat mac_st;
int intercepted = proc_intercept_stat(tx.intercept_path, &mac_st);
if (intercepted == 0) {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
linux_statfs_t lin_st;
fill_proc_statfs(&lin_st);
if (guest_write_small(g, buf_gva, &lin_st, sizeof(lin_st)) < 0)
return -LINUX_EFAULT;
return 0;
}
if (intercepted == -1)
return linux_errno();

/* It might be /proc itself or a host-backed file/directory under /proc
*/
if (stat(tx.host_path, &mac_st) == 0) {
linux_statfs_t lin_st;
fill_proc_statfs(&lin_st);
if (guest_write_small(g, buf_gva, &lin_st, sizeof(lin_st)) < 0)
return -LINUX_EFAULT;
return 0;
}
}

struct statfs mac_st;
Expand All @@ -413,6 +441,15 @@ int64_t sys_statfs(guest_t *g, uint64_t path_gva, uint64_t buf_gva)
return 0;
}

int64_t sys_statfs(guest_t *g, uint64_t path_gva, uint64_t buf_gva)
{
char path[LINUX_PATH_MAX];
if (guest_read_str_small(g, path_gva, path, sizeof(path)) < 0)
return -LINUX_EFAULT;

return sys_statfs_impl(g, path, buf_gva, 0);
}

int64_t sys_fstatfs(guest_t *g, int fd, uint64_t buf_gva)
{
fd_entry_t snap;
Expand Down
2 changes: 1 addition & 1 deletion src/syscall/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ static const char *proc_stateful_file_path(const char *path)
return NULL;
}

static bool proc_path_is_symlink(const char *path)
bool proc_path_is_symlink(const char *path)
{
if (!path)
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/syscall/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,8 @@ int64_t host_iov_prepare_msg(guest_t *g,
int required_perms,
host_iov_buf_t *buf);

/* Release any heap spillover backing a host_iov_buf_t. Idempotent. */
void host_iov_free(host_iov_buf_t *buf);
bool proc_path_is_symlink(const char *path);

/* Read a guest path string with small-buffer optimization.
*
Expand Down
4 changes: 4 additions & 0 deletions src/syscall/path.c
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,10 @@ static int dirfd_guest_base_path(guest_fd_t dirfd, char *out, size_t outsz)
return -1;
}
if (snap.proc_path[0] != '\0') {
if (snap.type != FD_DIR) {
errno = ENOTDIR;
return -1;
}
size_t len = str_copy_trunc(out, snap.proc_path, outsz);
if (len >= outsz) {
errno = ENAMETOOLONG;
Expand Down
55 changes: 46 additions & 9 deletions tests/test-proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,28 +208,35 @@ int main(void)
TEST("statfs /proc");
{
struct statfs st;
if (statfs("/proc", &st) < 0)
if (statfs("/proc", &st) < 0) {
FAIL("statfs failed");
else
PASS();
} else {
EXPECT_TRUE(st.f_type == 0x9fa0,
"statfs /proc f_type not PROC_SUPER_MAGIC");
}
}

TEST("statfs /proc/ (trailing slash)");
{
struct statfs st;
if (statfs("/proc/", &st) < 0)
if (statfs("/proc/", &st) < 0) {
FAIL("statfs failed");
else
PASS();
} else {
EXPECT_TRUE(st.f_type == 0x9fa0,
"statfs /proc/ f_type not PROC_SUPER_MAGIC");
}
}

TEST("statfs /proc/self/cmdline");
{
struct statfs st;
if (statfs("/proc/self/cmdline", &st) < 0)
if (statfs("/proc/self/cmdline", &st) < 0) {
FAIL("statfs failed");
else
PASS();
} else {
EXPECT_TRUE(
st.f_type == 0x9fa0,
"statfs /proc/self/cmdline f_type not PROC_SUPER_MAGIC");
}
}

TEST("statfs matches fstatfs for /proc/self/cmdline");
Expand All @@ -245,6 +252,36 @@ int main(void)
} else {
EXPECT_TRUE(path_st.f_type == fd_st.f_type,
"statfs/fstatfs f_type mismatch");
EXPECT_TRUE(path_st.f_type == 0x9fa0,
"statfs f_type not PROC_SUPER_MAGIC");
}
if (fd >= 0)
close(fd);
}

TEST("statfs follows /proc symlinks");
{
struct statfs exe_st, fd_st;
int fd = open("/proc/self/exe", O_RDONLY);
if (fd < 0) {
FAIL("open /proc/self/exe failed");
} else if (statfs("/proc/self/exe", &exe_st) < 0) {
FAIL("statfs /proc/self/exe failed");
} else {
/* /proc/self/exe points to the host binary file, which resides on a
* real filesystem, not procfs. */
EXPECT_TRUE(exe_st.f_type != 0x9fa0,
"statfs /proc/self/exe returned PROC_SUPER_MAGIC");

/* Build /proc/self/fd/N path */
char fd_path[64];
snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd);
if (statfs(fd_path, &fd_st) < 0) {
FAIL("statfs /proc/self/fd/N failed");
} else {
EXPECT_TRUE(fd_st.f_type != 0x9fa0,
"statfs /proc/self/fd/N returned PROC_SUPER_MAGIC");
}
}
if (fd >= 0)
close(fd);
Expand Down
Loading