From 1f7d3654498e17e0a91c83f57e6265e08628d5fe Mon Sep 17 00:00:00 2001 From: "Sv. Lockal" Date: Sat, 27 Jan 2024 10:44:55 +0000 Subject: Fix SIGSEGV in gtest death tests due to small stack In https://github.com/google/googletest/blob/v1.14.0/googletest/src/gtest-death-test.cc#L1307 on x86-64 gtest sallocates 8192 bytes for `clone`: ``` static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { const auto stack_size = static_cast(getpagesize() * 2); ... child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); ``` After that attempt to call execv is intercepted by libsandbox.so, which allocates 8192 + more bytes multiple times on stack, causing SIGSEGV (instead of expected types of crashes). This PR moves all allocations for related function to heap, so now call path fits `getpagesize() * 2` with large margin. Bug: https://bugs.gentoo.org/923013 Closes: https://github.com/gentoo/sandbox/pull/26 Signed-off-by: Sv. Lockal Signed-off-by: Mike Gilbert --- libsandbox/libsandbox.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/libsandbox/libsandbox.c b/libsandbox/libsandbox.c index 9705db1..acd8585 100644 --- a/libsandbox/libsandbox.c +++ b/libsandbox/libsandbox.c @@ -132,7 +132,8 @@ int resolve_dirfd_path(int dirfd, const char *path, char *resolved_path, save_errno(); - char fd_path[SB_PATH_MAX]; + char *fd_path = xmalloc(SB_PATH_MAX * sizeof(char)); + size_t at_len = resolved_path_len - 1 - 1 - (path ? strlen(path) : 0); if (trace_pid) { sprintf(fd_path, "/proc/%i/fd/%i", trace_pid, dirfd); @@ -148,12 +149,14 @@ int resolve_dirfd_path(int dirfd, const char *path, char *resolved_path, /* see comments at end of check_syscall() */ if (errno_is_too_long()) { restore_errno(); + free(fd_path); return 2; } sb_debug_dyn("AT_FD LOOKUP fail: %s: %s\n", fd_path, strerror(errno)); /* If the fd isn't found, some guys (glibc) expect errno */ if (errno == ENOENT) errno = EBADF; + free(fd_path); return -1; } resolved_path[ret] = '/'; @@ -162,6 +165,7 @@ int resolve_dirfd_path(int dirfd, const char *path, char *resolved_path, strcat(resolved_path, path); restore_errno(); + free(fd_path); return 0; } @@ -286,7 +290,7 @@ static char *resolve_path(const char *path, int follow_link) } if (!ret) { - char tmp_str1[SB_PATH_MAX]; + char *tmp_str1 = xmalloc(SB_PATH_MAX * sizeof(char)); snprintf(tmp_str1, SB_PATH_MAX, "%s", path); dname = dirname(tmp_str1); @@ -304,7 +308,7 @@ static char *resolve_path(const char *path, int follow_link) filtered_path = NULL; } } else { - char tmp_str2[SB_PATH_MAX]; + char *tmp_str2 = xmalloc(SB_PATH_MAX * sizeof(char)); /* OK, now add the basename to keep our access * checking happy (don't want '/usr/lib' if we * tried to do something with non-existing @@ -316,7 +320,10 @@ static char *resolve_path(const char *path, int follow_link) snprintf(filtered_path + len, SB_PATH_MAX - len, "%s%s", (filtered_path[len - 1] != '/') ? "/" : "", bname); + free(tmp_str2); } + + free(tmp_str1); } } @@ -1034,10 +1041,24 @@ bool is_sandbox_on(void) return result; } +static int resolve_dirfd_path_alloc(int dirfd, const char *path, char **resolved_path) +{ + size_t resolved_path_size = SB_PATH_MAX * sizeof(char); + *resolved_path = xmalloc(resolved_path_size); + int result = resolve_dirfd_path(dirfd, path, *resolved_path, resolved_path_size); + + if (result) { + free(*resolved_path); + *resolved_path = NULL; + } + + return result; +} + bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, int flags) { int result; - char at_file_buf[SB_PATH_MAX]; + char *at_file_buf; /* Some funcs operate on a fd directly and so filename is NULL, but * the rest should get rejected as "file/directory does not exist". @@ -1056,7 +1077,7 @@ bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, in } } - switch (resolve_dirfd_path(dirfd, file, at_file_buf, sizeof(at_file_buf))) { + switch (resolve_dirfd_path_alloc(dirfd, file, &at_file_buf)) { case -1: return false; case 0: file = at_file_buf; break; case 2: return true; @@ -1079,6 +1100,9 @@ bool before_syscall(int dirfd, int sb_nr, const char *func, const char *file, in result = check_syscall(&sbcontext, sb_nr, func, file, flags); + if (at_file_buf) + free(at_file_buf); + sb_unlock(); if (0 == result) { -- cgit v1.2.3-65-gdbad