commit:     9b81814db56a02384e40f6e19576a4e9777628ac
Author:     Mike Frysinger <vapier <AT> gentoo <DOT> org>
AuthorDate: Sat Oct 23 06:39:35 2021 +0000
Commit:     Mike Frysinger <vapier <AT> gentoo <DOT> org>
CommitDate: Sat Oct 23 22:18:03 2021 +0000
URL:        https://gitweb.gentoo.org/proj/sandbox.git/commit/?id=9b81814d

libsandbox: fix flags extraction for a few syscalls

While many syscalls follow similar patterns for dirfd & path handling,
the flags argument is less consistent -- it tends to be last with all
other arguments in between.  As a result, a few syscalls were pulling
the wrong argument for the flags settings:
* fchmodat: the syscall interface has no flags at all
* fchownat: the flags come after uid & gid
* utimensat: the flags come after the timespec

These syscalls haven't been a problem in practice because no one ever
tries to chmod/chown/utimes on symlinks themselves.

Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org>

 libsandbox/trace.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/libsandbox/trace.c b/libsandbox/trace.c
index 89bd591..7660d47 100644
--- a/libsandbox/trace.c
+++ b/libsandbox/trace.c
@@ -217,6 +217,7 @@ struct syscall_state {
        bool (*pre_check)(const char *func, const char *pathname, int dirfd);
 };
 
+/* Check syscall that only takes a path as its |ibase| argument. */
 static bool _trace_check_syscall_C(struct syscall_state *state, int ibase)
 {
        char *path = do_peekstr(trace_arg(state->regs, ibase));
@@ -233,6 +234,7 @@ static bool _trace_check_syscall_C(struct syscall_state 
*state, int ibase)
        free(path);
        return ret;
 }
+/* Check syscall that only takes a path as its first argument. */
 static bool trace_check_syscall_C(struct syscall_state *state)
 {
        return _trace_check_syscall_C(state, 1);
@@ -255,20 +257,24 @@ static bool __trace_check_syscall_DCF(struct 
syscall_state *state, int ibase, in
        free(path);
        return ret;
 }
-static bool _trace_check_syscall_DCF(struct syscall_state *state, int ibase)
+/* Check syscall that takes a dirfd & path starting at |ibase| argument, and 
flags at |fbase|. */
+static bool _trace_check_syscall_DCF(struct syscall_state *state, int ibase, 
int fbase)
 {
-       int flags = trace_arg(state->regs, ibase + 2);
+       int flags = trace_arg(state->regs, fbase);
        return __trace_check_syscall_DCF(state, ibase, flags);
 }
+/* Check syscall that takes a dirfd, path, and flags as its first 3 arguments. 
*/
 static bool trace_check_syscall_DCF(struct syscall_state *state)
 {
-       return _trace_check_syscall_DCF(state, 1);
+       return _trace_check_syscall_DCF(state, 1, 3);
 }
 
+/* Check syscall that takes a dirfd & path starting at |ibase| argument. */
 static bool _trace_check_syscall_DC(struct syscall_state *state, int ibase)
 {
        return __trace_check_syscall_DCF(state, ibase, 0);
 }
+/* Check syscall that takes a dirfd & path as its first 2 arguments (but no 
flags). */
 static bool trace_check_syscall_DC(struct syscall_state *state)
 {
        return _trace_check_syscall_DC(state, 1);
@@ -311,12 +317,13 @@ static bool trace_check_syscall(const struct 
syscall_entry *se, void *regs)
        else if (nr == SB_NR_CHMOD)     return  trace_check_syscall_C  (&state);
        else if (nr == SB_NR_CHOWN)     return  trace_check_syscall_C  (&state);
        else if (nr == SB_NR_CREAT)     return  trace_check_syscall_C  (&state);
-       else if (nr == SB_NR_FCHMODAT)  return  trace_check_syscall_DCF(&state);
-       else if (nr == SB_NR_FCHOWNAT)  return  trace_check_syscall_DCF(&state);
+       /* NB: Linux syscall does not have a flags argument. */
+       else if (nr == SB_NR_FCHMODAT)  return  trace_check_syscall_DC (&state);
+       else if (nr == SB_NR_FCHOWNAT)  return _trace_check_syscall_DCF(&state, 
1, 5);
        else if (nr == SB_NR_FUTIMESAT) return  trace_check_syscall_DC (&state);
        else if (nr == SB_NR_LCHOWN)    return  trace_check_syscall_C  (&state);
        else if (nr == SB_NR_LINK)      return _trace_check_syscall_C  (&state, 
2);
-       else if (nr == SB_NR_LINKAT)    return _trace_check_syscall_DCF(&state, 
3);
+       else if (nr == SB_NR_LINKAT)    return _trace_check_syscall_DCF(&state, 
3, 5);
        else if (nr == SB_NR_MKDIR)     return  trace_check_syscall_C  (&state);
        else if (nr == SB_NR_MKDIRAT)   return  trace_check_syscall_DC (&state);
        else if (nr == SB_NR_MKNOD)     return  trace_check_syscall_C  (&state);
@@ -336,7 +343,7 @@ static bool trace_check_syscall(const struct syscall_entry 
*se, void *regs)
        else if (nr == SB_NR_UNLINKAT)  return  trace_check_syscall_DCF(&state);
        else if (nr == SB_NR_UTIME)     return  trace_check_syscall_C  (&state);
        else if (nr == SB_NR_UTIMES)    return  trace_check_syscall_C  (&state);
-       else if (nr == SB_NR_UTIMENSAT) return _trace_check_syscall_DCF(&state, 
1);
+       else if (nr == SB_NR_UTIMENSAT) return _trace_check_syscall_DCF(&state, 
1, 4);
 
        else if (nr == SB_NR_ACCESS) {
                char *path = do_peekstr(trace_arg(regs, 1));

Reply via email to