17baee96fSChristian Brauner // SPDX-License-Identifier: GPL-2.0 27baee96fSChristian Brauner /* 37baee96fSChristian Brauner * Test the static transparent flag 'T' of binfmt_misc. A magic-matched 47baee96fSChristian Brauner * binary is dispatched to an interpreter with the argument vector left 57baee96fSChristian Brauner * untouched, the binary passed through AT_EXECFD and mm->exe_file labeled 67baee96fSChristian Brauner * with the binary. The asserting interpreter (binfmt_transparent_interp) 77baee96fSChristian Brauner * verifies the constructed identity from inside the process and exits 0. 87baee96fSChristian Brauner * 97baee96fSChristian Brauner * Needs root for the registration; no bpf toolchain involved. 107baee96fSChristian Brauner */ 117baee96fSChristian Brauner #define _GNU_SOURCE 127baee96fSChristian Brauner #include <stdio.h> 137baee96fSChristian Brauner #include <stdlib.h> 147baee96fSChristian Brauner 157baee96fSChristian Brauner #include "binfmt_misc_common.h" 167baee96fSChristian Brauner #include "kselftest_harness.h" 177baee96fSChristian Brauner 187baee96fSChristian Brauner #define MAGIC "#TRANSPARENT-SELFTEST#" 197baee96fSChristian Brauner #define TARGET_PATH "/tmp/binfmt_transparent_target" 207baee96fSChristian Brauner #define INTERP_PATH "/tmp/binfmt_transparent_interp" 217baee96fSChristian Brauner #define ENTRY "test_transparent" 227baee96fSChristian Brauner #define RULE(flags) ":" ENTRY ":M:0:" MAGIC "::" INTERP_PATH ":" flags 237baee96fSChristian Brauner 247baee96fSChristian Brauner /* The target only has to carry the magic; it is never actually loaded. */ 257baee96fSChristian Brauner static int create_target(void) 267baee96fSChristian Brauner { 277baee96fSChristian Brauner char buf[128] = MAGIC "\n"; 287baee96fSChristian Brauner int fd; 297baee96fSChristian Brauner 307baee96fSChristian Brauner unlink(TARGET_PATH); 317baee96fSChristian Brauner fd = open(TARGET_PATH, O_WRONLY | O_CREAT | O_EXCL, 0755); 327baee96fSChristian Brauner if (fd < 0) 337baee96fSChristian Brauner return -1; 347baee96fSChristian Brauner if (write(fd, buf, sizeof(buf)) != (ssize_t)sizeof(buf)) { 357baee96fSChristian Brauner close(fd); 367baee96fSChristian Brauner return -1; 377baee96fSChristian Brauner } 387baee96fSChristian Brauner close(fd); 397baee96fSChristian Brauner return 0; 407baee96fSChristian Brauner } 417baee96fSChristian Brauner 427baee96fSChristian Brauner FIXTURE(transparent) { 437baee96fSChristian Brauner }; 447baee96fSChristian Brauner 457baee96fSChristian Brauner FIXTURE_SETUP(transparent) 467baee96fSChristian Brauner { 477baee96fSChristian Brauner char src[PATH_MAX]; 487baee96fSChristian Brauner 497baee96fSChristian Brauner if (getuid() != 0) 507baee96fSChristian Brauner SKIP(return, "test must be run as root"); 517baee96fSChristian Brauner if (!binfmt_misc_available()) 527baee96fSChristian Brauner SKIP(return, "no binfmt_misc"); 537baee96fSChristian Brauner 547baee96fSChristian Brauner ASSERT_EQ(artifact_path(src, sizeof(src), "binfmt_transparent_interp"), 0); 557baee96fSChristian Brauner ASSERT_EQ(copy_file(src, INTERP_PATH), 0); 567baee96fSChristian Brauner ASSERT_EQ(create_target(), 0); 577baee96fSChristian Brauner 587baee96fSChristian Brauner /* Skip the whole suite on a kernel that does not know 'T'. */ 59*686585ecSChristian Brauner if (!binfmt_flag_supported('T')) { 607baee96fSChristian Brauner ASSERT_EQ(errno, EINVAL); 617baee96fSChristian Brauner SKIP(return, "kernel without the 'T' flag"); 627baee96fSChristian Brauner } 637baee96fSChristian Brauner } 647baee96fSChristian Brauner 657baee96fSChristian Brauner FIXTURE_TEARDOWN(transparent) 667baee96fSChristian Brauner { 677baee96fSChristian Brauner unregister(ENTRY); 687baee96fSChristian Brauner unlink(TARGET_PATH); 697baee96fSChristian Brauner unlink(INTERP_PATH); 707baee96fSChristian Brauner } 717baee96fSChristian Brauner 727baee96fSChristian Brauner /* Grammar sanity check: the same entry without 'T' has to register. */ 737baee96fSChristian Brauner TEST_F(transparent, plain_entry_registers) 747baee96fSChristian Brauner { 757baee96fSChristian Brauner ASSERT_EQ(write_reg(RULE("")), 0); 767baee96fSChristian Brauner } 777baee96fSChristian Brauner 787baee96fSChristian Brauner /* 'T' preserves the whole argv, so combining it with 'P' is rejected. */ 797baee96fSChristian Brauner TEST_F(transparent, rejects_preserve_argv0) 807baee96fSChristian Brauner { 817baee96fSChristian Brauner ASSERT_NE(write_reg(RULE("TP")), 0); 827baee96fSChristian Brauner EXPECT_EQ(errno, EINVAL); 837baee96fSChristian Brauner } 847baee96fSChristian Brauner 857baee96fSChristian Brauner /* The interpreter asserts the identity the kernel built for it. */ 867baee96fSChristian Brauner TEST_F(transparent, dispatch) 877baee96fSChristian Brauner { 887baee96fSChristian Brauner ASSERT_EQ(write_reg(RULE("T")), 0); 897baee96fSChristian Brauner 907baee96fSChristian Brauner setenv("BINFMT_TEST_BINARY", TARGET_PATH, 1); 917baee96fSChristian Brauner setenv("BINFMT_TEST_ARGV0", PAYLOAD_ARGV0, 1); 927baee96fSChristian Brauner EXPECT_EQ(run_payload(TARGET_PATH), 0); 937baee96fSChristian Brauner } 947baee96fSChristian Brauner 957baee96fSChristian Brauner TEST_HARNESS_MAIN 96