[PATCH v3 0/2] MR9882: kernelbase: Trim whitespaces from the quoted 'cmdline'
If 'name' does not have a suffix, a suffix will be appended in find_exe_file. If 'name' contains spaces, it will result in an incorrect filename being concatenated. In the log of a certain installer, I saw an entry like this: trace:process:CreateProcessInternalW app (null) cmdline L"\"C:\\Program Files\\some folder\\7za \" x -y -aos \"-oC://Program Files//target folder/\" \"C://Program Files//source.zip\"" Signed-off-by: YeshunYe <yeyeshun@uniontech.com> -- v3: kernelbase: Trim whitespaces from the quoted 'cmdline' kernelbase/tests: Add a test for CreateProcessInternalW https://gitlab.winehq.org/wine/wine/-/merge_requests/9882
From: YeshunYe <yeyeshun@uniontech.com> test for cmd with quotes and whitespaces Signed-off-by: YeshunYe <yeyeshun@uniontech.com> --- dlls/kernelbase/tests/process.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dlls/kernelbase/tests/process.c b/dlls/kernelbase/tests/process.c index 9217d07bc0c..373e8a587e4 100644 --- a/dlls/kernelbase/tests/process.c +++ b/dlls/kernelbase/tests/process.c @@ -627,6 +627,26 @@ static void test_QueryProcessCycleTime(void) ok( cycles2 > cycles1, "CPU cycles used by process should be increasing.\n" ); } +static void test_CreateProcessInternalW(void) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + WCHAR cmd[] = L"\"c:\\windows\\system32\\tasklist \" /?"; + BOOL ret; + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + ret = CreateProcessInternalW(NULL, NULL, (LPWSTR)cmd, NULL, NULL, FALSE, + CREATE_NO_WINDOW | DETACHED_PROCESS, + NULL, NULL, &si, &pi, NULL); + todo_wine + ok( ret, "CreateProcessInternalW failed, error %lu.\n", GetLastError() ); + if (pi.hProcess) + wait_child_process(&pi); +} + static void init_funcs(void) { HMODULE hmod = GetModuleHandleA("kernelbase.dll"); @@ -663,4 +683,5 @@ START_TEST(process) test_CreateFileMappingFromApp(); test_MapViewOfFileFromApp(); test_QueryProcessCycleTime(); + test_CreateProcessInternalW(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9882
From: YeshunYe <yeyeshun@uniontech.com> If 'name' does not have a suffix, a suffix will be appended in find_exe_file. If 'name' contains spaces, it will result in an incorrect filename being concatenated. Signed-off-by: YeshunYe <yeyeshun@uniontech.com> --- dlls/kernelbase/process.c | 10 ++++++++-- dlls/kernelbase/tests/process.c | 1 - 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 3656e40280d..d3a5b91074f 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -87,10 +87,16 @@ static WCHAR *get_file_name( WCHAR *cmdline, WCHAR *buffer, DWORD buflen ) if (cmdline[0] == '"' && (p = wcschr( cmdline + 1, '"' ))) { - int len = p - cmdline - 1; + int len; + /* trim spaces in quotes */ + const WCHAR* start = cmdline + 1; + const WCHAR* end = p - 1; + while (*start == ' ') start++; + while (*end == ' ') end--; + len = end - start + 1; /* extract the quoted portion as file name */ if (!(name = RtlAllocateHeap( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) return NULL; - memcpy( name, cmdline + 1, len * sizeof(WCHAR) ); + memcpy( name, start, len * sizeof(WCHAR) ); name[len] = 0; if (!find_exe_file( name, buffer, buflen )) goto done; diff --git a/dlls/kernelbase/tests/process.c b/dlls/kernelbase/tests/process.c index 373e8a587e4..2cf1ac407ca 100644 --- a/dlls/kernelbase/tests/process.c +++ b/dlls/kernelbase/tests/process.c @@ -641,7 +641,6 @@ static void test_CreateProcessInternalW(void) ret = CreateProcessInternalW(NULL, NULL, (LPWSTR)cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW | DETACHED_PROCESS, NULL, NULL, &si, &pi, NULL); - todo_wine ok( ret, "CreateProcessInternalW failed, error %lu.\n", GetLastError() ); if (pi.hProcess) wait_child_process(&pi); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9882
On Wed Jan 14 09:37:57 2026 +0000, eric pouech wrote:
CreateProcessW should do (in dlls/kernel32/tests/process.c) I have added the test cases. However, since I modified the code in `kernelbase`, I directly added the test cases there. It is unnecessary to add them across modules in `kernel32`.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9882#note_127021
On Thu Jan 15 09:07:46 2026 +0000, Yeshun Ye wrote:
I have added the test cases. However, since I modified the code in `kernelbase`, I directly added the test cases there. It is unnecessary to add them across modules in `kernel32`. actually, it would be preferrable to keep them in kernel32
that comes from decades ago when kernelbase didn"t exist, and hence everything was in kernel32; when kernelbase has been introduced, implementation has been moved from kernel32 into kernelbase, but tests have been kept in kernel32 we prefer keeping all tests at a single place ; all tests for CreateProcess are in dlls/kernel32/tests -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9882#note_127026
eric pouech (@epo) commented about dlls/kernelbase/tests/process.c:
ok( cycles2 > cycles1, "CPU cycles used by process should be increasing.\n" ); }
+static void test_CreateProcessInternalW(void) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + WCHAR cmd[] = L"\"c:\\windows\\system32\\tasklist \" /?";
we tend not to depend on other executables when possible; so the preferred solution is to spawn the same image (see other tests in kernel32/tests/process.c, using 'selfname' + a verb) this allows also to check passed parameters and adjust exit code in child process accordingly (if needed) I'd add also leading spaces so that we test both cases (leading and trailing) I would be tempted to test also for a tab (not sure if it works though) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9882#note_127029
eric pouech (@epo) commented about dlls/kernelbase/process.c:
if (cmdline[0] == '"' && (p = wcschr( cmdline + 1, '"' ))) { - int len = p - cmdline - 1; + int len; + /* trim spaces in quotes */ + const WCHAR* start = cmdline + 1; + const WCHAR* end = p - 1; + while (*start == ' ') start++; + while (*end == ' ') end--;
what if cmdline contains only spaces between the quotes? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9882#note_127032
eric pouech (@epo) commented about dlls/kernelbase/tests/process.c:
ok( cycles2 > cycles1, "CPU cycles used by process should be increasing.\n" ); }
+static void test_CreateProcessInternalW(void) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + WCHAR cmd[] = L"\"c:\\windows\\system32\\tasklist \" /?"; + BOOL ret; + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + ret = CreateProcessInternalW(NULL, NULL, (LPWSTR)cmd, NULL, NULL, FALSE,
I'd rather test CreateProcessW (moreover I'm not sure it's exported from all Windows version out there) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9882#note_127031
eric pouech (@epo) commented about dlls/kernelbase/tests/process.c:
ok( cycles2 > cycles1, "CPU cycles used by process should be increasing.\n" ); }
+static void test_CreateProcessInternalW(void) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + WCHAR cmd[] = L"\"c:\\windows\\system32\\tasklist \" /?"; + BOOL ret; + + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + ret = CreateProcessInternalW(NULL, NULL, (LPWSTR)cmd, NULL, NULL, FALSE, + CREATE_NO_WINDOW | DETACHED_PROCESS,
CREATE_NO_WINDOW and DETACHED_PROCESS are orthogonal; you can pick up either one (DETACHED_PROCESS is less hungry on resources though) -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9882#note_127030
participants (3)
-
eric pouech (@epo) -
Yeshun Ye (@yeyeshun) -
YeshunYe