commit:     1f7d3654498e17e0a91c83f57e6265e08628d5fe
Author:     Sv. Lockal <lockalsash <AT> gmail <DOT> com>
AuthorDate: Sat Jan 27 10:44:55 2024 +0000
Commit:     Mike Gilbert <floppym <AT> gentoo <DOT> org>
CommitDate: Sat Jan 27 18:04:16 2024 +0000
URL:        https://gitweb.gentoo.org/proj/sandbox.git/commit/?id=1f7d3654

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<size_t>(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 <lockalsash <AT> gmail.com>
Signed-off-by: Mike Gilbert <floppym <AT> gentoo.org>

 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) {

Reply via email to