mirror of
https://github.com/microsoft/WSL.git
synced 2025-12-10 00:44:55 -06:00
* link: report them to the Microsoft Security Response Center (MSRC) Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * link: Microsoft Security Response Center Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: , or Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: adapters Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: at Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: attaching them to the bug, please send them to wsl-gh-logs@microsoft.com with the github issue number in the subject, and include a link to your github issue comment in the message body. Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: disk Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: effect Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: for the file Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: for the mount to be available Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: grandchild Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: last pid Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: nonexistent Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: on which the... Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: path Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: pseudo Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: script Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: service, the Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: session Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: strip Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: that a Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: that the Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: the first one we find in the test Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: to which Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: waitpid Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: whether Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: while the relay was being set up, Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: with an Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> * spelling: without specifying Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --------- Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Co-authored-by: Ben Hillis <benhillis@gmail.com>
1701 lines
44 KiB
C
1701 lines
44 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
WaitPid.c
|
|
|
|
Abstract:
|
|
|
|
This file is a WaitPid test.
|
|
|
|
--*/
|
|
|
|
#include "lxtcommon.h"
|
|
#include "unittests.h"
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/prctl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "lxtutil.h"
|
|
|
|
#define LXT_NAME "WaitPid"
|
|
|
|
#define WAITPID_DEFAULT_WAIT_TIMEOUT_US 100000
|
|
#define WAITPID_DEFAULT_WAIT_COUNT 20
|
|
#define WAITPID_THREADGROUP_LEADER_UID 1044
|
|
#define WAITPID_PTHREAD_UID 1055
|
|
|
|
bool g_VmMode = false;
|
|
|
|
int GetPPidPoll(pid_t ExpectedPid);
|
|
|
|
LXT_VARIATION_HANDLER WaitPidVariationExitStatusBlock;
|
|
|
|
int WaitPidVariationExitStatusHelper(PLXT_ARGS Args, int Blocking);
|
|
|
|
LXT_VARIATION_HANDLER WaitPidVariationExitStatusPoll;
|
|
LXT_VARIATION_HANDLER WaitPidVariationInitPid;
|
|
LXT_VARIATION_HANDLER WaitPidVariationParentChild;
|
|
LXT_VARIATION_HANDLER WaitPidVariationProcessGroup;
|
|
LXT_VARIATION_HANDLER WaitPidVariationInvalidParameter;
|
|
LXT_VARIATION_HANDLER WaitPidVariationWaitId;
|
|
LXT_VARIATION_HANDLER WaitPidVariationCloneParent;
|
|
LXT_VARIATION_HANDLER WaitPidVariationZombie;
|
|
LXT_VARIATION_HANDLER WaitPidVariationZombieStress;
|
|
|
|
//
|
|
// Global constants
|
|
//
|
|
// FIXME: Enable parent\child test when clone gs issue is resolved.
|
|
//
|
|
|
|
static const LXT_VARIATION g_LxtVariations[] = {
|
|
{"WaitPidVariation - Exit status poll", WaitPidVariationExitStatusPoll},
|
|
{"WaitPidVariation - Exit status block", WaitPidVariationExitStatusBlock},
|
|
{"WaitPidVariation - Init pid", WaitPidVariationInitPid},
|
|
{"WaitPidVariation - Process groups", WaitPidVariationProcessGroup},
|
|
{"WaitPidVariation - Invalid parameter", WaitPidVariationInvalidParameter},
|
|
{"WaitPidVariation - waitid", WaitPidVariationWaitId},
|
|
{"WaitPidVariation - CLONE_PARENT", WaitPidVariationCloneParent},
|
|
{"WaitPidVariation - zombie support", WaitPidVariationZombie},
|
|
{"WaitPidVariation - zombie stress", WaitPidVariationZombieStress}};
|
|
|
|
int WaitPidTestEntry(int Argc, char* Argv[])
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
LXT_ARGS Args;
|
|
int Result;
|
|
|
|
if (LxtWslVersion() == 2)
|
|
{
|
|
g_VmMode = true;
|
|
}
|
|
|
|
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
|
|
LXT_SYNCHRONIZATION_POINT_INIT();
|
|
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
|
|
|
|
ErrorExit:
|
|
LXT_SYNCHRONIZATION_POINT_DESTROY();
|
|
LxtUninitialize();
|
|
return !LXT_SUCCESS(Result);
|
|
}
|
|
|
|
int GetPPidPoll(pid_t ExpectedPPid)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
pid_t CurrentPPid;
|
|
int Result;
|
|
int WaitCount;
|
|
|
|
//
|
|
// Wait for the current ppid to reach the expected ppid.
|
|
//
|
|
|
|
for (WaitCount = 0; WaitCount < WAITPID_DEFAULT_WAIT_COUNT; ++WaitCount)
|
|
{
|
|
CurrentPPid = getppid();
|
|
if (CurrentPPid == ExpectedPPid)
|
|
{
|
|
break;
|
|
}
|
|
|
|
usleep(WAITPID_DEFAULT_WAIT_TIMEOUT_US);
|
|
}
|
|
|
|
if (WaitCount == WAITPID_DEFAULT_WAIT_COUNT)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected pid, %d != %d", CurrentPPid, ExpectedPPid);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int LxtWaitPidHelper(pid_t ChildPid, int ExpectedWaitStatus, int Blocking)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int SecondWaitPidStatus;
|
|
int Result;
|
|
int WaitCount;
|
|
int WaitPidResult;
|
|
int WaitPidStatus;
|
|
|
|
if (Blocking == 0)
|
|
{
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, ExpectedWaitStatus));
|
|
}
|
|
else
|
|
{
|
|
LxtCheckErrno((WaitPidResult = waitpid(ChildPid, &WaitPidStatus, 0)));
|
|
if ((WaitPidStatus & 0xFFFF0000) != 0)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected high short status: %d - %d", WaitPidStatus, ExpectedWaitStatus);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (WaitPidStatus != ExpectedWaitStatus)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected status: %d != %d", WaitPidStatus, ExpectedWaitStatus);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (WIFEXITED(WaitPidStatus) != 0)
|
|
{
|
|
LxtCheckErrnoFailure(waitpid(ChildPid, &SecondWaitPidStatus, WNOHANG), ECHILD);
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationInitPid(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
pid_t ChildPid;
|
|
int ExitCodeIndex;
|
|
unsigned char ExitCode;
|
|
int ExpectedPid;
|
|
int ExpectedWaitStatus;
|
|
pid_t ParentParentPid;
|
|
LXT_PIPE Pipe = {-1, -1};
|
|
int Result;
|
|
pid_t WaitPidResult;
|
|
int WaitPidStatus;
|
|
int WaitPipe;
|
|
|
|
//
|
|
// Determine who the subreaper of the test is. On WSL 1 it will be init, on
|
|
// WSL 2 it will be the relay process (parent of this process).
|
|
//
|
|
|
|
ExpectedPid = LXT_INIT_PID;
|
|
if (g_VmMode != false)
|
|
{
|
|
ExpectedPid = getppid();
|
|
}
|
|
|
|
LxtCheckResult(LxtCreatePipe(&Pipe));
|
|
|
|
//
|
|
// Check when a parent dies, the child has a parent pid of the subreaper.
|
|
//
|
|
|
|
ExitCode = 0;
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtCheckResult(GetPPidPoll(ExpectedPid));
|
|
LxtCheckErrno(write(Pipe.Write, &WaitPipe, sizeof(WaitPipe)));
|
|
}
|
|
|
|
_exit(ExitCode);
|
|
}
|
|
else
|
|
{
|
|
LxtCheckResult(LxtWaitPidPoll(-1, ExitCode));
|
|
LxtCheckErrno(read(Pipe.Read, &WaitPipe, sizeof(WaitPipe)));
|
|
}
|
|
|
|
//
|
|
// The init process should never be a child.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(waitpid(LXT_INIT_PID, &WaitPidStatus, WNOHANG), ECHILD);
|
|
|
|
ErrorExit:
|
|
LxtClosePipe(&Pipe);
|
|
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationExitStatusBlock(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
return WaitPidVariationExitStatusHelper(Args, 1);
|
|
}
|
|
|
|
int WaitPidVariationExitStatusHelper(PLXT_ARGS Args, int Blocking)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int ExitCodeIndex;
|
|
unsigned char ExitCodes[] = {0, 1, 128, 255};
|
|
pid_t ChildPid[LXT_COUNT_OF(ExitCodes)];
|
|
int ExpectedWaitStatus;
|
|
pid_t ParentParentPid;
|
|
pid_t ParentPid;
|
|
int Result;
|
|
|
|
//
|
|
// Check that the correct _exit status is returned to a parent process and
|
|
// that it can only be checked once serially.
|
|
//
|
|
|
|
ParentParentPid = getppid();
|
|
ParentPid = getpid();
|
|
for (ExitCodeIndex = 0; ExitCodeIndex < LXT_COUNT_OF(ExitCodes); ++ExitCodeIndex)
|
|
{
|
|
LxtCheckErrno(ChildPid[ExitCodeIndex] = fork());
|
|
if (ChildPid[ExitCodeIndex] == 0)
|
|
{
|
|
ChildPid[ExitCodeIndex] = getpid();
|
|
if (getppid() != ParentPid)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected parent pid in child - %d != %d", ChildPid[ExitCodeIndex], ParentPid);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_exit(ExitCodes[ExitCodeIndex]);
|
|
}
|
|
else
|
|
{
|
|
ExpectedWaitStatus = ExitCodes[ExitCodeIndex] << 8;
|
|
LxtCheckResult(LxtWaitPidHelper(ChildPid[ExitCodeIndex], ExpectedWaitStatus, Blocking));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Recheck the results after launching the children in parallel.
|
|
//
|
|
|
|
LxtLogInfo("Running forks in parallel...");
|
|
for (ExitCodeIndex = 0; ExitCodeIndex < LXT_COUNT_OF(ExitCodes); ++ExitCodeIndex)
|
|
{
|
|
LxtCheckErrno(ChildPid[ExitCodeIndex] = fork());
|
|
if (ChildPid[ExitCodeIndex] == 0)
|
|
{
|
|
ChildPid[ExitCodeIndex] = getpid();
|
|
if (getppid() != ParentPid)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected parent pid in child - %d != %d", ChildPid[ExitCodeIndex], ParentPid);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
_exit(ExitCodes[ExitCodeIndex]);
|
|
}
|
|
}
|
|
|
|
while (ExitCodeIndex--)
|
|
{
|
|
ExpectedWaitStatus = ExitCodes[ExitCodeIndex] << 8;
|
|
LxtCheckResult(LxtWaitPidHelper(ChildPid[ExitCodeIndex], ExpectedWaitStatus, Blocking));
|
|
}
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationExitStatusPoll(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
return WaitPidVariationExitStatusHelper(Args, 0);
|
|
}
|
|
|
|
typedef struct _WAIT_PID_PARENT_DATA
|
|
{
|
|
int Generation;
|
|
pid_t ParentPid;
|
|
pid_t ParentTid;
|
|
LXT_PIPE Pipes[4];
|
|
LXT_CLONE_ARGS CloneArgs[2];
|
|
pid_t ForkPid;
|
|
} WAIT_PID_PARENT_DATA, *PWAIT_PID_PARENT_DATA;
|
|
|
|
void WaitPidPrintData(PWAIT_PID_PARENT_DATA CurrentData, char* Message)
|
|
|
|
{
|
|
|
|
int Index;
|
|
pid_t WaitResult;
|
|
int WaitStatus;
|
|
|
|
//
|
|
// TODO: Enable _CLONE
|
|
//
|
|
|
|
LxtLogInfo("***** %s", Message);
|
|
WaitStatus = -1;
|
|
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)
|
|
{
|
|
LxtLogInfo("Before waitpid", Message);
|
|
WaitResult = waitpid(CurrentData->CloneArgs[Index].CloneId, &WaitStatus, WNOHANG);
|
|
LxtLogInfo("%s - Clone %d WNOHANG - %d, %d", Message, CurrentData->CloneArgs[Index].CloneId, WaitResult, WaitStatus);
|
|
// WaitResult = waitpid(CurrentData->CloneArgs[Index].CloneId, &WaitStatus, WNOHANG | __WCLONE);
|
|
// LxtLogInfo("%s - Clone %d WNOHANG | __WCLONE - %d, %d", Message, CurrentData->CloneArgs[Index].CloneId, WaitResult, WaitStatus);
|
|
}
|
|
|
|
WaitResult = waitpid(CurrentData->ForkPid, &WaitStatus, WNOHANG);
|
|
LxtLogInfo("%s - Fork %d WNOHANG - %d, %d", Message, CurrentData->ForkPid, WaitResult, WaitStatus);
|
|
// WaitResult = waitpid(CurrentData->ForkPid, &WaitStatus, WNOHANG | __WCLONE);
|
|
// LxtLogInfo("%s - Fork %d WNOHANG | __WCLONE - %d, %d", Message, CurrentData->ForkPid, WaitResult, WaitStatus);
|
|
LxtLogInfo("***** %s", Message);
|
|
|
|
return;
|
|
}
|
|
|
|
int WaitPidVariationParentChildClone(void* Parameter)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int CloneIndex;
|
|
PWAIT_PID_PARENT_DATA CurrentData;
|
|
pid_t CurrentPid;
|
|
pid_t CurrentTid;
|
|
int Index;
|
|
int ParentIndex;
|
|
int Result;
|
|
int WaitPipe;
|
|
|
|
CurrentData = Parameter;
|
|
CurrentTid = gettid();
|
|
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)
|
|
{
|
|
if (CurrentData->CloneArgs[Index].CloneId == CurrentTid)
|
|
{
|
|
CloneIndex = Index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index == LXT_COUNT_OF(CurrentData->CloneArgs))
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unable to find clone tid %d", CurrentTid);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(read(CurrentData->Pipes[CloneIndex].Read, &WaitPipe, sizeof(WaitPipe)));
|
|
CurrentPid = getpid();
|
|
if (CurrentPid != CurrentData->ParentPid)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected pid for clone %d: %d != %d", CloneIndex, CurrentPid, CurrentData->ParentPid);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
WaitPidPrintData(CurrentData, "Clone");
|
|
ParentIndex = LXT_COUNT_OF(CurrentData->CloneArgs) + 1;
|
|
LxtCheckErrno(write(CurrentData->Pipes[ParentIndex].Write, &WaitPipe, sizeof(WaitPipe)));
|
|
LxtCheckErrno(read(CurrentData->Pipes[CloneIndex].Read, &WaitPipe, sizeof(WaitPipe)));
|
|
|
|
ErrorExit:
|
|
LxtLogInfo("Clone %d exit", CloneIndex);
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationParentChildFork(PWAIT_PID_PARENT_DATA ParentData)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
WAIT_PID_PARENT_DATA CurrentData;
|
|
int ForkIndex;
|
|
pid_t ParentPid;
|
|
int ParentIndex;
|
|
int Result;
|
|
int WaitPipe;
|
|
|
|
ForkIndex = LXT_COUNT_OF(ParentData->CloneArgs);
|
|
LxtCheckErrno(read(ParentData->Pipes[ForkIndex].Read, &WaitPipe, sizeof(WaitPipe)));
|
|
CurrentData.ParentPid = getpid();
|
|
CurrentData.ParentTid = gettid();
|
|
if (CurrentData.ParentPid != CurrentData.ParentTid)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Current fork thread is not thread group leader %d != %d", CurrentData.ParentPid, CurrentData.ParentTid);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ParentPid = getppid();
|
|
if (ParentPid != ParentData->ParentPid)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected ppid %d != %d", ParentPid, ParentData->ParentPid);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (CurrentData.ParentPid == ParentData->ParentPid)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Unexpected pid %d == %d", CurrentData.ParentPid, ParentData->ParentPid);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ParentData->ForkPid = getpid();
|
|
WaitPidPrintData(ParentData, "Fork");
|
|
ParentIndex = LXT_COUNT_OF(ParentData->CloneArgs) + 1;
|
|
LxtCheckErrno(write(ParentData->Pipes[ParentIndex].Write, &WaitPipe, sizeof(WaitPipe)));
|
|
LxtCheckErrno(read(ParentData->Pipes[ForkIndex].Read, &WaitPipe, sizeof(WaitPipe)));
|
|
|
|
ErrorExit:
|
|
_exit(Result);
|
|
}
|
|
|
|
int WaitPidVariationCreateParentData(PWAIT_PID_PARENT_DATA CurrentData)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
int Index;
|
|
int ParentIndex;
|
|
int Result;
|
|
int WaitPipe;
|
|
pid_t WaitResult;
|
|
int WaitStatus;
|
|
|
|
CurrentData->Generation = CurrentData->Generation + 1;
|
|
CurrentData->ParentPid = getpid();
|
|
CurrentData->ParentTid = gettid();
|
|
if (CurrentData->ParentPid != CurrentData->ParentTid)
|
|
{
|
|
Result = LXT_RESULT_FAILURE;
|
|
LxtLogError("Current thread is not thread group leader %d != %d", CurrentData->ParentPid, CurrentData->ParentTid);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes); ++Index)
|
|
{
|
|
LxtCheckResult(LxtCreatePipe(&CurrentData->Pipes[Index]));
|
|
}
|
|
|
|
//
|
|
// TODO: Enable _CLONE
|
|
//
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->CloneArgs); ++Index)
|
|
{
|
|
LxtCheckResult(LxtClone(WaitPidVariationParentChildClone, CurrentData, LXT_CLONE_FLAGS_DEFAULT, &CurrentData->CloneArgs[Index]));
|
|
}
|
|
|
|
LxtCheckErrno(CurrentData->ForkPid = fork());
|
|
if (CurrentData->ForkPid == 0)
|
|
{
|
|
WaitPidVariationParentChildFork(CurrentData);
|
|
}
|
|
|
|
WaitPidPrintData(CurrentData, "Parent");
|
|
ParentIndex = LXT_COUNT_OF(CurrentData->CloneArgs) + 1;
|
|
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes) - 1; ++Index)
|
|
{
|
|
LxtCheckErrno(write(CurrentData->Pipes[Index].Write, &WaitPipe, sizeof(WaitPipe)));
|
|
LxtCheckErrno(read(CurrentData->Pipes[ParentIndex].Read, &WaitPipe, sizeof(WaitPipe)));
|
|
}
|
|
|
|
//
|
|
// Release the waiters
|
|
//
|
|
|
|
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes) - 1; ++Index)
|
|
{
|
|
LxtCheckErrno(write(CurrentData->Pipes[Index].Write, &WaitPipe, sizeof(WaitPipe)));
|
|
}
|
|
|
|
ErrorExit:
|
|
for (Index = 0; Index < LXT_COUNT_OF(CurrentData->Pipes); ++Index)
|
|
{
|
|
LxtClosePipe(&CurrentData->Pipes[Index]);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationParentChild(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
--*/
|
|
|
|
{
|
|
|
|
WAIT_PID_PARENT_DATA CurrentData;
|
|
int Result;
|
|
|
|
memset(&CurrentData, 0, sizeof(CurrentData));
|
|
LxtCheckResult(WaitPidVariationCreateParentData(&CurrentData));
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationProcessGroup(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests waiting on all children in the same process group.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
const int OtherGroupChildCount = 2;
|
|
pid_t OtherGroupChild[OtherGroupChildCount];
|
|
int Result;
|
|
pid_t SameGroupChild;
|
|
int Status;
|
|
int WaitResult;
|
|
|
|
LxtCheckResult(LxtSignalBlock(SIGUSR1));
|
|
for (int Child = 0; Child < OtherGroupChildCount; Child++)
|
|
{
|
|
LxtCheckErrno(OtherGroupChild[Child] = fork());
|
|
if (OtherGroupChild[Child] == 0)
|
|
{
|
|
|
|
//
|
|
// Change process group, then signal the parent.
|
|
//
|
|
|
|
LxtCheckErrnoZeroSuccess(setpgid(0, 0));
|
|
LxtCheckErrnoZeroSuccess(kill(getppid(), SIGUSR1));
|
|
_exit(0);
|
|
}
|
|
|
|
//
|
|
// Wait to make sure the child changed its process group.
|
|
//
|
|
|
|
LxtCheckResult(LxtSignalWaitBlocked(SIGUSR1, OtherGroupChild[Child], 2));
|
|
}
|
|
|
|
LxtCheckErrno(SameGroupChild = fork());
|
|
if (SameGroupChild == 0)
|
|
{
|
|
_exit(0);
|
|
}
|
|
|
|
//
|
|
// Wait for the same process group, which should return the status of the
|
|
// second child.
|
|
//
|
|
|
|
LxtCheckResult(WaitResult = LxtWaitPidPoll(0, 0));
|
|
LxtCheckEqual(WaitResult, SameGroupChild, "%d");
|
|
|
|
//
|
|
// Wait again, which should fail because there are no more children in this
|
|
// process group.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(waitpid(0, &Status, WNOHANG), ECHILD);
|
|
|
|
//
|
|
// Wait on the specific process group of one of the remaining children.
|
|
//
|
|
|
|
LxtCheckResult(WaitResult = LxtWaitPidPoll(-OtherGroupChild[0], 0));
|
|
LxtCheckEqual(WaitResult, OtherGroupChild[0], "%d");
|
|
|
|
//
|
|
// Wait on all children, which should return the child in the other process
|
|
// group.
|
|
//
|
|
|
|
LxtCheckResult(WaitResult = LxtWaitPidPoll(-1, 0));
|
|
LxtCheckEqual(WaitResult, OtherGroupChild[1], "%d");
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationInvalidParameter(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests invalid parameter handling for the waitpid system call.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
int Status;
|
|
|
|
LxtCheckErrnoFailure(waitpid(0, &Status, WEXITED), EINVAL);
|
|
LxtCheckErrnoFailure(waitpid(0, &Status, WNOWAIT), EINVAL);
|
|
LxtCheckErrnoFailure(waitpid(0, NULL, WNOHANG), ECHILD);
|
|
LxtCheckErrnoFailure(waitpid(0, (void*)-1, WNOHANG), ECHILD);
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
void* WaitPidVariationWaitIdThread(void* Parameter)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine is the thread handler for the waitid test.
|
|
|
|
Arguments:
|
|
|
|
Parameter - Supplies the thread parameter.
|
|
|
|
Return Value:
|
|
|
|
Returns the thread id on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int Result;
|
|
LxtLogInfo("WaitPid child tid %d", gettid());
|
|
LxtCheckErrno(LxtSetUid(WAITPID_PTHREAD_UID));
|
|
|
|
//
|
|
// Enter a very long sleep, this will be interrupted when the threadgroup
|
|
// leader dies.
|
|
//
|
|
|
|
sleep(-1);
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
|
|
|
|
return (void*)Result;
|
|
|
|
#pragma GCC diagnostic pop
|
|
}
|
|
|
|
int WaitPidVariationWaitId(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests the waitid system call.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildPid;
|
|
int ExpectedStatus;
|
|
int Result;
|
|
siginfo_t SigInfo;
|
|
pthread_t Thread;
|
|
struct rusage Usage;
|
|
|
|
ExpectedStatus = 44;
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
|
|
//
|
|
// Create a child thread, set the uid of the threadgroup leader,
|
|
// and exit.
|
|
//
|
|
|
|
LxtCheckResultError(pthread_create(&Thread, NULL, WaitPidVariationWaitIdThread, NULL));
|
|
|
|
LxtLogInfo("Waitid parent tid %d", gettid());
|
|
|
|
//
|
|
// Briefly sleep to allow the pthread to run.
|
|
//
|
|
|
|
sleep(1);
|
|
LxtCheckErrno(LxtSetUid(WAITPID_THREADGROUP_LEADER_UID));
|
|
_exit(ExpectedStatus);
|
|
}
|
|
|
|
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_ALL, 0, &SigInfo, WEXITED, &Usage));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
|
|
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
|
|
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
|
|
LxtCheckEqual(SigInfo.si_uid, WAITPID_THREADGROUP_LEADER_UID, "%d");
|
|
|
|
//
|
|
// Wait for a specific child.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(ExpectedStatus);
|
|
}
|
|
|
|
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, WEXITED, &Usage));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
|
|
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
|
|
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
|
|
|
|
//
|
|
// Wait with WNOHANG specified.
|
|
//
|
|
// N.B. Parent must sleep to allow the child to exit before waiting.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(ExpectedStatus);
|
|
}
|
|
|
|
sleep(1);
|
|
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, (WEXITED | WNOHANG), &Usage));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
|
|
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
|
|
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
|
|
|
|
//
|
|
// Wait with a null siginfo structure.
|
|
//
|
|
// N.B. The man page states that the pid of the child should be returned but
|
|
// this is not the case. The wait should still be consumed so the
|
|
// second wait should fail with ECHILD.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(0);
|
|
}
|
|
|
|
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL));
|
|
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);
|
|
|
|
//
|
|
// Wait with the WNOWAIT option supplied which means the wait is not
|
|
// consumed. Wait again to consume the wait and verify it was not consumed
|
|
// by the first call.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(ExpectedStatus);
|
|
}
|
|
|
|
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, (WEXITED | WNOWAIT), &Usage));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
|
|
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
|
|
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
|
|
LxtCheckErrnoZeroSuccess(Result = LxtWaitId(P_PID, ChildPid, &SigInfo, WEXITED, &Usage));
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
LxtCheckEqual(SigInfo.si_code, CLD_EXITED, "%d");
|
|
LxtCheckEqual(SigInfo.si_status, ExpectedStatus, "%d");
|
|
LxtCheckEqual(SigInfo.si_pid, ChildPid, "%d");
|
|
|
|
//
|
|
// Call getrusage with the supported values.
|
|
//
|
|
|
|
LxtCheckErrno(getrusage(RUSAGE_SELF, &Usage));
|
|
LxtCheckErrno(getrusage(RUSAGE_THREAD, &Usage));
|
|
LxtCheckErrno(getrusage(RUSAGE_CHILDREN, &Usage));
|
|
|
|
//
|
|
// Invalid parameter variations.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(0);
|
|
}
|
|
|
|
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WNOHANG, NULL), EINVAL);
|
|
LxtCheckErrnoFailure(LxtWaitId(-1, 0, NULL, WEXITED, NULL), EINVAL);
|
|
LxtCheckErrnoFailure(LxtWaitId(P_PGID + 5, 0, NULL, WEXITED, NULL), EINVAL);
|
|
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, 0x10, NULL), EINVAL);
|
|
|
|
//
|
|
// N.B. Providing an invalid pointer to a siginfo structure returns efault
|
|
// but consumes the wait.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, (void*)-1, WEXITED, NULL), EFAULT);
|
|
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(0);
|
|
}
|
|
|
|
//
|
|
// N.B. Providing an invalid pointer to a rusage structure returns efault
|
|
// but consumes the wait.
|
|
//
|
|
|
|
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, (void*)-1), EFAULT);
|
|
LxtCheckErrnoFailure(LxtWaitId(P_ALL, 0, NULL, WEXITED, NULL), ECHILD);
|
|
|
|
ErrorExit:
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationCloneParentChild(void)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine is the child process for WaitPidVariationCloneParent.
|
|
|
|
Arguments:
|
|
|
|
None..
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
pid_t ChildPid;
|
|
pid_t GrandChildParent;
|
|
pid_t ChildParent;
|
|
int Result;
|
|
int WaitPidStatus;
|
|
|
|
Result = 0;
|
|
|
|
//
|
|
// Create a child process with the CLONE_PARENT flag.
|
|
//
|
|
// The new process should not be reported as a child.
|
|
//
|
|
|
|
ChildParent = getppid();
|
|
LxtLogInfo("ChildParent %d", ChildParent);
|
|
LxtCheckResult(ChildPid = LxtCloneSyscall(CLONE_PARENT | SIGCHLD, NULL, NULL, NULL, NULL));
|
|
if (ChildPid == 0)
|
|
{
|
|
GrandChildParent = getppid();
|
|
LxtCheckEqual(ChildParent, GrandChildParent, "%d");
|
|
LxtLogInfo("Grand child %d exiting", LxtGetTid());
|
|
}
|
|
else
|
|
{
|
|
LxtCheckErrnoFailure(waitpid(ChildPid, &WaitPidStatus, 0), ECHILD);
|
|
LxtLogInfo("Child %d exiting", LxtGetTid());
|
|
}
|
|
|
|
ErrorExit:
|
|
_exit(Result);
|
|
}
|
|
|
|
int WaitPidVariationCloneParent(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests invalid parameter handling for the waitpid system call.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
pid_t ChildPid;
|
|
pid_t GrandChildPid;
|
|
int Result;
|
|
int Status;
|
|
|
|
ChildPid = -1;
|
|
|
|
//
|
|
// Create a child process, that in turn creates a grandchild process
|
|
// with CLONE_PARENT.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
WaitPidVariationCloneParentChild();
|
|
}
|
|
|
|
//
|
|
// Wait for the child and the grandchild.
|
|
//
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
|
LxtCheckResult(GrandChildPid = LxtWaitPidPoll(0, 0));
|
|
LxtLogInfo("Waited on grandchild %d", GrandChildPid);
|
|
|
|
//
|
|
// Check that the same scenario works when parent dies before
|
|
// CLONE_PARENT.
|
|
//
|
|
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
sleep(1);
|
|
WaitPidVariationCloneParentChild();
|
|
}
|
|
|
|
_exit(0);
|
|
}
|
|
|
|
//
|
|
// Wait for the child and give the grandchild time to finish.
|
|
//
|
|
|
|
LxtCheckResult(LxtWaitPidPoll(ChildPid, 0));
|
|
LxtLogInfo("Waited on child %d", ChildPid);
|
|
sleep(2);
|
|
|
|
ErrorExit:
|
|
if (ChildPid == 0)
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
typedef struct _WAITPID_THREAD_PARAMETERS
|
|
{
|
|
pid_t ChildPid;
|
|
pid_t GrandChildPid1;
|
|
pid_t GrandChildPid2;
|
|
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(ChildPid);
|
|
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);
|
|
LXT_PIPE Pipe;
|
|
int ChildLevel;
|
|
int Variation;
|
|
} WAITPID_THREAD_PARAMETERS, *PWAITPID_THREAD_PARAMETERS;
|
|
|
|
int ThreadZombieThread(void* Context)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine is the thread proc used by WaitPidVariationThreadZombie.
|
|
|
|
Arguments:
|
|
|
|
Context - Supplies the thread context.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
int ChildLevel;
|
|
pid_t ChildPid;
|
|
int Flags;
|
|
pid_t GrandChildPid1;
|
|
pid_t GrandChildPid2;
|
|
LXT_CLONE_ARGS CloneArgs;
|
|
WAITPID_THREAD_PARAMETERS LocalParameter;
|
|
PWAITPID_THREAD_PARAMETERS Parameter;
|
|
int Result;
|
|
int Status;
|
|
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(ChildPid);
|
|
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);
|
|
|
|
memcpy(&LocalParameter, Context, sizeof(LocalParameter));
|
|
ChildPid = 0;
|
|
ChildLevel = LocalParameter.ChildLevel;
|
|
if (ChildLevel != 0)
|
|
{
|
|
free(Context);
|
|
}
|
|
|
|
LxtSyncChildPidParent = LocalParameter.LxtSyncChildPidParent;
|
|
LxtSyncChildPidChild = LocalParameter.LxtSyncChildPidChild;
|
|
GrandChildPid1 = -1;
|
|
GrandChildPid2 = -1;
|
|
LxtSyncGrandChildPid2Parent = LocalParameter.LxtSyncGrandChildPid2Parent;
|
|
LxtSyncGrandChildPid2Child = LocalParameter.LxtSyncGrandChildPid2Child;
|
|
Parameter = NULL;
|
|
|
|
LxtSignalInitializeThread();
|
|
|
|
if (ChildLevel == 0)
|
|
{
|
|
LxtLogInfo("Child %d starting, variation = %d...", getpid(), LocalParameter.Variation);
|
|
|
|
LxtCheckResult(LxtSignalInitialize());
|
|
LxtCheckResult(LxtSignalSetupHandler(SIGCHLD, SA_SIGINFO));
|
|
LxtCheckErrno(setsid());
|
|
switch (LocalParameter.Variation)
|
|
{
|
|
case 0:
|
|
Flags = SIGCHLD;
|
|
break;
|
|
|
|
case 1:
|
|
Flags = CLONE_FS | CLONE_FILES | SIGCHLD;
|
|
break;
|
|
|
|
case 2:
|
|
Flags = CLONE_FS | CLONE_FILES;
|
|
break;
|
|
|
|
case 3:
|
|
Flags = CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_FS | CLONE_FILES;
|
|
break;
|
|
};
|
|
|
|
Parameter = malloc(sizeof(*Parameter));
|
|
if (Parameter != NULL)
|
|
{
|
|
memcpy(Parameter, &LocalParameter, sizeof(*Parameter));
|
|
}
|
|
|
|
LxtCheckNotEqual(Parameter, NULL, "%p");
|
|
Parameter->ChildLevel = 1;
|
|
|
|
//
|
|
// The clone stack is leaked but the current process will exit shortly.
|
|
//
|
|
|
|
LxtCheckErrno(LxtClone(ThreadZombieThread, Parameter, Flags, &CloneArgs));
|
|
|
|
Parameter = NULL;
|
|
GrandChildPid1 = CloneArgs.CloneId;
|
|
LocalParameter.GrandChildPid1 = GrandChildPid1;
|
|
Parameter = malloc(sizeof(*Parameter));
|
|
if (Parameter != NULL)
|
|
{
|
|
memcpy(Parameter, &LocalParameter, sizeof(*Parameter));
|
|
}
|
|
|
|
LxtCheckNotEqual(Parameter, NULL, "%p");
|
|
Parameter->ChildLevel = 2;
|
|
|
|
//
|
|
// The clone stack is leaked but the current process will exit shortly.
|
|
//
|
|
|
|
LxtCheckErrno(LxtClone(ThreadZombieThread, Parameter, Flags, &CloneArgs));
|
|
|
|
Parameter = NULL;
|
|
LocalParameter.GrandChildPid2 = CloneArgs.CloneId;
|
|
|
|
LxtSignalWait();
|
|
if (LocalParameter.Variation < 2)
|
|
{
|
|
LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, GrandChildPid1, getuid(), 0));
|
|
}
|
|
else if (LocalParameter.Variation != 3)
|
|
{
|
|
LxtCheckResult(LxtSignalCheckNoSignal());
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_CHILD();
|
|
LxtCheckErrno(write(LocalParameter.Pipe.Write, &LocalParameter, sizeof(LocalParameter)));
|
|
|
|
LXT_SYNCHRONIZATION_POINT_CHILD();
|
|
|
|
//
|
|
// Exiting with one zombie child, and one child waiting for an exit
|
|
// signal.
|
|
//
|
|
|
|
LxtLogInfo("Exiting pid = %d", getpid());
|
|
Result = 0;
|
|
goto ErrorExit;
|
|
}
|
|
else if (ChildLevel == 1)
|
|
{
|
|
|
|
//
|
|
// First child created via this thread, grandchild of the original.
|
|
//
|
|
|
|
GrandChildPid1 = 0;
|
|
LxtLogInfo("Grandchild %d starting...", getpid());
|
|
LxtLogInfo("Exiting pid = %d", getpid());
|
|
Result = 0;
|
|
goto ErrorExit;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Second child created via this thread, second grandchild of the
|
|
// original.
|
|
//
|
|
|
|
GrandChildPid2 = 0;
|
|
LxtLogInfo("Grandchild %d starting...", getpid());
|
|
LxtCheckResult(LxtSignalInitialize());
|
|
LxtCheckResult(LxtSignalSetupHandler(SIGHUP, SA_SIGINFO));
|
|
LXT_SYNCHRONIZATION_POINT_CHILD_FOR(GrandChildPid2);
|
|
LxtCheckResult(LxtSignalCheckNoSignal());
|
|
LxtLogInfo("Exiting pid = %d", getpid());
|
|
Result = 0;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ErrorExit:
|
|
if (Result != 0)
|
|
{
|
|
|
|
//
|
|
// Intentionally orphaning the thread unless there was an error.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END_FOR(GrandChildPid2, FALSE);
|
|
}
|
|
|
|
if (ChildLevel == 0)
|
|
{
|
|
if (LocalParameter.Variation != 3)
|
|
{
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
}
|
|
else
|
|
{
|
|
LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD();
|
|
}
|
|
}
|
|
|
|
if (Parameter != NULL)
|
|
{
|
|
free(Parameter);
|
|
}
|
|
|
|
syscall(SYS_exit, Result);
|
|
}
|
|
|
|
int WaitPidVariationZombie(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine tests zombie handling with waitpid system call.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[50];
|
|
LXT_CLONE_ARGS ChildClone;
|
|
int ChildDir;
|
|
pid_t ChildPid;
|
|
FILE* ChildStatusFile;
|
|
pthread_t ChildThread;
|
|
int GrandChildDir;
|
|
pid_t GrandChildPid1;
|
|
pid_t GrandChildPid2;
|
|
FILE* GrandChildStatusFile;
|
|
int Index;
|
|
bool IsFork;
|
|
char Path[32];
|
|
LXT_PIPE Pipe = {-1, -1};
|
|
void* PointerResult;
|
|
int Result;
|
|
struct stat StatBuffer;
|
|
int Status;
|
|
char StatusDescription[25];
|
|
char* StatusFileEntry;
|
|
size_t StatusFileEntryLength;
|
|
char StatusToken;
|
|
WAITPID_THREAD_PARAMETERS ThreadParam;
|
|
LXT_SYNCHRONIZATION_POINT_DECLARE_FOR(GrandChildPid2);
|
|
|
|
ChildDir = -1;
|
|
ChildPid = -1;
|
|
ChildStatusFile = NULL;
|
|
ChildThread = 0;
|
|
GrandChildDir = -1;
|
|
GrandChildPid1 = -1;
|
|
GrandChildPid2 = -1;
|
|
GrandChildStatusFile = NULL;
|
|
IsFork = true;
|
|
StatusFileEntry = NULL;
|
|
LXT_SYNCHRONIZATION_POINT_INIT_FOR(GrandChildPid2);
|
|
|
|
//
|
|
// TODO: test needs to be debugged for WSL2.
|
|
//
|
|
|
|
if (g_VmMode != false)
|
|
{
|
|
Result = 0;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckResult(LxtSignalInitialize());
|
|
LxtCheckResult(LxtSignalSetupHandler(SIGCHLD, SA_SIGINFO));
|
|
LxtCheckResult(LxtSignalIgnore(SIGPIPE));
|
|
|
|
memset(&ThreadParam, 0, sizeof(ThreadParam));
|
|
ThreadParam.LxtSyncChildPidParent = LxtSyncChildPidParent;
|
|
ThreadParam.LxtSyncChildPidChild = LxtSyncChildPidChild;
|
|
ThreadParam.LxtSyncGrandChildPid2Parent = LxtSyncGrandChildPid2Parent;
|
|
ThreadParam.LxtSyncGrandChildPid2Child = LxtSyncGrandChildPid2Child;
|
|
for (Index = 0; Index < 4; Index += 1)
|
|
{
|
|
ChildPid = -1;
|
|
GrandChildPid1 = -1;
|
|
GrandChildPid2 = -1;
|
|
LXT_SYNCHRONIZATION_POINT_START();
|
|
LXT_SYNCHRONIZATION_POINT_START_FOR(GrandChildPid2);
|
|
ThreadParam.ChildPid = -1;
|
|
ThreadParam.GrandChildPid2 = -1;
|
|
ThreadParam.GrandChildPid2 = -1;
|
|
LxtCheckResult(LxtCreatePipe(&Pipe));
|
|
memcpy(&ThreadParam.Pipe, &Pipe, sizeof(Pipe));
|
|
ThreadParam.Variation = Index;
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
return ThreadZombieThread(&ThreadParam);
|
|
}
|
|
|
|
ThreadParam.ChildPid = ChildPid;
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtCheckErrno(read(Pipe.Read, &ThreadParam, sizeof(ThreadParam)));
|
|
GrandChildPid1 = ThreadParam.GrandChildPid1;
|
|
GrandChildPid2 = ThreadParam.GrandChildPid2;
|
|
|
|
//
|
|
// Check basic info of child after one of its children has entered
|
|
// zombie state.
|
|
//
|
|
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d", ChildPid));
|
|
LxtCheckErrno(ChildDir = open(Path, O_RDONLY));
|
|
LxtCheckErrno(fstat(ChildDir, &StatBuffer));
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/status", ChildPid));
|
|
LxtCheckNullErrno(ChildStatusFile = fopen(Path, "r"));
|
|
while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, ChildStatusFile)) >= 0)
|
|
{
|
|
|
|
if (strncmp(StatusFileEntry, "State:\t", 7) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
free(StatusFileEntry);
|
|
StatusFileEntry = NULL;
|
|
}
|
|
|
|
LxtCheckErrno(Result);
|
|
Result = sscanf(StatusFileEntry, "State: %c (%s)", &StatusToken, StatusDescription);
|
|
|
|
LxtCheckEqual(Result, 2, "%d");
|
|
if (StatusToken == 'R')
|
|
{
|
|
LxtCheckStringEqual(StatusDescription, "running)");
|
|
}
|
|
else if (StatusToken == 'S')
|
|
{
|
|
LxtCheckStringEqual(StatusDescription, "sleeping)");
|
|
}
|
|
else
|
|
{
|
|
LxtLogError("Unexpected status: %c (%s)", StatusToken, StatusDescription);
|
|
Result = -1;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtCheckErrno(getpgid(ChildPid));
|
|
LxtCheckErrno(kill(ChildPid, SIGWINCH));
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/ns/mnt", ChildPid));
|
|
LxtCheckErrno(readlink(Path, Buffer, sizeof(Buffer)));
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/fd/0", ChildPid));
|
|
LxtCheckErrno(stat(Path, &StatBuffer));
|
|
|
|
//
|
|
// Check basic info of first zombie.
|
|
//
|
|
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d", GrandChildPid1));
|
|
if (Index != 3)
|
|
{
|
|
LxtCheckErrno(getpgid(GrandChildPid1));
|
|
LxtCheckErrno(kill(GrandChildPid1, SIGWINCH));
|
|
LxtCheckErrno(GrandChildDir = open(Path, O_RDONLY));
|
|
LxtCheckErrno(fstat(GrandChildDir, &StatBuffer));
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/fd/0", GrandChildPid1));
|
|
|
|
//
|
|
// TODO_LX: Zombied procfs "fd" entry should be accessible but
|
|
// empty.
|
|
//
|
|
// LxtCheckErrnoFailure(stat(Path, &StatBuffer), ENOENT);
|
|
//
|
|
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/status", GrandChildPid1));
|
|
LxtCheckNullErrno(GrandChildStatusFile = fopen(Path, "r"));
|
|
free(StatusFileEntry);
|
|
StatusFileEntry = NULL;
|
|
while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, GrandChildStatusFile)) >= 0)
|
|
{
|
|
|
|
if (strncmp(StatusFileEntry, "State:\t", 7) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
free(StatusFileEntry);
|
|
StatusFileEntry = NULL;
|
|
}
|
|
|
|
LxtCheckErrno(Result);
|
|
Result = sscanf(StatusFileEntry, "State: %c (%s)", &StatusToken, StatusDescription);
|
|
|
|
LxtCheckEqual(Result, 2, "%d");
|
|
LxtCheckEqual(StatusToken, 'Z', "%c");
|
|
LxtCheckStringEqual(StatusDescription, "zombie)");
|
|
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/fd/0", GrandChildPid1));
|
|
|
|
//
|
|
// TODO_LX: Zombied procfs "fd" entry should be accessible but
|
|
// empty.
|
|
//
|
|
// LxtCheckErrnoFailure(open(Path, O_RDONLY), ENOENT);
|
|
//
|
|
}
|
|
else
|
|
{
|
|
LxtCheckErrnoFailure(GrandChildDir = open(Path, O_RDONLY), ENOENT);
|
|
}
|
|
|
|
//
|
|
// Close the read end of the pipe.
|
|
//
|
|
|
|
close(Pipe.Read);
|
|
Pipe.Read = -1;
|
|
LxtCheckErrno(write(Pipe.Write, &Result, sizeof(Result)));
|
|
|
|
//
|
|
// Allow the child to exit.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT();
|
|
LxtSignalWait();
|
|
if (Index != 3)
|
|
{
|
|
LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, ChildPid, getuid(), 0));
|
|
|
|
LxtSignalResetReceived();
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// The threadgroup leader should have exited but there is still a
|
|
// thread running so the signal will not yet be sent.
|
|
//
|
|
|
|
LxtCheckResult(LxtSignalCheckNoSignal());
|
|
}
|
|
|
|
//
|
|
// Check basic info of running grandchild after child exit.
|
|
//
|
|
|
|
if (Index != 3)
|
|
{
|
|
|
|
//
|
|
// TODO_LX: These calls fail due to lack of proper thread support.
|
|
//
|
|
|
|
LxtCheckErrno(getpgid(GrandChildPid2));
|
|
LxtCheckErrno(kill(GrandChildPid2, SIGWINCH));
|
|
}
|
|
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/ns/mnt", GrandChildPid2));
|
|
LxtCheckErrno(readlink(Path, Buffer, sizeof(Buffer)));
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/fd/0", GrandChildPid2));
|
|
LxtCheckErrno(stat(Path, &StatBuffer));
|
|
|
|
//
|
|
// Signal last grandchild to exit.
|
|
//
|
|
|
|
LXT_SYNCHRONIZATION_POINT_PARENT_FOR(GrandChildPid2);
|
|
LxtSignalWait();
|
|
if (Index == 3)
|
|
{
|
|
LxtCheckResult(LxtSignalCheckSigChldReceived(CLD_EXITED, ChildPid, getuid(), 0));
|
|
|
|
LxtSignalResetReceived();
|
|
}
|
|
|
|
//
|
|
// Check the child information after it is a zombie.
|
|
//
|
|
|
|
LxtCheckErrno(fstat(ChildDir, &StatBuffer));
|
|
rewind(ChildStatusFile);
|
|
free(StatusFileEntry);
|
|
StatusFileEntry = NULL;
|
|
while ((Result = getline(&StatusFileEntry, &StatusFileEntryLength, ChildStatusFile)) >= 0)
|
|
{
|
|
|
|
if (strncmp(StatusFileEntry, "State:\t", 7) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
free(StatusFileEntry);
|
|
StatusFileEntry = NULL;
|
|
}
|
|
|
|
LxtCheckErrno(Result);
|
|
Result = sscanf(StatusFileEntry, "State: %c (%s)", &StatusToken, StatusDescription);
|
|
|
|
LxtCheckEqual(Result, 2, "%d");
|
|
LxtCheckEqual(StatusToken, 'Z', "%c");
|
|
LxtCheckStringEqual(StatusDescription, "zombie)");
|
|
LxtCheckErrno(getpgid(ChildPid));
|
|
LxtCheckErrno(kill(ChildPid, SIGWINCH));
|
|
LxtCheckErrno(sprintf(Path, "/proc/%d/ns/mnt", ChildPid));
|
|
LxtCheckErrnoFailure(readlink(Path, Buffer, sizeof(Buffer)), ENOENT);
|
|
sleep(2);
|
|
LxtCheckErrnoFailure(write(Pipe.Write, &Result, sizeof(Result)), EPIPE);
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
ChildPid = -1;
|
|
LxtCheckErrnoFailure(waitpid(-1, &Status, WNOHANG), ECHILD);
|
|
LxtCheckEqual(Result, 0, "%d");
|
|
LxtClosePipe(&Pipe);
|
|
}
|
|
|
|
ErrorExit:
|
|
free(StatusFileEntry);
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
if (GrandChildPid2 > 0)
|
|
{
|
|
LXT_SYNCHRONIZATION_POINT_PTHREAD_END_THREAD_FOR(GrandChildPid2);
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_DESTROY_FOR(GrandChildPid2);
|
|
if (GrandChildStatusFile != NULL)
|
|
{
|
|
fclose(GrandChildStatusFile);
|
|
}
|
|
|
|
if (GrandChildDir >= 0)
|
|
{
|
|
close(GrandChildDir);
|
|
}
|
|
|
|
if (ChildStatusFile != NULL)
|
|
{
|
|
fclose(ChildStatusFile);
|
|
}
|
|
|
|
if (ChildDir >= 0)
|
|
{
|
|
close(ChildDir);
|
|
}
|
|
|
|
//
|
|
// Intentionally leaking pipes from children / grandchildren to test
|
|
// file descriptor cleanup on exit in the fork case.
|
|
//
|
|
|
|
LxtClosePipe(&Pipe);
|
|
|
|
LxtSignalDefault(SIGPIPE);
|
|
LxtSignalDefault(SIGCHLD);
|
|
return Result;
|
|
}
|
|
|
|
int WaitPidVariationZombieStress(PLXT_ARGS Args)
|
|
|
|
/*++
|
|
|
|
Description:
|
|
|
|
This routine stress tests zombie handling.
|
|
|
|
Arguments:
|
|
|
|
Args - Supplies the command line arguments.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
pid_t ChildPid;
|
|
pid_t GrandChildPid;
|
|
pid_t GreatGrandChildPid;
|
|
int Iterations;
|
|
int Result;
|
|
int Status;
|
|
|
|
GrandChildPid = -1;
|
|
GreatGrandChildPid = -1;
|
|
for (Iterations = 0; Iterations < 100; Iterations += 1)
|
|
{
|
|
ChildPid = -1;
|
|
LxtCheckErrno(ChildPid = fork());
|
|
if (ChildPid == 0)
|
|
{
|
|
LxtCheckErrno(setsid());
|
|
LxtCheckErrno(prctl(PR_SET_CHILD_SUBREAPER, 1));
|
|
for (Iterations = 0; Iterations < 3; Iterations += 1)
|
|
{
|
|
LxtCheckErrno(GrandChildPid = fork());
|
|
if (GrandChildPid == 0)
|
|
{
|
|
ChildPid = -1;
|
|
LxtCheckErrno(GreatGrandChildPid = fork());
|
|
if (GreatGrandChildPid == 0)
|
|
{
|
|
usleep(random() % 100);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
usleep(random() % 100);
|
|
(void)waitpid(GreatGrandChildPid, &Status, WNOHANG);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
(void)waitpid(GrandChildPid, &Status, WNOHANG);
|
|
}
|
|
|
|
usleep(random() % 100);
|
|
(void)waitpid(GrandChildPid, &Status, WNOHANG);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
ChildPid = -1;
|
|
}
|
|
|
|
ErrorExit:
|
|
if ((GreatGrandChildPid == 0) || (GrandChildPid == 0))
|
|
{
|
|
_exit(Result);
|
|
}
|
|
|
|
LXT_SYNCHRONIZATION_POINT_END();
|
|
return Result;
|
|
} |