xref: /reactos/dll/win32/kernel32/client/proc.c (revision 80733143)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            dll/win32/kernel32/client/proc.c
5  * PURPOSE:         Process functions
6  * PROGRAMMERS:     Ariadne (ariadne@xs4all.nl)
7  * UPDATE HISTORY:
8  *                  Created 01/11/98
9  */
10 
11 /* INCLUDES ****************************************************************/
12 
13 #include <k32.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 /* GLOBALS *******************************************************************/
19 
20 WaitForInputIdleType UserWaitForInputIdleRoutine;
21 UNICODE_STRING BaseUnicodeCommandLine;
22 ANSI_STRING BaseAnsiCommandLine;
23 UNICODE_STRING BasePathVariableName = RTL_CONSTANT_STRING(L"PATH");
24 LPSTARTUPINFOA BaseAnsiStartupInfo = NULL;
25 PLDR_DATA_TABLE_ENTRY BasepExeLdrEntry;
26 BOOLEAN g_AppCertInitialized;
27 BOOLEAN g_HaveAppCerts;
28 LIST_ENTRY BasepAppCertDllsList;
29 RTL_CRITICAL_SECTION gcsAppCert;
30 PBASEP_APPCERT_EMBEDDED_FUNC fEmbeddedCertFunc;
31 NTSTATUS g_AppCertStatus;
32 RTL_QUERY_REGISTRY_TABLE BasepAppCertTable[2] =
33 {
34     {
35         BasepConfigureAppCertDlls,
36         1,
37         L"AppCertDlls",
38         &BasepAppCertDllsList,
39         0,
40         NULL,
41         0
42     }
43 };
44 
45 PSAFER_REPLACE_PROCESS_THREAD_TOKENS g_SaferReplaceProcessThreadTokens;
46 HMODULE gSaferHandle = (HMODULE)-1;
47 
48 VOID WINAPI
49 RegisterWaitForInputIdle(WaitForInputIdleType lpfnRegisterWaitForInputIdle);
50 
51 #define CMD_STRING L"cmd /c "
52 
53 /* FUNCTIONS ****************************************************************/
54 
55 VOID
56 WINAPI
57 StuffStdHandle(IN HANDLE ProcessHandle,
58                IN HANDLE StandardHandle,
59                IN PHANDLE Address)
60 {
61     NTSTATUS Status;
62     HANDLE DuplicatedHandle;
63     SIZE_T NumberOfBytesWritten;
64 
65     /* If there is no handle to duplicate, return immediately */
66     if (!StandardHandle) return;
67 
68     /* Duplicate the handle */
69     Status = NtDuplicateObject(NtCurrentProcess(),
70                                StandardHandle,
71                                ProcessHandle,
72                                &DuplicatedHandle,
73                                0,
74                                0,
75                                DUPLICATE_SAME_ACCESS |
76                                DUPLICATE_SAME_ATTRIBUTES);
77     if (!NT_SUCCESS(Status)) return;
78 
79     /* Write it */
80     NtWriteVirtualMemory(ProcessHandle,
81                          Address,
82                          &DuplicatedHandle,
83                          sizeof(HANDLE),
84                          &NumberOfBytesWritten);
85 }
86 
87 BOOLEAN
88 WINAPI
89 BuildSubSysCommandLine(IN LPCWSTR SubsystemName,
90                        IN LPCWSTR ApplicationName,
91                        IN LPCWSTR CommandLine,
92                        OUT PUNICODE_STRING SubsysCommandLine)
93 {
94     UNICODE_STRING CommandLineString, ApplicationNameString;
95     PWCHAR Buffer;
96     ULONG Length;
97 
98     /* Convert to unicode strings */
99     RtlInitUnicodeString(&CommandLineString, ApplicationName);
100     RtlInitUnicodeString(&ApplicationNameString, CommandLine);
101 
102     /* Allocate buffer for the output string */
103     Length = CommandLineString.MaximumLength + ApplicationNameString.MaximumLength + 32;
104     Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
105     RtlInitEmptyUnicodeString(SubsysCommandLine, Buffer, (USHORT)Length);
106     if (!Buffer)
107     {
108         /* Fail, no memory */
109         BaseSetLastNTError(STATUS_NO_MEMORY);
110         return FALSE;
111     }
112 
113     /* Build the final subsystem command line */
114     RtlAppendUnicodeToString(SubsysCommandLine, SubsystemName);
115     RtlAppendUnicodeStringToString(SubsysCommandLine, &CommandLineString);
116     RtlAppendUnicodeToString(SubsysCommandLine, L" /C ");
117     RtlAppendUnicodeStringToString(SubsysCommandLine, &ApplicationNameString);
118     return TRUE;
119 }
120 
121 BOOLEAN
122 WINAPI
123 BasepIsImageVersionOk(IN ULONG ImageMajorVersion,
124                       IN ULONG ImageMinorVersion)
125 {
126     /* Accept images for NT 3.1 or higher */
127     if (ImageMajorVersion > 3 ||
128         (ImageMajorVersion == 3 && ImageMinorVersion >= 10))
129     {
130         /* ReactOS-specific: Accept images even if they are newer than our internal NT version. */
131         if (ImageMajorVersion > SharedUserData->NtMajorVersion ||
132             (ImageMajorVersion == SharedUserData->NtMajorVersion && ImageMinorVersion > SharedUserData->NtMinorVersion))
133         {
134             DPRINT1("Accepting image version %lu.%lu, although ReactOS is an NT %hu.%hu OS!\n",
135                 ImageMajorVersion,
136                 ImageMinorVersion,
137                 SharedUserData->NtMajorVersion,
138                 SharedUserData->NtMinorVersion);
139         }
140 
141         return TRUE;
142     }
143 
144     return FALSE;
145 }
146 
147 NTSTATUS
148 WINAPI
149 BasepCheckWebBladeHashes(IN HANDLE FileHandle)
150 {
151     NTSTATUS Status;
152     CHAR Hash[16];
153 
154     /* Get all the MD5 hashes */
155     Status = RtlComputeImportTableHash(FileHandle, Hash, 1);
156     if (!NT_SUCCESS(Status)) return Status;
157 
158     /* Depending on which suite this is, run a bsearch and block the appropriate ones */
159     if (SharedUserData->SuiteMask & VER_SUITE_COMPUTE_SERVER)
160     {
161         DPRINT1("Egad! This is a ReactOS Compute Server and we should prevent you from using certain APIs...but we won't.");
162     }
163     else if (SharedUserData->SuiteMask & VER_SUITE_STORAGE_SERVER)
164     {
165         DPRINT1("Gasp! This is a ReactOS Storage Server and we should prevent you from using certain APIs...but we won't.");
166     }
167     else if (SharedUserData->SuiteMask & VER_SUITE_BLADE)
168     {
169         DPRINT1("Golly! This is a ReactOS Web Blade Server and we should prevent you from using certain APIs...but we won't.");
170     }
171 
172     /* Actually, fuck it, don't block anything, we're open source */
173     return STATUS_SUCCESS;
174 }
175 
176 NTSTATUS
177 NTAPI
178 BasepSaveAppCertRegistryValue(IN PLIST_ENTRY List,
179                               IN PWCHAR ComponentName,
180                               IN PWCHAR DllName)
181 {
182     /* Pretty much the only thing this key is used for, is malware */
183     UNIMPLEMENTED;
184     return STATUS_NOT_IMPLEMENTED;
185 }
186 
187 NTSTATUS
188 NTAPI
189 BasepConfigureAppCertDlls(IN PWSTR ValueName,
190                           IN ULONG ValueType,
191                           IN PVOID ValueData,
192                           IN ULONG ValueLength,
193                           IN PVOID Context,
194                           IN PVOID EntryContext)
195 {
196     /* Add this to the certification list */
197     return BasepSaveAppCertRegistryValue(Context, ValueName, ValueData);
198 }
199 
200 NTSTATUS
201 WINAPI
202 BasepIsProcessAllowed(IN LPWSTR ApplicationName)
203 {
204     NTSTATUS Status, Status1;
205     PWCHAR Buffer;
206     UINT Length;
207     HMODULE TrustLibrary;
208     PBASEP_APPCERT_ENTRY Entry;
209     ULONG CertFlag;
210     PLIST_ENTRY NextEntry;
211     HANDLE KeyHandle;
212     UNICODE_STRING CertKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCertDlls");
213     OBJECT_ATTRIBUTES KeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&CertKey, OBJ_CASE_INSENSITIVE);
214 
215     /* Try to initialize the certification subsystem */
216     while (!g_AppCertInitialized)
217     {
218         /* Defaults */
219         Status = STATUS_SUCCESS;
220         Buffer = NULL;
221 
222         /* Acquire the lock while initializing and see if we lost a race */
223         RtlEnterCriticalSection(&gcsAppCert);
224         if (g_AppCertInitialized) break;
225 
226         /* On embedded, there is a special DLL */
227         if (SharedUserData->SuiteMask & VER_SUITE_EMBEDDEDNT)
228         {
229             /* Allocate a buffer for the name */
230             Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
231                                      0,
232                                      MAX_PATH * sizeof(WCHAR) +
233                                      sizeof(UNICODE_NULL));
234             if (!Buffer)
235             {
236                 /* Fail if no memory */
237                 Status = STATUS_NO_MEMORY;
238             }
239             else
240             {
241                 /* Now get the system32 directory in our buffer, make sure it fits */
242                 Length = GetSystemDirectoryW(Buffer, MAX_PATH - sizeof("EmbdTrst.DLL"));
243                 if ((Length) && (Length <= MAX_PATH - sizeof("EmbdTrst.DLL")))
244                 {
245                     /* Add a slash if needed, and add the embedded cert DLL name */
246                     if (Buffer[Length - 1] != '\\') Buffer[Length++] = '\\';
247                     RtlCopyMemory(&Buffer[Length],
248                                   L"EmbdTrst.DLL",
249                                   sizeof(L"EmbdTrst.DLL"));
250 
251                     /* Try to load it */
252                     TrustLibrary = LoadLibraryW(Buffer);
253                     if (TrustLibrary)
254                     {
255                         /* And extract the special function out of it */
256                         fEmbeddedCertFunc = (PVOID)GetProcAddress(TrustLibrary,
257                                                                   "ImageOkToRunOnEmbeddedNT");
258                     }
259                 }
260 
261                 /* If we didn't get this far, set a failure code */
262                 if (!fEmbeddedCertFunc) Status = STATUS_UNSUCCESSFUL;
263             }
264         }
265         else
266         {
267             /* Other systems have a registry entry for this */
268             Status1 = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
269             if (NT_SUCCESS(Status1))
270             {
271                 /* Close it, we'll query it through Rtl */
272                 NtClose(KeyHandle);
273 
274                 /* Do the query, which will call a special callback */
275                 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
276                                                 L"Session Manager",
277                                                 BasepAppCertTable,
278                                                 NULL,
279                                                 NULL);
280                 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
281                 {
282                     Status = STATUS_SUCCESS;
283                 }
284             }
285         }
286 
287         /* Free any buffer if we had one */
288         if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
289 
290         /* Check for errors, or a missing embedded/custom certification DLL */
291         if (!NT_SUCCESS(Status) ||
292             (!(fEmbeddedCertFunc) && (IsListEmpty(&BasepAppCertDllsList))))
293         {
294             /* The subsystem is not active on this machine, so give up */
295             g_HaveAppCerts = FALSE;
296             g_AppCertStatus = Status;
297         }
298         else
299         {
300             /* We have certification DLLs active, remember this */
301             g_HaveAppCerts = TRUE;
302         }
303 
304         /* We are done the initialization phase, release the lock */
305         g_AppCertInitialized = TRUE;
306         RtlLeaveCriticalSection(&gcsAppCert);
307     }
308 
309     /* If there's no certification DLLs present, return the failure code */
310     if (!g_HaveAppCerts) return g_AppCertStatus;
311 
312     /* Otherwise, assume success and make sure we have *something* */
313     ASSERT(fEmbeddedCertFunc || !IsListEmpty(&BasepAppCertDllsList));
314     Status = STATUS_SUCCESS;
315 
316     /* If the something is an embedded certification DLL, call it and return */
317     if (fEmbeddedCertFunc) return fEmbeddedCertFunc(ApplicationName);
318 
319     /* Otherwise we have custom certification DLLs, parse them */
320     NextEntry = BasepAppCertDllsList.Flink;
321     CertFlag = 2;
322     while (NextEntry != &BasepAppCertDllsList)
323     {
324         /* Make sure the entry has a callback */
325         Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
326         ASSERT(Entry->fPluginCertFunc != NULL);
327 
328         /* Call it and check if it failed */
329         Status = Entry->fPluginCertFunc(ApplicationName, 1);
330         if (!NT_SUCCESS(Status)) CertFlag = 3;
331 
332         /* Move on */
333         NextEntry = NextEntry->Flink;
334     }
335 
336     /* Now loop them again */
337     NextEntry = BasepAppCertDllsList.Flink;
338     while (NextEntry != &BasepAppCertDllsList)
339     {
340         /* Make sure the entry has a callback */
341         Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
342         ASSERT(Entry->fPluginCertFunc != NULL);
343 
344         /* Call it, this time with the flag from the loop above */
345         Status = Entry->fPluginCertFunc(ApplicationName, CertFlag);
346     }
347 
348     /* All done, return the status */
349     return Status;
350 }
351 
352 NTSTATUS
353 WINAPI
354 BasepReplaceProcessThreadTokens(IN HANDLE TokenHandle,
355                                 IN HANDLE ProcessHandle,
356                                 IN HANDLE ThreadHandle)
357 {
358     NTSTATUS Status;
359     ANSI_STRING SaferiReplaceProcessThreadTokens = RTL_CONSTANT_STRING("SaferiReplaceProcessThreadTokens");
360 
361     /* Enter the application certification lock */
362     RtlEnterCriticalSection(&gcsAppCert);
363 
364     /* Check if we already know the function */
365     if (g_SaferReplaceProcessThreadTokens)
366     {
367         /* Call it */
368         Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
369                                                    ProcessHandle,
370                                                    ThreadHandle) ?
371                                                    STATUS_SUCCESS :
372                                                    STATUS_UNSUCCESSFUL;
373     }
374     else
375     {
376         /* Check if the app certification DLL isn't loaded */
377         if (!(gSaferHandle) ||
378             (gSaferHandle == (HMODULE)-1) ||
379             (gSaferHandle == (HMODULE)-2))
380         {
381             /* Then we can't call the function */
382             Status = STATUS_ENTRYPOINT_NOT_FOUND;
383         }
384         else
385         {
386             /* We have the DLL, find the address of the Safer function */
387             Status = LdrGetProcedureAddress(gSaferHandle,
388                                             &SaferiReplaceProcessThreadTokens,
389                                             0,
390                                             (PVOID*)&g_SaferReplaceProcessThreadTokens);
391             if (NT_SUCCESS(Status))
392             {
393                 /* Found it, now call it */
394                 Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
395                                                            ProcessHandle,
396                                                            ThreadHandle) ?
397                                                            STATUS_SUCCESS :
398                                                            STATUS_UNSUCCESSFUL;
399             }
400             else
401             {
402                 /* We couldn't find it, so this must be an unsupported DLL */
403                 LdrUnloadDll(gSaferHandle);
404                 gSaferHandle = NULL;
405                 Status = STATUS_ENTRYPOINT_NOT_FOUND;
406             }
407         }
408     }
409 
410     /* Release the lock and return the result */
411     RtlLeaveCriticalSection(&gcsAppCert);
412     return Status;
413 }
414 
415 VOID
416 WINAPI
417 BasepSxsCloseHandles(IN PBASE_MSG_SXS_HANDLES Handles)
418 {
419     NTSTATUS Status;
420 
421     /* Sanity checks */
422     ASSERT(Handles != NULL);
423     ASSERT(Handles->Process == NULL || Handles->Process == NtCurrentProcess());
424 
425     /* Close the file handle */
426     if (Handles->File)
427     {
428         Status = NtClose(Handles->File);
429         ASSERT(NT_SUCCESS(Status));
430     }
431 
432     /* Close the section handle */
433     if (Handles->Section)
434     {
435         Status = NtClose(Handles->Section);
436         ASSERT(NT_SUCCESS(Status));
437     }
438 
439     /* Unmap the section view */
440     if (Handles->ViewBase.QuadPart)
441     {
442         Status = NtUnmapViewOfSection(NtCurrentProcess(),
443                                       (PVOID)(ULONG_PTR)Handles->ViewBase.QuadPart);
444         ASSERT(NT_SUCCESS(Status));
445     }
446 }
447 
448 static
449 LONG BaseExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo)
450 {
451     LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
452     LPTOP_LEVEL_EXCEPTION_FILTER RealFilter;
453     RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter);
454 
455     if (RealFilter != NULL)
456     {
457         _SEH2_TRY
458         {
459             ExceptionDisposition = RealFilter(ExceptionInfo);
460         }
461         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
462         {
463         }
464         _SEH2_END;
465     }
466     if ((ExceptionDisposition == EXCEPTION_CONTINUE_SEARCH || ExceptionDisposition == EXCEPTION_EXECUTE_HANDLER) &&
467         RealFilter != UnhandledExceptionFilter)
468     {
469        ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
470     }
471 
472     return ExceptionDisposition;
473 }
474 
475 VOID
476 WINAPI
477 BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress)
478 {
479     DPRINT("BaseProcessStartup(..) - setting up exception frame.\n");
480 
481     _SEH2_TRY
482     {
483         /* Set our Start Address */
484         NtSetInformationThread(NtCurrentThread(),
485                                ThreadQuerySetWin32StartAddress,
486                                &lpStartAddress,
487                                sizeof(PPROCESS_START_ROUTINE));
488 
489         /* Call the Start Routine */
490         ExitThread(lpStartAddress());
491     }
492     _SEH2_EXCEPT(BaseExceptionFilter(_SEH2_GetExceptionInformation()))
493     {
494         /* Get the Exit code from the SEH Handler */
495         if (!BaseRunningInServerProcess)
496         {
497             /* Kill the whole process, usually */
498             ExitProcess(_SEH2_GetExceptionCode());
499         }
500         else
501         {
502             /* If running inside CSRSS, kill just this thread */
503             ExitThread(_SEH2_GetExceptionCode());
504         }
505     }
506     _SEH2_END;
507 }
508 
509 NTSTATUS
510 WINAPI
511 BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,
512                        IN PCLIENT_ID ClientId)
513 {
514     BASE_API_MESSAGE ApiMessage;
515     PBASE_CREATE_THREAD CreateThreadRequest = &ApiMessage.Data.CreateThreadRequest;
516 
517     DPRINT("BasepNotifyCsrOfThread: Thread: %p, Handle %p\n",
518             ClientId->UniqueThread, ThreadHandle);
519 
520     /* Fill out the request */
521     CreateThreadRequest->ClientId = *ClientId;
522     CreateThreadRequest->ThreadHandle = ThreadHandle;
523 
524     /* Call CSR */
525     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
526                         NULL,
527                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateThread),
528                         sizeof(*CreateThreadRequest));
529     if (!NT_SUCCESS(ApiMessage.Status))
530     {
531         DPRINT1("Failed to tell CSRSS about new thread: %lx\n", ApiMessage.Status);
532         return ApiMessage.Status;
533     }
534 
535     /* Return Success */
536     return STATUS_SUCCESS;
537 }
538 
539 BOOLEAN
540 WINAPI
541 BasePushProcessParameters(IN ULONG ParameterFlags,
542                           IN HANDLE ProcessHandle,
543                           IN PPEB RemotePeb,
544                           IN LPCWSTR ApplicationPathName,
545                           IN LPWSTR lpCurrentDirectory,
546                           IN LPWSTR lpCommandLine,
547                           IN LPVOID lpEnvironment,
548                           IN LPSTARTUPINFOW StartupInfo,
549                           IN DWORD CreationFlags,
550                           IN BOOL InheritHandles,
551                           IN ULONG ImageSubsystem,
552                           IN PVOID AppCompatData,
553                           IN ULONG AppCompatDataSize)
554 {
555     WCHAR FullPath[MAX_PATH + 5];
556     PWCHAR Remaining, DllPathString, ScanChar;
557     PRTL_USER_PROCESS_PARAMETERS ProcessParameters, RemoteParameters;
558     PVOID RemoteAppCompatData;
559     UNICODE_STRING DllPath, ImageName, CommandLine, CurrentDirectory;
560     UNICODE_STRING Desktop, Shell, Runtime, Title;
561     NTSTATUS Status;
562     ULONG EnviroSize;
563     SIZE_T Size;
564     BOOLEAN HavePebLock = FALSE, Result;
565     PPEB Peb = NtCurrentPeb();
566 
567     /* Get the full path name */
568     Size = GetFullPathNameW(ApplicationPathName,
569                             MAX_PATH + 4,
570                             FullPath,
571                             &Remaining);
572     if ((Size) && (Size <= (MAX_PATH + 4)))
573     {
574         /* Get the DLL Path */
575         DllPathString = BaseComputeProcessDllPath(FullPath, lpEnvironment);
576         if (!DllPathString)
577         {
578             /* Fail */
579             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
580             return FALSE;
581         }
582 
583         /* Initialize Strings */
584         RtlInitUnicodeString(&DllPath, DllPathString);
585         RtlInitUnicodeString(&ImageName, FullPath);
586     }
587     else
588     {
589         /* Couldn't get the path name. Just take the original path */
590         DllPathString = BaseComputeProcessDllPath((LPWSTR)ApplicationPathName,
591                                                   lpEnvironment);
592         if (!DllPathString)
593         {
594             /* Fail */
595             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
596             return FALSE;
597         }
598 
599         /* Initialize Strings */
600         RtlInitUnicodeString(&DllPath, DllPathString);
601         RtlInitUnicodeString(&ImageName, ApplicationPathName);
602     }
603 
604     /* Initialize Strings */
605     RtlInitUnicodeString(&CommandLine, lpCommandLine);
606     RtlInitUnicodeString(&CurrentDirectory, lpCurrentDirectory);
607 
608     /* Initialize more Strings from the Startup Info */
609     if (StartupInfo->lpDesktop)
610     {
611         RtlInitUnicodeString(&Desktop, StartupInfo->lpDesktop);
612     }
613     else
614     {
615         RtlInitUnicodeString(&Desktop, L"");
616     }
617     if (StartupInfo->lpReserved)
618     {
619         RtlInitUnicodeString(&Shell, StartupInfo->lpReserved);
620     }
621     else
622     {
623         RtlInitUnicodeString(&Shell, L"");
624     }
625     if (StartupInfo->lpTitle)
626     {
627         RtlInitUnicodeString(&Title, StartupInfo->lpTitle);
628     }
629     else
630     {
631         RtlInitUnicodeString(&Title, ApplicationPathName);
632     }
633 
634     /* This one is special because the length can differ */
635     Runtime.Buffer = (LPWSTR)StartupInfo->lpReserved2;
636     Runtime.MaximumLength = Runtime.Length = StartupInfo->cbReserved2;
637 
638     /* Enforce no app compat data if the pointer was NULL */
639     if (!AppCompatData) AppCompatDataSize = 0;
640 
641     /* Create the Parameter Block */
642     ProcessParameters = NULL;
643     DPRINT("ImageName: '%wZ'\n", &ImageName);
644     DPRINT("DllPath  : '%wZ'\n", &DllPath);
645     DPRINT("CurDir   : '%wZ'\n", &CurrentDirectory);
646     DPRINT("CmdLine  : '%wZ'\n", &CommandLine);
647     DPRINT("Title    : '%wZ'\n", &Title);
648     DPRINT("Desktop  : '%wZ'\n", &Desktop);
649     DPRINT("Shell    : '%wZ'\n", &Shell);
650     DPRINT("Runtime  : '%wZ'\n", &Runtime);
651     Status = RtlCreateProcessParameters(&ProcessParameters,
652                                         &ImageName,
653                                         &DllPath,
654                                         lpCurrentDirectory ?
655                                         &CurrentDirectory : NULL,
656                                         &CommandLine,
657                                         lpEnvironment,
658                                         &Title,
659                                         &Desktop,
660                                         &Shell,
661                                         &Runtime);
662     if (!NT_SUCCESS(Status)) goto FailPath;
663 
664     /* Clear the current directory handle if not inheriting */
665     if (!InheritHandles) ProcessParameters->CurrentDirectory.Handle = NULL;
666 
667     /* Check if the user passed in an environment */
668     if (lpEnvironment)
669     {
670         /* We should've made it part of the parameters block, enforce this */
671         ASSERT(ProcessParameters->Environment == lpEnvironment);
672         lpEnvironment = ProcessParameters->Environment;
673     }
674     else
675     {
676         /* The user did not, so use the one from the current PEB */
677         HavePebLock = TRUE;
678         RtlAcquirePebLock();
679         lpEnvironment = Peb->ProcessParameters->Environment;
680     }
681 
682     /* Save pointer and start lookup */
683     ScanChar = lpEnvironment;
684     if (lpEnvironment)
685     {
686         /* Find the environment size */
687         while (*ScanChar++) while (*ScanChar++);
688         EnviroSize = (ULONG)((ULONG_PTR)ScanChar - (ULONG_PTR)lpEnvironment);
689 
690         /* Allocate and Initialize new Environment Block */
691         Size = EnviroSize;
692         ProcessParameters->Environment = NULL;
693         Status = NtAllocateVirtualMemory(ProcessHandle,
694                                          (PVOID*)&ProcessParameters->Environment,
695                                          0,
696                                          &Size,
697                                          MEM_COMMIT,
698                                          PAGE_READWRITE);
699         if (!NT_SUCCESS(Status)) goto FailPath;
700 
701         /* Write the Environment Block */
702         Status = NtWriteVirtualMemory(ProcessHandle,
703                                       ProcessParameters->Environment,
704                                       lpEnvironment,
705                                       EnviroSize,
706                                       NULL);
707 
708         /* No longer need the PEB lock anymore */
709         if (HavePebLock)
710         {
711             /* Release it */
712             RtlReleasePebLock();
713             HavePebLock = FALSE;
714         }
715 
716         /* Check if the write failed */
717         if (!NT_SUCCESS(Status)) goto FailPath;
718     }
719 
720     /* Write new parameters */
721     ProcessParameters->StartingX = StartupInfo->dwX;
722     ProcessParameters->StartingY = StartupInfo->dwY;
723     ProcessParameters->CountX = StartupInfo->dwXSize;
724     ProcessParameters->CountY = StartupInfo->dwYSize;
725     ProcessParameters->CountCharsX = StartupInfo->dwXCountChars;
726     ProcessParameters->CountCharsY = StartupInfo->dwYCountChars;
727     ProcessParameters->FillAttribute = StartupInfo->dwFillAttribute;
728     ProcessParameters->WindowFlags = StartupInfo->dwFlags;
729     ProcessParameters->ShowWindowFlags = StartupInfo->wShowWindow;
730 
731     /* Write the handles only if we have to */
732     if (StartupInfo->dwFlags &
733         (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
734     {
735         ProcessParameters->StandardInput = StartupInfo->hStdInput;
736         ProcessParameters->StandardOutput = StartupInfo->hStdOutput;
737         ProcessParameters->StandardError = StartupInfo->hStdError;
738     }
739 
740     /* Use Special Flags for ConDllInitialize in Kernel32 */
741     if (CreationFlags & DETACHED_PROCESS)
742     {
743         ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS;
744     }
745     else if (CreationFlags & CREATE_NEW_CONSOLE)
746     {
747         ProcessParameters->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE;
748     }
749     else if (CreationFlags & CREATE_NO_WINDOW)
750     {
751         ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
752     }
753     else
754     {
755         /* Inherit our Console Handle */
756         ProcessParameters->ConsoleHandle = Peb->ProcessParameters->ConsoleHandle;
757 
758         /* Make sure that the shell isn't trampling on our handles first */
759         if (!(StartupInfo->dwFlags &
760              (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
761         {
762             /* Copy the handle if we are inheriting or if it's a console handle */
763             if ((InheritHandles) ||
764                 (IsConsoleHandle(Peb->ProcessParameters->StandardInput)))
765             {
766                 ProcessParameters->StandardInput = Peb->ProcessParameters->StandardInput;
767             }
768             if ((InheritHandles) ||
769                 (IsConsoleHandle(Peb->ProcessParameters->StandardOutput)))
770             {
771                 ProcessParameters->StandardOutput = Peb->ProcessParameters->StandardOutput;
772             }
773             if ((InheritHandles) ||
774                 (IsConsoleHandle(Peb->ProcessParameters->StandardError)))
775             {
776                 ProcessParameters->StandardError = Peb->ProcessParameters->StandardError;
777             }
778         }
779     }
780 
781     /* Also set the Console Flag */
782     if ((CreationFlags & CREATE_NEW_PROCESS_GROUP) &&
783         (!(CreationFlags & CREATE_NEW_CONSOLE)))
784     {
785         ProcessParameters->ConsoleFlags = 1;
786     }
787 
788     /* Check if there's a .local file present */
789     if (ParameterFlags & 1)
790     {
791         ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH;
792     }
793 
794     /* Check if we failed to open the IFEO key */
795     if (ParameterFlags & 2)
796     {
797         ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING;
798     }
799 
800     /* Allocate memory for the parameter block */
801     Size = ProcessParameters->Length;
802     RemoteParameters = NULL;
803     Status = NtAllocateVirtualMemory(ProcessHandle,
804                                      (PVOID*)&RemoteParameters,
805                                      0,
806                                      &Size,
807                                      MEM_COMMIT,
808                                      PAGE_READWRITE);
809     if (!NT_SUCCESS(Status)) goto FailPath;
810 
811     /* Set the allocated size */
812     ProcessParameters->MaximumLength = Size;
813 
814     /* Handle some Parameter Flags */
815     ProcessParameters->Flags |= (CreationFlags & PROFILE_USER) ?
816                                  RTL_USER_PROCESS_PARAMETERS_PROFILE_USER : 0;
817     ProcessParameters->Flags |= (CreationFlags & PROFILE_KERNEL) ?
818                                  RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL : 0;
819     ProcessParameters->Flags |= (CreationFlags & PROFILE_SERVER) ?
820                                  RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER : 0;
821     ProcessParameters->Flags |= (Peb->ProcessParameters->Flags &
822                                  RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS);
823 
824     /* Write the Parameter Block */
825     Status = NtWriteVirtualMemory(ProcessHandle,
826                                   RemoteParameters,
827                                   ProcessParameters,
828                                   ProcessParameters->Length,
829                                   NULL);
830     if (!NT_SUCCESS(Status)) goto FailPath;
831 
832     /* Write the PEB Pointer */
833     Status = NtWriteVirtualMemory(ProcessHandle,
834                                   &RemotePeb->ProcessParameters,
835                                   &RemoteParameters,
836                                   sizeof(PVOID),
837                                   NULL);
838     if (!NT_SUCCESS(Status)) goto FailPath;
839 
840     /* Check if there's any app compat data to write */
841     RemoteAppCompatData = NULL;
842     if (AppCompatData)
843     {
844         /* Allocate some space for the application compatibility data */
845         Size = AppCompatDataSize;
846         Status = NtAllocateVirtualMemory(ProcessHandle,
847                                          &RemoteAppCompatData,
848                                          0,
849                                          &Size,
850                                          MEM_COMMIT,
851                                          PAGE_READWRITE);
852         if (!NT_SUCCESS(Status)) goto FailPath;
853 
854         /* Write the application compatibility data */
855         Status = NtWriteVirtualMemory(ProcessHandle,
856                                       RemoteAppCompatData,
857                                       AppCompatData,
858                                       AppCompatDataSize,
859                                       NULL);
860         if (!NT_SUCCESS(Status)) goto FailPath;
861     }
862 
863     /* Write the PEB Pointer to the app compat data (might be NULL) */
864     Status = NtWriteVirtualMemory(ProcessHandle,
865                                   &RemotePeb->pShimData,
866                                   &RemoteAppCompatData,
867                                   sizeof(PVOID),
868                                   NULL);
869     if (!NT_SUCCESS(Status)) goto FailPath;
870 
871     /* Now write Peb->ImageSubSystem */
872     if (ImageSubsystem)
873     {
874         NtWriteVirtualMemory(ProcessHandle,
875                              &RemotePeb->ImageSubsystem,
876                              &ImageSubsystem,
877                              sizeof(ImageSubsystem),
878                              NULL);
879     }
880 
881     /* Success path */
882     Result = TRUE;
883 
884 Quickie:
885     /* Cleanup */
886     if (HavePebLock) RtlReleasePebLock();
887     RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath.Buffer);
888     if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters);
889     return Result;
890 FailPath:
891     DPRINT1("Failure to create process parameters: %lx\n", Status);
892     BaseSetLastNTError(Status);
893     Result = FALSE;
894     goto Quickie;
895 }
896 
897 VOID
898 WINAPI
899 InitCommandLines(VOID)
900 {
901     NTSTATUS Status;
902 
903     /* Read the UNICODE_STRING from the PEB */
904     BaseUnicodeCommandLine = NtCurrentPeb()->ProcessParameters->CommandLine;
905 
906     /* Convert to ANSI_STRING for the *A callers */
907     Status = RtlUnicodeStringToAnsiString(&BaseAnsiCommandLine,
908                                           &BaseUnicodeCommandLine,
909                                           TRUE);
910     if (!NT_SUCCESS(Status)) RtlInitEmptyAnsiString(&BaseAnsiCommandLine, 0, 0);
911 }
912 
913 /* PUBLIC FUNCTIONS ***********************************************************/
914 
915 /*
916  * @implemented
917  */
918 BOOL
919 WINAPI
920 GetProcessAffinityMask(IN HANDLE hProcess,
921                        OUT PDWORD_PTR lpProcessAffinityMask,
922                        OUT PDWORD_PTR lpSystemAffinityMask)
923 {
924     PROCESS_BASIC_INFORMATION ProcessInfo;
925     NTSTATUS Status;
926 
927     /* Query information on the process from the kernel */
928     Status = NtQueryInformationProcess(hProcess,
929                                        ProcessBasicInformation,
930                                        &ProcessInfo,
931                                        sizeof(ProcessInfo),
932                                        NULL);
933     if (!NT_SUCCESS(Status))
934     {
935         /* Fail */
936         BaseSetLastNTError(Status);
937         return FALSE;
938     }
939 
940     /* Copy the affinity mask, and get the system one from our shared data */
941     *lpProcessAffinityMask = (DWORD)ProcessInfo.AffinityMask;
942     *lpSystemAffinityMask = (DWORD)BaseStaticServerData->SysInfo.ActiveProcessorsAffinityMask;
943     return TRUE;
944 }
945 
946 /*
947  * @implemented
948  */
949 BOOL
950 WINAPI
951 SetProcessAffinityMask(IN HANDLE hProcess,
952                        IN DWORD_PTR dwProcessAffinityMask)
953 {
954     NTSTATUS Status;
955 
956     /* Directly set the affinity mask */
957     Status = NtSetInformationProcess(hProcess,
958                                      ProcessAffinityMask,
959                                      (PVOID)&dwProcessAffinityMask,
960                                      sizeof(DWORD));
961     if (!NT_SUCCESS(Status))
962     {
963         /* Handle failure */
964         BaseSetLastNTError(Status);
965         return FALSE;
966     }
967 
968     /* Everything was ok */
969     return TRUE;
970 }
971 
972 /*
973  * @implemented
974  */
975 BOOL
976 WINAPI
977 GetProcessShutdownParameters(OUT LPDWORD lpdwLevel,
978                              OUT LPDWORD lpdwFlags)
979 {
980     BASE_API_MESSAGE ApiMessage;
981     PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
982 
983     /* Ask CSRSS for shutdown information */
984     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
985                         NULL,
986                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetProcessShutdownParam),
987                         sizeof(*ShutdownParametersRequest));
988     if (!NT_SUCCESS(ApiMessage.Status))
989     {
990         /* Return the failure from CSRSS */
991         BaseSetLastNTError(ApiMessage.Status);
992         return FALSE;
993     }
994 
995     /* Get the data back */
996     *lpdwLevel = ShutdownParametersRequest->ShutdownLevel;
997     *lpdwFlags = ShutdownParametersRequest->ShutdownFlags;
998     return TRUE;
999 }
1000 
1001 /*
1002  * @implemented
1003  */
1004 BOOL
1005 WINAPI
1006 SetProcessShutdownParameters(IN DWORD dwLevel,
1007                              IN DWORD dwFlags)
1008 {
1009     BASE_API_MESSAGE ApiMessage;
1010     PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
1011 
1012     /* Write the data into the CSRSS request and send it */
1013     ShutdownParametersRequest->ShutdownLevel = dwLevel;
1014     ShutdownParametersRequest->ShutdownFlags = dwFlags;
1015     CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1016                         NULL,
1017                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetProcessShutdownParam),
1018                         sizeof(*ShutdownParametersRequest));
1019     if (!NT_SUCCESS(ApiMessage.Status))
1020     {
1021         /* Return the failure from CSRSS */
1022         BaseSetLastNTError(ApiMessage.Status);
1023         return FALSE;
1024     }
1025 
1026     /* All went well */
1027     return TRUE;
1028 }
1029 
1030 /*
1031  * @implemented
1032  */
1033 BOOL
1034 WINAPI
1035 GetProcessWorkingSetSizeEx(IN HANDLE hProcess,
1036                            OUT PSIZE_T lpMinimumWorkingSetSize,
1037                            OUT PSIZE_T lpMaximumWorkingSetSize,
1038                            OUT PDWORD Flags)
1039 {
1040     QUOTA_LIMITS_EX QuotaLimits;
1041     NTSTATUS Status;
1042 
1043     /* Query the kernel about this */
1044     Status = NtQueryInformationProcess(hProcess,
1045                                        ProcessQuotaLimits,
1046                                        &QuotaLimits,
1047                                        sizeof(QuotaLimits),
1048                                        NULL);
1049     if (!NT_SUCCESS(Status))
1050     {
1051         /* Return error */
1052         BaseSetLastNTError(Status);
1053         return FALSE;
1054     }
1055 
1056     /* Copy the quota information out */
1057     *lpMinimumWorkingSetSize = QuotaLimits.MinimumWorkingSetSize;
1058     *lpMaximumWorkingSetSize = QuotaLimits.MaximumWorkingSetSize;
1059     *Flags = QuotaLimits.Flags;
1060     return TRUE;
1061 }
1062 
1063 /*
1064  * @implemented
1065  */
1066 BOOL
1067 WINAPI
1068 GetProcessWorkingSetSize(IN HANDLE hProcess,
1069                          OUT PSIZE_T lpMinimumWorkingSetSize,
1070                          OUT PSIZE_T lpMaximumWorkingSetSize)
1071 {
1072     DWORD Dummy;
1073     return GetProcessWorkingSetSizeEx(hProcess,
1074                                       lpMinimumWorkingSetSize,
1075                                       lpMaximumWorkingSetSize,
1076                                       &Dummy);
1077 }
1078 
1079 /*
1080  * @implemented
1081  */
1082 BOOL
1083 WINAPI
1084 SetProcessWorkingSetSizeEx(IN HANDLE hProcess,
1085                            IN SIZE_T dwMinimumWorkingSetSize,
1086                            IN SIZE_T dwMaximumWorkingSetSize,
1087                            IN DWORD Flags)
1088 {
1089     QUOTA_LIMITS_EX QuotaLimits;
1090     NTSTATUS Status, ReturnStatus;
1091     BOOL Result;
1092     PVOID State;
1093     ULONG Privilege = SE_INC_BASE_PRIORITY_PRIVILEGE;
1094 
1095     /* Zero out the input structure */
1096     RtlZeroMemory(&QuotaLimits, sizeof(QuotaLimits));
1097 
1098     /* Check if the caller sent any limits */
1099     if ((dwMinimumWorkingSetSize) && (dwMaximumWorkingSetSize))
1100     {
1101         /* Write the quota information */
1102         QuotaLimits.MinimumWorkingSetSize = dwMinimumWorkingSetSize;
1103         QuotaLimits.MaximumWorkingSetSize = dwMaximumWorkingSetSize;
1104         QuotaLimits.Flags = Flags;
1105 
1106         /* Acquire the required privilege */
1107         Status = RtlAcquirePrivilege(&Privilege, 1, 0, &State);
1108 
1109         /* Request the new quotas */
1110         ReturnStatus = NtSetInformationProcess(hProcess,
1111                                                ProcessQuotaLimits,
1112                                                &QuotaLimits,
1113                                                sizeof(QuotaLimits));
1114         Result = NT_SUCCESS(ReturnStatus);
1115         if (NT_SUCCESS(Status))
1116         {
1117             /* Release the privilege and set succes code */
1118             ASSERT(State != NULL);
1119             RtlReleasePrivilege(State);
1120             State = NULL;
1121         }
1122     }
1123     else
1124     {
1125         /* No limits, fail the call */
1126         ReturnStatus = STATUS_INVALID_PARAMETER;
1127         Result = FALSE;
1128     }
1129 
1130     /* Return result code, set error code if this was a failure */
1131     if (!Result) BaseSetLastNTError(ReturnStatus);
1132     return Result;
1133 }
1134 
1135 /*
1136  * @implemented
1137  */
1138 BOOL
1139 WINAPI
1140 SetProcessWorkingSetSize(IN HANDLE hProcess,
1141                          IN SIZE_T dwMinimumWorkingSetSize,
1142                          IN SIZE_T dwMaximumWorkingSetSize)
1143 {
1144     /* Call the newer API */
1145     return SetProcessWorkingSetSizeEx(hProcess,
1146                                       dwMinimumWorkingSetSize,
1147                                       dwMaximumWorkingSetSize,
1148                                       0);
1149 }
1150 
1151 /*
1152  * @implemented
1153  */
1154 BOOL
1155 WINAPI
1156 GetProcessTimes(IN HANDLE hProcess,
1157                 IN LPFILETIME lpCreationTime,
1158                 IN LPFILETIME lpExitTime,
1159                 IN LPFILETIME lpKernelTime,
1160                 IN LPFILETIME lpUserTime)
1161 {
1162     KERNEL_USER_TIMES Kut;
1163     NTSTATUS Status;
1164 
1165     /* Query the times */
1166     Status = NtQueryInformationProcess(hProcess,
1167                                        ProcessTimes,
1168                                        &Kut,
1169                                        sizeof(Kut),
1170                                        NULL);
1171     if (!NT_SUCCESS(Status))
1172     {
1173         /* Handle failure */
1174         BaseSetLastNTError(Status);
1175         return FALSE;
1176     }
1177 
1178     /* Copy all the times and return success */
1179     lpCreationTime->dwLowDateTime = Kut.CreateTime.u.LowPart;
1180     lpCreationTime->dwHighDateTime = Kut.CreateTime.u.HighPart;
1181     lpExitTime->dwLowDateTime = Kut.ExitTime.u.LowPart;
1182     lpExitTime->dwHighDateTime = Kut.ExitTime.u.HighPart;
1183     lpKernelTime->dwLowDateTime = Kut.KernelTime.u.LowPart;
1184     lpKernelTime->dwHighDateTime = Kut.KernelTime.u.HighPart;
1185     lpUserTime->dwLowDateTime = Kut.UserTime.u.LowPart;
1186     lpUserTime->dwHighDateTime = Kut.UserTime.u.HighPart;
1187     return TRUE;
1188 }
1189 
1190 /*
1191  * @implemented
1192  */
1193 HANDLE
1194 WINAPI
1195 GetCurrentProcess(VOID)
1196 {
1197     return (HANDLE)NtCurrentProcess();
1198 }
1199 
1200 /*
1201  * @implemented
1202  */
1203 HANDLE
1204 WINAPI
1205 GetCurrentThread(VOID)
1206 {
1207     return (HANDLE)NtCurrentThread();
1208 }
1209 
1210 /*
1211  * @implemented
1212  */
1213 DWORD
1214 WINAPI
1215 GetCurrentProcessId(VOID)
1216 {
1217     return HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess);
1218 }
1219 
1220 /*
1221  * @implemented
1222  */
1223 BOOL
1224 WINAPI
1225 GetExitCodeProcess(IN HANDLE hProcess,
1226                    IN LPDWORD lpExitCode)
1227 {
1228     PROCESS_BASIC_INFORMATION ProcessBasic;
1229     NTSTATUS Status;
1230 
1231     /* Ask the kernel */
1232     Status = NtQueryInformationProcess(hProcess,
1233                                        ProcessBasicInformation,
1234                                        &ProcessBasic,
1235                                        sizeof(ProcessBasic),
1236                                        NULL);
1237     if (!NT_SUCCESS(Status))
1238     {
1239         /* We failed, was this because this is a VDM process? */
1240         if (BaseCheckForVDM(hProcess, lpExitCode) != FALSE) return TRUE;
1241 
1242         /* Not a VDM process, fail the call */
1243         BaseSetLastNTError(Status);
1244         return FALSE;
1245     }
1246 
1247     /* Succes case, return the exit code */
1248     *lpExitCode = (DWORD)ProcessBasic.ExitStatus;
1249     return TRUE;
1250 }
1251 
1252 /*
1253  * @implemented
1254  */
1255 DWORD
1256 WINAPI
1257 GetProcessId(IN HANDLE Process)
1258 {
1259     PROCESS_BASIC_INFORMATION ProcessBasic;
1260     NTSTATUS Status;
1261 
1262     /* Query the kernel */
1263     Status = NtQueryInformationProcess(Process,
1264                                        ProcessBasicInformation,
1265                                        &ProcessBasic,
1266                                        sizeof(ProcessBasic),
1267                                        NULL);
1268     if (!NT_SUCCESS(Status))
1269     {
1270         /* Handle failure */
1271         BaseSetLastNTError(Status);
1272         return 0;
1273     }
1274 
1275     /* Return the PID */
1276     return (DWORD)ProcessBasic.UniqueProcessId;
1277 }
1278 
1279 /*
1280  * @implemented
1281  */
1282 HANDLE
1283 WINAPI
1284 OpenProcess(IN DWORD dwDesiredAccess,
1285             IN BOOL bInheritHandle,
1286             IN DWORD dwProcessId)
1287 {
1288     NTSTATUS Status;
1289     HANDLE ProcessHandle;
1290     OBJECT_ATTRIBUTES ObjectAttributes;
1291     CLIENT_ID ClientId;
1292 
1293     /* Setup the input client ID structure */
1294     ClientId.UniqueProcess = UlongToHandle(dwProcessId);
1295     ClientId.UniqueThread = 0;
1296 
1297     /* This is needed just to define the inheritance flags */
1298     InitializeObjectAttributes(&ObjectAttributes,
1299                                NULL,
1300                                (bInheritHandle ? OBJ_INHERIT : 0),
1301                                NULL,
1302                                NULL);
1303 
1304     /* Now try to open the process */
1305     Status = NtOpenProcess(&ProcessHandle,
1306                            dwDesiredAccess,
1307                            &ObjectAttributes,
1308                            &ClientId);
1309     if (!NT_SUCCESS(Status))
1310     {
1311         /* Handle failure */
1312         BaseSetLastNTError(Status);
1313         return NULL;
1314     }
1315 
1316     /* Otherwise return a handle to the process */
1317     return ProcessHandle;
1318 }
1319 
1320 /*
1321  * @implemented
1322  */
1323 VOID
1324 WINAPI
1325 RegisterWaitForInputIdle(IN WaitForInputIdleType lpfnRegisterWaitForInputIdle)
1326 {
1327     /* Write the global function pointer */
1328     UserWaitForInputIdleRoutine = lpfnRegisterWaitForInputIdle;
1329 }
1330 
1331 /*
1332  * @implemented
1333  */
1334 VOID
1335 WINAPI
1336 GetStartupInfoW(IN LPSTARTUPINFOW lpStartupInfo)
1337 {
1338     PRTL_USER_PROCESS_PARAMETERS Params;
1339 
1340     /* Get the process parameters */
1341     Params = NtCurrentPeb()->ProcessParameters;
1342 
1343     /* Copy the data out of there */
1344     lpStartupInfo->cb = sizeof(STARTUPINFOW);
1345     lpStartupInfo->lpReserved = Params->ShellInfo.Buffer;
1346     lpStartupInfo->lpDesktop = Params->DesktopInfo.Buffer;
1347     lpStartupInfo->lpTitle = Params->WindowTitle.Buffer;
1348     lpStartupInfo->dwX = Params->StartingX;
1349     lpStartupInfo->dwY = Params->StartingY;
1350     lpStartupInfo->dwXSize = Params->CountX;
1351     lpStartupInfo->dwYSize = Params->CountY;
1352     lpStartupInfo->dwXCountChars = Params->CountCharsX;
1353     lpStartupInfo->dwYCountChars = Params->CountCharsY;
1354     lpStartupInfo->dwFillAttribute = Params->FillAttribute;
1355     lpStartupInfo->dwFlags = Params->WindowFlags;
1356     lpStartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1357     lpStartupInfo->cbReserved2 = Params->RuntimeData.Length;
1358     lpStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1359 
1360     /* Check if the standard handles are being used for other features */
1361     if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES |
1362                                   STARTF_USEHOTKEY |
1363                                   STARTF_SHELLPRIVATE))
1364     {
1365         /* These are, so copy the standard handles too */
1366         lpStartupInfo->hStdInput = Params->StandardInput;
1367         lpStartupInfo->hStdOutput = Params->StandardOutput;
1368         lpStartupInfo->hStdError = Params->StandardError;
1369     }
1370 }
1371 
1372 /*
1373  * @implemented
1374  */
1375 VOID
1376 WINAPI
1377 GetStartupInfoA(IN LPSTARTUPINFOA lpStartupInfo)
1378 {
1379     PRTL_USER_PROCESS_PARAMETERS Params;
1380     ANSI_STRING TitleString, ShellString, DesktopString;
1381     LPSTARTUPINFOA StartupInfo;
1382     NTSTATUS Status;
1383 
1384     /* Get the cached information as well as the PEB parameters */
1385     StartupInfo = BaseAnsiStartupInfo;
1386     Params = NtCurrentPeb()->ProcessParameters;
1387 
1388     /* Check if this is the first time we have to get the cached version */
1389     while (!StartupInfo)
1390     {
1391         /* Create new ANSI startup info */
1392         StartupInfo = RtlAllocateHeap(RtlGetProcessHeap(),
1393                                       0,
1394                                       sizeof(*StartupInfo));
1395         if (StartupInfo)
1396         {
1397             /* Zero out string pointers in case we fail to create them */
1398             StartupInfo->lpReserved = NULL;
1399             StartupInfo->lpDesktop = NULL;
1400             StartupInfo->lpTitle = NULL;
1401 
1402             /* Set the size */
1403             StartupInfo->cb = sizeof(*StartupInfo);
1404 
1405             /* Copy what's already stored in the PEB */
1406             StartupInfo->dwX = Params->StartingX;
1407             StartupInfo->dwY = Params->StartingY;
1408             StartupInfo->dwXSize = Params->CountX;
1409             StartupInfo->dwYSize = Params->CountY;
1410             StartupInfo->dwXCountChars = Params->CountCharsX;
1411             StartupInfo->dwYCountChars = Params->CountCharsY;
1412             StartupInfo->dwFillAttribute = Params->FillAttribute;
1413             StartupInfo->dwFlags = Params->WindowFlags;
1414             StartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1415             StartupInfo->cbReserved2 = Params->RuntimeData.Length;
1416             StartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1417             StartupInfo->hStdInput = Params->StandardInput;
1418             StartupInfo->hStdOutput = Params->StandardOutput;
1419             StartupInfo->hStdError = Params->StandardError;
1420 
1421             /* Copy shell info string */
1422             Status = RtlUnicodeStringToAnsiString(&ShellString,
1423                                                   &Params->ShellInfo,
1424                                                   TRUE);
1425             if (NT_SUCCESS(Status))
1426             {
1427                 /* Save it */
1428                 StartupInfo->lpReserved = ShellString.Buffer;
1429 
1430                 /* Copy desktop info string */
1431                 Status = RtlUnicodeStringToAnsiString(&DesktopString,
1432                                                       &Params->DesktopInfo,
1433                                                       TRUE);
1434                 if (NT_SUCCESS(Status))
1435                 {
1436                     /* Save it */
1437                     StartupInfo->lpDesktop = DesktopString.Buffer;
1438 
1439                     /* Copy window title string */
1440                     Status = RtlUnicodeStringToAnsiString(&TitleString,
1441                                                           &Params->WindowTitle,
1442                                                           TRUE);
1443                     if (NT_SUCCESS(Status))
1444                     {
1445                         /* Save it */
1446                         StartupInfo->lpTitle = TitleString.Buffer;
1447 
1448                         /* We finished with the ANSI version, try to cache it */
1449                         if (!InterlockedCompareExchangePointer((PVOID*)&BaseAnsiStartupInfo,
1450                                                                StartupInfo,
1451                                                                NULL))
1452                         {
1453                             /* We were the first thread through, use the data */
1454                             break;
1455                         }
1456 
1457                         /* Someone beat us to it, use their data instead */
1458                         StartupInfo = BaseAnsiStartupInfo;
1459                         Status = STATUS_SUCCESS;
1460 
1461                         /* We're going to free our own stuff, but not raise */
1462                         RtlFreeAnsiString(&TitleString);
1463                     }
1464                     RtlFreeAnsiString(&DesktopString);
1465                 }
1466                 RtlFreeAnsiString(&ShellString);
1467             }
1468             RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo);
1469         }
1470         else
1471         {
1472             /* No memory, fail */
1473             Status = STATUS_NO_MEMORY;
1474         }
1475 
1476         /* Raise an error unless we got here due to the race condition */
1477         if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status);
1478     }
1479 
1480     /* Now copy from the cached ANSI version */
1481     lpStartupInfo->cb = StartupInfo->cb;
1482     lpStartupInfo->lpReserved = StartupInfo->lpReserved;
1483     lpStartupInfo->lpDesktop = StartupInfo->lpDesktop;
1484     lpStartupInfo->lpTitle = StartupInfo->lpTitle;
1485     lpStartupInfo->dwX = StartupInfo->dwX;
1486     lpStartupInfo->dwY = StartupInfo->dwY;
1487     lpStartupInfo->dwXSize = StartupInfo->dwXSize;
1488     lpStartupInfo->dwYSize = StartupInfo->dwYSize;
1489     lpStartupInfo->dwXCountChars = StartupInfo->dwXCountChars;
1490     lpStartupInfo->dwYCountChars = StartupInfo->dwYCountChars;
1491     lpStartupInfo->dwFillAttribute = StartupInfo->dwFillAttribute;
1492     lpStartupInfo->dwFlags = StartupInfo->dwFlags;
1493     lpStartupInfo->wShowWindow = StartupInfo->wShowWindow;
1494     lpStartupInfo->cbReserved2 = StartupInfo->cbReserved2;
1495     lpStartupInfo->lpReserved2 = StartupInfo->lpReserved2;
1496 
1497     /* Check if the shell is hijacking the handles for other features */
1498     if (lpStartupInfo->dwFlags &
1499         (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
1500     {
1501         /* It isn't, so we can return the raw values */
1502         lpStartupInfo->hStdInput = StartupInfo->hStdInput;
1503         lpStartupInfo->hStdOutput = StartupInfo->hStdOutput;
1504         lpStartupInfo->hStdError = StartupInfo->hStdError;
1505     }
1506     else
1507     {
1508         /* It is, so make sure nobody uses these as console handles */
1509         lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
1510         lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
1511         lpStartupInfo->hStdError = INVALID_HANDLE_VALUE;
1512     }
1513 }
1514 
1515 /*
1516  * @implemented
1517  */
1518 BOOL
1519 WINAPI
1520 FlushInstructionCache(IN HANDLE hProcess,
1521                       IN LPCVOID lpBaseAddress,
1522                       IN SIZE_T nSize)
1523 {
1524     NTSTATUS Status;
1525 
1526     /* Call the native function */
1527     Status = NtFlushInstructionCache(hProcess, (PVOID)lpBaseAddress, nSize);
1528     if (!NT_SUCCESS(Status))
1529     {
1530         /* Handle failure case */
1531         BaseSetLastNTError(Status);
1532         return FALSE;
1533     }
1534 
1535     /* All good */
1536     return TRUE;
1537 }
1538 
1539 /*
1540  * @implemented
1541  */
1542 VOID
1543 WINAPI
1544 ExitProcess(IN UINT uExitCode)
1545 {
1546     BASE_API_MESSAGE ApiMessage;
1547     PBASE_EXIT_PROCESS ExitProcessRequest = &ApiMessage.Data.ExitProcessRequest;
1548 
1549     ASSERT(!BaseRunningInServerProcess);
1550 
1551     _SEH2_TRY
1552     {
1553         /* Acquire the PEB lock */
1554         RtlAcquirePebLock();
1555 
1556         /* Kill all the threads */
1557         NtTerminateProcess(NULL, uExitCode);
1558 
1559         /* Unload all DLLs */
1560         LdrShutdownProcess();
1561 
1562         /* Notify Base Server of process termination */
1563         ExitProcessRequest->uExitCode = uExitCode;
1564         CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1565                             NULL,
1566                             CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitProcess),
1567                             sizeof(*ExitProcessRequest));
1568 
1569         /* Now do it again */
1570         NtTerminateProcess(NtCurrentProcess(), uExitCode);
1571     }
1572     _SEH2_FINALLY
1573     {
1574         /* Release the PEB lock */
1575         RtlReleasePebLock();
1576     }
1577     _SEH2_END;
1578 
1579     /* should never get here */
1580     ASSERT(0);
1581     while(1);
1582 }
1583 
1584 /*
1585  * @implemented
1586  */
1587 BOOL
1588 WINAPI
1589 TerminateProcess(IN HANDLE hProcess,
1590                  IN UINT uExitCode)
1591 {
1592     NTSTATUS Status;
1593 
1594     /* Check if no handle was passed in */
1595     if (!hProcess)
1596     {
1597         /* Set error code */
1598         SetLastError(ERROR_INVALID_HANDLE);
1599     }
1600     else
1601     {
1602         /* Otherwise, try to terminate the process */
1603         Status = NtTerminateProcess(hProcess, uExitCode);
1604         if (NT_SUCCESS(Status)) return TRUE;
1605 
1606         /* It failed, convert error code */
1607         BaseSetLastNTError(Status);
1608     }
1609 
1610     /* This is the failure path */
1611     return FALSE;
1612 }
1613 
1614 /*
1615  * @implemented
1616  */
1617 VOID
1618 WINAPI
1619 FatalAppExitA(UINT uAction,
1620               LPCSTR lpMessageText)
1621 {
1622     PUNICODE_STRING MessageTextU;
1623     ANSI_STRING MessageText;
1624     NTSTATUS Status;
1625 
1626     /* Initialize the string using the static TEB pointer */
1627     MessageTextU = &NtCurrentTeb()->StaticUnicodeString;
1628     RtlInitAnsiString(&MessageText, (LPSTR)lpMessageText);
1629 
1630     /* Convert to unicode, or just exit normally if this failed */
1631     Status = RtlAnsiStringToUnicodeString(MessageTextU, &MessageText, FALSE);
1632     if (!NT_SUCCESS(Status)) ExitProcess(0);
1633 
1634     /* Call the Wide function */
1635     FatalAppExitW(uAction, MessageTextU->Buffer);
1636 }
1637 
1638 /*
1639  * @implemented
1640  */
1641 VOID
1642 WINAPI
1643 FatalAppExitW(IN UINT uAction,
1644               IN LPCWSTR lpMessageText)
1645 {
1646     UNICODE_STRING UnicodeString;
1647     ULONG Response;
1648     NTSTATUS Status;
1649 
1650     /* Setup the string to print out */
1651     RtlInitUnicodeString(&UnicodeString, lpMessageText);
1652 
1653     /* Display the hard error no matter what */
1654     Status = NtRaiseHardError(STATUS_FATAL_APP_EXIT | HARDERROR_OVERRIDE_ERRORMODE,
1655                               1,
1656                               1,
1657                               (PULONG_PTR)&UnicodeString,
1658 #if DBG
1659     /* On Checked builds, Windows allows the user to cancel the operation */
1660                               OptionOkCancel,
1661 #else
1662                               OptionOk,
1663 #endif
1664                               &Response);
1665 
1666 #if DBG
1667     /* Give the user a chance to abort */
1668     if ((NT_SUCCESS(Status)) && (Response == ResponseCancel)) return;
1669 #endif
1670 
1671     /* Otherwise kill the process */
1672     ExitProcess(0);
1673 }
1674 
1675 /*
1676  * @implemented
1677  */
1678 VOID
1679 WINAPI
1680 FatalExit(IN int ExitCode)
1681 {
1682 #if DBG
1683     /* On Checked builds, Windows gives the user a nice little debugger UI */
1684     CHAR ch[2];
1685     DbgPrint("FatalExit...\n");
1686     DbgPrint("\n");
1687 
1688     while (TRUE)
1689     {
1690         DbgPrompt( "A (Abort), B (Break), I (Ignore)? ", ch, sizeof(ch));
1691         switch (ch[0])
1692         {
1693             case 'B': case 'b':
1694                  DbgBreakPoint();
1695                  break;
1696 
1697             case 'A': case 'a':
1698                 ExitProcess(ExitCode);
1699 
1700             case 'I': case 'i':
1701                 return;
1702         }
1703     }
1704 #endif
1705     /* On other builds, just kill the process */
1706     ExitProcess(ExitCode);
1707 }
1708 
1709 /*
1710  * @implemented
1711  */
1712 DWORD
1713 WINAPI
1714 GetPriorityClass(IN HANDLE hProcess)
1715 {
1716     NTSTATUS Status;
1717     PROCESS_PRIORITY_CLASS PriorityClass;
1718 
1719     /* Query the kernel */
1720     Status = NtQueryInformationProcess(hProcess,
1721                                        ProcessPriorityClass,
1722                                        &PriorityClass,
1723                                        sizeof(PriorityClass),
1724                                        NULL);
1725     if (NT_SUCCESS(Status))
1726     {
1727         /* Handle the conversion from NT to Win32 classes */
1728         switch (PriorityClass.PriorityClass)
1729         {
1730             case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS;
1731             case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
1732             case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS;
1733             case PROCESS_PRIORITY_CLASS_HIGH: return HIGH_PRIORITY_CLASS;
1734             case PROCESS_PRIORITY_CLASS_REALTIME: return REALTIME_PRIORITY_CLASS;
1735             case PROCESS_PRIORITY_CLASS_NORMAL: default: return NORMAL_PRIORITY_CLASS;
1736         }
1737     }
1738 
1739     /* Failure path */
1740     BaseSetLastNTError(Status);
1741     return FALSE;
1742 }
1743 
1744 /*
1745  * @implemented
1746  */
1747 BOOL
1748 WINAPI
1749 SetPriorityClass(IN HANDLE hProcess,
1750                  IN DWORD dwPriorityClass)
1751 {
1752     NTSTATUS Status;
1753     PVOID State = NULL;
1754     PROCESS_PRIORITY_CLASS PriorityClass;
1755 
1756     /* Handle conversion from Win32 to NT priority classes */
1757     switch (dwPriorityClass)
1758     {
1759         case IDLE_PRIORITY_CLASS:
1760             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
1761             break;
1762 
1763         case BELOW_NORMAL_PRIORITY_CLASS:
1764             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
1765             break;
1766 
1767         case NORMAL_PRIORITY_CLASS:
1768             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
1769             break;
1770 
1771         case ABOVE_NORMAL_PRIORITY_CLASS:
1772             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
1773             break;
1774 
1775         case HIGH_PRIORITY_CLASS:
1776             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1777             break;
1778 
1779         case REALTIME_PRIORITY_CLASS:
1780             /* Try to acquire the privilege. If it fails, just use HIGH */
1781             State = BasepIsRealtimeAllowed(TRUE);
1782             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1783             PriorityClass.PriorityClass += (State != NULL);
1784             break;
1785 
1786         default:
1787             /* Unrecognized priority classes don't make it to the kernel */
1788             SetLastError(ERROR_INVALID_PARAMETER);
1789             return FALSE;
1790     }
1791 
1792     /* Send the request to the kernel, and don't touch the foreground flag */
1793     PriorityClass.Foreground = FALSE;
1794     Status = NtSetInformationProcess(hProcess,
1795                                      ProcessPriorityClass,
1796                                      &PriorityClass,
1797                                      sizeof(PROCESS_PRIORITY_CLASS));
1798 
1799     /* Release the privilege if we had it */
1800     if (State) RtlReleasePrivilege(State);
1801     if (!NT_SUCCESS(Status))
1802     {
1803         /* Handle error path */
1804         BaseSetLastNTError(Status);
1805         return FALSE;
1806     }
1807 
1808     /* All done */
1809     return TRUE;
1810 }
1811 
1812 /*
1813  * @implemented
1814  */
1815 DWORD
1816 WINAPI
1817 GetProcessVersion(IN DWORD ProcessId)
1818 {
1819     DWORD Version = 0;
1820     PIMAGE_NT_HEADERS NtHeader;
1821     PIMAGE_DOS_HEADER DosHeader;
1822     PPEB Peb;
1823     PROCESS_BASIC_INFORMATION ProcessBasicInfo;
1824     PVOID BaseAddress;
1825     ULONG e_lfanew;
1826     HANDLE ProcessHandle = NULL;
1827     NTSTATUS Status;
1828     USHORT VersionData[2];
1829     BOOLEAN Result;
1830 
1831     /* We'll be accessing stuff that can fault, so protect everything with SEH */
1832     _SEH2_TRY
1833     {
1834         /* It this an in-process or out-of-process request? */
1835         if (!(ProcessId) || (GetCurrentProcessId() == ProcessId))
1836         {
1837             /* It's in-process, so just read our own header */
1838             NtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
1839             if (!NtHeader)
1840             {
1841                 /* Unable to read the NT header, something is wrong here... */
1842                 Status = STATUS_INVALID_IMAGE_FORMAT;
1843                 goto Error;
1844             }
1845 
1846             /* Get the version straight out of the NT header */
1847             Version = MAKELONG(NtHeader->OptionalHeader.MinorSubsystemVersion,
1848                                NtHeader->OptionalHeader.MajorSubsystemVersion);
1849         }
1850         else
1851         {
1852             /* Out-of-process, so open it */
1853             ProcessHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
1854                                         FALSE,
1855                                         ProcessId);
1856             if (!ProcessHandle) _SEH2_YIELD(return 0);
1857 
1858             /* Try to find out where its PEB lives */
1859             Status = NtQueryInformationProcess(ProcessHandle,
1860                                                ProcessBasicInformation,
1861                                                &ProcessBasicInfo,
1862                                                sizeof(ProcessBasicInfo),
1863                                                NULL);
1864 
1865             if (!NT_SUCCESS(Status)) goto Error;
1866             Peb = ProcessBasicInfo.PebBaseAddress;
1867 
1868             /* Now that we have the PEB, read the image base address out of it */
1869             Result = ReadProcessMemory(ProcessHandle,
1870                                        &Peb->ImageBaseAddress,
1871                                        &BaseAddress,
1872                                        sizeof(BaseAddress),
1873                                        NULL);
1874             if (!Result) goto Error;
1875 
1876             /* Now read the e_lfanew (offset to NT header) from the base */
1877             DosHeader = BaseAddress;
1878             Result = ReadProcessMemory(ProcessHandle,
1879                                        &DosHeader->e_lfanew,
1880                                        &e_lfanew,
1881                                        sizeof(e_lfanew),
1882                                        NULL);
1883             if (!Result) goto Error;
1884 
1885             /* And finally, read the NT header itself by adding the offset */
1886             NtHeader = (PVOID)((ULONG_PTR)BaseAddress + e_lfanew);
1887             Result = ReadProcessMemory(ProcessHandle,
1888                                        &NtHeader->OptionalHeader.MajorSubsystemVersion,
1889                                        &VersionData,
1890                                        sizeof(VersionData),
1891                                        NULL);
1892             if (!Result) goto Error;
1893 
1894             /* Get the version straight out of the NT header */
1895             Version = MAKELONG(VersionData[0], VersionData[1]);
1896 
1897 Error:
1898             /* If there was an error anywhere, set the last error */
1899             if (!NT_SUCCESS(Status)) BaseSetLastNTError(Status);
1900         }
1901     }
1902     _SEH2_FINALLY
1903     {
1904         /* Close the process handle */
1905         if (ProcessHandle) CloseHandle(ProcessHandle);
1906     }
1907     _SEH2_END;
1908 
1909     /* And return the version data */
1910     return Version;
1911 }
1912 
1913 /*
1914  * @implemented
1915  */
1916 BOOL
1917 WINAPI
1918 GetProcessIoCounters(IN HANDLE hProcess,
1919                      OUT PIO_COUNTERS lpIoCounters)
1920 {
1921     NTSTATUS Status;
1922 
1923     /* Query the kernel. Structures are identical, so let it do the copy too. */
1924     Status = NtQueryInformationProcess(hProcess,
1925                                        ProcessIoCounters,
1926                                        lpIoCounters,
1927                                        sizeof(IO_COUNTERS),
1928                                        NULL);
1929     if (!NT_SUCCESS(Status))
1930     {
1931         /* Handle error path */
1932         BaseSetLastNTError(Status);
1933         return FALSE;
1934     }
1935 
1936     /* All done */
1937     return TRUE;
1938 }
1939 
1940 /*
1941  * @implemented
1942  */
1943 BOOL
1944 WINAPI
1945 GetProcessPriorityBoost(IN HANDLE hProcess,
1946                         OUT PBOOL pDisablePriorityBoost)
1947 {
1948     NTSTATUS Status;
1949     ULONG PriorityBoost;
1950 
1951     /* Query the kernel */
1952     Status = NtQueryInformationProcess(hProcess,
1953                                        ProcessPriorityBoost,
1954                                        &PriorityBoost,
1955                                        sizeof(PriorityBoost),
1956                                        NULL);
1957     if (NT_SUCCESS(Status))
1958     {
1959         /* Convert from ULONG to a BOOL */
1960         *pDisablePriorityBoost = PriorityBoost ? TRUE : FALSE;
1961         return TRUE;
1962     }
1963 
1964     /* Handle error path */
1965     BaseSetLastNTError(Status);
1966     return FALSE;
1967 }
1968 
1969 /*
1970  * @implemented
1971  */
1972 BOOL
1973 WINAPI
1974 SetProcessPriorityBoost(IN HANDLE hProcess,
1975                         IN BOOL bDisablePriorityBoost)
1976 {
1977     NTSTATUS Status;
1978     ULONG PriorityBoost;
1979 
1980     /* Enforce that this is a BOOL, and send it to the kernel as a ULONG */
1981     PriorityBoost = (bDisablePriorityBoost ? TRUE : FALSE);
1982     Status = NtSetInformationProcess(hProcess,
1983                                      ProcessPriorityBoost,
1984                                      &PriorityBoost,
1985                                      sizeof(ULONG));
1986     if (!NT_SUCCESS(Status))
1987     {
1988         /* Handle error path */
1989         BaseSetLastNTError(Status);
1990         return FALSE;
1991     }
1992 
1993     /* All done */
1994     return TRUE;
1995 }
1996 
1997 /*
1998  * @implemented
1999  */
2000 BOOL
2001 WINAPI
2002 GetProcessHandleCount(IN HANDLE hProcess,
2003                       OUT PDWORD pdwHandleCount)
2004 {
2005     ULONG phc;
2006     NTSTATUS Status;
2007 
2008     /* Query the kernel */
2009     Status = NtQueryInformationProcess(hProcess,
2010                                        ProcessHandleCount,
2011                                        &phc,
2012                                        sizeof(phc),
2013                                        NULL);
2014     if (NT_SUCCESS(Status))
2015     {
2016         /* Copy the count and return success */
2017         *pdwHandleCount = phc;
2018         return TRUE;
2019     }
2020 
2021     /* Handle error path */
2022     BaseSetLastNTError(Status);
2023     return FALSE;
2024 }
2025 
2026 /*
2027  * @implemented
2028  */
2029 BOOL
2030 WINAPI
2031 IsWow64Process(IN HANDLE hProcess,
2032                OUT PBOOL Wow64Process)
2033 {
2034     ULONG_PTR pbi;
2035     NTSTATUS Status;
2036 
2037     /* Query the kernel */
2038     Status = NtQueryInformationProcess(hProcess,
2039                                        ProcessWow64Information,
2040                                        &pbi,
2041                                        sizeof(pbi),
2042                                        NULL);
2043     if (!NT_SUCCESS(Status))
2044     {
2045         /* Handle error path */
2046         BaseSetLastNTError(Status);
2047         return FALSE;
2048     }
2049 
2050     /* Enforce this is a BOOL, and return success */
2051     *Wow64Process = (pbi != 0);
2052     return TRUE;
2053 }
2054 
2055 /*
2056  * @implemented
2057  */
2058 LPSTR
2059 WINAPI
2060 GetCommandLineA(VOID)
2061 {
2062     return BaseAnsiCommandLine.Buffer;
2063 }
2064 
2065 /*
2066  * @implemented
2067  */
2068 LPWSTR
2069 WINAPI
2070 GetCommandLineW(VOID)
2071 {
2072     return BaseUnicodeCommandLine.Buffer;
2073 }
2074 
2075 /*
2076  * @implemented
2077  */
2078 BOOL
2079 NTAPI
2080 ReadProcessMemory(IN HANDLE hProcess,
2081                   IN LPCVOID lpBaseAddress,
2082                   IN LPVOID lpBuffer,
2083                   IN SIZE_T nSize,
2084                   OUT SIZE_T* lpNumberOfBytesRead)
2085 {
2086     NTSTATUS Status;
2087 
2088     /* Do the read */
2089     Status = NtReadVirtualMemory(hProcess,
2090                                  (PVOID)lpBaseAddress,
2091                                  lpBuffer,
2092                                  nSize,
2093                                  &nSize);
2094 
2095     /* In user-mode, this parameter is optional */
2096     if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize;
2097     if (!NT_SUCCESS(Status))
2098     {
2099         /* We failed */
2100         BaseSetLastNTError(Status);
2101         return FALSE;
2102     }
2103 
2104     /* Return success */
2105     return TRUE;
2106 }
2107 
2108 /*
2109  * @implemented
2110  */
2111 BOOL
2112 NTAPI
2113 WriteProcessMemory(IN HANDLE hProcess,
2114                    IN LPVOID lpBaseAddress,
2115                    IN LPCVOID lpBuffer,
2116                    IN SIZE_T nSize,
2117                    OUT SIZE_T *lpNumberOfBytesWritten)
2118 {
2119     NTSTATUS Status;
2120     ULONG OldValue;
2121     SIZE_T RegionSize;
2122     PVOID Base;
2123     BOOLEAN UnProtect;
2124 
2125     /* Set parameters for protect call */
2126     RegionSize = nSize;
2127     Base = lpBaseAddress;
2128 
2129     /* Check the current status */
2130     Status = NtProtectVirtualMemory(hProcess,
2131                                     &Base,
2132                                     &RegionSize,
2133                                     PAGE_EXECUTE_READWRITE,
2134                                     &OldValue);
2135     if (NT_SUCCESS(Status))
2136     {
2137         /* Check if we are unprotecting */
2138         UnProtect = OldValue & (PAGE_READWRITE |
2139                                 PAGE_WRITECOPY |
2140                                 PAGE_EXECUTE_READWRITE |
2141                                 PAGE_EXECUTE_WRITECOPY) ? FALSE : TRUE;
2142         if (!UnProtect)
2143         {
2144             /* Set the new protection */
2145             Status = NtProtectVirtualMemory(hProcess,
2146                                             &Base,
2147                                             &RegionSize,
2148                                             OldValue,
2149                                             &OldValue);
2150 
2151             /* Write the memory */
2152             Status = NtWriteVirtualMemory(hProcess,
2153                                           lpBaseAddress,
2154                                           (LPVOID)lpBuffer,
2155                                           nSize,
2156                                           &nSize);
2157 
2158             /* In Win32, the parameter is optional, so handle this case */
2159             if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2160 
2161             if (!NT_SUCCESS(Status))
2162             {
2163                 /* We failed */
2164                 BaseSetLastNTError(Status);
2165                 return FALSE;
2166             }
2167 
2168             /* Flush the ITLB */
2169             NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2170             return TRUE;
2171         }
2172         else
2173         {
2174             /* Check if we were read only */
2175             if (OldValue & (PAGE_NOACCESS | PAGE_READONLY))
2176             {
2177                 /* Restore protection and fail */
2178                 NtProtectVirtualMemory(hProcess,
2179                                        &Base,
2180                                        &RegionSize,
2181                                        OldValue,
2182                                        &OldValue);
2183                 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2184 
2185                 /* Note: This is what Windows returns and code depends on it */
2186                 return STATUS_ACCESS_VIOLATION;
2187             }
2188 
2189             /* Otherwise, do the write */
2190             Status = NtWriteVirtualMemory(hProcess,
2191                                           lpBaseAddress,
2192                                           (LPVOID)lpBuffer,
2193                                           nSize,
2194                                           &nSize);
2195 
2196             /* In Win32, the parameter is optional, so handle this case */
2197             if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2198 
2199             /* And restore the protection */
2200             NtProtectVirtualMemory(hProcess,
2201                                    &Base,
2202                                    &RegionSize,
2203                                    OldValue,
2204                                    &OldValue);
2205             if (!NT_SUCCESS(Status))
2206             {
2207                 /* We failed */
2208                 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2209 
2210                 /* Note: This is what Windows returns and code depends on it */
2211                 return STATUS_ACCESS_VIOLATION;
2212             }
2213 
2214             /* Flush the ITLB */
2215             NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2216             return TRUE;
2217         }
2218     }
2219     else
2220     {
2221         /* We failed */
2222         BaseSetLastNTError(Status);
2223         return FALSE;
2224     }
2225 }
2226 
2227 /*
2228  * @implemented
2229  */
2230 BOOL
2231 WINAPI
2232 ProcessIdToSessionId(IN DWORD dwProcessId,
2233                      OUT PDWORD pSessionId)
2234 {
2235     PROCESS_SESSION_INFORMATION SessionInformation;
2236     OBJECT_ATTRIBUTES ObjectAttributes;
2237     CLIENT_ID ClientId;
2238     HANDLE ProcessHandle;
2239     NTSTATUS Status;
2240 
2241     /* Do a quick check if the pointer is not writable */
2242     if (IsBadWritePtr(pSessionId, sizeof(DWORD)))
2243     {
2244         /* Fail fast */
2245         SetLastError(ERROR_INVALID_PARAMETER);
2246         return FALSE;
2247     }
2248 
2249     /* Open the process passed in by ID */
2250     ClientId.UniqueProcess = UlongToHandle(dwProcessId);
2251     ClientId.UniqueThread = 0;
2252     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2253     Status = NtOpenProcess(&ProcessHandle,
2254                            PROCESS_QUERY_INFORMATION,
2255                            &ObjectAttributes,
2256                            &ClientId);
2257     if (NT_SUCCESS(Status))
2258     {
2259         /* Query the session ID from the kernel */
2260         Status = NtQueryInformationProcess(ProcessHandle,
2261                                            ProcessSessionInformation,
2262                                            &SessionInformation,
2263                                            sizeof(SessionInformation),
2264                                            NULL);
2265 
2266         /* Close the handle and check if we succeeded */
2267         NtClose(ProcessHandle);
2268         if (NT_SUCCESS(Status))
2269         {
2270             /* Return the session ID */
2271             *pSessionId = SessionInformation.SessionId;
2272             return TRUE;
2273         }
2274     }
2275 
2276     /* Set error code and fail */
2277     BaseSetLastNTError(Status);
2278     return FALSE;
2279 }
2280 
2281 
2282 #define AddToHandle(x,y)  (x) = (HANDLE)((ULONG_PTR)(x) | (y));
2283 #define RemoveFromHandle(x,y)  (x) = (HANDLE)((ULONG_PTR)(x) & ~(y));
2284 C_ASSERT(PROCESS_PRIORITY_CLASS_REALTIME == (PROCESS_PRIORITY_CLASS_HIGH + 1));
2285 
2286 /*
2287  * @implemented
2288  */
2289 BOOL
2290 WINAPI
2291 CreateProcessInternalW(IN HANDLE hUserToken,
2292                        IN LPCWSTR lpApplicationName,
2293                        IN LPWSTR lpCommandLine,
2294                        IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
2295                        IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
2296                        IN BOOL bInheritHandles,
2297                        IN DWORD dwCreationFlags,
2298                        IN LPVOID lpEnvironment,
2299                        IN LPCWSTR lpCurrentDirectory,
2300                        IN LPSTARTUPINFOW lpStartupInfo,
2301                        IN LPPROCESS_INFORMATION lpProcessInformation,
2302                        OUT PHANDLE hNewToken)
2303 {
2304     //
2305     // Core variables used for creating the initial process and thread
2306     //
2307     SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes;
2308     OBJECT_ATTRIBUTES LocalObjectAttributes;
2309     POBJECT_ATTRIBUTES ObjectAttributes;
2310     SECTION_IMAGE_INFORMATION ImageInformation;
2311     IO_STATUS_BLOCK IoStatusBlock;
2312     CLIENT_ID ClientId;
2313     ULONG NoWindow, StackSize, ErrorCode, Flags;
2314     SIZE_T RegionSize;
2315     USHORT ImageMachine;
2316     ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse;
2317     ULONG_PTR ErrorParameters[2];
2318     BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege;
2319     BOOLEAN QuerySection, SkipSaferAndAppCompat;
2320     CONTEXT Context;
2321     BASE_API_MESSAGE CsrMsg[2];
2322     PBASE_CREATE_PROCESS CreateProcessMsg;
2323     PCSR_CAPTURE_BUFFER CaptureBuffer;
2324     PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState;
2325     HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle;
2326     HANDLE FileHandle, SectionHandle, ProcessHandle;
2327     ULONG ResumeCount;
2328     PROCESS_PRIORITY_CLASS PriorityClass;
2329     NTSTATUS Status, AppCompatStatus, SaferStatus, IFEOStatus, ImageDbgStatus;
2330     PPEB Peb, RemotePeb;
2331     PTEB Teb;
2332     INITIAL_TEB InitialTeb;
2333     PVOID TibValue;
2334     PIMAGE_NT_HEADERS NtHeaders;
2335     STARTUPINFOW StartupInfo;
2336     PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
2337     UNICODE_STRING DebuggerString;
2338     BOOL Result;
2339     //
2340     // Variables used for command-line and argument parsing
2341     //
2342     PCHAR pcScan;
2343     SIZE_T n;
2344     WCHAR SaveChar;
2345     ULONG Length, FileAttribs, CmdQuoteLength;
2346     ULONG ResultSize;
2347     SIZE_T EnvironmentLength, CmdLineLength;
2348     PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory;
2349     PWCHAR NullBuffer, ScanString, NameBuffer, SearchPath, DebuggerCmdLine;
2350     ANSI_STRING AnsiEnv;
2351     UNICODE_STRING UnicodeEnv, PathName;
2352     BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName, HasQuotes;
2353 
2354     //
2355     // Variables used for Fusion/SxS (Side-by-Side Assemblies)
2356     //
2357     RTL_PATH_TYPE SxsPathType, PathType;
2358 #if _SXS_SUPPORT_ENABLED_
2359     PRTL_BUFFER ByteBuffer;
2360     PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5];
2361     PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer;
2362     RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath;
2363     RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath;
2364     RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory;
2365     BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles;
2366     PVOID CapturedStrings[3];
2367     SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair;
2368     SXS_OVERRIDE_MANIFEST OverrideManifest;
2369     UNICODE_STRING FreeString, SxsNtExePath;
2370     PWCHAR SxsConglomeratedBuffer, StaticBuffer;
2371     ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i;
2372 #endif
2373     ULONG FusionFlags;
2374 
2375     //
2376     // Variables used for path conversion (and partially Fusion/SxS)
2377     //
2378     PWCHAR FilePart, PathBuffer, FreeBuffer;
2379     BOOLEAN TranslationStatus;
2380     RTL_RELATIVE_NAME_U SxsWin32RelativePath;
2381     UNICODE_STRING PathBufferString, SxsWin32ExePath;
2382 
2383     //
2384     // Variables used by Application Compatibility (and partially Fusion/SxS)
2385     //
2386     PVOID AppCompatSxsData, AppCompatData;
2387     ULONG AppCompatSxsDataSize, AppCompatDataSize;
2388     //
2389     // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support)
2390     //
2391     ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve;
2392     ULONG VdmUndoLevel;
2393     BOOLEAN UseVdmReserve;
2394     HANDLE VdmWaitObject;
2395     ANSI_STRING VdmAnsiEnv;
2396     UNICODE_STRING VdmString, VdmUnicodeEnv;
2397     BOOLEAN IsWowApp;
2398     PBASE_CHECK_VDM CheckVdmMsg;
2399 
2400     /* Zero out the initial core variables and handles */
2401     QuerySection = FALSE;
2402     InJob = FALSE;
2403     SkipSaferAndAppCompat = FALSE;
2404     ParameterFlags = 0;
2405     Flags = 0;
2406     DebugHandle = NULL;
2407     JobHandle = NULL;
2408     TokenHandle = NULL;
2409     FileHandle = NULL;
2410     SectionHandle = NULL;
2411     ProcessHandle = NULL;
2412     ThreadHandle = NULL;
2413     BaseAddress = (PVOID)1;
2414 
2415     /* Zero out initial SxS and Application Compatibility state */
2416     AppCompatData = NULL;
2417     AppCompatDataSize = 0;
2418     AppCompatSxsData = NULL;
2419     AppCompatSxsDataSize = 0;
2420     CaptureBuffer = NULL;
2421 #if _SXS_SUPPORT_ENABLED_
2422     SxsConglomeratedBuffer = NULL;
2423 #endif
2424     FusionFlags = 0;
2425 
2426     /* Zero out initial parsing variables -- others are initialized later */
2427     DebuggerCmdLine = NULL;
2428     PathBuffer = NULL;
2429     SearchPath = NULL;
2430     NullBuffer = NULL;
2431     FreeBuffer = NULL;
2432     NameBuffer = NULL;
2433     CurrentDirectory = NULL;
2434     FilePart = NULL;
2435     DebuggerString.Buffer = NULL;
2436     HasQuotes = FALSE;
2437     QuotedCmdLine = NULL;
2438 
2439     /* Zero out initial VDM state */
2440     VdmAnsiEnv.Buffer = NULL;
2441     VdmUnicodeEnv.Buffer = NULL;
2442     VdmString.Buffer = NULL;
2443     VdmTask = 0;
2444     VdmUndoLevel = 0;
2445     VdmBinaryType = 0;
2446     VdmReserve = 0;
2447     VdmWaitObject = NULL;
2448     UseVdmReserve = FALSE;
2449     IsWowApp = FALSE;
2450 
2451     /* Set message structures */
2452     CreateProcessMsg = &CsrMsg[0].Data.CreateProcessRequest;
2453     CheckVdmMsg = &CsrMsg[1].Data.CheckVDMRequest;
2454 
2455     /* Clear the more complex structures by zeroing out their entire memory */
2456     RtlZeroMemory(&Context, sizeof(Context));
2457 #if _SXS_SUPPORT_ENABLED_
2458     RtlZeroMemory(&FileHandles, sizeof(FileHandles));
2459     RtlZeroMemory(&MappedHandles, sizeof(MappedHandles));
2460     RtlZeroMemory(&Handles, sizeof(Handles));
2461 #endif
2462     RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs));
2463     RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes));
2464     RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes));
2465 
2466     /* Zero out output arguments as well */
2467     RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation));
2468     if (hNewToken) *hNewToken = NULL;
2469 
2470     /* Capture the special window flag */
2471     NoWindow = dwCreationFlags & CREATE_NO_WINDOW;
2472     dwCreationFlags &= ~CREATE_NO_WINDOW;
2473 
2474 #if _SXS_SUPPORT_ENABLED_
2475     /* Setup the SxS static string arrays and buffers */
2476     SxsStaticBuffers[0] = &SxsWin32ManifestPath;
2477     SxsStaticBuffers[1] = &SxsWin32PolicyPath;
2478     SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory;
2479     SxsStaticBuffers[3] = &SxsNtManifestPath;
2480     SxsStaticBuffers[4] = &SxsNtPolicyPath;
2481     ExePathPair.Win32 = &SxsWin32ExePath;
2482     ExePathPair.Nt = &SxsNtExePath;
2483     ManifestPathPair.Win32 = &SxsWin32ManifestPath.String;
2484     ManifestPathPair.Nt = &SxsNtManifestPath.String;
2485     PolicyPathPair.Win32 = &SxsWin32PolicyPath.String;
2486     PolicyPathPair.Nt = &SxsNtPolicyPath.String;
2487 #endif
2488 
2489     DPRINT("CreateProcessInternalW: '%S' '%S' %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags);
2490 
2491     /* Finally, set our TEB and PEB */
2492     Teb = NtCurrentTeb();
2493     Peb = NtCurrentPeb();
2494 
2495     /* This combination is illegal (see MSDN) */
2496     if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
2497         (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
2498     {
2499         DPRINT1("Invalid flag combo used\n");
2500         SetLastError(ERROR_INVALID_PARAMETER);
2501         return FALSE;
2502     }
2503 
2504     /* Convert the priority class */
2505     if (dwCreationFlags & IDLE_PRIORITY_CLASS)
2506     {
2507         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
2508     }
2509     else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
2510     {
2511         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2512     }
2513     else if (dwCreationFlags & NORMAL_PRIORITY_CLASS)
2514     {
2515         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
2516     }
2517     else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
2518     {
2519         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2520     }
2521     else if (dwCreationFlags & HIGH_PRIORITY_CLASS)
2522     {
2523         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2524     }
2525     else if (dwCreationFlags & REALTIME_PRIORITY_CLASS)
2526     {
2527         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2528         PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(FALSE) != NULL);
2529     }
2530     else
2531     {
2532         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID;
2533     }
2534 
2535     /* Done with the priority masks, so get rid of them */
2536     PriorityClass.Foreground = FALSE;
2537     dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS |
2538                          IDLE_PRIORITY_CLASS |
2539                          HIGH_PRIORITY_CLASS |
2540                          REALTIME_PRIORITY_CLASS |
2541                          BELOW_NORMAL_PRIORITY_CLASS |
2542                          ABOVE_NORMAL_PRIORITY_CLASS);
2543 
2544     /* You cannot request both a shared and a separate WoW VDM */
2545     if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2546         (dwCreationFlags & CREATE_SHARED_WOW_VDM))
2547     {
2548         /* Fail such nonsensical attempts */
2549         DPRINT1("Invalid WOW flags\n");
2550         SetLastError(ERROR_INVALID_PARAMETER);
2551         return FALSE;
2552     }
2553     else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) &&
2554              (BaseStaticServerData->DefaultSeparateVDM))
2555     {
2556         /* A shared WoW VDM was not requested but system enforces separation */
2557         dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
2558     }
2559 
2560     /* If a shared WoW VDM is used, make sure the process isn't in a job */
2561     if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2562         (NtIsProcessInJob(NtCurrentProcess(), NULL)))
2563     {
2564         /* Remove the shared flag and add the separate flag */
2565         dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
2566                                               CREATE_SEPARATE_WOW_VDM;
2567     }
2568 
2569     /* Convert the environment */
2570     if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
2571     {
2572         /* Scan the environment to calculate its Unicode size */
2573         AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
2574         while ((*pcScan) || (*(pcScan + 1))) ++pcScan;
2575 
2576         /* Make sure the environment is not too large */
2577         EnvironmentLength = (pcScan + sizeof(ANSI_NULL) - (PCHAR)lpEnvironment);
2578         if (EnvironmentLength > MAXUSHORT)
2579         {
2580             /* Fail */
2581             SetLastError(ERROR_INVALID_PARAMETER);
2582             return FALSE;
2583         }
2584 
2585         /* Create our ANSI String */
2586         AnsiEnv.Length = (USHORT)EnvironmentLength;
2587         AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL);
2588 
2589         /* Allocate memory for the Unicode Environment */
2590         UnicodeEnv.Buffer = NULL;
2591         RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
2592         Status = NtAllocateVirtualMemory(NtCurrentProcess(),
2593                                          (PVOID)&UnicodeEnv.Buffer,
2594                                          0,
2595                                          &RegionSize,
2596                                          MEM_COMMIT,
2597                                          PAGE_READWRITE);
2598         if (!NT_SUCCESS(Status))
2599         {
2600             /* Fail */
2601             BaseSetLastNTError(Status);
2602             return FALSE;
2603         }
2604 
2605         /* Use the allocated size and convert */
2606         UnicodeEnv.MaximumLength = (USHORT)RegionSize;
2607         Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
2608         if (!NT_SUCCESS(Status))
2609         {
2610             /* Fail */
2611             NtFreeVirtualMemory(NtCurrentProcess(),
2612                                 (PVOID)&UnicodeEnv.Buffer,
2613                                 &RegionSize,
2614                                 MEM_RELEASE);
2615             BaseSetLastNTError(Status);
2616             return FALSE;
2617         }
2618 
2619         /* Now set the Unicode environment as the environment string pointer */
2620         lpEnvironment = UnicodeEnv.Buffer;
2621     }
2622 
2623     /* Make a copy of the caller's startup info since we'll modify it */
2624     StartupInfo = *lpStartupInfo;
2625 
2626     /* Check if private data is being sent on the same channel as std handles */
2627     if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
2628         (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
2629     {
2630         /* Cannot use the std handles since we have monitor/hotkey values */
2631         StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
2632     }
2633 
2634     /* If there's a debugger, or we have to launch cmd.exe, we go back here */
2635 AppNameRetry:
2636     /* New iteration -- free any existing name buffer */
2637     if (NameBuffer)
2638     {
2639         RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
2640         NameBuffer = NULL;
2641     }
2642 
2643     /* New iteration -- free any existing free buffer */
2644     if (FreeBuffer)
2645     {
2646         RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
2647         FreeBuffer = NULL;
2648     }
2649 
2650     /* New iteration -- close any existing file handle */
2651     if (FileHandle)
2652     {
2653         NtClose(FileHandle);
2654         FileHandle = NULL;
2655     }
2656 
2657     /* Set the initial parsing state. This code can loop -- don't move this! */
2658     ErrorCode = 0;
2659     SearchRetry = TRUE;
2660     QuotesNeeded = FALSE;
2661     CmdLineIsAppName = FALSE;
2662 
2663     /* First check if we don't have an application name */
2664     if (!lpApplicationName)
2665     {
2666         /* This should be the first time we attempt creating one */
2667         ASSERT(NameBuffer == NULL);
2668 
2669         /* Allocate a buffer to hold it */
2670         NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2671                                      0,
2672                                      MAX_PATH * sizeof(WCHAR));
2673         if (!NameBuffer)
2674         {
2675             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2676             Result = FALSE;
2677             goto Quickie;
2678         }
2679 
2680         /* Initialize the application name and our parsing parameters */
2681         lpApplicationName = NullBuffer = ScanString = lpCommandLine;
2682 
2683         /* Check for an initial quote*/
2684         if (*lpCommandLine == L'\"')
2685         {
2686             /* We found a quote, keep searching for another one */
2687             SearchRetry = FALSE;
2688             ScanString++;
2689             lpApplicationName = ScanString;
2690             while (*ScanString)
2691             {
2692                 /* Have we found the terminating quote? */
2693                 if (*ScanString == L'\"')
2694                 {
2695                     /* We're done, get out of here */
2696                     NullBuffer = ScanString;
2697                     HasQuotes = TRUE;
2698                     break;
2699                 }
2700 
2701                 /* Keep searching for the quote */
2702                 ScanString++;
2703                 NullBuffer = ScanString;
2704             }
2705         }
2706         else
2707         {
2708 StartScan:
2709             /* We simply make the application name be the command line*/
2710             lpApplicationName = lpCommandLine;
2711             while (*ScanString)
2712             {
2713                 /* Check if it starts with a space or tab */
2714                 if ((*ScanString == L' ') || (*ScanString == L'\t'))
2715                 {
2716                     /* Break out of the search loop */
2717                     NullBuffer = ScanString;
2718                     break;
2719                 }
2720 
2721                 /* Keep searching for a space or tab */
2722                 ScanString++;
2723                 NullBuffer = ScanString;
2724             }
2725         }
2726 
2727         /* We have found the end of the application name, terminate it */
2728         SaveChar = *NullBuffer;
2729         *NullBuffer = UNICODE_NULL;
2730 
2731         /* New iteration -- free any existing saved path */
2732         if (SearchPath)
2733         {
2734             RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
2735             SearchPath = NULL;
2736         }
2737 
2738         /* Now compute the final EXE path based on the name */
2739         SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName);
2740         DPRINT("Search Path: %S\n", SearchPath);
2741         if (!SearchPath)
2742         {
2743             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2744             Result = FALSE;
2745             goto Quickie;
2746         }
2747 
2748         /* And search for the executable in the search path */
2749         Length = SearchPathW(SearchPath,
2750                              lpApplicationName,
2751                              L".exe",
2752                              MAX_PATH,
2753                              NameBuffer,
2754                              NULL);
2755 
2756         /* Did we find it? */
2757         if ((Length) && (Length < MAX_PATH))
2758         {
2759             /* Get file attributes */
2760             FileAttribs = GetFileAttributesW(NameBuffer);
2761             if ((FileAttribs != INVALID_FILE_ATTRIBUTES) &&
2762                 (FileAttribs & FILE_ATTRIBUTE_DIRECTORY))
2763             {
2764                 /* This was a directory, fail later on */
2765                 Length = 0;
2766             }
2767             else
2768             {
2769                 /* It's a file! */
2770                 Length++;
2771             }
2772         }
2773 
2774         DPRINT("Length: %lu Buffer: %S\n", Length, NameBuffer);
2775 
2776         /* Check if there was a failure in SearchPathW */
2777         if ((Length) && (Length < MAX_PATH))
2778         {
2779             /* Everything looks good, restore the name */
2780             *NullBuffer = SaveChar;
2781             lpApplicationName = NameBuffer;
2782         }
2783         else
2784         {
2785             /* Check if this was a relative path, which would explain it */
2786             PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2787             if (PathType != RtlPathTypeRelative)
2788             {
2789                 /* This should fail, and give us a detailed LastError */
2790                 FileHandle = CreateFileW(lpApplicationName,
2791                                          GENERIC_READ,
2792                                          FILE_SHARE_READ |
2793                                          FILE_SHARE_WRITE,
2794                                          NULL,
2795                                          OPEN_EXISTING,
2796                                          FILE_ATTRIBUTE_NORMAL,
2797                                          NULL);
2798                 if (FileHandle != INVALID_HANDLE_VALUE)
2799                 {
2800                     /* It worked? Return a generic error */
2801                     CloseHandle(FileHandle);
2802                     FileHandle = NULL;
2803                     BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2804                 }
2805             }
2806             else
2807             {
2808                 /* Path was absolute, which means it doesn't exist */
2809                 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2810             }
2811 
2812             /* Did we already fail once? */
2813             if (ErrorCode)
2814             {
2815                 /* Set the error code */
2816                 SetLastError(ErrorCode);
2817             }
2818             else
2819             {
2820                 /* Not yet, cache it */
2821                 ErrorCode = GetLastError();
2822             }
2823 
2824             /* Put back the command line */
2825             *NullBuffer = SaveChar;
2826             lpApplicationName = NameBuffer;
2827 
2828             /* It's possible there's whitespace in the directory name */
2829             if (!(*ScanString) || !(SearchRetry))
2830             {
2831                 /* Not the case, give up completely */
2832                 Result = FALSE;
2833                 goto Quickie;
2834             }
2835 
2836             /* There are spaces, so keep trying the next possibility */
2837             ScanString++;
2838             NullBuffer = ScanString;
2839 
2840             /* We will have to add a quote, since there is a space */
2841             QuotesNeeded = TRUE;
2842             HasQuotes = TRUE;
2843             goto StartScan;
2844         }
2845     }
2846     else if (!(lpCommandLine) || !(*lpCommandLine))
2847     {
2848         /* We don't have a command line, so just use the application name */
2849         CmdLineIsAppName = TRUE;
2850         lpCommandLine = (LPWSTR)lpApplicationName;
2851     }
2852 
2853     /* Convert the application name to its NT path */
2854     TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName,
2855                                                              &PathName,
2856                                                              NULL,
2857                                                              &SxsWin32RelativePath);
2858     if (!TranslationStatus)
2859     {
2860         /* Path must be invalid somehow, bail out */
2861         DPRINT1("Path translation for SxS failed\n");
2862         SetLastError(ERROR_PATH_NOT_FOUND);
2863         Result = FALSE;
2864         goto Quickie;
2865     }
2866 
2867     /* Setup the buffer that needs to be freed at the end */
2868     ASSERT(FreeBuffer == NULL);
2869     FreeBuffer = PathName.Buffer;
2870 
2871     /* Check what kind of path the application is, for SxS (Fusion) purposes */
2872     RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);
2873     SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2874     if ((SxsPathType != RtlPathTypeDriveAbsolute) &&
2875         (SxsPathType != RtlPathTypeLocalDevice) &&
2876         (SxsPathType != RtlPathTypeRootLocalDevice) &&
2877         (SxsPathType != RtlPathTypeUncAbsolute))
2878     {
2879         /* Relative-type path, get the full path */
2880         RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0);
2881         Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath,
2882                                            NULL,
2883                                            &PathBufferString,
2884                                            NULL,
2885                                            NULL,
2886                                            NULL,
2887                                            &SxsPathType,
2888                                            NULL);
2889         if (!NT_SUCCESS(Status))
2890         {
2891             /* Fail the rest of the create */
2892             RtlReleaseRelativeName(&SxsWin32RelativePath);
2893             BaseSetLastNTError(Status);
2894             Result = FALSE;
2895             goto Quickie;
2896         }
2897 
2898         /* Use this full path as the SxS path */
2899         SxsWin32ExePath = PathBufferString;
2900         PathBuffer = PathBufferString.Buffer;
2901         PathBufferString.Buffer = NULL;
2902         DPRINT("SxS Path: %S\n", PathBuffer);
2903     }
2904 
2905     /* Also set the .EXE path based on the path name */
2906 #if _SXS_SUPPORT_ENABLED_
2907     SxsNtExePath = PathName;
2908 #endif
2909     if (SxsWin32RelativePath.RelativeName.Length)
2910     {
2911         /* If it's relative, capture the relative name */
2912         PathName = SxsWin32RelativePath.RelativeName;
2913     }
2914     else
2915     {
2916         /* Otherwise, it's absolute, make sure no relative dir is used */
2917         SxsWin32RelativePath.ContainingDirectory = NULL;
2918     }
2919 
2920     /* Now use the path name, and the root path, to try opening the app */
2921     DPRINT("Path: %wZ. Dir: %p\n", &PathName, SxsWin32RelativePath.ContainingDirectory);
2922     InitializeObjectAttributes(&LocalObjectAttributes,
2923                                &PathName,
2924                                OBJ_CASE_INSENSITIVE,
2925                                SxsWin32RelativePath.ContainingDirectory,
2926                                NULL);
2927     Status = NtOpenFile(&FileHandle,
2928                         SYNCHRONIZE |
2929                         FILE_READ_ATTRIBUTES |
2930                         FILE_READ_DATA |
2931                         FILE_EXECUTE,
2932                         &LocalObjectAttributes,
2933                         &IoStatusBlock,
2934                         FILE_SHARE_READ | FILE_SHARE_DELETE,
2935                         FILE_SYNCHRONOUS_IO_NONALERT |
2936                         FILE_NON_DIRECTORY_FILE);
2937     if (!NT_SUCCESS(Status))
2938     {
2939         /* Try to open the app just for execute purposes instead */
2940         Status = NtOpenFile(&FileHandle,
2941                             SYNCHRONIZE | FILE_EXECUTE,
2942                             &LocalObjectAttributes,
2943                             &IoStatusBlock,
2944                             FILE_SHARE_READ | FILE_SHARE_DELETE,
2945                             FILE_SYNCHRONOUS_IO_NONALERT |
2946                             FILE_NON_DIRECTORY_FILE);
2947     }
2948 
2949     /* Failure path, display which file failed to open */
2950     if (!NT_SUCCESS(Status))
2951         DPRINT1("Open file failed: %lx (%wZ)\n", Status, &PathName);
2952 
2953     /* Cleanup in preparation for failure or success */
2954     RtlReleaseRelativeName(&SxsWin32RelativePath);
2955 
2956     if (!NT_SUCCESS(Status))
2957     {
2958         /* Failure path, try to understand why */
2959         if (RtlIsDosDeviceName_U(lpApplicationName))
2960         {
2961             /* If a device is being executed, return this special error code */
2962             SetLastError(ERROR_BAD_DEVICE);
2963             Result = FALSE;
2964             goto Quickie;
2965         }
2966         else
2967         {
2968             /* Otherwise return the converted NT error code */
2969             BaseSetLastNTError(Status);
2970             Result = FALSE;
2971             goto Quickie;
2972         }
2973     }
2974 
2975     /* Did the caller specify a desktop? */
2976     if (!StartupInfo.lpDesktop)
2977     {
2978         /* Use the one from the current process */
2979         StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
2980     }
2981 
2982     /* Create a section for this file */
2983     Status = NtCreateSection(&SectionHandle,
2984                              SECTION_ALL_ACCESS,
2985                              NULL,
2986                              NULL,
2987                              PAGE_EXECUTE,
2988                              SEC_IMAGE,
2989                              FileHandle);
2990     DPRINT("Section status: %lx\n", Status);
2991     if (NT_SUCCESS(Status))
2992     {
2993         /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */
2994         if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT |
2995                                          VER_SUITE_DATACENTER |
2996                                          VER_SUITE_PERSONAL |
2997                                          VER_SUITE_BLADE))
2998         {
2999             /* These SKUs do not allow running certain applications */
3000             Status = BasepCheckWebBladeHashes(FileHandle);
3001             if (Status == STATUS_ACCESS_DENIED)
3002             {
3003                 /* And this is one of them! */
3004                 DPRINT1("Invalid Blade hashes!\n");
3005                 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE);
3006                 Result = FALSE;
3007                 goto Quickie;
3008             }
3009 
3010             /* Did we get some other failure? */
3011             if (!NT_SUCCESS(Status))
3012             {
3013                 /* If we couldn't check the hashes, assume nefariousness */
3014                 DPRINT1("Tampered Blade hashes!\n");
3015                 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER);
3016                 Result = FALSE;
3017                 goto Quickie;
3018             }
3019         }
3020 
3021         /* Now do Winsafer, etc, checks */
3022         Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName);
3023         if (!NT_SUCCESS(Status))
3024         {
3025             /* Fail if we're not allowed to launch the process */
3026             DPRINT1("Process not allowed to launch: %lx\n", Status);
3027             BaseSetLastNTError(Status);
3028             if (SectionHandle)
3029             {
3030                 NtClose(SectionHandle);
3031                 SectionHandle = NULL;
3032             }
3033             Result = FALSE;
3034             goto Quickie;
3035         }
3036 
3037         /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */
3038         if ((dwCreationFlags & CREATE_FORCEDOS) &&
3039             (BaseStaticServerData->IsWowTaskReady))
3040         {
3041             /* This request can't be satisfied, instead, a separate VDM is needed */
3042             dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM);
3043             dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3044 
3045             /* Set a failure code, ask for VDM reservation */
3046             Status = STATUS_INVALID_IMAGE_WIN_16;
3047             UseVdmReserve = TRUE;
3048 
3049             /* Close the current handle */
3050             NtClose(SectionHandle);
3051             SectionHandle = NULL;
3052 
3053             /* Don't query the section later */
3054             QuerySection = FALSE;
3055         }
3056     }
3057 
3058     /* Did we already do these checks? */
3059     if (!SkipSaferAndAppCompat)
3060     {
3061         /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */
3062         if ((NT_SUCCESS(Status)) ||
3063             ((Status == STATUS_INVALID_IMAGE_NOT_MZ) &&
3064             !(BaseIsDosApplication(&PathName, Status))))
3065         {
3066             /* Clear the machine type in case of failure */
3067             ImageMachine = 0;
3068 
3069             /* Clean any app compat data that may have accumulated */
3070             BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
3071             AppCompatData = NULL;
3072             AppCompatSxsData = NULL;
3073 
3074             /* Do we have a section? */
3075             if (SectionHandle)
3076             {
3077                 /* Have we already queried it? */
3078                 if (QuerySection)
3079                 {
3080                     /* Nothing to do */
3081                     AppCompatStatus = STATUS_SUCCESS;
3082                 }
3083                 else
3084                 {
3085                     /* Get some information about the executable */
3086                     AppCompatStatus = NtQuerySection(SectionHandle,
3087                                             SectionImageInformation,
3088                                             &ImageInformation,
3089                                             sizeof(ImageInformation),
3090                                             NULL);
3091                 }
3092 
3093                 /* Do we have section information now? */
3094                 if (NT_SUCCESS(AppCompatStatus))
3095                 {
3096                     /* Don't ask for it again, save the machine type */
3097                     QuerySection = TRUE;
3098                     ImageMachine = ImageInformation.Machine;
3099                 }
3100             }
3101 
3102             /* Is there a reason/Shim we shouldn't run this application? */
3103             AppCompatStatus = BasepCheckBadapp(FileHandle,
3104                                       FreeBuffer,
3105                                       lpEnvironment,
3106                                       ImageMachine,
3107                                       &AppCompatData,
3108                                       &AppCompatDataSize,
3109                                       &AppCompatSxsData,
3110                                       &AppCompatSxsDataSize,
3111                                       &FusionFlags);
3112             if (!NT_SUCCESS(AppCompatStatus))
3113             {
3114                 /* This is usually the status we get back */
3115                 DPRINT1("App compat launch failure: %lx\n", AppCompatStatus);
3116                 if (AppCompatStatus == STATUS_ACCESS_DENIED)
3117                 {
3118                     /* Convert it to something more Win32-specific */
3119                     SetLastError(ERROR_CANCELLED);
3120                 }
3121                 else
3122                 {
3123                     /* Some other error */
3124                     BaseSetLastNTError(AppCompatStatus);
3125                 }
3126 
3127                 /* Did we have a section? */
3128                 if (SectionHandle)
3129                 {
3130                     /* Clean it up */
3131                     NtClose(SectionHandle);
3132                     SectionHandle = NULL;
3133                 }
3134 
3135                 /* Fail the call */
3136                 Result = FALSE;
3137                 goto Quickie;
3138             }
3139         }
3140     }
3141 
3142     //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
3143 
3144     /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
3145     if (!(SkipSaferAndAppCompat) &&
3146         ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
3147     {
3148         /* Assume yes */
3149         SaferNeeded = TRUE;
3150         switch (Status)
3151         {
3152             case STATUS_INVALID_IMAGE_NE_FORMAT:
3153             case STATUS_INVALID_IMAGE_PROTECT:
3154             case STATUS_INVALID_IMAGE_WIN_16:
3155             case STATUS_FILE_IS_OFFLINE:
3156                 /* For all DOS, 16-bit, OS/2 images, we do*/
3157                 break;
3158 
3159             case STATUS_INVALID_IMAGE_NOT_MZ:
3160                 /* For invalid files, we don't, unless it's a .BAT file */
3161                 if (BaseIsDosApplication(&PathName, Status)) break;
3162 
3163             default:
3164                 /* Any other error codes we also don't */
3165                 if (!NT_SUCCESS(Status))
3166                 {
3167                     SaferNeeded = FALSE;
3168                 }
3169 
3170                 /* But for success, we do */
3171                 break;
3172         }
3173 
3174         /* Okay, so what did the checks above result in? */
3175         if (SaferNeeded)
3176         {
3177             /* We have to call into the WinSafer library and actually check */
3178             SaferStatus = BasepCheckWinSaferRestrictions(hUserToken,
3179                                                     (LPWSTR)lpApplicationName,
3180                                                     FileHandle,
3181                                                     &InJob,
3182                                                     &TokenHandle,
3183                                                     &JobHandle);
3184             if (SaferStatus == 0xFFFFFFFF)
3185             {
3186                 /* Back in 2003, they didn't have an NTSTATUS for this... */
3187                 DPRINT1("WinSafer blocking process launch\n");
3188                 SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
3189                 Result = FALSE;
3190                 goto Quickie;
3191             }
3192 
3193             /* Other status codes are not-Safer related, just convert them */
3194             if (!NT_SUCCESS(SaferStatus))
3195             {
3196                 DPRINT1("Error checking WinSafer: %lx\n", SaferStatus);
3197                 BaseSetLastNTError(SaferStatus);
3198                 Result = FALSE;
3199                 goto Quickie;
3200             }
3201         }
3202     }
3203 
3204     /* The last step is to figure out why the section object was not created */
3205     switch (Status)
3206     {
3207         case STATUS_INVALID_IMAGE_WIN_16:
3208         {
3209             /* 16-bit binary. Should we use WOW or does the caller force VDM? */
3210             if (!(dwCreationFlags & CREATE_FORCEDOS))
3211             {
3212                 /* Remember that we're launching WOW */
3213                 IsWowApp = TRUE;
3214 
3215                 /* Create the VDM environment, it's valid for WOW too */
3216                 Result = BaseCreateVDMEnvironment(lpEnvironment,
3217                                                   &VdmAnsiEnv,
3218                                                   &VdmUnicodeEnv);
3219                 if (!Result)
3220                 {
3221                     DPRINT1("VDM environment for WOW app failed\n");
3222                     goto Quickie;
3223                 }
3224 
3225                 /* We're going to try this twice, so do a loop */
3226                 while (TRUE)
3227                 {
3228                     /* Pick which kind of WOW mode we want to run in */
3229                     VdmBinaryType = (dwCreationFlags &
3230                                      CREATE_SEPARATE_WOW_VDM) ?
3231                                      BINARY_TYPE_SEPARATE_WOW : BINARY_TYPE_WOW;
3232 
3233                     /* Get all the VDM settings and current status */
3234                     Status = BaseCheckVDM(VdmBinaryType,
3235                                           lpApplicationName,
3236                                           lpCommandLine,
3237                                           lpCurrentDirectory,
3238                                           &VdmAnsiEnv,
3239                                           &CsrMsg[1],
3240                                           &VdmTask,
3241                                           dwCreationFlags,
3242                                           &StartupInfo,
3243                                           hUserToken);
3244 
3245                     /* If it worked, no need to try again */
3246                     if (NT_SUCCESS(Status)) break;
3247 
3248                     /* Check if it's disallowed or if it's our second time */
3249                     BaseSetLastNTError(Status);
3250                     if ((Status == STATUS_VDM_DISALLOWED) ||
3251                         (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) ||
3252                         (GetLastError() == ERROR_ACCESS_DENIED))
3253                     {
3254                         /* Fail the call -- we won't try again */
3255                         DPRINT1("VDM message failure for WOW: %lx\n", Status);
3256                         Result = FALSE;
3257                         goto Quickie;
3258                     }
3259 
3260                     /* Try one more time, but with a separate WOW instance */
3261                     dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3262                 }
3263 
3264                 /* Check which VDM state we're currently in */
3265                 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3266                                                  VDM_NOT_READY |
3267                                                  VDM_READY))
3268                 {
3269                     case VDM_NOT_LOADED:
3270                         /* VDM is not fully loaded, so not that much to undo */
3271                         VdmUndoLevel = VDM_UNDO_PARTIAL;
3272 
3273                         /* Reset VDM reserve if needed */
3274                         if (UseVdmReserve) VdmReserve = 1;
3275 
3276                         /* Get the required parameters and names for launch */
3277                         Result = BaseGetVdmConfigInfo(lpCommandLine,
3278                                                       VdmTask,
3279                                                       VdmBinaryType,
3280                                                       &VdmString,
3281                                                       &VdmReserve);
3282                         if (!Result)
3283                         {
3284                             DPRINT1("VDM Configuration failed for WOW\n");
3285                             BaseSetLastNTError(Status);
3286                             goto Quickie;
3287                         }
3288 
3289                         /* Update the command-line with the VDM one instead */
3290                         lpCommandLine = VdmString.Buffer;
3291                         lpApplicationName = NULL;
3292 
3293                         /* We don't want a console, detachment, nor a window */
3294                         dwCreationFlags |= CREATE_NO_WINDOW;
3295                         dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
3296 
3297                         /* Force feedback on */
3298                         StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
3299                         break;
3300 
3301 
3302                     case VDM_READY:
3303                         /* VDM is ready, so we have to undo everything */
3304                         VdmUndoLevel = VDM_UNDO_REUSE;
3305 
3306                         /* Check if CSRSS wants us to wait on VDM */
3307                         VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3308                         break;
3309 
3310                     case VDM_NOT_READY:
3311                         /* Something is wrong with VDM, we'll fail the call */
3312                         DPRINT1("VDM is not ready for WOW\n");
3313                         SetLastError(ERROR_NOT_READY);
3314                         Result = FALSE;
3315                         goto Quickie;
3316 
3317                     default:
3318                         break;
3319                 }
3320 
3321                 /* Since to get NULL, we allocate from 0x1, account for this */
3322                 VdmReserve--;
3323 
3324                 /* This implies VDM is ready, so skip everything else */
3325                 if (VdmWaitObject) goto VdmShortCircuit;
3326 
3327                 /* Don't inherit handles since we're doing VDM now */
3328                 bInheritHandles = FALSE;
3329 
3330                 /* Had the user passed in environment? If so, destroy it */
3331                 if ((lpEnvironment) &&
3332                     !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3333                 {
3334                     RtlDestroyEnvironment(lpEnvironment);
3335                 }
3336 
3337                 /* We've already done all these checks, don't do them again */
3338                 SkipSaferAndAppCompat = TRUE;
3339                 goto AppNameRetry;
3340             }
3341 
3342             // There is no break here on purpose, so FORCEDOS drops down!
3343         }
3344 
3345         case STATUS_INVALID_IMAGE_PROTECT:
3346         case STATUS_INVALID_IMAGE_NOT_MZ:
3347         case STATUS_INVALID_IMAGE_NE_FORMAT:
3348         {
3349             /* We're launching an executable application */
3350             BinarySubType = BINARY_TYPE_EXE;
3351 
3352             /* We can drop here from other "cases" above too, so check */
3353             if ((Status == STATUS_INVALID_IMAGE_PROTECT) ||
3354                 (Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
3355                 (BinarySubType = BaseIsDosApplication(&PathName, Status)))
3356             {
3357                 /* We're launching a DOS application */
3358                 VdmBinaryType = BINARY_TYPE_DOS;
3359 
3360                 /* Based on the caller environment, create a VDM one */
3361                 Result = BaseCreateVDMEnvironment(lpEnvironment,
3362                                                   &VdmAnsiEnv,
3363                                                   &VdmUnicodeEnv);
3364                 if (!Result)
3365                 {
3366                     DPRINT1("VDM environment for DOS failed\n");
3367                     goto Quickie;
3368                 }
3369 
3370                 /* Check the current state of the VDM subsystem */
3371                 Status = BaseCheckVDM(VdmBinaryType | BinarySubType,
3372                                       lpApplicationName,
3373                                       lpCommandLine,
3374                                       lpCurrentDirectory,
3375                                       &VdmAnsiEnv,
3376                                       &CsrMsg[1],
3377                                       &VdmTask,
3378                                       dwCreationFlags,
3379                                       &StartupInfo,
3380                                       NULL);
3381                 if (!NT_SUCCESS(Status))
3382                 {
3383                     /* Failed to inquire about VDM, fail the call */
3384                     DPRINT1("VDM message failure for DOS: %lx\n", Status);
3385                     BaseSetLastNTError(Status);
3386                     Result = FALSE;
3387                     goto Quickie;
3388                 };
3389 
3390                 /* Handle possible VDM states */
3391                 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3392                                                  VDM_NOT_READY |
3393                                                  VDM_READY))
3394                 {
3395                     case VDM_NOT_LOADED:
3396                         /* If VDM is not loaded, we'll do a partial undo */
3397                         VdmUndoLevel = VDM_UNDO_PARTIAL;
3398 
3399                         /* A VDM process can't also be detached, so fail */
3400                         if (dwCreationFlags & DETACHED_PROCESS)
3401                         {
3402                             DPRINT1("Detached process but no VDM, not allowed\n");
3403                             SetLastError(ERROR_ACCESS_DENIED);
3404                             return FALSE;
3405                         }
3406 
3407                         /* Get the required parameters and names for launch */
3408                         Result = BaseGetVdmConfigInfo(lpCommandLine,
3409                                                       VdmTask,
3410                                                       VdmBinaryType,
3411                                                       &VdmString,
3412                                                       &VdmReserve);
3413                         if (!Result)
3414                         {
3415                             DPRINT1("VDM Configuration failed for DOS\n");
3416                             BaseSetLastNTError(Status);
3417                             goto Quickie;
3418                         }
3419 
3420                         /* Update the command-line to launch VDM instead */
3421                         lpCommandLine = VdmString.Buffer;
3422                         lpApplicationName = NULL;
3423                         break;
3424 
3425                     case VDM_READY:
3426                         /* VDM is ready, so we have to undo everything */
3427                         VdmUndoLevel = VDM_UNDO_REUSE;
3428 
3429                         /* Check if CSRSS wants us to wait on VDM */
3430                         VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3431                         break;
3432 
3433                     case VDM_NOT_READY:
3434                         /* Something is wrong with VDM, we'll fail the call */
3435                         DPRINT1("VDM is not ready for DOS\n");
3436                         SetLastError(ERROR_NOT_READY);
3437                         Result = FALSE;
3438                         goto Quickie;
3439 
3440                     default:
3441                         break;
3442                 }
3443 
3444                 /* Since to get NULL, we allocate from 0x1, account for this */
3445                 VdmReserve--;
3446 
3447                 /* This implies VDM is ready, so skip everything else */
3448                 if (VdmWaitObject) goto VdmShortCircuit;
3449 
3450                 /* Don't inherit handles since we're doing VDM now */
3451                 bInheritHandles = FALSE;
3452 
3453                 /* Had the user passed in environment? If so, destroy it */
3454                 if ((lpEnvironment) &&
3455                     !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3456                 {
3457                     RtlDestroyEnvironment(lpEnvironment);
3458                 }
3459 
3460                 /* Use our VDM Unicode environment instead */
3461                 lpEnvironment = VdmUnicodeEnv.Buffer;
3462             }
3463             else
3464             {
3465                 /* It's a batch file, get the extension */
3466                 ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
3467 
3468                 /* Make sure the extensions are correct */
3469                 if ((PathName.Length < (4 * sizeof(WCHAR))) ||
3470                     ((_wcsnicmp(ExtBuffer, L".bat", 4)) &&
3471                      (_wcsnicmp(ExtBuffer, L".cmd", 4))))
3472                 {
3473                     DPRINT1("'%wZ': Invalid EXE, and not a batch or script file\n", &PathName);
3474                     SetLastError(ERROR_BAD_EXE_FORMAT);
3475                     Result = FALSE;
3476                     goto Quickie;
3477                 }
3478 
3479                 /* Check if we need to account for quotes around the path */
3480                 CmdQuoteLength = CmdLineIsAppName || HasQuotes;
3481                 if (!CmdLineIsAppName)
3482                 {
3483                     if (HasQuotes) CmdQuoteLength++;
3484                 }
3485                 else
3486                 {
3487                     CmdQuoteLength++;
3488                 }
3489 
3490                 /* Calculate the length of the command line */
3491                 CmdLineLength = wcslen(lpCommandLine);
3492                 CmdLineLength += wcslen(CMD_STRING);
3493                 CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL);
3494                 CmdLineLength *= sizeof(WCHAR);
3495 
3496                 /* Allocate space for the new command line */
3497                 AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(),
3498                                                  0,
3499                                                  CmdLineLength);
3500                 if (!AnsiCmdCommand)
3501                 {
3502                     BaseSetLastNTError(STATUS_NO_MEMORY);
3503                     Result = FALSE;
3504                     goto Quickie;
3505                 }
3506 
3507                 /* Build it */
3508                 wcscpy(AnsiCmdCommand, CMD_STRING);
3509                 if ((CmdLineIsAppName) || (HasQuotes))
3510                 {
3511                     wcscat(AnsiCmdCommand, L"\"");
3512                 }
3513                 wcscat(AnsiCmdCommand, lpCommandLine);
3514                 if ((CmdLineIsAppName) || (HasQuotes))
3515                 {
3516                     wcscat(AnsiCmdCommand, L"\"");
3517                 }
3518 
3519                 /* Create it as a Unicode String */
3520                 RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
3521 
3522                 /* Set the command line to this */
3523                 lpCommandLine = DebuggerString.Buffer;
3524                 lpApplicationName = NULL;
3525                 DPRINT1("Retrying with: %S\n", lpCommandLine);
3526             }
3527 
3528             /* We've already done all these checks, don't do them again */
3529             SkipSaferAndAppCompat = TRUE;
3530             goto AppNameRetry;
3531         }
3532 
3533         case STATUS_INVALID_IMAGE_WIN_64:
3534         {
3535             /* 64-bit binaries are not allowed to run on 32-bit ReactOS */
3536             DPRINT1("64-bit binary, failing\n");
3537             SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3538             Result = FALSE;
3539             goto Quickie;
3540         }
3541 
3542         case STATUS_FILE_IS_OFFLINE:
3543         {
3544             /* Set the correct last error for this */
3545             DPRINT1("File is offline, failing\n");
3546             SetLastError(ERROR_FILE_OFFLINE);
3547             break;
3548         }
3549 
3550         default:
3551         {
3552             /* Any other error, convert it to a generic Win32 error */
3553             if (!NT_SUCCESS(Status))
3554             {
3555                 DPRINT1("Failed to create section: %lx\n", Status);
3556                 SetLastError(ERROR_BAD_EXE_FORMAT);
3557                 Result = FALSE;
3558                 goto Quickie;
3559             }
3560 
3561             /* Otherwise, this must be success */
3562             ASSERT(Status == STATUS_SUCCESS);
3563             break;
3564         }
3565     }
3566 
3567     /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
3568     if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
3569     {
3570         /* Ignore the nonsensical request */
3571         dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
3572     }
3573 
3574     /* Did we already check information for the section? */
3575     if (!QuerySection)
3576     {
3577         /* Get some information about the executable */
3578         Status = NtQuerySection(SectionHandle,
3579                                 SectionImageInformation,
3580                                 &ImageInformation,
3581                                 sizeof(ImageInformation),
3582                                 NULL);
3583         if (!NT_SUCCESS(Status))
3584         {
3585             /* We failed, bail out */
3586             DPRINT1("Section query failed\n");
3587             BaseSetLastNTError(Status);
3588             Result = FALSE;
3589             goto Quickie;
3590         }
3591 
3592         /* Don't check this later */
3593         QuerySection = TRUE;
3594     }
3595 
3596     /* Check if this was linked as a DLL */
3597     if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3598     {
3599         /* These aren't valid images to try to execute! */
3600         DPRINT1("Trying to launch a DLL, failing\n");
3601         SetLastError(ERROR_BAD_EXE_FORMAT);
3602         Result = FALSE;
3603         goto Quickie;
3604     }
3605 
3606     /* Don't let callers pass in this flag -- we'll only get it from IFEO */
3607     Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
3608 
3609     /* Clear the IFEO-missing flag, before we know for sure... */
3610     ParameterFlags &= ~2;
3611 
3612     /* If the process is being debugged, only read IFEO if the PEB says so */
3613     if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
3614         (NtCurrentPeb()->ReadImageFileExecOptions))
3615     {
3616         /* Let's do this! Attempt to open IFEO */
3617         IFEOStatus = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle);
3618         if (!NT_SUCCESS(IFEOStatus))
3619         {
3620             /* We failed, set the flag so we store this in the parameters */
3621             if (IFEOStatus == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2;
3622         }
3623         else
3624         {
3625             /* Was this our first time going through this path? */
3626             if (!DebuggerCmdLine)
3627             {
3628                 /* Allocate a buffer for the debugger path */
3629                 DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3630                                                   0,
3631                                                   MAX_PATH * sizeof(WCHAR));
3632                 if (!DebuggerCmdLine)
3633                 {
3634                     /* Close IFEO on failure */
3635                     IFEOStatus = NtClose(KeyHandle);
3636                     ASSERT(NT_SUCCESS(IFEOStatus));
3637 
3638                     /* Fail the call */
3639                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3640                     Result = FALSE;
3641                     goto Quickie;
3642                 }
3643             }
3644 
3645             /* Now query for the debugger */
3646             IFEOStatus = LdrQueryImageFileKeyOption(KeyHandle,
3647                                                  L"Debugger",
3648                                                  REG_SZ,
3649                                                  DebuggerCmdLine,
3650                                                  MAX_PATH * sizeof(WCHAR),
3651                                                  &ResultSize);
3652             if (!(NT_SUCCESS(IFEOStatus)) ||
3653                 (ResultSize < sizeof(WCHAR)) ||
3654                 (DebuggerCmdLine[0] == UNICODE_NULL))
3655             {
3656                 /* If it's not there, or too small, or invalid, ignore it */
3657                 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3658                 DebuggerCmdLine = NULL;
3659             }
3660 
3661             /* Also query if we should map with large pages */
3662             IFEOStatus = LdrQueryImageFileKeyOption(KeyHandle,
3663                                                  L"UseLargePages",
3664                                                  REG_DWORD,
3665                                                  &UseLargePages,
3666                                                  sizeof(UseLargePages),
3667                                                  NULL);
3668             if ((NT_SUCCESS(IFEOStatus)) && (UseLargePages))
3669             {
3670                 /* Do it! This is the only way this flag can be set */
3671                 Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES;
3672             }
3673 
3674             /* We're done with IFEO, can close it now */
3675             IFEOStatus = NtClose(KeyHandle);
3676             ASSERT(NT_SUCCESS(IFEOStatus));
3677         }
3678     }
3679 
3680     /* Make sure the image was compiled for this processor */
3681     if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) ||
3682         (ImageInformation.Machine > SharedUserData->ImageNumberHigh))
3683     {
3684         /* It was not -- raise a hard error */
3685         ErrorResponse = ResponseOk;
3686         ErrorParameters[0] = (ULONG_PTR)&PathName;
3687         NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
3688                          1,
3689                          1,
3690                          ErrorParameters,
3691                          OptionOk,
3692                          &ErrorResponse);
3693         if (Peb->ImageSubsystemMajorVersion <= 3)
3694         {
3695             /* If it's really old, return this error */
3696             SetLastError(ERROR_BAD_EXE_FORMAT);
3697         }
3698         else
3699         {
3700             /* Otherwise, return a more modern error */
3701             SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3702         }
3703 
3704         /* Go to the failure path */
3705         DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
3706         Result = FALSE;
3707         goto Quickie;
3708     }
3709 
3710     /* Check if this isn't a Windows image */
3711     if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
3712         (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
3713     {
3714         /* Get rid of section-related information since we'll retry */
3715         NtClose(SectionHandle);
3716         SectionHandle = NULL;
3717         QuerySection = FALSE;
3718 
3719         /* The only other non-Windows image type we support here is POSIX */
3720         if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI)
3721         {
3722             /* Bail out if it's something else */
3723             SetLastError(ERROR_CHILD_NOT_COMPLETE);
3724             Result = FALSE;
3725             goto Quickie;
3726         }
3727 
3728         /* Now build the command-line to have posix launch this image */
3729         Result = BuildSubSysCommandLine(L"POSIX /P ",
3730                                         lpApplicationName,
3731                                         lpCommandLine,
3732                                         &DebuggerString);
3733         if (!Result)
3734         {
3735             /* Bail out if that failed */
3736             DPRINT1("Subsystem command line failed\n");
3737             goto Quickie;
3738         }
3739 
3740         /* And re-try launching the process, with the new command-line now */
3741         lpCommandLine = DebuggerString.Buffer;
3742         lpApplicationName = NULL;
3743 
3744         /* We've already done all these checks, don't do them again */
3745         SkipSaferAndAppCompat = TRUE;
3746         DPRINT1("Retrying with: %S\n", lpCommandLine);
3747         goto AppNameRetry;
3748     }
3749 
3750     /* Was this image built for a version of Windows whose images we can run? */
3751     Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
3752                                    ImageInformation.SubSystemMinorVersion);
3753     if (!Result)
3754     {
3755         /* It was not, bail out */
3756         DPRINT1("Invalid subsystem version: %hu.%hu\n",
3757                 ImageInformation.SubSystemMajorVersion,
3758                 ImageInformation.SubSystemMinorVersion);
3759         SetLastError(ERROR_BAD_EXE_FORMAT);
3760         goto Quickie;
3761     }
3762 
3763     /* Check if there is a debugger associated with the application */
3764     if (DebuggerCmdLine)
3765     {
3766         /* Get the length of the command line */
3767         n = wcslen(lpCommandLine);
3768         if (!n)
3769         {
3770             /* There's no command line, use the application name instead */
3771             lpCommandLine = (LPWSTR)lpApplicationName;
3772             n = wcslen(lpCommandLine);
3773         }
3774 
3775         /* Protect against overflow */
3776         if (n > UNICODE_STRING_MAX_CHARS)
3777         {
3778             BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3779             Result = FALSE;
3780             goto Quickie;
3781         }
3782 
3783         /* Now add the length of the debugger command-line */
3784         n += wcslen(DebuggerCmdLine);
3785 
3786         /* Again make sure we don't overflow */
3787         if (n > UNICODE_STRING_MAX_CHARS)
3788         {
3789             BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3790             Result = FALSE;
3791             goto Quickie;
3792         }
3793 
3794         /* Account for the quotes and space between the two */
3795         n += sizeof("\" \"") - sizeof(ANSI_NULL);
3796 
3797         /* Convert to bytes, and make sure we don't overflow */
3798         n *= sizeof(WCHAR);
3799         if (n > UNICODE_STRING_MAX_BYTES)
3800         {
3801             BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3802             Result = FALSE;
3803             goto Quickie;
3804         }
3805 
3806         /* Allocate space for the string */
3807         DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n);
3808         if (!DebuggerString.Buffer)
3809         {
3810             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3811             Result = FALSE;
3812             goto Quickie;
3813         }
3814 
3815         /* Set the length */
3816         RtlInitEmptyUnicodeString(&DebuggerString,
3817                                   DebuggerString.Buffer,
3818                                   (USHORT)n);
3819 
3820         /* Now perform the command line creation */
3821         ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString,
3822                                                   DebuggerCmdLine);
3823         ASSERT(NT_SUCCESS(ImageDbgStatus));
3824         ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" ");
3825         ASSERT(NT_SUCCESS(ImageDbgStatus));
3826         ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine);
3827         ASSERT(NT_SUCCESS(ImageDbgStatus));
3828 
3829         /* Make sure it all looks nice */
3830         DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString);
3831 
3832         /* Update the command line and application name */
3833         lpCommandLine = DebuggerString.Buffer;
3834         lpApplicationName = NULL;
3835 
3836         /* Close all temporary state */
3837         NtClose(SectionHandle);
3838         SectionHandle = NULL;
3839         QuerySection = FALSE;
3840 
3841         /* Free all temporary memory */
3842         RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
3843         NameBuffer = NULL;
3844         RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
3845         FreeBuffer = NULL;
3846         RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3847         DebuggerCmdLine = NULL;
3848         DPRINT1("Retrying with: %S\n", lpCommandLine);
3849         goto AppNameRetry;
3850     }
3851 
3852     /* Initialize the process object attributes */
3853     ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3854                                                   lpProcessAttributes,
3855                                                   NULL);
3856     if ((hUserToken) && (lpProcessAttributes))
3857     {
3858         /* Augment them with information from the user */
3859 
3860         LocalProcessAttributes = *lpProcessAttributes;
3861         LocalProcessAttributes.lpSecurityDescriptor = NULL;
3862         ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3863                                                       &LocalProcessAttributes,
3864                                                       NULL);
3865     }
3866 
3867     /* Check if we're going to be debugged */
3868     if (dwCreationFlags & DEBUG_PROCESS)
3869     {
3870         /* Set process flag */
3871         Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
3872     }
3873 
3874     /* Check if we're going to be debugged */
3875     if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3876     {
3877         /* Connect to DbgUi */
3878         Status = DbgUiConnectToDbg();
3879         if (!NT_SUCCESS(Status))
3880         {
3881             DPRINT1("Failed to connect to DbgUI!\n");
3882             BaseSetLastNTError(Status);
3883             Result = FALSE;
3884             goto Quickie;
3885         }
3886 
3887         /* Get the debug object */
3888         DebugHandle = DbgUiGetThreadDebugObject();
3889 
3890         /* Check if only this process will be debugged */
3891         if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
3892         {
3893             /* Set process flag */
3894             Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
3895         }
3896     }
3897 
3898     /* Set inherit flag */
3899     if (bInheritHandles) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
3900 
3901     /* Check if the process should be created with large pages */
3902     HavePrivilege = FALSE;
3903     PrivilegeState = NULL;
3904     if (Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES)
3905     {
3906         /* Acquire the required privilege so that the kernel won't fail the call */
3907         PrivilegeValue = SE_LOCK_MEMORY_PRIVILEGE;
3908         Status = RtlAcquirePrivilege(&PrivilegeValue, 1, 0, &PrivilegeState);
3909         if (NT_SUCCESS(Status))
3910         {
3911             /* Remember to release it later */
3912             HavePrivilege = TRUE;
3913         }
3914     }
3915 
3916     /* Save the current TIB value since kernel overwrites it to store PEB */
3917     TibValue = Teb->NtTib.ArbitraryUserPointer;
3918 
3919     /* Tell the kernel to create the process */
3920     Status = NtCreateProcessEx(&ProcessHandle,
3921                                PROCESS_ALL_ACCESS,
3922                                ObjectAttributes,
3923                                NtCurrentProcess(),
3924                                Flags,
3925                                SectionHandle,
3926                                DebugHandle,
3927                                NULL,
3928                                InJob);
3929 
3930     /* Load the PEB address from the hacky location where the kernel stores it */
3931     RemotePeb = Teb->NtTib.ArbitraryUserPointer;
3932 
3933     /* And restore the old TIB value */
3934     Teb->NtTib.ArbitraryUserPointer = TibValue;
3935 
3936     /* Release the large page privilege if we had acquired it */
3937     if (HavePrivilege) RtlReleasePrivilege(PrivilegeState);
3938 
3939     /* And now check if the kernel failed to create the process */
3940     if (!NT_SUCCESS(Status))
3941     {
3942         /* Go to failure path */
3943         DPRINT1("Failed to create process: %lx\n", Status);
3944         BaseSetLastNTError(Status);
3945         Result = FALSE;
3946         goto Quickie;
3947     }
3948 
3949     /* Check if there is a priority class to set */
3950     if (PriorityClass.PriorityClass)
3951     {
3952         /* Reset current privilege state */
3953         RealTimePrivilegeState = NULL;
3954 
3955         /* Is realtime priority being requested? */
3956         if (PriorityClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME)
3957         {
3958             /* Check if the caller has real-time access, and enable it if so */
3959             RealTimePrivilegeState = BasepIsRealtimeAllowed(TRUE);
3960         }
3961 
3962         /* Set the new priority class and release the privilege */
3963         Status = NtSetInformationProcess(ProcessHandle,
3964                                          ProcessPriorityClass,
3965                                          &PriorityClass,
3966                                          sizeof(PROCESS_PRIORITY_CLASS));
3967         if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState);
3968 
3969         /* Check if we failed to set the priority class */
3970         if (!NT_SUCCESS(Status))
3971         {
3972             /* Bail out on failure */
3973             DPRINT1("Failed to set priority class: %lx\n", Status);
3974             BaseSetLastNTError(Status);
3975             Result = FALSE;
3976             goto Quickie;
3977         }
3978     }
3979 
3980     /* Check if the caller wants the default error mode */
3981     if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE)
3982     {
3983         /* Set Error Mode to only fail on critical errors */
3984         HardErrorMode = SEM_FAILCRITICALERRORS;
3985         NtSetInformationProcess(ProcessHandle,
3986                                 ProcessDefaultHardErrorMode,
3987                                 &HardErrorMode,
3988                                 sizeof(ULONG));
3989     }
3990 
3991     /* Check if this was a VDM binary */
3992     if (VdmBinaryType)
3993     {
3994         /* Update VDM by telling it the process has now been created */
3995         VdmWaitObject = ProcessHandle;
3996         Result = BaseUpdateVDMEntry(VdmEntryUpdateProcess,
3997                                     &VdmWaitObject,
3998                                     VdmTask,
3999                                     VdmBinaryType);
4000 
4001         if (!Result)
4002         {
4003             /* Bail out on failure */
4004             DPRINT1("Failed to update VDM with wait object\n");
4005             VdmWaitObject = NULL;
4006             goto Quickie;
4007         }
4008 
4009         /* At this point, a failure means VDM has to undo all the state */
4010         VdmUndoLevel |= VDM_UNDO_FULL;
4011     }
4012 
4013     /* Check if VDM needed reserved low-memory */
4014     if (VdmReserve)
4015     {
4016         /* Reserve the requested allocation */
4017         RegionSize = VdmReserve;
4018         Status = NtAllocateVirtualMemory(ProcessHandle,
4019                                          &BaseAddress,
4020                                          0,
4021                                          &RegionSize,
4022                                          MEM_RESERVE,
4023                                          PAGE_EXECUTE_READWRITE);
4024         if (!NT_SUCCESS(Status))
4025         {
4026             /* Bail out on failure */
4027             DPRINT1("Failed to reserve memory for VDM: %lx\n", Status);
4028             BaseSetLastNTError(Status);
4029             Result = FALSE;
4030             goto Quickie;
4031         }
4032 
4033         VdmReserve = (ULONG)RegionSize;
4034     }
4035 
4036     /* Check if we've already queried information on the section */
4037     if (!QuerySection)
4038     {
4039         /* We haven't, so get some information about the executable */
4040         Status = NtQuerySection(SectionHandle,
4041                                 SectionImageInformation,
4042                                 &ImageInformation,
4043                                 sizeof(ImageInformation),
4044                                 NULL);
4045         if (!NT_SUCCESS(Status))
4046         {
4047             /* Bail out on failure */
4048             DPRINT1("Failed to query section: %lx\n", Status);
4049             BaseSetLastNTError(Status);
4050             Result = FALSE;
4051             goto Quickie;
4052         }
4053 
4054         /* If we encounter a restart, don't re-query this information again */
4055         QuerySection = TRUE;
4056     }
4057 
4058     /* Do we need to apply SxS to this image? */
4059     if (!(ImageInformation.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION))
4060     {
4061         /* Too bad, we don't support this yet */
4062         DPRINT1("Image should receive SxS Fusion Isolation\n");
4063     }
4064 
4065     /* There's some SxS flag that we need to set if fusion flags have 1 set */
4066     if (FusionFlags & 1) CreateProcessMsg->Sxs.Flags |= 0x10;
4067 
4068     /* Check if we have a current directory */
4069     if (lpCurrentDirectory)
4070     {
4071         /* Allocate a buffer so we can keep a Unicode copy */
4072         DPRINT("Current directory: %S\n", lpCurrentDirectory);
4073         CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(),
4074                                            0,
4075                                            (MAX_PATH * sizeof(WCHAR)) +
4076                                            sizeof(UNICODE_NULL));
4077         if (!CurrentDirectory)
4078         {
4079             /* Bail out if this failed */
4080             BaseSetLastNTError(STATUS_NO_MEMORY);
4081             Result = FALSE;
4082             goto Quickie;
4083         }
4084 
4085         /* Get the length in Unicode */
4086         Length = GetFullPathNameW(lpCurrentDirectory,
4087                                   MAX_PATH,
4088                                   CurrentDirectory,
4089                                   &FilePart);
4090         if (Length > MAX_PATH)
4091         {
4092             /* The directory is too long, so bail out */
4093             SetLastError(ERROR_DIRECTORY);
4094             Result = FALSE;
4095             goto Quickie;
4096         }
4097 
4098         /* Make sure the directory is actually valid */
4099         FileAttribs = GetFileAttributesW(CurrentDirectory);
4100         if ((FileAttribs == INVALID_FILE_ATTRIBUTES) ||
4101            !(FileAttribs & FILE_ATTRIBUTE_DIRECTORY))
4102         {
4103             /* It isn't, so bail out */
4104             DPRINT1("Current directory is invalid\n");
4105             SetLastError(ERROR_DIRECTORY);
4106             Result = FALSE;
4107             goto Quickie;
4108         }
4109     }
4110 
4111     /* Insert quotes if needed */
4112     if ((QuotesNeeded) || (CmdLineIsAppName))
4113     {
4114         /* Allocate our buffer, plus enough space for quotes and a NULL */
4115         QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
4116                                         0,
4117                                         (wcslen(lpCommandLine) * sizeof(WCHAR)) +
4118                                         (2 * sizeof(L'\"') + sizeof(UNICODE_NULL)));
4119         if (QuotedCmdLine)
4120         {
4121             /* Copy the first quote */
4122             wcscpy(QuotedCmdLine, L"\"");
4123 
4124             /* Save the current null-character */
4125             if (QuotesNeeded)
4126             {
4127                 SaveChar = *NullBuffer;
4128                 *NullBuffer = UNICODE_NULL;
4129             }
4130 
4131             /* Copy the command line and the final quote */
4132             wcscat(QuotedCmdLine, lpCommandLine);
4133             wcscat(QuotedCmdLine, L"\"");
4134 
4135             /* Copy the null-char back */
4136             if (QuotesNeeded)
4137             {
4138                 *NullBuffer = SaveChar;
4139                 wcscat(QuotedCmdLine, NullBuffer);
4140             }
4141         }
4142         else
4143         {
4144             /* We can't put quotes around the thing, so try it anyway */
4145             if (QuotesNeeded) QuotesNeeded = FALSE;
4146             if (CmdLineIsAppName) CmdLineIsAppName = FALSE;
4147         }
4148     }
4149 
4150     /* Use isolation if needed */
4151     if (CreateProcessMsg->Sxs.Flags & 1) ParameterFlags |= 1;
4152 
4153     /* Set the new command-line if needed */
4154     if ((QuotesNeeded) || (CmdLineIsAppName)) lpCommandLine = QuotedCmdLine;
4155 
4156     /* Call the helper function in charge of RTL_USER_PROCESS_PARAMETERS */
4157     Result = BasePushProcessParameters(ParameterFlags,
4158                                        ProcessHandle,
4159                                        RemotePeb,
4160                                        lpApplicationName,
4161                                        CurrentDirectory,
4162                                        lpCommandLine,
4163                                        lpEnvironment,
4164                                        &StartupInfo,
4165                                        dwCreationFlags | NoWindow,
4166                                        bInheritHandles,
4167                                        IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0,
4168                                        AppCompatData,
4169                                        AppCompatDataSize);
4170     if (!Result)
4171     {
4172         /* The remote process would have an undefined state, so fail the call */
4173         DPRINT1("BasePushProcessParameters failed\n");
4174         goto Quickie;
4175     }
4176 
4177     /* Free the VDM command line string as it's no longer needed */
4178     RtlFreeUnicodeString(&VdmString);
4179     VdmString.Buffer = NULL;
4180 
4181     /* Non-VDM console applications usually inherit handles unless specified */
4182     if (!(VdmBinaryType) &&
4183         !(bInheritHandles) &&
4184         !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
4185         !(dwCreationFlags & (CREATE_NO_WINDOW |
4186                              CREATE_NEW_CONSOLE |
4187                              DETACHED_PROCESS)) &&
4188         (ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI))
4189     {
4190         /* Get the remote parameters */
4191         Status = NtReadVirtualMemory(ProcessHandle,
4192                                      &RemotePeb->ProcessParameters,
4193                                      &ProcessParameters,
4194                                      sizeof(PRTL_USER_PROCESS_PARAMETERS),
4195                                      NULL);
4196         if (NT_SUCCESS(Status))
4197         {
4198             /* Duplicate standard input unless it's a console handle */
4199             if (!IsConsoleHandle(Peb->ProcessParameters->StandardInput))
4200             {
4201                 StuffStdHandle(ProcessHandle,
4202                                Peb->ProcessParameters->StandardInput,
4203                                &ProcessParameters->StandardInput);
4204             }
4205 
4206             /* Duplicate standard output unless it's a console handle */
4207             if (!IsConsoleHandle(Peb->ProcessParameters->StandardOutput))
4208             {
4209                 StuffStdHandle(ProcessHandle,
4210                                Peb->ProcessParameters->StandardOutput,
4211                                &ProcessParameters->StandardOutput);
4212             }
4213 
4214             /* Duplicate standard error unless it's a console handle */
4215             if (!IsConsoleHandle(Peb->ProcessParameters->StandardError))
4216             {
4217                 StuffStdHandle(ProcessHandle,
4218                                Peb->ProcessParameters->StandardError,
4219                                &ProcessParameters->StandardError);
4220             }
4221         }
4222     }
4223 
4224     /* Create the Thread's Stack */
4225     StackSize = max(256 * 1024, ImageInformation.MaximumStackSize);
4226     Status = BaseCreateStack(ProcessHandle,
4227                              ImageInformation.CommittedStackSize,
4228                              StackSize,
4229                              &InitialTeb);
4230     if (!NT_SUCCESS(Status))
4231     {
4232         DPRINT1("Creating the thread stack failed: %lx\n", Status);
4233         BaseSetLastNTError(Status);
4234         Result = FALSE;
4235         goto Quickie;
4236     }
4237 
4238     /* Create the Thread's Context */
4239     BaseInitializeContext(&Context,
4240                           Peb,
4241                           ImageInformation.TransferAddress,
4242                           InitialTeb.StackBase,
4243                           0);
4244 
4245     /* Convert the thread attributes */
4246     ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4247                                                   lpThreadAttributes,
4248                                                   NULL);
4249     if ((hUserToken) && (lpThreadAttributes))
4250     {
4251         /* If the caller specified a user token, zero the security descriptor */
4252         LocalThreadAttributes = *lpThreadAttributes;
4253         LocalThreadAttributes.lpSecurityDescriptor = NULL;
4254         ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4255                                                       &LocalThreadAttributes,
4256                                                       NULL);
4257     }
4258 
4259     /* Create the Kernel Thread Object */
4260     Status = NtCreateThread(&ThreadHandle,
4261                             THREAD_ALL_ACCESS,
4262                             ObjectAttributes,
4263                             ProcessHandle,
4264                             &ClientId,
4265                             &Context,
4266                             &InitialTeb,
4267                             TRUE);
4268     if (!NT_SUCCESS(Status))
4269     {
4270         /* A process is not allowed to exist without a main thread, so fail */
4271         DPRINT1("Creating the main thread failed: %lx\n", Status);
4272         BaseSetLastNTError(Status);
4273         Result = FALSE;
4274         goto Quickie;
4275     }
4276 
4277     /* Begin filling out the CSRSS message, first with our IDs and handles */
4278     CreateProcessMsg->ProcessHandle = ProcessHandle;
4279     CreateProcessMsg->ThreadHandle = ThreadHandle;
4280     CreateProcessMsg->ClientId = ClientId;
4281 
4282     /* Write the remote PEB address and clear it locally, we no longer use it */
4283     CreateProcessMsg->PebAddressNative = RemotePeb;
4284 #ifdef _WIN64
4285     DPRINT1("TODO: WOW64 is not supported yet\n");
4286     CreateProcessMsg->PebAddressWow64 = 0;
4287 #else
4288     CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb;
4289 #endif
4290     RemotePeb = NULL;
4291 
4292     /* Now check what kind of architecture this image was made for */
4293     switch (ImageInformation.Machine)
4294     {
4295         /* IA32, IA64 and AMD64 are supported in Server 2003 */
4296         case IMAGE_FILE_MACHINE_I386:
4297             CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
4298             break;
4299         case IMAGE_FILE_MACHINE_IA64:
4300             CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
4301             break;
4302         case IMAGE_FILE_MACHINE_AMD64:
4303             CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
4304             break;
4305 
4306         /* Anything else results in image unknown -- but no failure */
4307         default:
4308             DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n",
4309                      ImageInformation.Machine);
4310             CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
4311             break;
4312     }
4313 
4314     /* Write the input creation flags except any debugger-related flags */
4315     CreateProcessMsg->CreationFlags = dwCreationFlags &
4316                                       ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
4317 
4318     /* CSRSS needs to know if this is a GUI app or not */
4319     if ((ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
4320         (IsWowApp))
4321     {
4322         /*
4323          * For GUI apps we turn on the 2nd bit. This allow CSRSS server dlls
4324          * (basesrv in particular) to know whether or not this is a GUI or a
4325          * TUI application.
4326          */
4327         AddToHandle(CreateProcessMsg->ProcessHandle, 2);
4328 
4329         /* Also check if the parent is also a GUI process */
4330         NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL));
4331         if ((NtHeaders) &&
4332             (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI))
4333         {
4334             /* Let it know that it should display the hourglass mouse cursor */
4335             AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4336         }
4337     }
4338 
4339     /* For all apps, if this flag is on, the hourglass mouse cursor is shown */
4340     if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
4341     {
4342         AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4343     }
4344 
4345     /* Likewise, the opposite holds as well */
4346     if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
4347     {
4348         RemoveFromHandle(CreateProcessMsg->ProcessHandle, 1);
4349     }
4350 
4351     /* Also store which kind of VDM app (if any) this is */
4352     CreateProcessMsg->VdmBinaryType = VdmBinaryType;
4353 
4354     /* And if it really is a VDM app... */
4355     if (VdmBinaryType)
4356     {
4357         /* Store the task ID and VDM console handle */
4358         CreateProcessMsg->hVDM = VdmTask ? 0 : Peb->ProcessParameters->ConsoleHandle;
4359         CreateProcessMsg->VdmTask = VdmTask;
4360     }
4361     else if (VdmReserve)
4362     {
4363         /* Extended VDM, set a flag */
4364         CreateProcessMsg->VdmBinaryType |= BINARY_TYPE_WOW_EX;
4365     }
4366 
4367     /* Check if there's side-by-side assembly data associated with the process */
4368     if (CreateProcessMsg->Sxs.Flags)
4369     {
4370         /* This should not happen in ReactOS yet */
4371         DPRINT1("This is an SxS Message -- should not happen yet\n");
4372         BaseSetLastNTError(STATUS_NOT_IMPLEMENTED);
4373         NtTerminateProcess(ProcessHandle, STATUS_NOT_IMPLEMENTED);
4374         Result = FALSE;
4375         goto Quickie;
4376     }
4377 
4378     /* We are finally ready to call CSRSS to tell it about our new process! */
4379     CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg[0],
4380                         CaptureBuffer,
4381                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
4382                                               BasepCreateProcess),
4383                         sizeof(*CreateProcessMsg));
4384 
4385     /* CSRSS has returned, free the capture buffer now if we had one */
4386     if (CaptureBuffer)
4387     {
4388         CsrFreeCaptureBuffer(CaptureBuffer);
4389         CaptureBuffer = NULL;
4390     }
4391 
4392     /* Check if CSRSS failed to accept ownership of the new Windows process */
4393     if (!NT_SUCCESS(CsrMsg[0].Status))
4394     {
4395         /* Terminate the process and enter failure path with the CSRSS status */
4396         DPRINT1("Failed to tell csrss about new process\n");
4397         BaseSetLastNTError(CsrMsg[0].Status);
4398         NtTerminateProcess(ProcessHandle, CsrMsg[0].Status);
4399         Result = FALSE;
4400         goto Quickie;
4401     }
4402 
4403     /* Check if we have a token due to Authz/Safer, not passed by the user */
4404     if ((TokenHandle) && !(hUserToken))
4405     {
4406         /* Replace the process and/or thread token with the one from Safer */
4407         Status = BasepReplaceProcessThreadTokens(TokenHandle,
4408                                                  ProcessHandle,
4409                                                  ThreadHandle);
4410         if (!NT_SUCCESS(Status))
4411         {
4412             /* If this failed, kill the process and enter the failure path */
4413             DPRINT1("Failed to update process token: %lx\n", Status);
4414             NtTerminateProcess(ProcessHandle, Status);
4415             BaseSetLastNTError(Status);
4416             Result = FALSE;
4417             goto Quickie;
4418         }
4419     }
4420 
4421     /* Check if a job was associated with this process */
4422     if (JobHandle)
4423     {
4424         /* Bind the process and job together now */
4425         Status = NtAssignProcessToJobObject(JobHandle, ProcessHandle);
4426         if (!NT_SUCCESS(Status))
4427         {
4428             /* Kill the process and enter the failure path if binding failed */
4429             DPRINT1("Failed to assign process to job: %lx\n", Status);
4430             NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
4431             BaseSetLastNTError(Status);
4432             Result = FALSE;
4433             goto Quickie;
4434         }
4435     }
4436 
4437     /* Finally, resume the thread to actually get the process started */
4438     if (!(dwCreationFlags & CREATE_SUSPENDED))
4439     {
4440         NtResumeThread(ThreadHandle, &ResumeCount);
4441     }
4442 
4443 VdmShortCircuit:
4444     /* We made it this far, meaning we have a fully created process and thread */
4445     Result = TRUE;
4446 
4447     /* Anyone doing a VDM undo should now undo everything, since we are done */
4448     if (VdmUndoLevel) VdmUndoLevel |= VDM_UNDO_COMPLETED;
4449 
4450     /* Having a VDM wait object implies this must be a VDM process */
4451     if (VdmWaitObject)
4452     {
4453         /* Check if it's a 16-bit separate WOW process */
4454         if (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW)
4455         {
4456             /* OR-in the special flag to indicate this, and return to caller */
4457             AddToHandle(VdmWaitObject, 2);
4458             lpProcessInformation->hProcess = VdmWaitObject;
4459 
4460             /* Check if this was a re-used VDM */
4461             if (VdmUndoLevel & VDM_UNDO_REUSE)
4462             {
4463                 /* No Client ID should be returned in this case */
4464                 ClientId.UniqueProcess = 0;
4465                 ClientId.UniqueThread = 0;
4466             }
4467         }
4468         else
4469         {
4470             /* OR-in the special flag to indicate this is not a separate VDM */
4471             AddToHandle(VdmWaitObject, 1);
4472 
4473             /* Return handle to the caller */
4474             lpProcessInformation->hProcess = VdmWaitObject;
4475         }
4476 
4477         /* Close the original process handle, since it's not needed for VDM */
4478         if (ProcessHandle) NtClose(ProcessHandle);
4479     }
4480     else
4481     {
4482         /* This is a regular process, so return the real process handle */
4483         lpProcessInformation->hProcess = ProcessHandle;
4484     }
4485 
4486     /* Return the rest of the process information based on what we have so far */
4487     lpProcessInformation->hThread = ThreadHandle;
4488     lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
4489     lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
4490 
4491     /* NULL these out here so we know to treat this as a success scenario */
4492     ProcessHandle = NULL;
4493     ThreadHandle = NULL;
4494 
4495 Quickie:
4496     /* Free the debugger command line if one was allocated */
4497     if (DebuggerCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
4498 
4499     /* Check if an SxS full path as queried */
4500     if (PathBuffer)
4501     {
4502         /* Reinitialize the executable path */
4503         RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0);
4504         SxsWin32ExePath.Length = 0;
4505 
4506         /* Free the path buffer */
4507         RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
4508     }
4509 
4510 #if _SXS_SUPPORT_ENABLED_
4511     /* Check if this was a non-VDM process */
4512     if (!VdmBinaryType)
4513     {
4514         /* Then it must've had SxS data, so close the handles used for it */
4515         BasepSxsCloseHandles(&Handles);
4516         BasepSxsCloseHandles(&FileHandles);
4517 
4518         /* Check if we built SxS byte buffers for this create process request */
4519         if (SxsConglomeratedBuffer)
4520         {
4521             /* Loop all of them */
4522             for (i = 0; i < 5; i++)
4523             {
4524                 /* Check if this one was allocated */
4525                 ThisBuffer = SxsStaticBuffers[i];
4526                 if (ThisBuffer)
4527                 {
4528                     /* Get the underlying RTL_BUFFER structure */
4529                     ByteBuffer = &ThisBuffer->ByteBuffer;
4530                     if ((ThisBuffer != (PVOID)-8) && (ByteBuffer->Buffer))
4531                     {
4532                         /* Check if it was dynamic */
4533                         if (ByteBuffer->Buffer != ByteBuffer->StaticBuffer)
4534                         {
4535                             /* Free it from the heap */
4536                             FreeString.Buffer = (PWCHAR)ByteBuffer->Buffer;
4537                             RtlFreeUnicodeString(&FreeString);
4538                         }
4539 
4540                         /* Reset the buffer to its static data */
4541                         ByteBuffer->Buffer = ByteBuffer->StaticBuffer;
4542                         ByteBuffer->Size = ByteBuffer->StaticSize;
4543                     }
4544 
4545                     /* Reset the string to the static buffer */
4546                     RtlInitEmptyUnicodeString(&ThisBuffer->String,
4547                                               (PWCHAR)ByteBuffer->StaticBuffer,
4548                                               ByteBuffer->StaticSize);
4549                     if (ThisBuffer->String.Buffer)
4550                     {
4551                         /* Also NULL-terminate it */
4552                         *ThisBuffer->String.Buffer = UNICODE_NULL;
4553                     }
4554                 }
4555             }
4556         }
4557     }
4558 #endif
4559     /* Check if an environment was passed in */
4560     if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
4561     {
4562         /* Destroy it */
4563         RtlDestroyEnvironment(lpEnvironment);
4564 
4565         /* If this was the VDM environment too, clear that as well */
4566         if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL;
4567         lpEnvironment = NULL;
4568     }
4569 
4570     /* Unconditionally free all the name parsing buffers we always allocate */
4571     RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine);
4572     RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
4573     RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory);
4574     RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
4575 
4576     /* Close open file/section handles */
4577     if (FileHandle) NtClose(FileHandle);
4578     if (SectionHandle) NtClose(SectionHandle);
4579 
4580     /* If we have a thread handle, this was a failure path */
4581     if (ThreadHandle)
4582     {
4583         /* So kill the process and close the thread handle */
4584         NtTerminateProcess(ProcessHandle, 0);
4585         NtClose(ThreadHandle);
4586     }
4587 
4588     /* If we have a process handle, this was a failure path, so close it */
4589     if (ProcessHandle) NtClose(ProcessHandle);
4590 
4591     /* Thread/process handles, if any, are now processed. Now close this one. */
4592     if (JobHandle) NtClose(JobHandle);
4593 
4594     /* Check if we had created a token */
4595     if (TokenHandle)
4596     {
4597         /* And if the user asked for one */
4598         if (hUserToken)
4599         {
4600             /* Then return it */
4601             *hNewToken = TokenHandle;
4602         }
4603         else
4604         {
4605             /* User didn't want it, so we used it temporarily -- close it */
4606             NtClose(TokenHandle);
4607         }
4608     }
4609 
4610     /* Free any temporary app compatibility data, it's no longer needed */
4611     BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
4612 
4613     /* Free a few strings. The API takes care of these possibly being NULL */
4614     RtlFreeUnicodeString(&VdmString);
4615     RtlFreeUnicodeString(&DebuggerString);
4616 
4617     /* Check if we had built any sort of VDM environment */
4618     if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer))
4619     {
4620         /* Free it */
4621         BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv);
4622     }
4623 
4624     /* Check if this was any kind of VDM application that we ended up creating */
4625     if ((VdmUndoLevel) && (!(VdmUndoLevel & VDM_UNDO_COMPLETED)))
4626     {
4627         /* Send an undo */
4628         BaseUpdateVDMEntry(VdmEntryUndo,
4629                            (PHANDLE)&VdmTask,
4630                            VdmUndoLevel,
4631                            VdmBinaryType);
4632 
4633         /* And close whatever VDM handle we were using for notifications */
4634         if (VdmWaitObject) NtClose(VdmWaitObject);
4635     }
4636 
4637     /* Check if we ended up here with an allocated search path, and free it */
4638     if (SearchPath) RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
4639 
4640     /* Finally, return the API's result */
4641     return Result;
4642 }
4643 
4644 /*
4645  * @implemented
4646  */
4647 BOOL
4648 WINAPI
4649 DECLSPEC_HOTPATCH
4650 CreateProcessW(LPCWSTR lpApplicationName,
4651                LPWSTR lpCommandLine,
4652                LPSECURITY_ATTRIBUTES lpProcessAttributes,
4653                LPSECURITY_ATTRIBUTES lpThreadAttributes,
4654                BOOL bInheritHandles,
4655                DWORD dwCreationFlags,
4656                LPVOID lpEnvironment,
4657                LPCWSTR lpCurrentDirectory,
4658                LPSTARTUPINFOW lpStartupInfo,
4659                LPPROCESS_INFORMATION lpProcessInformation)
4660 {
4661     /* Call the internal (but exported) version */
4662     return CreateProcessInternalW(NULL,
4663                                   lpApplicationName,
4664                                   lpCommandLine,
4665                                   lpProcessAttributes,
4666                                   lpThreadAttributes,
4667                                   bInheritHandles,
4668                                   dwCreationFlags,
4669                                   lpEnvironment,
4670                                   lpCurrentDirectory,
4671                                   lpStartupInfo,
4672                                   lpProcessInformation,
4673                                   NULL);
4674 }
4675 
4676 /*
4677  * @implemented
4678  */
4679 BOOL
4680 WINAPI
4681 CreateProcessInternalA(HANDLE hToken,
4682                        LPCSTR lpApplicationName,
4683                        LPSTR lpCommandLine,
4684                        LPSECURITY_ATTRIBUTES lpProcessAttributes,
4685                        LPSECURITY_ATTRIBUTES lpThreadAttributes,
4686                        BOOL bInheritHandles,
4687                        DWORD dwCreationFlags,
4688                        LPVOID lpEnvironment,
4689                        LPCSTR lpCurrentDirectory,
4690                        LPSTARTUPINFOA lpStartupInfo,
4691                        LPPROCESS_INFORMATION lpProcessInformation,
4692                        PHANDLE hNewToken)
4693 {
4694     UNICODE_STRING CommandLine;
4695     UNICODE_STRING ApplicationName;
4696     UNICODE_STRING CurrentDirectory;
4697     BOOL bRetVal;
4698     STARTUPINFOW StartupInfo;
4699 
4700     DPRINT("dwCreationFlags %x, lpEnvironment %p, lpCurrentDirectory %p, "
4701             "lpStartupInfo %p, lpProcessInformation %p\n",
4702             dwCreationFlags, lpEnvironment, lpCurrentDirectory,
4703             lpStartupInfo, lpProcessInformation);
4704 
4705     /* Copy Startup Info */
4706     RtlMoveMemory(&StartupInfo, lpStartupInfo, sizeof(*lpStartupInfo));
4707 
4708     /* Initialize all strings to nothing */
4709     CommandLine.Buffer = NULL;
4710     ApplicationName.Buffer = NULL;
4711     CurrentDirectory.Buffer = NULL;
4712     StartupInfo.lpDesktop = NULL;
4713     StartupInfo.lpReserved = NULL;
4714     StartupInfo.lpTitle = NULL;
4715 
4716     /* Convert the Command line */
4717     if (lpCommandLine)
4718     {
4719         Basep8BitStringToDynamicUnicodeString(&CommandLine,
4720                                               lpCommandLine);
4721     }
4722 
4723     /* Convert the Name and Directory */
4724     if (lpApplicationName)
4725     {
4726         Basep8BitStringToDynamicUnicodeString(&ApplicationName,
4727                                               lpApplicationName);
4728     }
4729     if (lpCurrentDirectory)
4730     {
4731         Basep8BitStringToDynamicUnicodeString(&CurrentDirectory,
4732                                               lpCurrentDirectory);
4733     }
4734 
4735     /* Now convert Startup Strings */
4736     if (lpStartupInfo->lpReserved)
4737     {
4738         BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpReserved,
4739                                            &StartupInfo.lpReserved);
4740     }
4741     if (lpStartupInfo->lpDesktop)
4742     {
4743         BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpDesktop,
4744                                            &StartupInfo.lpDesktop);
4745     }
4746     if (lpStartupInfo->lpTitle)
4747     {
4748         BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpTitle,
4749                                            &StartupInfo.lpTitle);
4750     }
4751 
4752     /* Call the Unicode function */
4753     bRetVal = CreateProcessInternalW(hToken,
4754                                      ApplicationName.Buffer,
4755                                      CommandLine.Buffer,
4756                                      lpProcessAttributes,
4757                                      lpThreadAttributes,
4758                                      bInheritHandles,
4759                                      dwCreationFlags,
4760                                      lpEnvironment,
4761                                      CurrentDirectory.Buffer,
4762                                      &StartupInfo,
4763                                      lpProcessInformation,
4764                                      hNewToken);
4765 
4766     /* Clean up */
4767     RtlFreeUnicodeString(&ApplicationName);
4768     RtlFreeUnicodeString(&CommandLine);
4769     RtlFreeUnicodeString(&CurrentDirectory);
4770     RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpDesktop);
4771     RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpReserved);
4772     RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpTitle);
4773 
4774     /* Return what Unicode did */
4775     return bRetVal;
4776 }
4777 
4778 /*
4779  * FUNCTION: The CreateProcess function creates a new process and its
4780  * primary thread. The new process executes the specified executable file
4781  * ARGUMENTS:
4782  *
4783  *     lpApplicationName = Pointer to name of executable module
4784  *     lpCommandLine = Pointer to command line string
4785  *     lpProcessAttributes = Process security attributes
4786  *     lpThreadAttributes = Thread security attributes
4787  *     bInheritHandles = Handle inheritance flag
4788  *     dwCreationFlags = Creation flags
4789  *     lpEnvironment = Pointer to new environment block
4790  *     lpCurrentDirectory = Pointer to current directory name
4791  *     lpStartupInfo = Pointer to startup info
4792  *     lpProcessInformation = Pointer to process information
4793  *
4794  * @implemented
4795  */
4796 BOOL
4797 WINAPI
4798 DECLSPEC_HOTPATCH
4799 CreateProcessA(LPCSTR lpApplicationName,
4800                LPSTR lpCommandLine,
4801                LPSECURITY_ATTRIBUTES lpProcessAttributes,
4802                LPSECURITY_ATTRIBUTES lpThreadAttributes,
4803                BOOL bInheritHandles,
4804                DWORD dwCreationFlags,
4805                LPVOID lpEnvironment,
4806                LPCSTR lpCurrentDirectory,
4807                LPSTARTUPINFOA lpStartupInfo,
4808                LPPROCESS_INFORMATION lpProcessInformation)
4809 {
4810     /* Call the internal (but exported) version */
4811     return CreateProcessInternalA(NULL,
4812                                   lpApplicationName,
4813                                   lpCommandLine,
4814                                   lpProcessAttributes,
4815                                   lpThreadAttributes,
4816                                   bInheritHandles,
4817                                   dwCreationFlags,
4818                                   lpEnvironment,
4819                                   lpCurrentDirectory,
4820                                   lpStartupInfo,
4821                                   lpProcessInformation,
4822                                   NULL);
4823 }
4824 
4825 /*
4826  * @implemented
4827  */
4828 UINT
4829 WINAPI
4830 DECLSPEC_HOTPATCH
4831 WinExec(LPCSTR lpCmdLine,
4832         UINT uCmdShow)
4833 {
4834     STARTUPINFOA StartupInfo;
4835     PROCESS_INFORMATION  ProcessInformation;
4836     DWORD dosErr;
4837 
4838     RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
4839     StartupInfo.cb = sizeof(STARTUPINFOA);
4840     StartupInfo.wShowWindow = (WORD)uCmdShow;
4841     StartupInfo.dwFlags = 0;
4842 
4843     if (!CreateProcessA(NULL,
4844                         (PVOID)lpCmdLine,
4845                         NULL,
4846                         NULL,
4847                         FALSE,
4848                         0,
4849                         NULL,
4850                         NULL,
4851                         &StartupInfo,
4852                         &ProcessInformation))
4853     {
4854         dosErr = GetLastError();
4855         return dosErr < 32 ? dosErr : ERROR_BAD_FORMAT;
4856     }
4857 
4858     if (NULL != UserWaitForInputIdleRoutine)
4859     {
4860         UserWaitForInputIdleRoutine(ProcessInformation.hProcess,
4861                                            10000);
4862     }
4863 
4864     NtClose(ProcessInformation.hProcess);
4865     NtClose(ProcessInformation.hThread);
4866 
4867     return 33; /* Something bigger than 31 means success. */
4868 }
4869 
4870 /* EOF */
4871