WSL/test/linux/unit_tests/binfmt.c

1323 lines
34 KiB
C

/*++
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 <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <pthread.h>
#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 <stdio.h>\n" \
"#include <string.h>\n" \
"#include <stdlib.h>\n" \
"#include <fcntl.h>\n" \
"#include <unistd.h>\n" \
"#include <errno.h>\n" \
"#include <sys/auxv.h>\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;
}