mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-04 07:43:21 +00:00
620 lines
13 KiB
C
620 lines
13 KiB
C
![]() |
/*++
|
||
|
|
||
|
Copyright (c) Microsoft. All rights reserved.
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
execve.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This file contains the execve tests.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "lxtcommon.h"
|
||
|
#include "unittests.h"
|
||
|
#include <pthread.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/wait.h>
|
||
|
#include <linux/binfmts.h>
|
||
|
#include "lxtutil.h"
|
||
|
|
||
|
#define LXT_NAME "Execve"
|
||
|
|
||
|
#define LXT_EXECV_TEST_DIRECTORY "/data/test/execvDir"
|
||
|
|
||
|
int ExecveExecValidate(char* Path);
|
||
|
|
||
|
int ExecveValidate(int ExpectedPid);
|
||
|
|
||
|
int ExecveVariationArguments(PLXT_ARGS Args);
|
||
|
|
||
|
int ExecveVariationSingle(PLXT_ARGS Args);
|
||
|
|
||
|
int ExecveVariationMultipleWithThreads(PLXT_ARGS Args);
|
||
|
|
||
|
int ExecveWaitForChild(void);
|
||
|
|
||
|
void* ExecveWorkerThread(void* Arg);
|
||
|
|
||
|
void* ExecveWorkerThread2(void* Arg);
|
||
|
|
||
|
//
|
||
|
// Global constants
|
||
|
//
|
||
|
|
||
|
static const LXT_VARIATION g_LxtVariations[] = {
|
||
|
{"Execve - Single", ExecveVariationSingle},
|
||
|
{"Execve - Multiple with threads", ExecveVariationMultipleWithThreads},
|
||
|
{"Execve - Arguments", ExecveVariationArguments}};
|
||
|
|
||
|
int ExecveTestEntry(int Argc, char* Argv[], char** Envp)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
LXT_ARGS Args;
|
||
|
int Result;
|
||
|
|
||
|
//
|
||
|
// For a child invocation, validate that the PID/TID is as expected.
|
||
|
//
|
||
|
|
||
|
if ((Argc == 3) && (strcmp(Argv[1], "-c") == 0))
|
||
|
{
|
||
|
|
||
|
int Pid;
|
||
|
|
||
|
Pid = atoi(Argv[2]);
|
||
|
LxtCheckResult(ExecveValidate(Pid));
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For environment variable child validation, make sure a non-NULL pointer
|
||
|
// is passed but there are no arguments.
|
||
|
//
|
||
|
|
||
|
if ((Argc == 2) && (strcmp(Argv[1], "-e") == 0))
|
||
|
{
|
||
|
LxtCheckTrue(Envp != NULL);
|
||
|
|
||
|
char** Env;
|
||
|
int Count = 0;
|
||
|
for (Env = Envp; *Env != NULL; Env += 1)
|
||
|
{
|
||
|
Count += 1;
|
||
|
}
|
||
|
|
||
|
LxtCheckTrue(Count == 0);
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Run the master test.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
|
||
|
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
|
||
|
|
||
|
ErrorExit:
|
||
|
LxtUninitialize();
|
||
|
return !LXT_SUCCESS(Result);
|
||
|
}
|
||
|
|
||
|
int ExecveExecValidate(char* Path)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
char* ExecArgs[5];
|
||
|
char ExpectedPid[16];
|
||
|
int Pid;
|
||
|
int Result;
|
||
|
|
||
|
Pid = getpid();
|
||
|
LxtLogInfo("Execve'ing validation for PID %d", Pid);
|
||
|
|
||
|
sprintf(ExpectedPid, "%d", Pid);
|
||
|
ExecArgs[0] = Path;
|
||
|
ExecArgs[1] = "execve";
|
||
|
ExecArgs[2] = "-c";
|
||
|
ExecArgs[3] = ExpectedPid;
|
||
|
ExecArgs[4] = NULL;
|
||
|
execv(ExecArgs[0], ExecArgs);
|
||
|
LxtCheckTrue(FALSE);
|
||
|
Result = LXT_RESULT_SUCCESS;
|
||
|
|
||
|
ErrorExit:
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
int ExecveValidate(int ExpectedPid)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
int Pid;
|
||
|
int Result;
|
||
|
int ThreadCount;
|
||
|
int Tid;
|
||
|
|
||
|
//
|
||
|
// Check that the PID/TID matches the expected value.
|
||
|
//
|
||
|
|
||
|
Pid = getpid();
|
||
|
Tid = gettid();
|
||
|
LxtCheckTrue(Pid == ExpectedPid);
|
||
|
LxtCheckTrue(Tid == ExpectedPid);
|
||
|
Result = LXT_RESULT_SUCCESS;
|
||
|
|
||
|
ErrorExit:
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
#define COMMAND_LINE_SIZE (((size_t)MAX_ARG_STRINGS) + 1)
|
||
|
#define LARGE_STRING_SIZE (((size_t)MAX_ARG_STRLEN) + 1)
|
||
|
#define MAX_STACK_SIZE ((size_t)(2 * 1024 * 1024))
|
||
|
|
||
|
int ExecveVariationArguments(PLXT_ARGS Args)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
int ChildPid;
|
||
|
char* ExecArgs[4];
|
||
|
char** ExecArgsLong;
|
||
|
size_t Index;
|
||
|
char* LongString;
|
||
|
int Result;
|
||
|
int Status;
|
||
|
size_t TotalSize;
|
||
|
|
||
|
ExecArgsLong = NULL;
|
||
|
LongString = NULL;
|
||
|
|
||
|
//
|
||
|
// Test a null environment block.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = "/bin/true";
|
||
|
ExecArgs[1] = NULL;
|
||
|
LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(ExecveWaitForChild());
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Test exec args with spaces and path.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = "/bin/true with a space";
|
||
|
ExecArgs[1] = NULL;
|
||
|
LxtCheckErrno(LxtExecve("/bin/true", ExecArgs, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(ExecveWaitForChild());
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Validate that a null environment block results in zero entries for the
|
||
|
// environment argument to the main function.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = WSL_UNIT_TEST_BINARY;
|
||
|
ExecArgs[1] = "execve";
|
||
|
ExecArgs[2] = "-e";
|
||
|
ExecArgs[3] = NULL;
|
||
|
LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(ExecveWaitForChild());
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate a very long string of 'a' and null terminate to
|
||
|
// validate command line parsing limits.
|
||
|
//
|
||
|
|
||
|
LongString = malloc(LARGE_STRING_SIZE);
|
||
|
if (LongString == NULL)
|
||
|
{
|
||
|
LxtLogError("malloc");
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
memset(LongString, 'a', LARGE_STRING_SIZE);
|
||
|
LongString[LARGE_STRING_SIZE - 1] = '\0';
|
||
|
|
||
|
//
|
||
|
// Create a child and verify that exec fails with too long of a string
|
||
|
// in the command line or environment variable array.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = "/bin/false";
|
||
|
ExecArgs[1] = LongString;
|
||
|
ExecArgs[2] = NULL;
|
||
|
LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), E2BIG);
|
||
|
LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], NULL, &ExecArgs[1]), E2BIG);
|
||
|
|
||
|
//
|
||
|
// Shorten the string and verify the exec call succeeds.
|
||
|
//
|
||
|
|
||
|
ExecArgs[0] = "/bin/true";
|
||
|
ExecArgs[1] = LongString;
|
||
|
ExecArgs[2] = NULL;
|
||
|
LongString[LARGE_STRING_SIZE - 2] = '\0';
|
||
|
LxtCheckErrno(LxtExecve(ExecArgs[0], ExecArgs, NULL));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(ExecveWaitForChild());
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate a huge argument array.
|
||
|
//
|
||
|
|
||
|
ExecArgsLong = malloc(MAX_STACK_SIZE * sizeof(char*));
|
||
|
if (ExecArgsLong == NULL)
|
||
|
{
|
||
|
LxtLogError("malloc of %zd size buffer", (MAX_STACK_SIZE * sizeof(char*)));
|
||
|
|
||
|
goto ErrorExit;
|
||
|
}
|
||
|
|
||
|
for (Index = 0; Index < MAX_STACK_SIZE; Index += 1)
|
||
|
{
|
||
|
ExecArgsLong[Index] = "a";
|
||
|
}
|
||
|
|
||
|
ExecArgsLong[MAX_STACK_SIZE - 1] = NULL;
|
||
|
|
||
|
//
|
||
|
// Create a child and verify that exec fails with too many arguments in the
|
||
|
// command line or environment variable array.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgsLong[0] = "/bin/false";
|
||
|
ExecArgsLong[MAX_STACK_SIZE - 1] = NULL;
|
||
|
LxtCheckErrnoFailure(LxtExecve(ExecArgsLong[0], ExecArgsLong, NULL), E2BIG);
|
||
|
LxtCheckErrnoFailure(LxtExecve(ExecArgsLong[0], NULL, &ExecArgsLong[1]), E2BIG);
|
||
|
|
||
|
//
|
||
|
// Shorten the argument list and verify that the command succeeds.
|
||
|
//
|
||
|
|
||
|
ExecArgsLong[0] = "/bin/true";
|
||
|
ExecArgsLong[(MAX_STACK_SIZE / 4)] = NULL;
|
||
|
LxtCheckErrno(LxtExecve(ExecArgsLong[0], ExecArgsLong, NULL));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(ExecveWaitForChild());
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Test an empty command line array.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
ExecArgs[0] = NULL;
|
||
|
LxtCheckErrno(LxtExecve("/bin/echo", ExecArgs, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit with SIGABRT.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtLogInfo("Waiting for child to exit");
|
||
|
wait(&Status);
|
||
|
LxtLogInfo("Status %d", Status);
|
||
|
LxtCheckTrue((WIFSIGNALED(Status)) && (WTERMSIG(Status) == SIGABRT));
|
||
|
LxtLogInfo("Child exited with SIGABRT");
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Test a null command line pointer.
|
||
|
//
|
||
|
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
LxtCheckErrno(LxtExecve("/bin/echo", NULL, NULL));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit SIGABRT.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtLogInfo("Waiting for child to exit");
|
||
|
wait(&Status);
|
||
|
LxtLogInfo("Status %d", Status);
|
||
|
LxtCheckTrue((WIFSIGNALED(Status)) && (WTERMSIG(Status) == SIGABRT));
|
||
|
LxtLogInfo("Child exited with SIGABRT");
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check that executing a directory fails with the expected error code.
|
||
|
//
|
||
|
|
||
|
mkdir(LXT_EXECV_TEST_DIRECTORY, 0777);
|
||
|
ExecArgs[0] = LXT_EXECV_TEST_DIRECTORY;
|
||
|
ExecArgs[1] = NULL;
|
||
|
LxtCheckErrnoFailure(LxtExecve(ExecArgs[0], ExecArgs, NULL), EACCES);
|
||
|
|
||
|
//
|
||
|
// Verify that exec with a NULL filename fails.
|
||
|
//
|
||
|
|
||
|
LxtCheckErrnoFailure(LxtExecve(NULL, NULL, NULL), EFAULT);
|
||
|
ExecArgs[0] = "/bin/echo";
|
||
|
ExecArgs[1] = NULL;
|
||
|
LxtCheckErrnoFailure(LxtExecve(NULL, ExecArgs, NULL), EFAULT);
|
||
|
|
||
|
Result = LXT_RESULT_SUCCESS;
|
||
|
|
||
|
ErrorExit:
|
||
|
rmdir(LXT_EXECV_TEST_DIRECTORY);
|
||
|
if (LongString != NULL)
|
||
|
{
|
||
|
free(LongString);
|
||
|
}
|
||
|
|
||
|
if (ExecArgsLong != NULL)
|
||
|
{
|
||
|
free(ExecArgsLong);
|
||
|
}
|
||
|
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
_exit(Result);
|
||
|
}
|
||
|
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
int ExecveVariationSingle(PLXT_ARGS Args)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
int ChildPid;
|
||
|
int Result;
|
||
|
|
||
|
LxtLogInfo("Forking single child");
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
|
||
|
//
|
||
|
// The child process executes the validation program.
|
||
|
//
|
||
|
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
LxtCheckResult(ExecveExecValidate(WSL_UNIT_TEST_BINARY));
|
||
|
|
||
|
//
|
||
|
// The parent waits for the child to exit successfully.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LxtCheckResult(ExecveWaitForChild());
|
||
|
}
|
||
|
|
||
|
Result = LXT_RESULT_SUCCESS;
|
||
|
|
||
|
ErrorExit:
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
int ExecveVariationMultipleWithThreads(PLXT_ARGS Args)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
#define NUM_CHILDREN 32
|
||
|
|
||
|
int ChildPid;
|
||
|
int ProcessIndex;
|
||
|
int Result;
|
||
|
|
||
|
//
|
||
|
// Launch all child processes.
|
||
|
//
|
||
|
|
||
|
for (ProcessIndex = 0; ProcessIndex < NUM_CHILDREN; ProcessIndex += 1)
|
||
|
{
|
||
|
LxtLogInfo("Forking child (#%d)", ProcessIndex);
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
|
||
|
//
|
||
|
// In the child, create worker threads and then exec the validation
|
||
|
// step from the leader.
|
||
|
//
|
||
|
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
|
||
|
pthread_t Thread;
|
||
|
int ThreadCount;
|
||
|
int ThreadIndex;
|
||
|
|
||
|
ThreadCount = ProcessIndex + 1;
|
||
|
LxtLogInfo("Creating %d thread(s) for PID %d", ThreadCount, getpid());
|
||
|
for (ThreadIndex = 0; ThreadIndex < ThreadCount; ThreadIndex += 1)
|
||
|
{
|
||
|
LxtCheckResultError(pthread_create(&Thread, NULL, ExecveWorkerThread, NULL));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Sleep for 100ms and then execute the validation step.
|
||
|
//
|
||
|
|
||
|
usleep(100000);
|
||
|
LxtCheckResult(ExecveExecValidate(WSL_UNIT_TEST_BINARY));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Launch again, this time calling exec from the non-leader thread.
|
||
|
//
|
||
|
|
||
|
for (ProcessIndex = 0; ProcessIndex < NUM_CHILDREN; ProcessIndex += 1)
|
||
|
{
|
||
|
LxtLogInfo("Forking child (#%d)", ProcessIndex);
|
||
|
LxtCheckResult(ChildPid = fork());
|
||
|
if (ChildPid == 0)
|
||
|
{
|
||
|
pthread_t Thread;
|
||
|
int ThreadCount;
|
||
|
int ThreadIndex;
|
||
|
|
||
|
ThreadCount = ProcessIndex + 1;
|
||
|
LxtLogInfo("Creating %d thread(s) for PID %d", ThreadCount, getpid());
|
||
|
for (ThreadIndex = 0; ThreadIndex < ThreadCount; ThreadIndex += 1)
|
||
|
{
|
||
|
LxtCheckResultError(pthread_create(&Thread, NULL, ExecveWorkerThread2, Args));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Continuously sleep for 100ms.
|
||
|
//
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
usleep(100000);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Wait for all child processes to exit.
|
||
|
//
|
||
|
|
||
|
LxtLogInfo("Waiting for children to exit");
|
||
|
for (ProcessIndex = 0; ProcessIndex < (2 * NUM_CHILDREN); ProcessIndex += 1)
|
||
|
{
|
||
|
|
||
|
LxtCheckResult(ExecveWaitForChild());
|
||
|
LxtLogInfo("Child exited (#%d)", ProcessIndex);
|
||
|
}
|
||
|
|
||
|
Result = LXT_RESULT_SUCCESS;
|
||
|
|
||
|
ErrorExit:
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
int ExecveWaitForChild(void)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
int Result;
|
||
|
int Status;
|
||
|
|
||
|
wait(&Status);
|
||
|
LxtCheckTrue((WIFEXITED(Status)) && (WEXITSTATUS(Status) == 0));
|
||
|
Result = 0;
|
||
|
|
||
|
ErrorExit:
|
||
|
return Result;
|
||
|
}
|
||
|
|
||
|
void* ExecveWorkerThread(void* Arg)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// Continuously sleep for 100ms.
|
||
|
//
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
usleep(100000);
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void* ExecveWorkerThread2(void* Arg)
|
||
|
|
||
|
/*++
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
|
||
|
int Result;
|
||
|
|
||
|
PLXT_ARGS Args = (PLXT_ARGS)Arg;
|
||
|
|
||
|
//
|
||
|
// Sleep for 100ms and then execute the validation step.
|
||
|
//
|
||
|
|
||
|
usleep(100000);
|
||
|
LxtCheckResult(ExecveExecValidate(WSL_UNIT_TEST_BINARY));
|
||
|
|
||
|
ErrorExit:
|
||
|
return (void*)(long)Result;
|
||
|
}
|