WSL/test/linux/unit_tests/binfmt.c
WSL Team 697572d664 Initial open source commit for WSL.
Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016.

The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021:

Number of commits on the main branch: 2930
Number of contributors: 31

Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
2025-05-15 12:09:45 -07:00

1322 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;
}