mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 07:23:20 +00:00

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.
619 lines
13 KiB
C
619 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;
|
|
}
|