/*++ Copyright (c) Microsoft. All rights reserved. Module Name: binfmt.c Abstract: This file contains tests for the binfmt file system. --*/ #include "lxtcommon.h" #include "unittests.h" #include #include #include #include #include #include #include #include #include #include #define LXT_NAME "BinFmt" #define BINFMT_MNT "/proc/sys/fs/binfmt_misc" #define BINFMT_TEST_FILE "/data/test/lxt_binfmt_test" #define BINFMT_TIMEOUT 60 #define BINFMT_DISABLE_STRING "0" #define BINFMT_ENABLE_STRING "1" #define BINFMT_REMOVE_STRING "-1" #define BINFMT_REGISTER_NAME "Test" #define BINFMT_INTERPRETER_SCRIPT "/data/test/lxt_binfmt_interpreter.sh" #define BINFMT_REGISTER_SCRIPT_STRING ":" BINFMT_REGISTER_NAME ":M::\\xff\\xff\\xff\\xff::" BINFMT_INTERPRETER_SCRIPT ":" #define BINFMT_INTERPRETER_SCRIPT_CONTENTS \ "#!/bin/bash\n" \ "# " BINFMT_INTERPRETER_SCRIPT " - the wrapper for WSL binfmt_misc testing\n" WSL_UNIT_TEST_BINARY " binfmt -a -i \"$@\"" #define BINFMT_INTERPRETER_BINARY "/data/test/lxt_binfmt_interpreter_binary" #define BINFMT_INTERPRETER_BINARY_SOURCEFILE "/data/test/lxt_binfmt_interpreter_binary.c" // // N.B. These UID and GID values must be kept in-sync with the values in the // source below. // #define BINFMT_CALLER_UID 0 #define BINFMT_CALLER_GID 0 #define BINFMT_BINARY_UID 1044 #define BINFMT_BINARY_GID 1044 #define BINFMT_P_FLAG_ARG "foo" #define BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \ "#define _GNU_SOURCE\n" \ "#include \n" \ "#include \n" \ "#include \n" \ "#include \n" \ "#include \n" \ "#include \n" \ "#include \n" \ "#define BINFMT_CALLER_UID 0\n" \ "#define BINFMT_CALLER_GID 0\n" \ "#define BINFMT_BINARY_UID 1044\n" \ "#define BINFMT_BINARY_GID 1044\n" \ "#define BINFMT_P_FLAG_ARG \"foo\"\n" \ "#define BINFMT_INTERPRETER_BINARY \"/data/test/lxt_binfmt_interpreter_binary\"\n" \ "#define BINFMT_TEST_FILE \"/data/test/lxt_binfmt_test\"\n" \ "\n" \ "int main(int Argc, char** Argv)\n" \ "{\n" \ " struct stat Buffer;\n" \ " int Fd;\n" \ " int Index;\n" \ " uid_t Real, Effective, Saved;\n" \ " printf(\"Pid = %d\\n\", getpid());\n" \ " Fd = getauxval(AT_EXECFD);\n" \ " printf(\"AT_EXECFD = %d errno = %d\\n\", Fd, errno);\n" \ " getresuid(&Real, &Effective, &Saved);\n" \ " printf(\"Real %d Effective %d Saved %d\\n\", Real, Effective, Saved);\n" \ " printf(\"Argc = %d\\n\", Argc);\n" \ " for (Index = 0; Index < Argc; Index += 1) {\n" \ " printf(\"Argv[%d] = %s\\n\", Index, Argv[Index]);\n" \ " }\n" #define BINFMT_INTERPRETER_BINARY_SOURCE_VERIFY_TWO_ARGS \ " if (Argc != 2) {\n" \ " return -1;\n" \ " }\n" \ " if (strcmp(Argv[0], BINFMT_INTERPRETER_BINARY) != 0) {\n" \ " return -1;\n" \ " }\n" \ " if (strcmp(Argv[1], BINFMT_TEST_FILE) != 0) {\n" \ " return -1;\n" \ " }\n" #define BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_C_FLAG \ " if ((Fd == 0) && (errno == ENOENT)) {\n" \ " return -1;\n" \ " }\n" \ " if (fcntl(Fd, F_GETFD) != 0) {\n" \ " return -1;\n" \ " }\n" \ " if ((Real != BINFMT_CALLER_UID) ||\n" \ " (Effective != BINFMT_BINARY_UID) ||\n" \ " (Saved != BINFMT_BINARY_UID)) {\n" \ " return -1;\n" \ " }\n" BINFMT_INTERPRETER_BINARY_SOURCE_VERIFY_TWO_ARGS #define BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_O_FLAG \ " if ((Fd == 0) && (errno == ENOENT)) {\n" \ " return -1;\n" \ " }\n" \ " if (fcntl(Fd, F_GETFD) != 0) {\n" \ " return -1;\n" \ " }\n" \ " if ((Real != BINFMT_CALLER_UID) ||\n" \ " (Effective != BINFMT_CALLER_UID) ||\n" \ " (Saved != BINFMT_CALLER_UID)) {\n" \ " return -1;\n" \ " }\n" BINFMT_INTERPRETER_BINARY_SOURCE_VERIFY_TWO_ARGS #define BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_P_FLAG \ " if ((Fd != 0) || (errno != ENOENT)) {\n" \ " return -1;\n" \ " }\n" \ " if (Argc != 4) {\n" \ " return -1;\n" \ " }\n" \ " if (strcmp(Argv[0], BINFMT_INTERPRETER_BINARY) != 0) {\n" \ " return -1;\n" \ " }\n" \ " if (strcmp(Argv[1], BINFMT_TEST_FILE) != 0) {\n" \ " return -1;\n" \ " }\n" \ " if (strcmp(Argv[2], BINFMT_TEST_FILE) != 0) {\n" \ " return -1;\n" \ " }\n" \ " if (strcmp(Argv[3], BINFMT_P_FLAG_ARG) != 0) {\n" \ " return -1;\n" \ " }\n" #define BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_NO_FLAGS \ " if ((Fd != 0) || (errno != ENOENT)) {\n" \ " return -1;\n" \ " }\n" #define BINFMT_INTERPRETER_BINARY_SOURCE_END \ " return 0;\n" \ "}" #define BINFMT_INTERPRETER_BINARY_SOURCE_C_FLAG \ BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \ BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_C_FLAG \ BINFMT_INTERPRETER_BINARY_SOURCE_END #define BINFMT_INTERPRETER_BINARY_SOURCE_O_FLAG \ BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \ BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_O_FLAG \ BINFMT_INTERPRETER_BINARY_SOURCE_END #define BINFMT_INTERPRETER_BINARY_SOURCE_P_FLAG \ BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \ BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_P_FLAG \ BINFMT_INTERPRETER_BINARY_SOURCE_END #define BINFMT_INTERPRETER_BINARY_SOURCE_NO_FLAGS \ BINFMT_INTERPRETER_BINARY_SOURCE_BEGIN \ BINFMT_INTERPRETER_BINARY_SOURCE_MIDDLE_NO_FLAGS \ BINFMT_INTERPRETER_BINARY_SOURCE_END #define BINFMT_OFFSET_TEST "/data/test/binfmt_offset" #define BINFMT_OFFSET_TEST_PATTERN "GSH" #define BINFMT_REGISTER_BINARY_STRING ":" BINFMT_REGISTER_NAME ":M::\\xff\\xff\\xff\\xff::" BINFMT_INTERPRETER_BINARY ":" #define BINFMT_REGISTER_BINARY_STRING_C BINFMT_REGISTER_BINARY_STRING "C" #define BINFMT_REGISTER_BINARY_STRING_O BINFMT_REGISTER_BINARY_STRING "O" #define BINFMT_REGISTER_BINARY_STRING_P BINFMT_REGISTER_BINARY_STRING "P" #define BINFMT_STATUS_ENABLED "enabled\n" #define BINFMT_STATUS_DISABLED "disabled\n" #define BINFMT_REGISTRATION_ENABLED_STRING \ BINFMT_STATUS_ENABLED \ "interpreter " BINFMT_INTERPRETER_SCRIPT \ "\n" \ "flags: \n" \ "offset 0\n" \ "magic ffffffff\n" #define BINFMT_REGISTRATION_DISABLED_STRING \ BINFMT_STATUS_DISABLED \ "interpreter " BINFMT_INTERPRETER_SCRIPT \ "\n" \ "flags: \n" \ "offset 0\n" \ "magic ffffffff\n" typedef struct _LXT_BINFMT_REGISTRATION { char* RegistrationString; char Magic[4]; char* TestFile; } LXT_BINFMT_REGISTRATION, PLXT_BINFMT_REGISTRATION; LXT_BINFMT_REGISTRATION g_BinfmtRegistrations[] = { {":binfmt_1:M::\\x01\x01\x01\x01::/data/test/lxt_binfmt_2:", {0x1, 0x1, 0x1, 0x1}, "/data/test/lxt_binfmt_1"}, {":binfmt_2:M::\\x02\x02\x02\x02::/data/test/lxt_binfmt_3:", {0x2, 0x2, 0x2, 0x2}, "/data/test/lxt_binfmt_2"}, {":binfmt_3:M::\\x03\x03\x03\x03::/data/test/lxt_binfmt_4:", {0x3, 0x3, 0x3, 0x3}, "/data/test/lxt_binfmt_3"}, {":binfmt_4:M::\\x04\x04\x04\x04::/data/test/lxt_binfmt_5:", {0x4, 0x4, 0x4, 0x4}, "/data/test/lxt_binfmt_4"}, {":binfmt_5:M::\\x05\x05\x05\x05::/data/test/lxt_binfmt_6:", {0x5, 0x5, 0x5, 0x5}, "/data/test/lxt_binfmt_5"}, {":binfmt_6:M::\\x06\x06\x06\x06::/data/test/lxt_binfmt_7:", {0x6, 0x6, 0x6, 0x6}, "/data/test/lxt_binfmt_6"}, {":binfmt_7:M::\\x07\x07\x07\x07::/bin/echo:", {0x7, 0x7, 0x7, 0x7}, "/data/test/lxt_binfmt_7"}}; void BimFmtCleanup(void); int BinFmtExtension(PLXT_ARGS Args); int BinFmtInvalidParam(PLXT_ARGS Args); int BinFmtOffset(PLXT_ARGS Args); int BinFmtOptions(PLXT_ARGS Args); int BinFmtRegister(PLXT_ARGS Args); int BinFmtRoot(PLXT_ARGS Args); int BinFmtStatus(PLXT_ARGS Args); int BinFmtInterpreterEntry(PLXT_ARGS Args); // // Global constants // static const LXT_VARIATION g_LxtVariations[] = { {"BinFmt - " BINFMT_MNT " root", BinFmtRoot}, {"BinFmt - " BINFMT_MNT "/register", BinFmtRegister}, {"BinFmt - " BINFMT_MNT "/status", BinFmtStatus}, {"BinFmt - Extensions", BinFmtExtension}, {"BinFmt - Options", BinFmtOptions}, {"BinFmt - Offset", BinFmtOffset}, {"BinFmt - Invalid Parameter", BinFmtInvalidParam}}; static const LXT_CHILD_INFO g_BinFmtRootChildren[] = {{"register", DT_REG}, {"status", DT_REG}}; static const char* g_BinFmtRegisterInvalid[] = { "::M::BACON::/usr/bin/test:", ":Test:B::BACON::/usr/bin/test:", ":Test:M::BACON:BACONISAWESOME:/usr/bin/test:", ":Test:M::BACON:\\xff:/usr/bin/test:", ":Test:M::BACON:\\xff\\xff\\xff\\xff\\xf:/usr/bin/test:", ":Test:M::BACON:::", ":Test:M::BACON::/usr/bin/test:B", ":Test:M::BACON::/usr/bin/test: ", ":Test:M::BACON::/usr/bin/test:\nO", ":Test:E::B/ACON::/usr/bin/test:", /* ":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaa:", ":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaaaaaaaa:", ":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaaaaaaaaaaaaaaaaa:", ":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:", ":aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:M::BACON::/usr/bin/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:", */ ":::::::::::::::::", "", "\0"}; int BinFmtTestEntry(int Argc, char* Argv[]) /*++ Routine Description: This routine is the main entry point for the binfmt tests. Arguments: Argc - Supplies the number of command line arguments. Argv - Supplies the command line arguments. Return Value: LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME)); Returns 0 on success, -1 on failure. --*/ { LXT_ARGS Args; int Opt; int Result; LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME)); optind = 0; opterr = 0; while ((Opt = getopt(Argc, Argv, "i")) != -1) { switch (Opt) { case 'i': Result = BinFmtInterpreterEntry(&Args); goto ErrorExit; } } LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations))); ErrorExit: LxtUninitialize(); return !LXT_SUCCESS(Result); } void BimFmtCleanup(void) { int Fd; int Result; int Size; // // Remove the test entry via the registration file. // Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR); if (Fd >= 0) { Size = strlen(BINFMT_REMOVE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size)); } ErrorExit: if (Fd != -1) { LxtClose(Fd); } return; } int BinFmtExtension(PLXT_ARGS Args) /*++ Description: This routine tests binformat extensions. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { char Buffer[16]; size_t BytesWritten; int ChildPid; char* ExecArgs[2]; int Fd = -1; int Index; int RegisterFd; LXT_CHILD_INFO Registration; int Result; ssize_t Size; int Status; // // Clean any binfmt interpreters from a previous iteration of the test. // BimFmtCleanup(); // // Create the binfmt interpreter. // LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_SCRIPT, 0777)); LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_SCRIPT_CONTENTS, (sizeof(BINFMT_INTERPRETER_SCRIPT_CONTENTS) - 1))); LxtClose(Fd); Fd = -1; // // Register a binfmt extension. // LxtCheckErrno(RegisterFd = open(BINFMT_MNT "/register", O_WRONLY)); Size = strlen(BINFMT_REGISTER_SCRIPT_STRING); LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_SCRIPT_STRING, Size)); // // Create a file that will be handled by the binfmt extension. // LxtCheckErrno(Fd = creat(BINFMT_TEST_FILE, 0777)); memset(Buffer, 0xff, sizeof(Buffer)); LxtCheckErrno(BytesWritten = write(Fd, Buffer, sizeof(Buffer))); LxtClose(Fd); Fd = -1; // // Fork and exec the file. // LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { ExecArgs[0] = BINFMT_TEST_FILE; ExecArgs[1] = NULL; LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL)); // // The parent waits for the child to exit successfully. // } else { LxtCheckResult(LxtWaitPidPoll(ChildPid, 0)); } // // Remove the new entry via the registration file. // LxtCheckErrno(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR)); Size = strlen(BINFMT_REMOVE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size)); LxtClose(Fd); Fd = -1; // // Create many registrations and test files. // for (Index = 0; Index < LXT_COUNT_OF(g_BinfmtRegistrations); Index += 1) { Size = strlen(g_BinfmtRegistrations[Index].RegistrationString); LxtCheckErrno(Size = write(RegisterFd, g_BinfmtRegistrations[Index].RegistrationString, Size)); LxtCheckErrno(Fd = creat(g_BinfmtRegistrations[Index].TestFile, 0777)); LxtCheckErrno(BytesWritten = write(Fd, g_BinfmtRegistrations[Index].Magic, sizeof(g_BinfmtRegistrations[Index].Magic))); LxtClose(Fd); Fd = -1; } // // Fork and exec the file to test the interpreter depth. // Index = 2; LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { ExecArgs[0] = g_BinfmtRegistrations[Index].TestFile; ExecArgs[1] = NULL; LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL)); // // The parent waits for the child to exit successfully. // } else { LxtCheckResult(LxtWaitPidPoll(ChildPid, 0)); } // // Test max interpreter link depth (should fail). // Index = 1; LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { ExecArgs[0] = g_BinfmtRegistrations[Index].TestFile; ExecArgs[1] = NULL; LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), ELOOP); exit(0); // // The parent waits for the child to exit. // } else { LxtCheckResult(LxtWaitPidPoll(ChildPid, 0)); } // // Remove the entries via the status file. // LxtCheckErrno(Fd = open(BINFMT_MNT "/status", O_RDWR)); Size = strlen(BINFMT_REMOVE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size)); LxtClose(Fd); Fd = -1; ErrorExit: if (RegisterFd > 0) { LxtClose(RegisterFd); } if (Fd > 0) { LxtClose(Fd); } if (ChildPid == 0) { _exit(Result); } return Result; } int BinFmtInvalidParam(PLXT_ARGS Args) /*++ Description: This routine tests invalid argument handling for the binfmt register file. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { int Fd; int Index; int Result; ssize_t Size; LxtCheckErrno(Fd = open(BINFMT_MNT "/register", O_WRONLY)); for (Index = 0; Index < LXT_COUNT_OF(g_BinFmtRegisterInvalid); Index += 1) { LxtLogInfo("Index[%d] %s", Index, g_BinFmtRegisterInvalid[Index]); Size = strlen(g_BinFmtRegisterInvalid[Index]); LxtCheckErrnoFailure(Size = write(Fd, g_BinFmtRegisterInvalid[Index], Size), EINVAL); } ErrorExit: if (Fd > 0) { LxtClose(Fd); } return Result; } int BinFmtOffset(PLXT_ARGS Args) /*++ Description: This routine tests binformat interpreter options. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { int ChildPid; char* ExecArgs[2]; int Fd; char RegisterString[] = ":" BINFMT_REGISTER_NAME ":M:2:" BINFMT_OFFSET_TEST_PATTERN "::/bin/true:"; int Result; ssize_t Size; // // Register an interpreter with a known string at a two byte offset. // LxtCheckErrno(Fd = open(BINFMT_MNT "/register", O_WRONLY)); Size = strlen(RegisterString); LxtCheckErrno(Size = write(Fd, RegisterString, Size)); // // Create a test file that matches this pattern. // LxtClose(Fd); LxtCheckErrno(Fd = creat(BINFMT_OFFSET_TEST, 0777)); LxtCheckErrno(Size = write(Fd, "00" BINFMT_OFFSET_TEST_PATTERN, sizeof(BINFMT_OFFSET_TEST_PATTERN) + 2)); LxtClose(Fd); Fd = -1; // // Fork and exec the file and ensure that the binfmt interpreter is invoked. // LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { ExecArgs[0] = BINFMT_OFFSET_TEST; ExecArgs[1] = NULL; LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, 0)); // // Create a test file that does not match the pattern. // LxtCheckErrno(Fd = creat(BINFMT_OFFSET_TEST, 0777)); LxtCheckErrno(Size = write(Fd, BINFMT_OFFSET_TEST_PATTERN, sizeof(BINFMT_OFFSET_TEST_PATTERN))); LxtClose(Fd); Fd = -1; // // Fork and exec the file and ensure that the exec fails. // LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { ExecArgs[0] = BINFMT_OFFSET_TEST; ExecArgs[1] = NULL; LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), ENOEXEC); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPoll(ChildPid, 0)); Result = LXT_RESULT_SUCCESS; ErrorExit: if (Fd > 0) { LxtClose(Fd); } if (ChildPid == 0) { _exit(Result); } // // Unregister the interpreter and delete the test file. // BimFmtCleanup(); unlink(BINFMT_OFFSET_TEST); return Result; } int BinFmtOptions(PLXT_ARGS Args) /*++ Description: This routine tests binformat interpreter options. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { char* Argv[5]; char Buffer[16]; size_t BytesWritten; int ChildPid; char* ExecArgs[3]; int Fd = -1; int Index; int RegisterFd; LXT_CHILD_INFO Registration; int Result; ssize_t Size; int Status; // // Clean any binfmt interpreters from a previous iteration of the test. // BimFmtCleanup(); // // Create a file that will be handled by the binfmt extension. // LxtCheckErrno(Fd = creat(BINFMT_TEST_FILE, 0777)); memset(Buffer, 0xff, sizeof(Buffer)); LxtCheckErrno(BytesWritten = write(Fd, Buffer, sizeof(Buffer))); LxtCheckErrno(fchown(Fd, BINFMT_BINARY_UID, BINFMT_BINARY_GID)); LxtCheckErrno(fchmod(Fd, 0777 | S_ISUID)); LxtClose(Fd); Fd = -1; // // Create a binfmt interpreter without any flags. // LxtLogInfo("Testing no flags"); LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_BINARY_SOURCEFILE, 0777)); LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_BINARY_SOURCE_NO_FLAGS, (sizeof(BINFMT_INTERPRETER_BINARY_SOURCE_NO_FLAGS) - 1))); LxtClose(Fd); Fd = -1; // // Compile the binary // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[0] = "gcc"; Argv[1] = BINFMT_INTERPRETER_BINARY_SOURCEFILE; Argv[2] = "-o"; Argv[3] = BINFMT_INTERPRETER_BINARY; Argv[4] = NULL; LxtCheckErrno(execv("/usr/bin/gcc", Argv)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, BINFMT_TIMEOUT)); // // Register a binfmt extension without flags. // LxtCheckErrno(RegisterFd = open(BINFMT_MNT "/register", O_WRONLY)); Size = strlen(BINFMT_REGISTER_BINARY_STRING); LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_BINARY_STRING, Size)); // // Fork and exec the file. // LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(LxtSetresuid(BINFMT_CALLER_UID, BINFMT_CALLER_UID, BINFMT_CALLER_UID)); ExecArgs[0] = BINFMT_TEST_FILE; ExecArgs[1] = NULL; LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL)); } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, 0, 0, BINFMT_TIMEOUT)); // // Unregister the interpreter. // BimFmtCleanup(); // // Create the binfmt interpreter that handles the 'O' flag. // LxtLogInfo("Testing 'O' flag"); LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_BINARY_SOURCEFILE, 0777)); LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_BINARY_SOURCE_O_FLAG, (sizeof(BINFMT_INTERPRETER_BINARY_SOURCE_O_FLAG) - 1))); LxtClose(Fd); Fd = -1; // // Compile the binary // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[0] = "gcc"; Argv[1] = BINFMT_INTERPRETER_BINARY_SOURCEFILE; Argv[2] = "-o"; Argv[3] = BINFMT_INTERPRETER_BINARY; Argv[4] = NULL; LxtCheckErrno(execv("/usr/bin/gcc", Argv)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, BINFMT_TIMEOUT)); // // Register a binfmt extension. // LxtCheckErrno(RegisterFd = open(BINFMT_MNT "/register", O_WRONLY)); Size = strlen(BINFMT_REGISTER_BINARY_STRING_O); LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_BINARY_STRING_O, Size)); // // Fork and exec the file. // LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(LxtSetresuid(BINFMT_CALLER_UID, BINFMT_CALLER_UID, BINFMT_CALLER_UID)); ExecArgs[0] = BINFMT_TEST_FILE; ExecArgs[1] = NULL; LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL)); } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, 0, 0, BINFMT_TIMEOUT)); // // Unregister the interpreter. // BimFmtCleanup(); // // Create the binfmt interpreter that handles the 'C' flag. // LxtLogInfo("Testing 'C' flag"); LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_BINARY_SOURCEFILE, 0777)); LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_BINARY_SOURCE_C_FLAG, (sizeof(BINFMT_INTERPRETER_BINARY_SOURCE_C_FLAG) - 1))); LxtClose(Fd); Fd = -1; // // Compile the binary // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[0] = "gcc"; Argv[1] = BINFMT_INTERPRETER_BINARY_SOURCEFILE; Argv[2] = "-o"; Argv[3] = BINFMT_INTERPRETER_BINARY; Argv[4] = NULL; LxtCheckErrno(execv("/usr/bin/gcc", Argv)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, BINFMT_TIMEOUT)); // // Register a binfmt extension. // LxtCheckErrno(RegisterFd = open(BINFMT_MNT "/register", O_WRONLY)); Size = strlen(BINFMT_REGISTER_BINARY_STRING_C); LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_BINARY_STRING_C, Size)); // // Fork and exec the file. // LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { LxtCheckErrno(LxtSetresuid(BINFMT_CALLER_UID, BINFMT_CALLER_UID, BINFMT_CALLER_UID)); ExecArgs[0] = BINFMT_TEST_FILE; ExecArgs[1] = NULL; LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL)); } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, 0, 0, BINFMT_TIMEOUT)); // // Unregister the interpreter. // BimFmtCleanup(); // // Create the binfmt interpreter that handles the 'P' flag. // LxtLogInfo("Testing 'P' flag"); LxtCheckErrno(Fd = creat(BINFMT_INTERPRETER_BINARY_SOURCEFILE, 0777)); LxtCheckErrno(BytesWritten = write(Fd, BINFMT_INTERPRETER_BINARY_SOURCE_P_FLAG, (sizeof(BINFMT_INTERPRETER_BINARY_SOURCE_P_FLAG) - 1))); LxtClose(Fd); Fd = -1; // // Compile the binary // LxtCheckErrno(ChildPid = fork()); if (ChildPid == 0) { Argv[0] = "gcc"; Argv[1] = BINFMT_INTERPRETER_BINARY_SOURCEFILE; Argv[2] = "-o"; Argv[3] = BINFMT_INTERPRETER_BINARY; Argv[4] = NULL; LxtCheckErrno(execv("/usr/bin/gcc", Argv)); goto ErrorExit; } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, LXT_RESULT_SUCCESS, 0, BINFMT_TIMEOUT)); // // Register a binfmt extension. // LxtCheckErrno(RegisterFd = open(BINFMT_MNT "/register", O_WRONLY)); Size = strlen(BINFMT_REGISTER_BINARY_STRING_P); LxtCheckErrno(Size = write(RegisterFd, BINFMT_REGISTER_BINARY_STRING_P, Size)); // // Fork and exec the file. // LxtCheckResult(ChildPid = fork()); if (ChildPid == 0) { ExecArgs[0] = BINFMT_TEST_FILE; ExecArgs[1] = BINFMT_P_FLAG_ARG; ExecArgs[2] = NULL; LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL)); } // // Wait for the child to exit. // LxtCheckResult(LxtWaitPidPollOptions(ChildPid, 0, 0, BINFMT_TIMEOUT)); // // Unregister the interpreter. // BimFmtCleanup(); ErrorExit: if (RegisterFd > 0) { LxtClose(RegisterFd); } if (Fd > 0) { LxtClose(Fd); } if (ChildPid == 0) { _exit(Result); } return Result; } int BinFmtRoot(PLXT_ARGS Args) /*++ Description: This routine tests the contents of the binfmt directory. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { int Result; // LxtCheckResult(LxtCheckStat(BINFMT_MNT, 1, DT_DIR)); LxtCheckResult(LxtCheckDirectoryContents(BINFMT_MNT, g_BinFmtRootChildren, LXT_COUNT_OF(g_BinFmtRootChildren))); ErrorExit: return Result; } int BinFmtRegister(PLXT_ARGS Args) /*++ Description: This routine tests the binfmt register file. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { char Buffer[128]; int Fd; LXT_CHILD_INFO Registration; int Result; ssize_t Size; // // Clean up any previously registered interpreters. // BimFmtCleanup(); // // Open the register file and verify that binfmt registrations are able to be registered. // LxtCheckErrno(Fd = open(BINFMT_MNT "/register", O_WRONLY)); Size = strlen(BINFMT_REGISTER_SCRIPT_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_REGISTER_SCRIPT_STRING, Size)); LxtClose(Fd); Fd = -1; Registration.Name = BINFMT_REGISTER_NAME; Registration.FileType = DT_REG; LxtCheckResult(LxtCheckDirectoryContents(BINFMT_MNT, &Registration, 1)); // // Open the registration file and verify that it behaves as expected. // Status should initially be enabled. // LxtCheckErrno(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR)); LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1)); Buffer[Size] = '\0'; LxtClose(Fd); Fd = -1; LxtCheckStringEqual(Buffer, BINFMT_REGISTRATION_ENABLED_STRING); // // Disable the registration and verify the string changes. // LxtCheckErrno(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR)); Size = strlen(BINFMT_DISABLE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_DISABLE_STRING, Size)); LxtClose(Fd); Fd = -1; LxtCheckErrno(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR)); LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1)); Buffer[Size] = '\0'; LxtClose(Fd); Fd = -1; LxtCheckStringEqual(Buffer, BINFMT_REGISTRATION_DISABLED_STRING); // // Enable and verify the string changes. // LxtCheckErrno(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR)); Size = strlen(BINFMT_ENABLE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_ENABLE_STRING, Size)); LxtClose(Fd); Fd = -1; LxtCheckErrno(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR)); LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1)); Buffer[Size] = '\0'; LxtClose(Fd); Fd = -1; LxtCheckStringEqual(Buffer, BINFMT_REGISTRATION_ENABLED_STRING); // // Remove the new entry via the registration file. // LxtCheckErrno(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR)); Size = strlen(BINFMT_REMOVE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size)); LxtClose(Fd); Fd = -1; // // Attempt to open the file (should fail); // LxtCheckErrnoFailure(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR), ENOENT); ErrorExit: if (Fd > 0) { LxtClose(Fd); } return Result; } int BinFmtStatus(PLXT_ARGS Args) /*++ Description: This routine tests the binfmt status file. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { char Buffer[64]; int Fd; LXT_CHILD_INFO Registration; int Result; ssize_t Size; // // Open the status file and verify that status behaves as expected. // Status should initially be enabled. // LxtCheckErrno(Fd = open(BINFMT_MNT "/status", O_RDWR)); LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1)); Buffer[Size] = '\0'; LxtClose(Fd); Fd = -1; LxtCheckStringEqual(Buffer, BINFMT_STATUS_ENABLED); // // Disable status and verify the string changes. // LxtCheckErrno(Fd = open(BINFMT_MNT "/status", O_RDWR)); Size = strlen(BINFMT_DISABLE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_DISABLE_STRING, Size)); LxtClose(Fd); Fd = -1; LxtCheckErrno(Fd = open(BINFMT_MNT "/status", O_RDWR)); LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1)); Buffer[Size] = '\0'; LxtClose(Fd); Fd = -1; LxtCheckStringEqual(Buffer, BINFMT_STATUS_DISABLED); // // Enable and verify the string changes. // LxtCheckErrno(Fd = open(BINFMT_MNT "/status", O_RDWR)); Size = strlen(BINFMT_ENABLE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_ENABLE_STRING, Size)); LxtClose(Fd); Fd = -1; LxtCheckErrno(Fd = open(BINFMT_MNT "/status", O_RDWR)); LxtCheckErrno(Size = read(Fd, Buffer, sizeof(Buffer) - 1)); Buffer[Size] = '\0'; LxtClose(Fd); Fd = -1; LxtCheckStringEqual(Buffer, BINFMT_STATUS_ENABLED); // // Register a binfmt extension and verify that it is removed when // -1 is written to the status file. // LxtCheckErrno(Fd = open(BINFMT_MNT "/register", O_RDWR)); Size = strlen(BINFMT_REGISTER_SCRIPT_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_REGISTER_SCRIPT_STRING, Size)); LxtClose(Fd); Fd = -1; Registration.Name = BINFMT_REGISTER_NAME; Registration.FileType = DT_REG; LxtCheckResult(LxtCheckDirectoryContents(BINFMT_MNT, &Registration, 1)); // // Remove the new entry via the status file. // LxtCheckErrno(Fd = open(BINFMT_MNT "/status", O_RDWR)); Size = strlen(BINFMT_REMOVE_STRING); LxtCheckErrno(Size = write(Fd, BINFMT_REMOVE_STRING, Size)); LxtClose(Fd); Fd = -1; // // Attempt to open the file (should fail); // LxtCheckErrnoFailure(Fd = open(BINFMT_MNT "/" BINFMT_REGISTER_NAME, O_RDWR), ENOENT); ErrorExit: if (Fd > 0) { LxtClose(Fd); } return Result; } int BinFmtInterpreterEntry(PLXT_ARGS Args) /*++ Description: This routine implements the entry point for the test binfmt interpreter. Arguments: Args - Supplies the command line arguments. Return Value: Returns 0 on success, -1 on failure. --*/ { int Index; int Result; printf("Pid = %d\n", getpid()); for (Index = 0; Index < Args->Argc; Index += 1) { printf("Argv[%d]: %s\n", Index, Args->Argv[Index]); } return 0; }