Commit 1009d6e7 authored by Konrad Borowski's avatar Konrad Borowski
Browse files

nixos/wrappers: create a new assert macro that always asserts

C's assert macro only works when NDEBUG is undefined. Previously
NDEBUG was undefined incorrectly which meant that the assert
macros in wrapper.c did not work.
parent 0796461e
Loading
Loading
Loading
Loading
+20 −17
Original line number Diff line number Diff line
@@ -2,12 +2,12 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdnoreturn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <fcntl.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>
#include <linux/capability.h>
#include <sys/prctl.h>
@@ -16,10 +16,7 @@
#include <syscall.h>
#include <byteswap.h>

// Make sure assertions are not compiled out, we use them to codify
// invariants about this program and we want it to fail fast and
// loudly if they are violated.
#undef NDEBUG
#define ASSERT(expr) ((expr) ? (void) 0 : assert_failure(#expr))

extern char **environ;

@@ -38,6 +35,12 @@ static char *wrapper_debug = "WRAPPER_DEBUG";
#define LE32_TO_H(x) (x)
#endif

static noreturn void assert_failure(const char *assertion) {
    fprintf(stderr, "Assertion `%s` in NixOS's wrapper.c failed.\n", assertion);
    fflush(stderr);
    abort();
}

int get_last_cap(unsigned *last_cap) {
    FILE* file = fopen("/proc/sys/kernel/cap_last_cap", "r");
    if (file == NULL) {
@@ -181,36 +184,36 @@ int main(int argc, char **argv) {
    int len = strlen(wrapper_dir);
    if (len > 0 && '/' == wrapper_dir[len - 1])
      --len;
    assert(!strncmp(self_path, wrapper_dir, len));
    assert('/' == wrapper_dir[0]);
    assert('/' == self_path[len]);
    ASSERT(!strncmp(self_path, wrapper_dir, len));
    ASSERT('/' == wrapper_dir[0]);
    ASSERT('/' == self_path[len]);

    // Make *really* *really* sure that we were executed as
    // `self_path', and not, say, as some other setuid program. That
    // is, our effective uid/gid should match the uid/gid of
    // `self_path'.
    struct stat st;
    assert(lstat(self_path, &st) != -1);
    ASSERT(lstat(self_path, &st) != -1);

    assert(!(st.st_mode & S_ISUID) || (st.st_uid == geteuid()));
    assert(!(st.st_mode & S_ISGID) || (st.st_gid == getegid()));
    ASSERT(!(st.st_mode & S_ISUID) || (st.st_uid == geteuid()));
    ASSERT(!(st.st_mode & S_ISGID) || (st.st_gid == getegid()));

    // And, of course, we shouldn't be writable.
    assert(!(st.st_mode & (S_IWGRP | S_IWOTH)));
    ASSERT(!(st.st_mode & (S_IWGRP | S_IWOTH)));

    // Read the path of the real (wrapped) program from <self>.real.
    char real_fn[PATH_MAX + 10];
    int real_fn_size = snprintf(real_fn, sizeof(real_fn), "%s.real", self_path);
    assert(real_fn_size < sizeof(real_fn));
    ASSERT(real_fn_size < sizeof(real_fn));

    int fd_self = open(real_fn, O_RDONLY);
    assert(fd_self != -1);
    ASSERT(fd_self != -1);

    char source_prog[PATH_MAX];
    len = read(fd_self, source_prog, PATH_MAX);
    assert(len != -1);
    assert(len < sizeof(source_prog));
    assert(len > 0);
    ASSERT(len != -1);
    ASSERT(len < sizeof(source_prog));
    ASSERT(len > 0);
    source_prog[len] = 0;

    close(fd_self);