Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,31 @@
# WinPR: Windows Portable Runtime
# libwinpr-thread cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
winpr_module_add(
apc.h
apc.c
argv.c
process.c
processor.c
thread.c
thread.h
tls.c
)
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
add_subdirectory(test)
endif()

View File

@@ -0,0 +1,9 @@
set(MINWIN_LAYER "1")
set(MINWIN_GROUP "core")
set(MINWIN_MAJOR_VERSION "1")
set(MINWIN_MINOR_VERSION "1")
set(MINWIN_SHORT_NAME "processthreads")
set(MINWIN_LONG_NAME "Process and Thread Functions")
set(MODULE_LIBRARY_NAME
"api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}"
)

View File

@@ -0,0 +1,272 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* APC implementation
*
* Copyright 2021 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _WIN32
#include "apc.h"
#include "thread.h"
#include "../log.h"
#include "../synch/pollset.h"
#include <winpr/assert.h>
#define TAG WINPR_TAG("apc")
BOOL apc_init(APC_QUEUE* apc)
{
pthread_mutexattr_t attr;
BOOL ret = FALSE;
WINPR_ASSERT(apc);
pthread_mutexattr_init(&attr);
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
{
WLog_ERR(TAG, "failed to initialize mutex attributes to recursive");
return FALSE;
}
memset(apc, 0, sizeof(*apc));
if (pthread_mutex_init(&apc->mutex, &attr) != 0)
{
WLog_ERR(TAG, "failed to initialize main thread APC mutex");
goto out;
}
ret = TRUE;
out:
pthread_mutexattr_destroy(&attr);
return ret;
}
BOOL apc_uninit(APC_QUEUE* apc)
{
WINPR_ASSERT(apc);
return pthread_mutex_destroy(&apc->mutex) == 0;
}
void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem)
{
WINPR_APC_ITEM** nextp = nullptr;
APC_QUEUE* apc = nullptr;
WINPR_ASSERT(thread);
WINPR_ASSERT(addItem);
apc = &thread->apc;
WINPR_ASSERT(apc);
pthread_mutex_lock(&apc->mutex);
if (apc->tail)
{
nextp = &apc->tail->next;
addItem->last = apc->tail;
}
else
{
nextp = &apc->head;
}
*nextp = addItem;
apc->tail = addItem;
apc->length++;
addItem->markedForRemove = FALSE;
addItem->boundThread = GetCurrentThreadId();
addItem->linked = TRUE;
pthread_mutex_unlock(&apc->mutex);
}
static inline void apc_item_remove(APC_QUEUE* apc, WINPR_APC_ITEM* item)
{
WINPR_ASSERT(apc);
WINPR_ASSERT(item);
if (!item->last)
apc->head = item->next;
else
item->last->next = item->next;
if (!item->next)
apc->tail = item->last;
else
item->next->last = item->last;
apc->length--;
}
APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item)
{
WINPR_THREAD* thread = winpr_GetCurrentThread();
APC_QUEUE* apc = nullptr;
APC_REMOVE_RESULT ret = APC_REMOVE_OK;
WINPR_ASSERT(item);
if (!item->linked)
return APC_REMOVE_OK;
if (item->boundThread != GetCurrentThreadId())
{
WLog_ERR(TAG, "removing an APC entry should be done in the creating thread");
return APC_REMOVE_ERROR;
}
if (!thread)
{
WLog_ERR(TAG, "unable to retrieve current thread");
return APC_REMOVE_ERROR;
}
apc = &thread->apc;
WINPR_ASSERT(apc);
pthread_mutex_lock(&apc->mutex);
if (apc->treatingCompletions)
{
item->markedForRemove = TRUE;
ret = APC_REMOVE_DELAY_FREE;
goto out;
}
apc_item_remove(apc, item);
out:
pthread_mutex_unlock(&apc->mutex);
item->boundThread = 0xFFFFFFFF;
item->linked = FALSE;
return ret;
}
BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled)
{
WINPR_APC_ITEM* item = nullptr;
BOOL ret = FALSE;
APC_QUEUE* apc = nullptr;
WINPR_ASSERT(thread);
WINPR_ASSERT(haveAutoSignaled);
apc = &thread->apc;
WINPR_ASSERT(apc);
*haveAutoSignaled = FALSE;
pthread_mutex_lock(&apc->mutex);
item = apc->head;
for (; item; item = item->next)
{
if (item->alwaysSignaled)
{
*haveAutoSignaled = TRUE;
}
else if (!pollset_add(set, item->pollFd, item->pollMode))
goto out;
}
ret = TRUE;
out:
pthread_mutex_unlock(&apc->mutex);
return ret;
}
int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t startIndex)
{
APC_QUEUE* apc = nullptr;
WINPR_APC_ITEM* nextItem = nullptr;
size_t idx = startIndex;
int ret = 0;
WINPR_ASSERT(thread);
apc = &thread->apc;
WINPR_ASSERT(apc);
pthread_mutex_lock(&apc->mutex);
apc->treatingCompletions = TRUE;
/* first pass to compute signaled items */
for (WINPR_APC_ITEM* item = apc->head; item; item = item->next)
{
item->isSignaled = item->alwaysSignaled || pollset_isSignaled(set, idx);
if (!item->alwaysSignaled)
idx++;
}
/* second pass: run completions */
for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem)
{
if (item->isSignaled)
{
if (item->completion && !item->markedForRemove)
item->completion(item->completionArgs);
ret++;
}
nextItem = item->next;
}
/* third pass: to do final cleanup */
for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem)
{
nextItem = item->next;
if (item->markedForRemove)
{
apc_item_remove(apc, item);
if (item->markedForFree)
free(item);
}
}
apc->treatingCompletions = FALSE;
pthread_mutex_unlock(&apc->mutex);
return ret;
}
void apc_cleanupThread(WINPR_THREAD* thread)
{
WINPR_APC_ITEM* item = nullptr;
WINPR_APC_ITEM* nextItem = nullptr;
APC_QUEUE* apc = nullptr;
WINPR_ASSERT(thread);
apc = &thread->apc;
WINPR_ASSERT(apc);
pthread_mutex_lock(&apc->mutex);
item = apc->head;
for (; item; item = nextItem)
{
nextItem = item->next;
if (item->type == APC_TYPE_HANDLE_FREE)
item->completion(item->completionArgs);
item->last = item->next = nullptr;
item->linked = FALSE;
if (item->markedForFree)
free(item);
}
apc->head = apc->tail = nullptr;
pthread_mutex_unlock(&apc->mutex);
}
#endif

View File

@@ -0,0 +1,85 @@
/**
* WinPR: Windows Portable Runtime
* APC implementation
*
* Copyright 2021 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef WINPR_APC_H
#define WINPR_APC_H
#include <winpr/winpr.h>
#include <winpr/wtypes.h>
#ifndef _WIN32
#include <pthread.h>
typedef struct winpr_thread WINPR_THREAD;
typedef struct winpr_APC_item WINPR_APC_ITEM;
typedef struct winpr_poll_set WINPR_POLL_SET;
typedef void (*apc_treatment)(LPVOID arg);
typedef enum
{
APC_TYPE_USER,
APC_TYPE_TIMER,
APC_TYPE_HANDLE_FREE
} ApcType;
struct winpr_APC_item
{
ApcType type;
int pollFd;
DWORD pollMode;
apc_treatment completion;
LPVOID completionArgs;
BOOL markedForFree;
/* private fields used by the APC */
BOOL alwaysSignaled;
BOOL isSignaled;
DWORD boundThread;
BOOL linked;
BOOL markedForRemove;
WINPR_APC_ITEM *last, *next;
};
typedef enum
{
APC_REMOVE_OK,
APC_REMOVE_ERROR,
APC_REMOVE_DELAY_FREE
} APC_REMOVE_RESULT;
typedef struct
{
pthread_mutex_t mutex;
DWORD length;
WINPR_APC_ITEM *head, *tail;
BOOL treatingCompletions;
} APC_QUEUE;
BOOL apc_init(APC_QUEUE* apc);
BOOL apc_uninit(APC_QUEUE* apc);
void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem);
APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item);
BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled);
int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t startIndex);
void apc_cleanupThread(WINPR_THREAD* thread);
#endif
#endif /* WINPR_APC_H */

View File

@@ -0,0 +1,285 @@
/**
* WinPR: Windows Portable Runtime
* Process Argument Vector Functions
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/handle.h>
#include <winpr/thread.h>
#ifdef WINPR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "../log.h"
#define TAG WINPR_TAG("thread")
/**
* CommandLineToArgvW function:
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391/
*
* CommandLineToArgvW has a special interpretation of backslash characters
* when they are followed by a quotation mark character ("), as follows:
*
* 2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark.
* (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a
* quotation mark. n backslashes not followed by a quotation mark simply produce n backslashes.
*
* The address returned by CommandLineToArgvW is the address of the first element in an array of
* LPWSTR values; the number of pointers in this array is indicated by pNumArgs. Each pointer to a
* null-terminated Unicode string represents an individual argument found on the command line.
*
* CommandLineToArgvW allocates a block of contiguous memory for pointers to the argument strings,
* and for the argument strings themselves; the calling application must free the memory used by the
* argument list when it is no longer needed. To free the memory, use a single call to the LocalFree
* function.
*/
/**
* Parsing C++ Command-Line Arguments:
* http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft
*
* Microsoft C/C++ startup code uses the following rules when
* interpreting arguments given on the operating system command line:
*
* Arguments are delimited by white space, which is either a space or a tab.
*
* The caret character (^) is not recognized as an escape character or delimiter.
* The character is handled completely by the command-line parser in the operating
* system before being passed to the argv array in the program.
*
* A string surrounded by double quotation marks ("string") is interpreted as a
* single argument, regardless of white space contained within. A quoted string
* can be embedded in an argument.
*
* A double quotation mark preceded by a backslash (\") is interpreted as a
* literal double quotation mark character (").
*
* Backslashes are interpreted literally, unless they immediately
* precede a double quotation mark.
*
* If an even number of backslashes is followed by a double quotation mark,
* one backslash is placed in the argv array for every pair of backslashes,
* and the double quotation mark is interpreted as a string delimiter.
*
* If an odd number of backslashes is followed by a double quotation mark,
* one backslash is placed in the argv array for every pair of backslashes,
* and the double quotation mark is "escaped" by the remaining backslash,
* causing a literal double quotation mark (") to be placed in argv.
*
*/
LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs)
{
const char* p = nullptr;
size_t length = 0;
const char* pBeg = nullptr;
const char* pEnd = nullptr;
char* buffer = nullptr;
char* pOutput = nullptr;
int numArgs = 0;
LPSTR* pArgs = nullptr;
size_t maxNumArgs = 0;
size_t maxBufferSize = 0;
size_t cmdLineLength = 0;
BOOL* lpEscapedChars = nullptr;
LPSTR lpEscapedCmdLine = nullptr;
if (!lpCmdLine)
return nullptr;
if (!pNumArgs)
return nullptr;
pArgs = nullptr;
lpEscapedCmdLine = nullptr;
cmdLineLength = strlen(lpCmdLine);
lpEscapedChars = (BOOL*)calloc(cmdLineLength + 1, sizeof(BOOL));
if (!lpEscapedChars)
return nullptr;
if (strstr(lpCmdLine, "\\\""))
{
size_t n = 0;
const char* pLastEnd = nullptr;
lpEscapedCmdLine = (char*)calloc(cmdLineLength + 1, sizeof(char));
if (!lpEscapedCmdLine)
{
free(lpEscapedChars);
return nullptr;
}
p = (const char*)lpCmdLine;
pLastEnd = (const char*)lpCmdLine;
pOutput = (char*)lpEscapedCmdLine;
while (p < &lpCmdLine[cmdLineLength])
{
pBeg = strstr(p, "\\\"");
if (!pBeg)
{
length = strlen(p);
CopyMemory(pOutput, p, length);
pOutput += length;
break;
}
pEnd = pBeg + 2;
while (pBeg >= lpCmdLine)
{
if (*pBeg != '\\')
{
pBeg++;
break;
}
pBeg--;
}
n = WINPR_ASSERTING_INT_CAST(size_t, ((pEnd - pBeg) - 1));
length = WINPR_ASSERTING_INT_CAST(size_t, (pBeg - pLastEnd));
CopyMemory(pOutput, p, length);
pOutput += length;
p += length;
for (size_t i = 0; i < (n / 2); i++)
*pOutput++ = '\\';
p += n + 1;
if ((n % 2) != 0)
lpEscapedChars[pOutput - lpEscapedCmdLine] = TRUE;
*pOutput++ = '"';
pLastEnd = p;
}
*pOutput++ = '\0';
lpCmdLine = (LPCSTR)lpEscapedCmdLine;
cmdLineLength = strlen(lpCmdLine);
}
maxNumArgs = 2;
p = (const char*)lpCmdLine;
while (p < lpCmdLine + cmdLineLength)
{
p += strcspn(p, " \t");
p += strspn(p, " \t");
maxNumArgs++;
}
maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1);
buffer = calloc(maxBufferSize, sizeof(char));
if (!buffer)
{
free(lpEscapedCmdLine);
free(lpEscapedChars);
return nullptr;
}
pArgs = (LPSTR*)buffer;
pOutput = &buffer[maxNumArgs * (sizeof(char*))];
p = (const char*)lpCmdLine;
while (p < lpCmdLine + cmdLineLength)
{
pBeg = p;
while (1)
{
p += strcspn(p, " \t\"\0");
if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
break;
p++;
}
if (*p != '"')
{
/* no whitespace escaped with double quotes */
length = WINPR_ASSERTING_INT_CAST(size_t, (p - pBeg));
CopyMemory(pOutput, pBeg, length);
pOutput[length] = '\0';
pArgs[numArgs++] = pOutput;
pOutput += (length + 1);
}
else
{
p++;
while (1)
{
p += strcspn(p, "\"\0");
if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
break;
p++;
}
if (*p != '"')
WLog_ERR(TAG, "parsing error: uneven number of unescaped double quotes!");
if (*p)
{
p++;
if (*p)
p += strcspn(p, " \t\0");
}
pArgs[numArgs++] = pOutput;
while (pBeg < p)
{
if (*pBeg != '"')
*pOutput++ = *pBeg;
pBeg++;
}
*pOutput++ = '\0';
}
p += strspn(p, " \t");
}
free(lpEscapedCmdLine);
free(lpEscapedChars);
*pNumArgs = numArgs;
return pArgs;
}
#ifndef _WIN32
LPWSTR* CommandLineToArgvW(WINPR_ATTR_UNUSED LPCWSTR lpCmdLine, WINPR_ATTR_UNUSED int* pNumArgs)
{
WLog_ERR("TODO", "TODO: Implement");
return nullptr;
}
#endif

View File

@@ -0,0 +1,634 @@
/**
* WinPR: Windows Portable Runtime
* Process Thread Functions
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2014 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/config.h>
#include <winpr/handle.h>
#include "../handle/nonehandle.h"
#include <winpr/thread.h>
#include <winpr/wlog.h>
/**
* CreateProcessA
* CreateProcessW
* CreateProcessAsUserA
* CreateProcessAsUserW
* ExitProcess
* GetCurrentProcess
* GetCurrentProcessId
* GetExitCodeProcess
* GetProcessHandleCount
* GetProcessId
* GetProcessIdOfThread
* GetProcessMitigationPolicy
* GetProcessTimes
* GetProcessVersion
* OpenProcess
* OpenProcessToken
* ProcessIdToSessionId
* SetProcessAffinityUpdateMode
* SetProcessMitigationPolicy
* SetProcessShutdownParameters
* TerminateProcess
*/
#ifndef _WIN32
#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/path.h>
#include <winpr/environment.h>
#include <grp.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifdef __linux__
#include <sys/syscall.h>
#include <fcntl.h>
#include <errno.h>
#endif /* __linux__ */
#include "thread.h"
#include "../security/security.h"
#ifndef NSIG
#ifdef SIGMAX
#define NSIG SIGMAX
#else
#define NSIG 64
#endif
#endif
/**
* If the file name does not contain a directory path, the system searches for the executable file
* in the following sequence:
*
* 1) The directory from which the application loaded.
* 2) The current directory for the parent process.
* 3) The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of
* this directory. 4) The 16-bit Windows system directory. There is no function that obtains the
* path of this directory, but it is searched. The name of this directory is System. 5) The Windows
* directory. Use the GetWindowsDirectory function to get the path of this directory. 6) The
* directories that are listed in the PATH environment variable. Note that this function does not
* search the per-application path specified by the App Paths registry key. To include this
* per-application path in the search sequence, use the ShellExecute function.
*/
static char* FindApplicationPath(char* application)
{
LPCSTR pathName = "PATH";
char* path = nullptr;
char* save = nullptr;
DWORD nSize = 0;
LPSTR lpSystemPath = nullptr;
char* filename = nullptr;
if (!application)
return nullptr;
if (application[0] == '/')
return _strdup(application);
nSize = GetEnvironmentVariableA(pathName, nullptr, 0);
if (!nSize)
return _strdup(application);
lpSystemPath = (LPSTR)malloc(nSize);
if (!lpSystemPath)
return nullptr;
if (GetEnvironmentVariableA(pathName, lpSystemPath, nSize) != nSize - 1)
{
free(lpSystemPath);
return nullptr;
}
save = nullptr;
path = strtok_s(lpSystemPath, ":", &save);
while (path)
{
filename = GetCombinedPath(path, application);
if (winpr_PathFileExists(filename))
{
break;
}
free(filename);
filename = nullptr;
path = strtok_s(nullptr, ":", &save);
}
free(lpSystemPath);
return filename;
}
static HANDLE CreateProcessHandle(pid_t pid);
static BOOL ProcessHandleCloseHandle(HANDLE handle);
static BOOL CreateProcessExA(HANDLE hToken, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
LPCSTR lpApplicationName, WINPR_ATTR_UNUSED LPSTR lpCommandLine,
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpProcessAttributes,
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
WINPR_ATTR_UNUSED BOOL bInheritHandles,
WINPR_ATTR_UNUSED DWORD dwCreationFlags, LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{
pid_t pid = 0;
int numArgs = 0;
LPSTR* pArgs = nullptr;
char** envp = nullptr;
char* filename = nullptr;
HANDLE thread = nullptr;
HANDLE process = nullptr;
WINPR_ACCESS_TOKEN* token = nullptr;
LPTCH lpszEnvironmentBlock = nullptr;
BOOL ret = FALSE;
sigset_t oldSigMask;
sigset_t newSigMask;
BOOL restoreSigMask = FALSE;
numArgs = 0;
lpszEnvironmentBlock = nullptr;
/* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
*/
if (lpCommandLine)
pArgs = CommandLineToArgvA(lpCommandLine, &numArgs);
else
pArgs = CommandLineToArgvA(lpApplicationName, &numArgs);
if (!pArgs)
return FALSE;
token = (WINPR_ACCESS_TOKEN*)hToken;
if (lpEnvironment)
{
envp = EnvironmentBlockToEnvpA(lpEnvironment);
}
else
{
lpszEnvironmentBlock = GetEnvironmentStrings();
if (!lpszEnvironmentBlock)
goto finish;
envp = EnvironmentBlockToEnvpA(lpszEnvironmentBlock);
}
if (!envp)
goto finish;
filename = FindApplicationPath(pArgs[0]);
if (nullptr == filename)
goto finish;
/* block all signals so that the child can safely reset the caller's handlers */
sigfillset(&newSigMask);
restoreSigMask = !pthread_sigmask(SIG_SETMASK, &newSigMask, &oldSigMask);
/* fork and exec */
pid = fork();
if (pid < 0)
{
/* fork failure */
goto finish;
}
if (pid == 0)
{
/* child process */
#ifndef __sun
int maxfd = 0;
#endif
sigset_t set = WINPR_C_ARRAY_INIT;
struct sigaction act = WINPR_C_ARRAY_INIT;
/* set default signal handlers */
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
for (int sig = 1; sig < NSIG; sig++)
sigaction(sig, &act, nullptr);
/* unblock all signals */
sigfillset(&set);
pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
if (lpStartupInfo)
{
int handle_fd = 0;
handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdOutput);
if (handle_fd != -1)
dup2(handle_fd, STDOUT_FILENO);
handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdError);
if (handle_fd != -1)
dup2(handle_fd, STDERR_FILENO);
handle_fd = winpr_Handle_getFd(lpStartupInfo->hStdInput);
if (handle_fd != -1)
dup2(handle_fd, STDIN_FILENO);
}
#ifdef __sun
closefrom(3);
#else
#ifdef F_MAXFD // on some BSD derivates
maxfd = fcntl(0, F_MAXFD);
#else
{
const long rc = sysconf(_SC_OPEN_MAX);
if ((rc < INT32_MIN) || (rc > INT32_MAX))
goto finish;
maxfd = (int)rc;
}
#endif
for (int fd = 3; fd < maxfd; fd++)
close(fd);
#endif // __sun
if (token)
{
if (token->GroupId)
{
int rc = setgid((gid_t)token->GroupId);
if (rc < 0)
{
}
else
{
initgroups(token->Username, (gid_t)token->GroupId);
}
}
if (token->UserId)
{
int rc = setuid((uid_t)token->UserId);
if (rc != 0)
goto finish;
}
}
/* TODO: add better cwd handling and error checking */
if (lpCurrentDirectory && strlen(lpCurrentDirectory) > 0)
{
int rc = chdir(lpCurrentDirectory);
if (rc != 0)
goto finish;
}
if (execve(filename, pArgs, envp) < 0)
{
/* execve failed - end the process */
_exit(1);
}
}
else
{
/* parent process */
}
process = CreateProcessHandle(pid);
if (!process)
{
goto finish;
}
thread = CreateNoneHandle();
if (!thread)
{
ProcessHandleCloseHandle(process);
goto finish;
}
lpProcessInformation->hProcess = process;
lpProcessInformation->hThread = thread;
lpProcessInformation->dwProcessId = (DWORD)pid;
lpProcessInformation->dwThreadId = (DWORD)pid;
ret = TRUE;
finish:
/* restore caller's original signal mask */
if (restoreSigMask)
pthread_sigmask(SIG_SETMASK, &oldSigMask, nullptr);
free(filename);
free((void*)pArgs);
if (lpszEnvironmentBlock)
FreeEnvironmentStrings(lpszEnvironmentBlock);
if (envp)
{
int i = 0;
while (envp[i])
{
free(envp[i]);
i++;
}
free((void*)envp);
}
return ret;
}
BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
return CreateProcessExA(nullptr, 0, lpApplicationName, lpCommandLine, lpProcessAttributes,
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
}
BOOL CreateProcessW(WINPR_ATTR_UNUSED LPCWSTR lpApplicationName,
WINPR_ATTR_UNUSED LPWSTR lpCommandLine,
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpProcessAttributes,
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
WINPR_ATTR_UNUSED BOOL bInheritHandles, WINPR_ATTR_UNUSED DWORD dwCreationFlags,
WINPR_ATTR_UNUSED LPVOID lpEnvironment,
WINPR_ATTR_UNUSED LPCWSTR lpCurrentDirectory,
WINPR_ATTR_UNUSED LPSTARTUPINFOW lpStartupInfo,
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
{
WLog_ERR("TODO", "TODO: implement");
return FALSE;
}
BOOL CreateProcessAsUserA(HANDLE hToken, LPCSTR lpApplicationName, LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,
DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
return CreateProcessExA(hToken, 0, lpApplicationName, lpCommandLine, lpProcessAttributes,
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
}
BOOL CreateProcessAsUserW(WINPR_ATTR_UNUSED HANDLE hToken,
WINPR_ATTR_UNUSED LPCWSTR lpApplicationName,
WINPR_ATTR_UNUSED LPWSTR lpCommandLine,
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpProcessAttributes,
WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpThreadAttributes,
WINPR_ATTR_UNUSED BOOL bInheritHandles,
WINPR_ATTR_UNUSED DWORD dwCreationFlags,
WINPR_ATTR_UNUSED LPVOID lpEnvironment,
WINPR_ATTR_UNUSED LPCWSTR lpCurrentDirectory,
WINPR_ATTR_UNUSED LPSTARTUPINFOW lpStartupInfo,
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
{
WLog_ERR("TODO", "TODO: implement");
return FALSE;
}
BOOL CreateProcessWithLogonA(
WINPR_ATTR_UNUSED LPCSTR lpUsername, WINPR_ATTR_UNUSED LPCSTR lpDomain,
WINPR_ATTR_UNUSED LPCSTR lpPassword, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
WINPR_ATTR_UNUSED LPCSTR lpApplicationName, WINPR_ATTR_UNUSED LPSTR lpCommandLine,
WINPR_ATTR_UNUSED DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPVOID lpEnvironment,
WINPR_ATTR_UNUSED LPCSTR lpCurrentDirectory, WINPR_ATTR_UNUSED LPSTARTUPINFOA lpStartupInfo,
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
{
WLog_ERR("TODO", "TODO: implement");
return FALSE;
}
BOOL CreateProcessWithLogonW(
WINPR_ATTR_UNUSED LPCWSTR lpUsername, WINPR_ATTR_UNUSED LPCWSTR lpDomain,
WINPR_ATTR_UNUSED LPCWSTR lpPassword, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
WINPR_ATTR_UNUSED LPCWSTR lpApplicationName, WINPR_ATTR_UNUSED LPWSTR lpCommandLine,
WINPR_ATTR_UNUSED DWORD dwCreationFlags, WINPR_ATTR_UNUSED LPVOID lpEnvironment,
WINPR_ATTR_UNUSED LPCWSTR lpCurrentDirectory, WINPR_ATTR_UNUSED LPSTARTUPINFOW lpStartupInfo,
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
{
WLog_ERR("TODO", "TODO: implement");
return FALSE;
}
BOOL CreateProcessWithTokenA(WINPR_ATTR_UNUSED HANDLE hToken, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
LPCSTR lpApplicationName, LPSTR lpCommandLine, DWORD dwCreationFlags,
LPVOID lpEnvironment, LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{
return CreateProcessExA(nullptr, 0, lpApplicationName, lpCommandLine, nullptr, nullptr, FALSE,
dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo,
lpProcessInformation);
}
BOOL CreateProcessWithTokenW(WINPR_ATTR_UNUSED HANDLE hToken, WINPR_ATTR_UNUSED DWORD dwLogonFlags,
WINPR_ATTR_UNUSED LPCWSTR lpApplicationName,
WINPR_ATTR_UNUSED LPWSTR lpCommandLine,
WINPR_ATTR_UNUSED DWORD dwCreationFlags,
WINPR_ATTR_UNUSED LPVOID lpEnvironment,
WINPR_ATTR_UNUSED LPCWSTR lpCurrentDirectory,
WINPR_ATTR_UNUSED LPSTARTUPINFOW lpStartupInfo,
WINPR_ATTR_UNUSED LPPROCESS_INFORMATION lpProcessInformation)
{
WLog_ERR("TODO", "TODO: implement");
return FALSE;
}
VOID ExitProcess(UINT uExitCode)
{
// NOLINTNEXTLINE(concurrency-mt-unsafe)
exit((int)uExitCode);
}
BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode)
{
WINPR_PROCESS* process = nullptr;
if (!hProcess)
return FALSE;
if (!lpExitCode)
return FALSE;
process = (WINPR_PROCESS*)hProcess;
*lpExitCode = process->dwExitCode;
return TRUE;
}
HANDLE _GetCurrentProcess(VOID)
{
WLog_ERR("TODO", "TODO: implement");
return nullptr;
}
DWORD GetCurrentProcessId(VOID)
{
return ((DWORD)getpid());
}
BOOL TerminateProcess(HANDLE hProcess, WINPR_ATTR_UNUSED UINT uExitCode)
{
WINPR_PROCESS* process = nullptr;
process = (WINPR_PROCESS*)hProcess;
if (!process || (process->pid <= 0))
return FALSE;
if (kill(process->pid, SIGTERM))
return FALSE;
return TRUE;
}
static BOOL ProcessHandleCloseHandle(HANDLE handle)
{
WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
WINPR_ASSERT(process);
if (process->fd >= 0)
{
close(process->fd);
process->fd = -1;
}
free(process);
return TRUE;
}
static BOOL ProcessHandleIsHandle(HANDLE handle)
{
return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_PROCESS, FALSE);
}
static int ProcessGetFd(HANDLE handle)
{
WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
if (!ProcessHandleIsHandle(handle))
return -1;
return process->fd;
}
static DWORD ProcessCleanupHandle(HANDLE handle)
{
WINPR_PROCESS* process = (WINPR_PROCESS*)handle;
WINPR_ASSERT(process);
if (process->fd > 0)
{
if (waitpid(process->pid, &process->status, WNOHANG) == process->pid)
process->dwExitCode = (DWORD)process->status;
}
return WAIT_OBJECT_0;
}
static HANDLE_OPS ops = { ProcessHandleIsHandle,
ProcessHandleCloseHandle,
ProcessGetFd,
ProcessCleanupHandle, /* CleanupHandle */
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr };
static int pidfd_open(pid_t pid)
{
#ifdef __linux__
#if !defined(__NR_pidfd_open)
#define __NR_pidfd_open 434
#endif /* __NR_pidfd_open */
#ifndef PIDFD_NONBLOCK
#define PIDFD_NONBLOCK O_NONBLOCK
#endif /* PIDFD_NONBLOCK */
long fd = syscall(__NR_pidfd_open, pid, PIDFD_NONBLOCK);
if (fd < 0 && errno == EINVAL)
{
/* possibly PIDFD_NONBLOCK is not supported, let's try to create a pidfd and set it
* non blocking afterward */
int flags = 0;
fd = syscall(__NR_pidfd_open, pid, 0);
if ((fd < 0) || (fd > INT32_MAX))
return -1;
flags = fcntl((int)fd, F_GETFL);
if ((flags < 0) || fcntl((int)fd, F_SETFL, flags | O_NONBLOCK) < 0)
{
close((int)fd);
fd = -1;
}
}
if ((fd < 0) || (fd > INT32_MAX))
return -1;
return (int)fd;
#else
return -1;
#endif
}
HANDLE CreateProcessHandle(pid_t pid)
{
WINPR_PROCESS* process = nullptr;
process = (WINPR_PROCESS*)calloc(1, sizeof(WINPR_PROCESS));
if (!process)
return nullptr;
process->pid = pid;
process->common.Type = HANDLE_TYPE_PROCESS;
process->common.ops = &ops;
process->fd = pidfd_open(pid);
if (process->fd >= 0)
process->common.Mode = WINPR_FD_READ;
return (HANDLE)process;
}
#endif

View File

@@ -0,0 +1,41 @@
/**
* WinPR: Windows Portable Runtime
* Process Thread Functions
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/config.h>
#include <winpr/handle.h>
#include <winpr/thread.h>
/**
* GetCurrentProcessorNumber
* GetCurrentProcessorNumberEx
* GetThreadIdealProcessorEx
* SetThreadIdealProcessorEx
* IsProcessorFeaturePresent
*/
#ifndef _WIN32
DWORD GetCurrentProcessorNumber(VOID)
{
return 0;
}
#endif

View File

@@ -0,0 +1,23 @@
set(MODULE_NAME "TestThread")
set(MODULE_PREFIX "TEST_THREAD")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS TestThreadCommandLineToArgv.c TestThreadCreateProcess.c TestThreadExitThread.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} winpr)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")

View File

@@ -0,0 +1,116 @@
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/thread.h>
static const char* test_args_line_1 = "app.exe abc d e";
static const char* test_args_list_1[] = { "app.exe", "abc", "d", "e" };
static const char* test_args_line_2 = "app.exe abc \t def";
static const char* test_args_list_2[] = { "app.exe", "abc", "def" };
static const char* test_args_line_3 = "app.exe \"abc\" d e";
static const char* test_args_list_3[] = { "app.exe", "abc", "d", "e" };
static const char* test_args_line_4 = "app.exe a\\\\b d\"e f\"g h";
static const char* test_args_list_4[] = { "app.exe", "a\\\\b", "de fg", "h" };
static const char* test_args_line_5 = "app.exe a\\\\\\\"b c d";
static const char* test_args_list_5[] = { "app.exe", "a\\\"b", "c", "d" };
static const char* test_args_line_6 = "app.exe a\\\\\\\\\"b c\" d e";
static const char* test_args_list_6[] = { "app.exe", "a\\\\b c", "d", "e" };
static const char* test_args_line_7 = "app.exe a\\\\\\\\\"b c\" d e f\\\\\\\\\"g h\" i j";
static const char* test_args_list_7[] = { "app.exe", "a\\\\b c", "d", "e", "f\\\\g h", "i", "j" };
static const char* test_args_line_8 = "app.exe arg1 \"arg2\"";
static const char* test_args_list_8[] = { "app.exe", "arg1", "arg2" };
static BOOL test_command_line_parsing_case(const char* line, const char** list, size_t expect)
{
BOOL rc = FALSE;
int numArgs = 0;
printf("Parsing: %s\n", line);
LPSTR* pArgs = CommandLineToArgvA(line, &numArgs);
if (numArgs < 0)
{
(void)fprintf(stderr, "expected %" PRIuz " arguments, got %d return\n", expect, numArgs);
goto fail;
}
if (numArgs != expect)
{
(void)fprintf(stderr, "expected %" PRIuz " arguments, got %d from '%s'\n", expect, numArgs,
line);
goto fail;
}
if ((numArgs > 0) && !pArgs)
{
(void)fprintf(stderr, "expected %d arguments, got nullptr return\n", numArgs);
goto fail;
}
printf("pNumArgs: %d\n", numArgs);
for (int i = 0; i < numArgs; i++)
{
printf("argv[%d] = %s\n", i, pArgs[i]);
if (strcmp(pArgs[i], list[i]) != 0)
{
(void)fprintf(stderr, "failed at argument %d: got '%s' but expected '%s'\n", i,
pArgs[i], list[i]);
goto fail;
}
}
rc = TRUE;
fail:
free((void*)pArgs);
return rc;
}
int TestThreadCommandLineToArgv(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!test_command_line_parsing_case(test_args_line_1, test_args_list_1,
ARRAYSIZE(test_args_list_1)))
return -1;
if (!test_command_line_parsing_case(test_args_line_2, test_args_list_2,
ARRAYSIZE(test_args_list_2)))
return -1;
if (!test_command_line_parsing_case(test_args_line_3, test_args_list_3,
ARRAYSIZE(test_args_list_3)))
return -1;
if (!test_command_line_parsing_case(test_args_line_4, test_args_list_4,
ARRAYSIZE(test_args_list_4)))
return -1;
if (!test_command_line_parsing_case(test_args_line_5, test_args_list_5,
ARRAYSIZE(test_args_list_5)))
return -1;
if (!test_command_line_parsing_case(test_args_line_6, test_args_list_6,
ARRAYSIZE(test_args_list_6)))
return -1;
if (!test_command_line_parsing_case(test_args_line_7, test_args_list_7,
ARRAYSIZE(test_args_list_7)))
return -1;
if (!test_command_line_parsing_case(test_args_line_8, test_args_list_8,
ARRAYSIZE(test_args_list_8)))
return -1;
return 0;
}

View File

@@ -0,0 +1,156 @@
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/environment.h>
#include <winpr/pipe.h>
#define TESTENV_A "HELLO=WORLD"
#define TESTENV_T _T(TESTENV_A)
int TestThreadCreateProcess(int argc, char* argv[])
{
BOOL status = 0;
DWORD exitCode = 0;
LPCTSTR lpApplicationName = nullptr;
#ifdef _WIN32
TCHAR lpCommandLine[200] = _T("cmd /C set");
#else
TCHAR lpCommandLine[200] = _T("printenv");
#endif
// LPTSTR lpCommandLine;
LPSECURITY_ATTRIBUTES lpProcessAttributes = nullptr;
LPSECURITY_ATTRIBUTES lpThreadAttributes = nullptr;
BOOL bInheritHandles = 0;
DWORD dwCreationFlags = 0;
LPVOID lpEnvironment = nullptr;
LPCTSTR lpCurrentDirectory = nullptr;
STARTUPINFO StartupInfo = WINPR_C_ARRAY_INIT;
PROCESS_INFORMATION ProcessInformation = WINPR_C_ARRAY_INIT;
LPTCH lpszEnvironmentBlock = nullptr;
HANDLE pipe_read = nullptr;
HANDLE pipe_write = nullptr;
char buf[1024] = WINPR_C_ARRAY_INIT;
DWORD read_bytes = 0;
int ret = 0;
SECURITY_ATTRIBUTES saAttr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
lpszEnvironmentBlock = GetEnvironmentStrings();
lpApplicationName = nullptr;
lpProcessAttributes = nullptr;
lpThreadAttributes = nullptr;
bInheritHandles = FALSE;
dwCreationFlags = 0;
#ifdef _UNICODE
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
#endif
lpEnvironment = lpszEnvironmentBlock;
lpCurrentDirectory = nullptr;
StartupInfo.cb = sizeof(STARTUPINFO);
status = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes,
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
lpCurrentDirectory, &StartupInfo, &ProcessInformation);
if (!status)
{
printf("CreateProcess failed. error=%" PRIu32 "\n", GetLastError());
return 1;
}
if (WaitForSingleObject(ProcessInformation.hProcess, 5000) != WAIT_OBJECT_0)
{
printf("Failed to wait for first process. error=%" PRIu32 "\n", GetLastError());
return 1;
}
exitCode = 0;
status = GetExitCodeProcess(ProcessInformation.hProcess, &exitCode);
printf("GetExitCodeProcess status: %" PRId32 "\n", status);
printf("Process exited with code: 0x%08" PRIX32 "\n", exitCode);
(void)CloseHandle(ProcessInformation.hProcess);
(void)CloseHandle(ProcessInformation.hThread);
FreeEnvironmentStrings(lpszEnvironmentBlock);
/* Test stdin,stdout,stderr redirection */
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = nullptr;
if (!CreatePipe(&pipe_read, &pipe_write, &saAttr, 0))
{
printf("Pipe creation failed. error=%" PRIu32 "\n", GetLastError());
return 1;
}
bInheritHandles = TRUE;
ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
StartupInfo.hStdOutput = pipe_write;
StartupInfo.hStdError = pipe_write;
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
ZeroMemory(&ProcessInformation, sizeof(PROCESS_INFORMATION));
if (!(lpEnvironment = calloc(1, sizeof(TESTENV_T) + sizeof(TCHAR))))
{
printf("Failed to allocate environment buffer. error=%" PRIu32 "\n", GetLastError());
return 1;
}
memcpy(lpEnvironment, (void*)TESTENV_T, sizeof(TESTENV_T));
status = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes,
lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
lpCurrentDirectory, &StartupInfo, &ProcessInformation);
free(lpEnvironment);
if (!status)
{
(void)CloseHandle(pipe_read);
(void)CloseHandle(pipe_write);
printf("CreateProcess failed. error=%" PRIu32 "\n", GetLastError());
return 1;
}
if (WaitForSingleObject(ProcessInformation.hProcess, 5000) != WAIT_OBJECT_0)
{
printf("Failed to wait for second process. error=%" PRIu32 "\n", GetLastError());
return 1;
}
ZeroMemory(buf, sizeof(buf));
ReadFile(pipe_read, buf, sizeof(buf) - 1, &read_bytes, nullptr);
if (!strstr((const char*)buf, TESTENV_A))
{
printf("No or unexpected data read from pipe\n");
ret = 1;
}
(void)CloseHandle(pipe_read);
(void)CloseHandle(pipe_write);
exitCode = 0;
status = GetExitCodeProcess(ProcessInformation.hProcess, &exitCode);
printf("GetExitCodeProcess status: %" PRId32 "\n", status);
printf("Process exited with code: 0x%08" PRIX32 "\n", exitCode);
(void)CloseHandle(ProcessInformation.hProcess);
(void)CloseHandle(ProcessInformation.hThread);
return ret;
}

View File

@@ -0,0 +1,53 @@
// Copyright © 2015 Hewlett-Packard Development Company, L.P.
#include <winpr/file.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
static DWORD WINAPI thread_func(LPVOID arg)
{
WINPR_UNUSED(arg);
/* exists of the thread the quickest as possible */
ExitThread(0);
return 0;
}
int TestThreadExitThread(int argc, char* argv[])
{
HANDLE thread = nullptr;
DWORD waitResult = 0;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
/* FIXME: create some noise to better guaranty the test validity and
* decrease the number of loops */
for (int i = 0; i < 100; i++)
{
thread = CreateThread(nullptr, 0, thread_func, nullptr, 0, nullptr);
if (thread == INVALID_HANDLE_VALUE)
{
(void)fprintf(stderr, "Got an invalid thread!\n");
return -1;
}
waitResult = WaitForSingleObject(thread, 300);
if (waitResult != WAIT_OBJECT_0)
{
/* When the thread exits before the internal thread_list
* was updated, ExitThread() is not able to retrieve the
* related WINPR_THREAD object and is not able to signal
* the end of the thread. Therefore WaitForSingleObject
* never get the signal.
*/
(void)fprintf(
stderr, "300ms should have been enough for the thread to be in a signaled state\n");
return -1;
}
(void)CloseHandle(thread);
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
/**
* WinPR: Windows Portable Runtime
* Process Thread Functions
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Hewlett-Packard Development Company, L.P.
* Copyright 2021 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef WINPR_THREAD_PRIVATE_H
#define WINPR_THREAD_PRIVATE_H
#ifndef _WIN32
#include <pthread.h>
#include <winpr/thread.h>
#include "../handle/handle.h"
#include "../synch/event.h"
#include "apc.h"
typedef void* (*pthread_start_routine)(void*);
typedef struct winpr_APC_item WINPR_APC_ITEM;
typedef struct
{
WINPR_ALIGN64 pthread_mutex_t mux;
WINPR_ALIGN64 pthread_cond_t cond;
WINPR_ALIGN64 BOOL val;
} mux_condition_bundle;
struct winpr_thread
{
WINPR_HANDLE common;
WINPR_ALIGN64 BOOL started;
WINPR_ALIGN64 WINPR_EVENT_IMPL event;
WINPR_ALIGN64 BOOL mainProcess;
WINPR_ALIGN64 BOOL detached;
WINPR_ALIGN64 BOOL joined;
WINPR_ALIGN64 BOOL exited;
WINPR_ALIGN64 DWORD dwExitCode;
WINPR_ALIGN64 pthread_t thread;
WINPR_ALIGN64 size_t dwStackSize;
WINPR_ALIGN64 LPVOID lpParameter;
WINPR_ALIGN64 pthread_mutex_t mutex;
mux_condition_bundle isRunning;
mux_condition_bundle isCreated;
WINPR_ALIGN64 LPTHREAD_START_ROUTINE lpStartAddress;
WINPR_ALIGN64 LPSECURITY_ATTRIBUTES lpThreadAttributes;
WINPR_ALIGN64 APC_QUEUE apc;
#if defined(WITH_DEBUG_THREADS)
WINPR_ALIGN64 void* create_stack;
WINPR_ALIGN64 void* exit_stack;
#endif
};
WINPR_THREAD* winpr_GetCurrentThread(VOID);
typedef struct
{
WINPR_HANDLE common;
pid_t pid;
int status;
DWORD dwExitCode;
int fd;
} WINPR_PROCESS;
#endif
#endif /* WINPR_THREAD_PRIVATE_H */

View File

@@ -0,0 +1,72 @@
/**
* WinPR: Windows Portable Runtime
* Process Thread Functions
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/config.h>
#include <winpr/handle.h>
#include <winpr/thread.h>
/**
* TlsAlloc
* TlsFree
* TlsGetValue
* TlsSetValue
*/
#ifndef _WIN32
#include <pthread.h>
DWORD TlsAlloc(void)
{
pthread_key_t key = 0;
if (pthread_key_create(&key, nullptr) != 0)
return TLS_OUT_OF_INDEXES;
return key;
}
LPVOID TlsGetValue(DWORD dwTlsIndex)
{
LPVOID value = nullptr;
pthread_key_t key = 0;
key = (pthread_key_t)dwTlsIndex;
value = (LPVOID)pthread_getspecific(key);
return value;
}
BOOL TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue)
{
pthread_key_t key = 0;
key = (pthread_key_t)dwTlsIndex;
pthread_setspecific(key, lpTlsValue);
return TRUE;
}
BOOL TlsFree(DWORD dwTlsIndex)
{
pthread_key_t key = 0;
key = (pthread_key_t)dwTlsIndex;
pthread_key_delete(key);
return TRUE;
}
#endif