1*7baee96fSChristian Brauner // SPDX-License-Identifier: GPL-2.0 2*7baee96fSChristian Brauner /* 3*7baee96fSChristian Brauner * Asserting interpreter for the transparent binfmt_misc mode. It runs in 4*7baee96fSChristian Brauner * place of the dispatched binary and verifies the identity the kernel 5*7baee96fSChristian Brauner * constructed: the aux vector contract, the exe link, argv, cmdline, comm 6*7baee96fSChristian Brauner * and the write denial on the binary. BINFMT_TEST_BINARY names the binary; 7*7baee96fSChristian Brauner * the harness execs it with the arguments "argone argtwo". Prints 8*7baee96fSChristian Brauner * TRANSPARENT_OK and exits 0 when every check holds. 9*7baee96fSChristian Brauner */ 10*7baee96fSChristian Brauner #define _GNU_SOURCE 11*7baee96fSChristian Brauner #include <errno.h> 12*7baee96fSChristian Brauner #include <fcntl.h> 13*7baee96fSChristian Brauner #include <limits.h> 14*7baee96fSChristian Brauner #include <stdio.h> 15*7baee96fSChristian Brauner #include <stdlib.h> 16*7baee96fSChristian Brauner #include <string.h> 17*7baee96fSChristian Brauner #include <sys/auxv.h> 18*7baee96fSChristian Brauner #include <sys/stat.h> 19*7baee96fSChristian Brauner #include <unistd.h> 20*7baee96fSChristian Brauner 21*7baee96fSChristian Brauner #include "binfmt_misc_common.h" 22*7baee96fSChristian Brauner #include "kselftest.h" 23*7baee96fSChristian Brauner 24*7baee96fSChristian Brauner #ifndef AT_FLAGS_TRANSPARENT_INTERP 25*7baee96fSChristian Brauner #define AT_FLAGS_TRANSPARENT_INTERP (1 << 1) 26*7baee96fSChristian Brauner #endif 27*7baee96fSChristian Brauner 28*7baee96fSChristian Brauner static int fail; 29*7baee96fSChristian Brauner 30*7baee96fSChristian Brauner static void ok(int cond, const char *what) 31*7baee96fSChristian Brauner { 32*7baee96fSChristian Brauner if (!cond) { 33*7baee96fSChristian Brauner fprintf(stderr, "TRANSPARENT_FAIL: %s (errno %d)\n", what, errno); 34*7baee96fSChristian Brauner fail = 1; 35*7baee96fSChristian Brauner } 36*7baee96fSChristian Brauner } 37*7baee96fSChristian Brauner 38*7baee96fSChristian Brauner int main(int argc, char **argv) 39*7baee96fSChristian Brauner { 40*7baee96fSChristian Brauner const char *binary = getenv("BINFMT_TEST_BINARY"); 41*7baee96fSChristian Brauner const char *argv0 = getenv("BINFMT_TEST_ARGV0"); 42*7baee96fSChristian Brauner char expect[PATH_MAX + 32], buf[PATH_MAX]; 43*7baee96fSChristian Brauner unsigned long execfd; 44*7baee96fSChristian Brauner struct stat stb, stfd; 45*7baee96fSChristian Brauner const char *want[3]; 46*7baee96fSChristian Brauner const char *base; 47*7baee96fSChristian Brauner size_t expect_len, i; 48*7baee96fSChristian Brauner int fd, have_stb, have_stfd; 49*7baee96fSChristian Brauner ssize_t n; 50*7baee96fSChristian Brauner 51*7baee96fSChristian Brauner if (!binary) { 52*7baee96fSChristian Brauner fprintf(stderr, "TRANSPARENT_FAIL: BINFMT_TEST_BINARY unset\n"); 53*7baee96fSChristian Brauner return 1; 54*7baee96fSChristian Brauner } 55*7baee96fSChristian Brauner /* Distinct from the binary path, so a classic argv splice is caught. */ 56*7baee96fSChristian Brauner want[0] = argv0 ? argv0 : binary; 57*7baee96fSChristian Brauner want[1] = PAYLOAD_ARG1; 58*7baee96fSChristian Brauner want[2] = PAYLOAD_ARG2; 59*7baee96fSChristian Brauner 60*7baee96fSChristian Brauner /* The aux vector announces the transparent contract. */ 61*7baee96fSChristian Brauner ok(getauxval(AT_FLAGS) & AT_FLAGS_TRANSPARENT_INTERP, 62*7baee96fSChristian Brauner "AT_FLAGS lacks AT_FLAGS_TRANSPARENT_INTERP"); 63*7baee96fSChristian Brauner 64*7baee96fSChristian Brauner /* AT_EXECFD refers to the very file that was executed. */ 65*7baee96fSChristian Brauner execfd = getauxval(AT_EXECFD); 66*7baee96fSChristian Brauner ok(execfd > 2, "no AT_EXECFD"); 67*7baee96fSChristian Brauner have_stb = !stat(binary, &stb); 68*7baee96fSChristian Brauner ok(have_stb, "cannot stat the binary"); 69*7baee96fSChristian Brauner have_stfd = !fstat((int)execfd, &stfd); 70*7baee96fSChristian Brauner ok(have_stfd, "cannot fstat AT_EXECFD"); 71*7baee96fSChristian Brauner ok(have_stb && have_stfd && stb.st_dev == stfd.st_dev && 72*7baee96fSChristian Brauner stb.st_ino == stfd.st_ino, "AT_EXECFD is not the binary"); 73*7baee96fSChristian Brauner 74*7baee96fSChristian Brauner /* The exe link names the binary, not this interpreter. */ 75*7baee96fSChristian Brauner ok(exe_is(binary), "/proc/self/exe is not the binary"); 76*7baee96fSChristian Brauner 77*7baee96fSChristian Brauner /* argv arrived unspliced. */ 78*7baee96fSChristian Brauner ok(argc == (int)ARRAY_SIZE(want), "argv was rewritten"); 79*7baee96fSChristian Brauner for (i = 0; i < ARRAY_SIZE(want) && i < (size_t)argc; i++) 80*7baee96fSChristian Brauner ok(!strcmp(argv[i], want[i]), "argv was rewritten"); 81*7baee96fSChristian Brauner 82*7baee96fSChristian Brauner /* And so did the kernel's copy of it: the same strings, NUL separated. */ 83*7baee96fSChristian Brauner for (i = 0, expect_len = 0; i < ARRAY_SIZE(want); i++) { 84*7baee96fSChristian Brauner size_t len = strlen(want[i]) + 1; 85*7baee96fSChristian Brauner 86*7baee96fSChristian Brauner if (expect_len + len > sizeof(expect)) { 87*7baee96fSChristian Brauner ok(0, "argv does not fit the expectation buffer"); 88*7baee96fSChristian Brauner break; 89*7baee96fSChristian Brauner } 90*7baee96fSChristian Brauner memcpy(expect + expect_len, want[i], len); 91*7baee96fSChristian Brauner expect_len += len; 92*7baee96fSChristian Brauner } 93*7baee96fSChristian Brauner fd = open("/proc/self/cmdline", O_RDONLY); 94*7baee96fSChristian Brauner n = fd >= 0 ? read(fd, buf, sizeof(buf)) : -1; 95*7baee96fSChristian Brauner if (fd >= 0) 96*7baee96fSChristian Brauner close(fd); 97*7baee96fSChristian Brauner ok(n == (ssize_t)expect_len && !memcmp(buf, expect, expect_len), 98*7baee96fSChristian Brauner "/proc/self/cmdline was rewritten"); 99*7baee96fSChristian Brauner 100*7baee96fSChristian Brauner /* comm is the binary's basename. */ 101*7baee96fSChristian Brauner base = strrchr(binary, '/'); 102*7baee96fSChristian Brauner base = base ? base + 1 : binary; 103*7baee96fSChristian Brauner ok(comm_is(base), "comm is not the binary's basename"); 104*7baee96fSChristian Brauner 105*7baee96fSChristian Brauner /* The binary is write-denied while it runs, like a direct exec. */ 106*7baee96fSChristian Brauner ok(write_denied(binary), "binary is writable while running"); 107*7baee96fSChristian Brauner ok(write_denied("/proc/self/exe"), "exe link is writable while running"); 108*7baee96fSChristian Brauner 109*7baee96fSChristian Brauner if (!fail) 110*7baee96fSChristian Brauner printf("TRANSPARENT_OK\n"); 111*7baee96fSChristian Brauner return fail; 112*7baee96fSChristian Brauner } 113