1 /* $Id: kWorker.c 3089 2017-10-04 13:10:41Z bird $ */
2 /** @file
3  * kWorker - experimental process reuse worker for Windows.
4  *
5  * Note! This module must be linked statically in order to avoid
6  *       accidentally intercepting our own CRT calls.
7  */
8 
9 /*
10  * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
11  *
12  * This file is part of kBuild.
13  *
14  * kBuild is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * kBuild is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with kBuild.  If not, see <http://www.gnu.org/licenses/>
26  *
27  */
28 
29 
30 /*********************************************************************************************************************************
31 *   Header Files                                                                                                                 *
32 *********************************************************************************************************************************/
33 //#undef NDEBUG
34 //#define K_STRICT 1
35 //#define KW_LOG_ENABLED
36 
37 #define PSAPI_VERSION 1
38 #include <k/kHlp.h>
39 #include <k/kLdr.h>
40 
41 #include <stdio.h>
42 #include <intrin.h>
43 #include <setjmp.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <process.h>
47 
48 #include "nt/ntstat.h"
49 #include "kbuild_version.h"
50 
51 #include "nt/ntstuff.h"
52 #include <psapi.h>
53 
54 #include "nt/kFsCache.h"
55 #include "nt_fullpath.h"
56 #include "quote_argv.h"
57 #include "md5.h"
58 
59 #include "../kmk/kmkbuiltin.h"
60 
61 
62 /*********************************************************************************************************************************
63 *   Defined Constants And Macros                                                                                                 *
64 *********************************************************************************************************************************/
65 /** @def WITH_TEMP_MEMORY_FILES
66  * Enables temporary memory files for cl.exe.  */
67 #define WITH_TEMP_MEMORY_FILES
68 
69 /** @def WITH_HASH_MD5_CACHE
70  * Enables caching of MD5 sums for cl.exe.
71  * This prevents wasting time on rehashing common headers each time
72  * they are included. */
73 #define WITH_HASH_MD5_CACHE
74 
75 /** @def WITH_CRYPT_CTX_REUSE
76  * Enables reusing crypt contexts.  The Visual C++ compiler always creates a
77  * context which is only used for MD5 and maybe some random bytes (VS 2010).
78  * So, only create it once and add a reference to it instead of creating new
79  * ones.  Saves registry access among other things. */
80 #define WITH_CRYPT_CTX_REUSE
81 
82 /** @def WITH_CONSOLE_OUTPUT_BUFFERING
83  * Enables buffering of all console output as well as removal of annoying
84  * source file echo by cl.exe. */
85 #define WITH_CONSOLE_OUTPUT_BUFFERING
86 
87 /** @def WITH_STD_OUT_ERR_BUFFERING
88  * Enables buffering of standard output and standard error buffer as well as
89  * removal of annoying source file echo by cl.exe. */
90 #define WITH_STD_OUT_ERR_BUFFERING
91 
92 /** @def WITH_LOG_FILE
93  * Log to file instead of stderr. */
94 #define WITH_LOG_FILE
95 
96 /** @def WITH_HISTORY
97  * Keep history of the last jobs.  For debugging.  */
98 #define WITH_HISTORY
99 
100 /** @def WITH_FIXED_VIRTUAL_ALLOCS
101  * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
102  * there is only one, but an important one, from cl.exe).
103  */
104 #if K_ARCH == K_ARCH_X86_32
105 # define WITH_FIXED_VIRTUAL_ALLOCS
106 #endif
107 
108 /** @def WITH_PCH_CACHING
109  * Enables read caching of precompiled header files. */
110 #if K_ARCH_BITS >= 64
111 # define WITH_PCH_CACHING
112 #endif
113 
114 
115 #ifndef NDEBUG
116 # define KW_LOG_ENABLED
117 #endif
118 
119 /** @def KW_LOG
120  * Generic logging.
121  * @param a     Argument list for kwDbgPrintf  */
122 #ifdef KW_LOG_ENABLED
123 # define KW_LOG(a) kwDbgPrintf a
124 #else
125 # define KW_LOG(a) do { } while (0)
126 #endif
127 
128 /** @def KWLDR_LOG
129  * Loader related logging.
130  * @param a     Argument list for kwDbgPrintf  */
131 #ifdef KW_LOG_ENABLED
132 # define KWLDR_LOG(a) kwDbgPrintf a
133 #else
134 # define KWLDR_LOG(a) do { } while (0)
135 #endif
136 
137 
138 /** @def KWFS_LOG
139  * FS cache logging.
140  * @param a     Argument list for kwDbgPrintf  */
141 #ifdef KW_LOG_ENABLED
142 # define KWFS_LOG(a) kwDbgPrintf a
143 #else
144 # define KWFS_LOG(a) do { } while (0)
145 #endif
146 
147 /** @def KWOUT_LOG
148  * Output related logging.
149  * @param a     Argument list for kwDbgPrintf  */
150 #ifdef KW_LOG_ENABLED
151 # define KWOUT_LOG(a) kwDbgPrintf a
152 #else
153 # define KWOUT_LOG(a) do { } while (0)
154 #endif
155 
156 /** @def KWCRYPT_LOG
157  * FS cache logging.
158  * @param a     Argument list for kwDbgPrintf  */
159 #ifdef KW_LOG_ENABLED
160 # define KWCRYPT_LOG(a) kwDbgPrintf a
161 #else
162 # define KWCRYPT_LOG(a) do { } while (0)
163 #endif
164 
165 /** Converts a windows handle to a handle table index.
166  * @note We currently just mask off the 31th bit, and do no shifting or anything
167  *     else to create an index of the handle.
168  * @todo consider shifting by 2 or 3. */
169 #define KW_HANDLE_TO_INDEX(a_hHandle)   ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
170 /** Maximum handle value we can deal with.   */
171 #define KW_HANDLE_MAX                   0x20000
172 
173 /** Max temporary file size (memory backed).  */
174 #if K_ARCH_BITS >= 64
175 # define KWFS_TEMP_FILE_MAX             (256*1024*1024)
176 #else
177 # define KWFS_TEMP_FILE_MAX             (64*1024*1024)
178 #endif
179 
180 /** Marks unfinished code.  */
181 #if 1
182 # define KWFS_TODO()    do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
183 #else
184 # define KWFS_TODO()    do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
185 #endif
186 
187 /** User data key for tools. */
188 #define KW_DATA_KEY_TOOL                (~(KUPTR)16381)
189 /** User data key for a cached file. */
190 #define KW_DATA_KEY_CACHED_FILE         (~(KUPTR)65521)
191 
192 /** String constant comma length.   */
193 #define TUPLE(a_sz)                     a_sz, sizeof(a_sz) - 1
194 
195 
196 /*********************************************************************************************************************************
197 *   Structures and Typedefs                                                                                                      *
198 *********************************************************************************************************************************/
199 typedef enum KWLOCATION
200 {
201     KWLOCATION_INVALID = 0,
202     KWLOCATION_EXE_DIR,
203     KWLOCATION_IMPORTER_DIR,
204     KWLOCATION_SYSTEM32,
205     KWLOCATION_UNKNOWN_NATIVE,
206     KWLOCATION_UNKNOWN,
207 } KWLOCATION;
208 
209 typedef enum KWMODSTATE
210 {
211     KWMODSTATE_INVALID = 0,
212     KWMODSTATE_NEEDS_BITS,
213     KWMODSTATE_NEEDS_INIT,
214     KWMODSTATE_BEING_INITED,
215     KWMODSTATE_INIT_FAILED,
216     KWMODSTATE_READY,
217 } KWMODSTATE;
218 
219 typedef struct KWMODULE *PKWMODULE;
220 typedef struct KWMODULE
221 {
222     /** Pointer to the next image. */
223     PKWMODULE           pNext;
224     /** The normalized path to the image. */
225     const char         *pszPath;
226     /** The hash of the program path. */
227     KU32                uHashPath;
228     /** Number of references. */
229     KU32                cRefs;
230     /** UTF-16 version of pszPath. */
231     const wchar_t      *pwszPath;
232     /** The offset of the filename in pszPath. */
233     KU16                offFilename;
234     /** Set if executable. */
235     KBOOL               fExe;
236     /** Set if native module entry. */
237     KBOOL               fNative;
238     /** Loader module handle. */
239     PKLDRMOD            pLdrMod;
240     /** The windows module handle. */
241     HMODULE             hOurMod;
242     /** The of the loaded image bits. */
243     KSIZE               cbImage;
244 
245     union
246     {
247         /** Data for a manually loaded image. */
248         struct
249         {
250             /** Where we load the image. */
251             KU8                *pbLoad;
252             /** Virgin copy of the image. */
253             KU8                *pbCopy;
254             /** Ldr pvBits argument.  This is NULL till we've successfully resolved
255              *  the imports. */
256             void               *pvBits;
257             /** The state. */
258             KWMODSTATE          enmState;
259 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
260             /** The number of entries in the table. */
261             KU32                cFunctions;
262             /** The function table address (in the copy). */
263             PRUNTIME_FUNCTION   paFunctions;
264             /** Set if we've already registered a function table already. */
265             KBOOL               fRegisteredFunctionTable;
266 #endif
267             /** Set if we share memory with other executables. */
268             KBOOL               fUseLdBuf;
269             /** Set after the first whole image copy is done. */
270             KBOOL               fCanDoQuick;
271             /** Number of quick copy chunks. */
272             KU8                 cQuickCopyChunks;
273             /** Number of quick zero chunks. */
274             KU8                 cQuickZeroChunks;
275             /** Quicker image copy instructions that skips non-writable parts when
276              * possible.  Need to check fCanDoQuick, fUseLdBuf and previous executable
277              * image. */
278             struct
279             {
280                 /** The copy destination.   */
281                 KU8            *pbDst;
282                 /** The copy source.   */
283                 KU8 const      *pbSrc;
284                 /** How much to copy. */
285                 KSIZE           cbToCopy;
286             } aQuickCopyChunks[3];
287             /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
288             struct
289             {
290                 /** Where to start zeroing. */
291                 KU8            *pbDst;
292                 /** How much to zero. */
293                 KSIZE           cbToZero;
294             } aQuickZeroChunks[3];
295 
296             /** TLS index if one was allocated, otherwise KU32_MAX. */
297             KU32                idxTls;
298             /** Offset (RVA) of the TLS initialization data. */
299             KU32                offTlsInitData;
300             /** Number of bytes of TLS initialization data. */
301             KU32                cbTlsInitData;
302             /** Number of allocated bytes for TLS. */
303             KU32                cbTlsAlloc;
304             /** Number of TLS callbacks. */
305             KU32                cTlsCallbacks;
306             /** Offset (RVA) of the TLS callback table. */
307             KU32                offTlsCallbacks;
308 
309             /** Number of imported modules. */
310             KSIZE               cImpMods;
311             /** Import array (variable size). */
312             PKWMODULE           apImpMods[1];
313         } Manual;
314     } u;
315 } KWMODULE;
316 
317 
318 typedef struct KWDYNLOAD *PKWDYNLOAD;
319 typedef struct KWDYNLOAD
320 {
321     /** Pointer to the next in the list. */
322     PKWDYNLOAD          pNext;
323 
324     /** The module handle we present to the application.
325      * This is the LoadLibraryEx return value for special modules and the
326      * KWMODULE.hOurMod value for the others. */
327     HMODULE             hmod;
328 
329     /** The module for non-special resource stuff, NULL if special. */
330     PKWMODULE           pMod;
331 
332     /** The length of the LoadLibary filename. */
333     KSIZE               cchRequest;
334     /** The LoadLibrary filename. */
335     char                szRequest[1];
336 } KWDYNLOAD;
337 
338 
339 /**
340  * GetModuleHandle cache for system modules frequently queried.
341  */
342 typedef struct KWGETMODULEHANDLECACHE
343 {
344     const char     *pszName;
345     KU8             cchName;
346     KU8             cwcName;
347     const wchar_t  *pwszName;
348     HANDLE          hmod;
349 } KWGETMODULEHANDLECACHE;
350 typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
351 
352 
353 /**
354  * A cached file.
355  */
356 typedef struct KFSWCACHEDFILE
357 {
358     /** The user data core. */
359     KFSUSERDATA         Core;
360 
361     /** Cached file handle. */
362     HANDLE              hCached;
363     /** Cached file section handle. */
364     HANDLE              hSection;
365     /** Cached file content. */
366     KU8                *pbCached;
367     /** The file size. */
368     KU32                cbCached;
369 #ifdef WITH_HASH_MD5_CACHE
370     /** Set if we've got a valid MD5 hash in abMd5Digest. */
371     KBOOL               fValidMd5;
372     /** The MD5 digest if fValidMd5 is set. */
373     KU8                 abMd5Digest[16];
374 #endif
375 
376     /** Circular self reference. Prevents the object from ever going away and
377      * keeps it handy for debugging. */
378     PKFSOBJ             pFsObj;
379     /** The file path (for debugging).   */
380     char                szPath[1];
381 } KFSWCACHEDFILE;
382 /** Pointer to a cached filed. */
383 typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
384 
385 #ifdef WITH_HASH_MD5_CACHE
386 
387 /** Pointer to a MD5 hash instance. */
388 typedef struct KWHASHMD5 *PKWHASHMD5;
389 /**
390  * A MD5 hash instance.
391  */
392 typedef struct KWHASHMD5
393 {
394     /** The magic value. */
395     KUPTR               uMagic;
396     /** Pointer to the next hash handle. */
397     PKWHASHMD5          pNext;
398     /** The cached file we've associated this handle with. */
399     PKFSWCACHEDFILE     pCachedFile;
400     /** The number of bytes we've hashed. */
401     KU32                cbHashed;
402     /** Set if this has gone wrong. */
403     KBOOL               fGoneBad;
404     /** Set if we're in fallback mode (file not cached). */
405     KBOOL               fFallbackMode;
406     /** Set if we've already finalized the digest. */
407     KBOOL               fFinal;
408     /** The MD5 fallback context. */
409     struct MD5Context   Md5Ctx;
410     /** The finalized digest. */
411     KU8                 abDigest[16];
412 
413 } KWHASHMD5;
414 /** Magic value for KWHASHMD5::uMagic (Les McCann). */
415 # define KWHASHMD5_MAGIC    KUPTR_C(0x19350923)
416 
417 #endif /* WITH_HASH_MD5_CACHE */
418 #ifdef WITH_TEMP_MEMORY_FILES
419 
420 typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
421 typedef struct KWFSTEMPFILESEG
422 {
423     /** File offset of data. */
424     KU32                offData;
425     /** The size of the buffer pbData points to. */
426     KU32                cbDataAlloc;
427     /** The segment data. */
428     KU8                *pbData;
429 } KWFSTEMPFILESEG;
430 
431 typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
432 typedef struct KWFSTEMPFILE
433 {
434     /** Pointer to the next temporary file for this run. */
435     PKWFSTEMPFILE       pNext;
436     /** The UTF-16 path. (Allocated after this structure.)  */
437     const wchar_t      *pwszPath;
438     /** The path length. */
439     KU16                cwcPath;
440     /** Number of active handles using this file/mapping (<= 2). */
441     KU8                 cActiveHandles;
442     /** Number of active mappings (mapped views) (0 or 1). */
443     KU8                 cMappings;
444     /** The amount of space allocated in the segments. */
445     KU32                cbFileAllocated;
446     /** The current file size. */
447     KU32                cbFile;
448     /** The number of segments. */
449     KU32                cSegs;
450     /** Segments making up the file. */
451     PKWFSTEMPFILESEG    paSegs;
452 } KWFSTEMPFILE;
453 
454 #endif /* WITH_TEMP_MEMORY_FILES */
455 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
456 
457 /**
458  * Console line buffer or output full buffer.
459  */
460 typedef struct KWOUTPUTSTREAMBUF
461 {
462     /** The main output handle. */
463     HANDLE              hOutput;
464     /** Our backup handle. */
465     HANDLE              hBackup;
466     /** Set if this is a console handle and we're in line buffered mode.
467      * When clear, we may buffer multiple lines, though try flush on line
468      * boundraries when ever possible. */
469     KBOOL               fIsConsole;
470     /** Compressed GetFileType result. */
471     KU8                 fFileType;
472     KU8                 abPadding[2];
473     union
474     {
475         /** Line buffer mode (fIsConsole == K_TRUE). */
476         struct
477         {
478             /** Amount of pending console output in wchar_t's. */
479             KU32                cwcBuf;
480             /** The allocated buffer size.   */
481             KU32                cwcBufAlloc;
482             /** Pending console output. */
483             wchar_t            *pwcBuf;
484         } Con;
485         /** Fully buffered mode (fIsConsole == K_FALSE). */
486         struct
487         {
488             /** Amount of pending output (in chars). */
489             KU32                cchBuf;
490 #ifdef WITH_STD_OUT_ERR_BUFFERING
491             /** The allocated buffer size (in chars).   */
492             KU32                cchBufAlloc;
493             /** Pending output. */
494             char               *pchBuf;
495 #endif
496         } Fully;
497     } u;
498 } KWOUTPUTSTREAMBUF;
499 /** Pointer to a console line buffer. */
500 typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
501 
502 /**
503  * Combined console buffer of complete lines.
504  */
505 typedef struct KWCONSOLEOUTPUT
506 {
507     /** The console output handle.
508      * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
509      * combined output buffering. */
510     HANDLE              hOutput;
511     /** The current code page for the console. */
512     KU32                uCodepage;
513     /** Amount of pending console output in wchar_t's. */
514     KU32                cwcBuf;
515     /** Number of times we've flushed it in any way (for cl.exe hack). */
516     KU32                cFlushes;
517     /** Pending console output. */
518     wchar_t             wszBuf[8192];
519 } KWCONSOLEOUTPUT;
520 /** Pointer to a combined console buffer. */
521 typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
522 
523 #endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
524 
525 /** Handle type.   */
526 typedef enum KWHANDLETYPE
527 {
528     KWHANDLETYPE_INVALID = 0,
529     KWHANDLETYPE_FSOBJ_READ_CACHE,
530     KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
531     KWHANDLETYPE_TEMP_FILE,
532     KWHANDLETYPE_TEMP_FILE_MAPPING,
533     KWHANDLETYPE_OUTPUT_BUF
534 } KWHANDLETYPE;
535 
536 /** Handle data. */
537 typedef struct KWHANDLE
538 {
539     KWHANDLETYPE        enmType;
540     /** Number of references   */
541     KU32                cRefs;
542     /** The current file offset. */
543     KU32                offFile;
544     /** Handle access. */
545     KU32                dwDesiredAccess;
546     /** The handle. */
547     HANDLE              hHandle;
548 
549     /** Type specific data. */
550     union
551     {
552         /** The file system object.   */
553         PKFSWCACHEDFILE     pCachedFile;
554         /** Temporary file handle or mapping handle. */
555         PKWFSTEMPFILE       pTempFile;
556 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
557         /** Buffered output stream. */
558         PKWOUTPUTSTREAMBUF  pOutBuf;
559 #endif
560     } u;
561 } KWHANDLE;
562 typedef KWHANDLE *PKWHANDLE;
563 
564 /**
565  * Tracking one of our memory mappings.
566  */
567 typedef struct KWMEMMAPPING
568 {
569     /** Number of references. */
570     KU32                cRefs;
571     /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
572      *  KWHANDLETYPE_TEMP_FILE_MAPPING). */
573     KWHANDLETYPE        enmType;
574     /** The mapping address. */
575     PVOID               pvMapping;
576     /** Type specific data. */
577     union
578     {
579         /** The file system object.   */
580         PKFSWCACHEDFILE     pCachedFile;
581         /** Temporary file handle or mapping handle. */
582         PKWFSTEMPFILE       pTempFile;
583     } u;
584 } KWMEMMAPPING;
585 /** Pointer to a memory mapping tracker. */
586 typedef KWMEMMAPPING *PKWMEMMAPPING;
587 
588 
589 /** Pointer to a VirtualAlloc tracker entry. */
590 typedef struct KWVIRTALLOC *PKWVIRTALLOC;
591 /**
592  * Tracking an VirtualAlloc allocation.
593  */
594 typedef struct KWVIRTALLOC
595 {
596     PKWVIRTALLOC        pNext;
597     void               *pvAlloc;
598     KSIZE               cbAlloc;
599     /** This is KU32_MAX if not a preallocated chunk. */
600     KU32                idxPreAllocated;
601 } KWVIRTALLOC;
602 
603 
604 /** Pointer to a heap (HeapCreate) tracker entry. */
605 typedef struct KWHEAP *PKWHEAP;
606 /**
607  * Tracking an heap (HeapCreate)
608  */
609 typedef struct KWHEAP
610 {
611     PKWHEAP             pNext;
612     HANDLE              hHeap;
613 } KWHEAP;
614 
615 
616 /** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
617 typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
618 /**
619  * Tracking an FlsAlloc/TlsAlloc index.
620  */
621 typedef struct KWLOCALSTORAGE
622 {
623     PKWLOCALSTORAGE     pNext;
624     KU32                idx;
625 } KWLOCALSTORAGE;
626 
627 
628 /** Pointer to an at exit callback record */
629 typedef struct KWEXITCALLACK *PKWEXITCALLACK;
630 /**
631  * At exit callback record.
632  */
633 typedef struct KWEXITCALLACK
634 {
635     PKWEXITCALLACK      pNext;
636     _onexit_t           pfnCallback;
637     /** At exit doesn't have an exit code. */
638     KBOOL               fAtExit;
639 } KWEXITCALLACK;
640 
641 
642 typedef enum KWTOOLTYPE
643 {
644     KWTOOLTYPE_INVALID = 0,
645     KWTOOLTYPE_SANDBOXED,
646     KWTOOLTYPE_WATCOM,
647     KWTOOLTYPE_EXEC,
648     KWTOOLTYPE_END
649 } KWTOOLTYPE;
650 
651 typedef enum KWTOOLHINT
652 {
653     KWTOOLHINT_INVALID = 0,
654     KWTOOLHINT_NONE,
655     KWTOOLHINT_VISUAL_CPP_CL,
656     KWTOOLHINT_VISUAL_CPP_LINK,
657     KWTOOLHINT_END
658 } KWTOOLHINT;
659 
660 
661 /**
662  * A kWorker tool.
663  */
664 typedef struct KWTOOL
665 {
666     /** The user data core structure. */
667     KFSUSERDATA         Core;
668 
669     /** The normalized path to the program. */
670     const char         *pszPath;
671     /** UTF-16 version of pszPath. */
672     wchar_t const      *pwszPath;
673     /** The kind of tool. */
674     KWTOOLTYPE          enmType;
675 
676     union
677     {
678         struct
679         {
680             /** The main entry point. */
681             KUPTR       uMainAddr;
682             /** The executable. */
683             PKWMODULE   pExe;
684             /** List of dynamically loaded modules.
685              * These will be kept loaded till the tool is destroyed (if we ever do that). */
686             PKWDYNLOAD  pDynLoadHead;
687             /** Module array sorted by hOurMod. */
688             PKWMODULE  *papModules;
689             /** Number of entries in papModules. */
690             KU32        cModules;
691 
692             /** Tool hint (for hacks and such). */
693             KWTOOLHINT  enmHint;
694         } Sandboxed;
695     } u;
696 } KWTOOL;
697 /** Pointer to a tool. */
698 typedef struct KWTOOL *PKWTOOL;
699 
700 
701 typedef struct KWSANDBOX *PKWSANDBOX;
702 typedef struct KWSANDBOX
703 {
704     /** The tool currently running in the sandbox. */
705     PKWTOOL     pTool;
706     /** Jump buffer. */
707     jmp_buf     JmpBuf;
708     /** The thread ID of the main thread (owner of JmpBuf). */
709     DWORD       idMainThread;
710     /** Copy of the NT TIB of the main thread. */
711     NT_TIB      TibMainThread;
712     /** The NT_TIB::ExceptionList value inside the try case.
713      * We restore this prior to the longjmp.  */
714     void       *pOutXcptListHead;
715     /** The exit code in case of longjmp.   */
716     int         rcExitCode;
717     /** Set if we're running. */
718     KBOOL       fRunning;
719     /** Whether to disable caching of ".pch" files. */
720     KBOOL       fNoPchCaching;
721 
722     /** The command line.   */
723     char       *pszCmdLine;
724     /** The UTF-16 command line. */
725     wchar_t    *pwszCmdLine;
726     /** Number of arguments in papszArgs. */
727     int         cArgs;
728     /** The argument vector. */
729     char      **papszArgs;
730     /** The argument vector. */
731     wchar_t   **papwszArgs;
732 
733     /** The _pgmptr msvcrt variable.  */
734     char       *pgmptr;
735     /** The _wpgmptr msvcrt variable. */
736     wchar_t    *wpgmptr;
737 
738     /** The _initenv msvcrt variable. */
739     char      **initenv;
740     /** The _winitenv msvcrt variable. */
741     wchar_t   **winitenv;
742 
743     /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
744     KSIZE       cEnvVarsAllocated;
745     /** The _environ msvcrt variable. */
746     char      **environ;
747     /** The _wenviron msvcrt variable. */
748     wchar_t   **wenviron;
749     /** The shadow _environ msvcrt variable. */
750     char      **papszEnvVars;
751     /** The shadow _wenviron msvcrt variable. */
752     wchar_t   **papwszEnvVars;
753 
754 
755     /** Handle table. */
756     PKWHANDLE      *papHandles;
757     /** Size of the handle table. */
758     KU32            cHandles;
759     /** Number of active handles in the table. */
760     KU32            cActiveHandles;
761     /** Number of handles in the handle table that will not be freed.   */
762     KU32            cFixedHandles;
763     /** Total number of leaked handles. */
764     KU32            cLeakedHandles;
765 
766     /** Number of active memory mappings in paMemMappings. */
767     KU32            cMemMappings;
768     /** The allocated size of paMemMappings. */
769     KU32            cMemMappingsAlloc;
770     /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
771     PKWMEMMAPPING   paMemMappings;
772 
773     /** Head of the list of temporary file. */
774     PKWFSTEMPFILE   pTempFileHead;
775 
776     /** Head of the virtual alloc allocations. */
777     PKWVIRTALLOC    pVirtualAllocHead;
778     /** Head of the heap list (HeapCreate).
779      * This is only done from images we forcibly restore.  */
780     PKWHEAP         pHeapHead;
781     /** Head of the FlsAlloc indexes. */
782     PKWLOCALSTORAGE pFlsAllocHead;
783     /** Head of the TlsAlloc indexes. */
784     PKWLOCALSTORAGE pTlsAllocHead;
785 
786     /** The at exit callback head.
787      * This is only done from images we forcibly restore.  */
788     PKWEXITCALLACK  pExitCallbackHead;
789 
790     MY_UNICODE_STRING SavedCommandLine;
791 
792 #ifdef WITH_HASH_MD5_CACHE
793     /** The special MD5 hash instance. */
794     PKWHASHMD5      pHashHead;
795     /** ReadFile sets these while CryptHashData claims and clears them.
796      *
797      * This is part of the heuristics we use for MD5 caching for header files. The
798      * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
799      * header, then passes the same buffer and read byte count to CryptHashData.
800      */
801     struct
802     {
803         /** The cached file last read from. */
804         PKFSWCACHEDFILE pCachedFile;
805         /** The file offset of the last cached read. */
806         KU32            offRead;
807         /** The number of bytes read last. */
808         KU32            cbRead;
809         /** The buffer pointer of the last read. */
810         void           *pvRead;
811     } LastHashRead;
812 #endif
813 
814 #ifdef WITH_CRYPT_CTX_REUSE
815     /** Reusable crypt contexts.  */
816     struct
817     {
818         /** The creation provider type.  */
819         KU32            dwProvType;
820         /** The creation flags. */
821         KU32            dwFlags;
822         /** The length of the container name. */
823         KU32            cwcContainer;
824         /** The length of the provider name. */
825         KU32            cwcProvider;
826         /** The container name string. */
827         wchar_t        *pwszContainer;
828         /** The provider name string. */
829         wchar_t        *pwszProvider;
830         /** The context handle. */
831         HCRYPTPROV      hProv;
832     }                   aCryptCtxs[4];
833     /** Number of reusable crypt conexts in aCryptCtxs. */
834     KU32                cCryptCtxs;
835 #endif
836 
837 
838 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
839     /** The internal standard output handle. */
840     KWHANDLE            HandleStdOut;
841     /** The internal standard error handle. */
842     KWHANDLE            HandleStdErr;
843     /** Standard output (and whatever else) buffer. */
844     KWOUTPUTSTREAMBUF   StdOut;
845     /** Standard error buffer. */
846     KWOUTPUTSTREAMBUF   StdErr;
847     /** Combined buffer of completed lines. */
848     KWCONSOLEOUTPUT     Combined;
849 #endif
850 } KWSANDBOX;
851 
852 /** Replacement function entry. */
853 typedef struct KWREPLACEMENTFUNCTION
854 {
855     /** The function name. */
856     const char *pszFunction;
857     /** The length of the function name. */
858     KSIZE       cchFunction;
859     /** The module name (optional). */
860     const char *pszModule;
861     /** The replacement function or data address. */
862     KUPTR       pfnReplacement;
863     /** Only replace in the executable.
864      * @todo fix the reinitialization of non-native DLLs!  */
865     KBOOL       fOnlyExe;
866 } KWREPLACEMENTFUNCTION;
867 typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
868 
869 #if 0
870 /** Replacement function entry. */
871 typedef struct KWREPLACEMENTDATA
872 {
873     /** The function name. */
874     const char *pszFunction;
875     /** The length of the function name. */
876     KSIZE       cchFunction;
877     /** The module name (optional). */
878     const char *pszModule;
879     /** Function providing the replacement. */
880     KUPTR     (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
881 } KWREPLACEMENTDATA;
882 typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
883 #endif
884 
885 
886 /*********************************************************************************************************************************
887 *   Global Variables                                                                                                             *
888 *********************************************************************************************************************************/
889 /** The sandbox data. */
890 static KWSANDBOX    g_Sandbox;
891 
892 /** The module currently occupying g_abDefLdBuf. */
893 static PKWMODULE    g_pModInLdBuf = NULL;
894 
895 /** The module that previuosly occupied g_abDefLdBuf. */
896 static PKWMODULE    g_pModPrevInLdBuf = NULL;
897 
898 /** Module hash table. */
899 static PKWMODULE    g_apModules[127];
900 
901 /** GetModuleHandle cache. */
902 static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
903 {
904 #define MOD_CACHE_STRINGS(str) str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1, L##str
905     { MOD_CACHE_STRINGS("KERNEL32.DLL"),    NULL },
906     { MOD_CACHE_STRINGS("mscoree.dll"),     NULL },
907 };
908 
909 /** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
910 static PKWMODULE    g_pModPendingTlsAlloc = NULL;
911 
912 
913 /** The file system cache. */
914 static PKFSCACHE    g_pFsCache;
915 /** The current directory (referenced). */
916 static PKFSOBJ      g_pCurDirObj = NULL;
917 #ifdef KBUILD_OS_WINDOWS
918 /** The windows system32 directory (referenced). */
919 static PKFSDIR      g_pWinSys32 = NULL;
920 #endif
921 
922 /** Verbosity level. */
923 static int          g_cVerbose = 2;
924 
925 /** Whether we should restart the worker. */
926 static KBOOL        g_fRestart = K_FALSE;
927 
928 /** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
929 static KBOOL volatile g_fCtrlC = K_FALSE;
930 
931 /* Further down. */
932 extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
933 extern KU32                  const g_cSandboxReplacements;
934 
935 extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
936 extern KU32                  const g_cSandboxNativeReplacements;
937 
938 extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
939 extern KU32                  const g_cSandboxGetProcReplacements;
940 
941 
942 /** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
943  * cover the default executable link address of 0x400000.
944  * @remarks Early main() makes it read+write+executable.  Attempts as having
945  *          it as a separate section failed because the linker insists on
946  *          writing out every zero in the uninitialized section, resulting in
947  *          really big binaries. */
948 __declspec(align(0x1000))
949 static KU8          g_abDefLdBuf[16*1024*1024];
950 
951 #ifdef WITH_LOG_FILE
952 /** Log file handle.   */
953 static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
954 #endif
955 
956 
957 #ifdef WITH_FIXED_VIRTUAL_ALLOCS
958 /** Virtual address space reserved for CL.EXE heap manager.
959  *
960  * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
961  * address.  It's among other things used for precompiled headers, which
962  * seemingly have addresses hardcoded into them and won't work if mapped
963  * elsewhere.  Thus, we have to make sure the area is available when cl.exe asks
964  * for it.  (The /Zm option may affect this allocation.)
965  */
966 static struct
967 {
968     /** The memory address we need.   */
969     KUPTR const     uFixed;
970     /** How much we need to fix. */
971     KSIZE const     cbFixed;
972     /** What we actually got, NULL if given back. */
973     void           *pvReserved;
974     /** Whether it is in use or not. */
975     KBOOL           fInUse;
976 } g_aFixedVirtualAllocs[] =
977 {
978 # if K_ARCH == K_ARCH_X86_32
979     /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
980        0x05300000.  We get 0x0f000000 to handle large precompiled header files. */
981     { KUPTR_C(        0x11000000), KSIZE_C(        0x0f000000), NULL },
982 # else
983     { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
984 # endif
985 };
986 #endif
987 
988 
989 #ifdef WITH_HISTORY
990 /** The job history. */
991 static char     *g_apszHistory[32];
992 /** Index of the next history entry. */
993 static unsigned  g_iHistoryNext = 0;
994 #endif
995 
996 
997 /** Number of jobs executed. */
998 static KU32     g_cJobs;
999 /** Number of tools. */
1000 static KU32     g_cTools;
1001 /** Number of modules. */
1002 static KU32     g_cModules;
1003 /** Number of non-native modules. */
1004 static KU32     g_cNonNativeModules;
1005 /** Number of read-cached files. */
1006 static KU32     g_cReadCachedFiles;
1007 /** Total size of read-cached files. */
1008 static KSIZE    g_cbReadCachedFiles;
1009 
1010 /** Total number of ReadFile calls. */
1011 static KSIZE    g_cReadFileCalls;
1012 /** Total bytes read via ReadFile. */
1013 static KSIZE    g_cbReadFileTotal;
1014 /** Total number of read from read-cached files. */
1015 static KSIZE    g_cReadFileFromReadCached;
1016 /** Total bytes read from read-cached files. */
1017 static KSIZE    g_cbReadFileFromReadCached;
1018 /** Total number of read from in-memory temporary files. */
1019 static KSIZE    g_cReadFileFromInMemTemp;
1020 /** Total bytes read from in-memory temporary files. */
1021 static KSIZE    g_cbReadFileFromInMemTemp;
1022 
1023 /** Total number of WriteFile calls. */
1024 static KSIZE    g_cWriteFileCalls;
1025 /** Total bytes written via WriteFile. */
1026 static KSIZE    g_cbWriteFileTotal;
1027 /** Total number of written to from in-memory temporary files. */
1028 static KSIZE    g_cWriteFileToInMemTemp;
1029 /** Total bytes written to in-memory temporary files. */
1030 static KSIZE    g_cbWriteFileToInMemTemp;
1031 
1032 
1033 /*********************************************************************************************************************************
1034 *   Internal Functions                                                                                                           *
1035 *********************************************************************************************************************************/
1036 static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1037 static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1038                                        const char *pszSearchPath, PKWMODULE *ppMod);
1039 static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1040 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1041 static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1042 #endif
1043 static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1044 
1045 
1046 
1047 
1048 /**
1049  * Debug printing.
1050  * @param   pszFormat           Debug format string.
1051  * @param   ...                 Format argument.
1052  */
kwDbgPrintfV(const char * pszFormat,va_list va)1053 static void kwDbgPrintfV(const char *pszFormat, va_list va)
1054 {
1055     if (g_cVerbose >= 2)
1056     {
1057         DWORD const dwSavedErr = GetLastError();
1058 #ifdef WITH_LOG_FILE
1059         DWORD       dwIgnored;
1060         char        szTmp[2048];
1061         int         cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1062         int         cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1063         if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1064             cch += cchPrefix;
1065         else
1066         {
1067             cch = sizeof(szTmp) - 1;
1068             szTmp[cch] = '\0';
1069         }
1070 
1071         if (g_hLogFile == INVALID_HANDLE_VALUE)
1072         {
1073             wchar_t wszFilename[128];
1074             _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1075             g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1076                                      FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1077         }
1078 
1079         WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1080 #else
1081         fprintf(stderr, "debug: ");
1082         vfprintf(stderr, pszFormat, va);
1083 #endif
1084 
1085         SetLastError(dwSavedErr);
1086     }
1087 }
1088 
1089 
1090 /**
1091  * Debug printing.
1092  * @param   pszFormat           Debug format string.
1093  * @param   ...                 Format argument.
1094  */
kwDbgPrintf(const char * pszFormat,...)1095 static void kwDbgPrintf(const char *pszFormat, ...)
1096 {
1097     if (g_cVerbose >= 2)
1098     {
1099         va_list va;
1100         va_start(va, pszFormat);
1101         kwDbgPrintfV(pszFormat, va);
1102         va_end(va);
1103     }
1104 }
1105 
1106 
1107 /**
1108  * Debugger printing.
1109  * @param   pszFormat           Debug format string.
1110  * @param   ...                 Format argument.
1111  */
kwDebuggerPrintfV(const char * pszFormat,va_list va)1112 static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1113 {
1114     if (IsDebuggerPresent())
1115     {
1116         DWORD const dwSavedErr = GetLastError();
1117         char szTmp[2048];
1118 
1119         _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1120         OutputDebugStringA(szTmp);
1121 
1122         SetLastError(dwSavedErr);
1123     }
1124 }
1125 
1126 
1127 /**
1128  * Debugger printing.
1129  * @param   pszFormat           Debug format string.
1130  * @param   ...                 Format argument.
1131  */
kwDebuggerPrintf(const char * pszFormat,...)1132 static void kwDebuggerPrintf(const char *pszFormat, ...)
1133 {
1134     va_list va;
1135     va_start(va, pszFormat);
1136     kwDebuggerPrintfV(pszFormat, va);
1137     va_end(va);
1138 }
1139 
1140 
1141 
1142 /**
1143  * Error printing.
1144  * @param   pszFormat           Message format string.
1145  * @param   ...                 Format argument.
1146  */
kwErrPrintfV(const char * pszFormat,va_list va)1147 static void kwErrPrintfV(const char *pszFormat, va_list va)
1148 {
1149     DWORD const dwSavedErr = GetLastError();
1150 
1151     fprintf(stderr, "kWorker: error: ");
1152     vfprintf(stderr, pszFormat, va);
1153 
1154     SetLastError(dwSavedErr);
1155 }
1156 
1157 
1158 /**
1159  * Error printing.
1160  * @param   pszFormat           Message format string.
1161  * @param   ...                 Format argument.
1162  */
kwErrPrintf(const char * pszFormat,...)1163 static void kwErrPrintf(const char *pszFormat, ...)
1164 {
1165     va_list va;
1166     va_start(va, pszFormat);
1167     kwErrPrintfV(pszFormat, va);
1168     va_end(va);
1169 }
1170 
1171 
1172 /**
1173  * Error printing.
1174  * @return  rc;
1175  * @param   rc                  Return value
1176  * @param   pszFormat           Message format string.
1177  * @param   ...                 Format argument.
1178  */
kwErrPrintfRc(int rc,const char * pszFormat,...)1179 static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1180 {
1181     va_list va;
1182     va_start(va, pszFormat);
1183     kwErrPrintfV(pszFormat, va);
1184     va_end(va);
1185     return rc;
1186 }
1187 
1188 
1189 #ifdef K_STRICT
1190 
kHlpAssertMsg1(const char * pszExpr,const char * pszFile,unsigned iLine,const char * pszFunction)1191 KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1192 {
1193     DWORD const dwSavedErr = GetLastError();
1194 
1195     fprintf(stderr,
1196             "\n"
1197             "!!Assertion failed!!\n"
1198             "Expression: %s\n"
1199             "Function :  %s\n"
1200             "File:       %s\n"
1201             "Line:       %d\n"
1202             ,  pszExpr, pszFunction, pszFile, iLine);
1203 
1204     SetLastError(dwSavedErr);
1205 }
1206 
1207 
kHlpAssertMsg2(const char * pszFormat,...)1208 KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1209 {
1210     DWORD const dwSavedErr = GetLastError();
1211     va_list va;
1212 
1213     va_start(va, pszFormat);
1214     fprintf(stderr, pszFormat, va);
1215     va_end(va);
1216 
1217     SetLastError(dwSavedErr);
1218 }
1219 
1220 #endif /* K_STRICT */
1221 
1222 
1223 /**
1224  * Hashes a string.
1225  *
1226  * @returns 32-bit string hash.
1227  * @param   pszString           String to hash.
1228  */
kwStrHash(const char * pszString)1229 static KU32 kwStrHash(const char *pszString)
1230 {
1231     /* This algorithm was created for sdbm (a public-domain reimplementation of
1232        ndbm) database library. it was found to do well in scrambling bits,
1233        causing better distribution of the keys and fewer splits. it also happens
1234        to be a good general hashing function with good distribution. the actual
1235        function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1236        is the faster version used in gawk. [there is even a faster, duff-device
1237        version] the magic constant 65599 was picked out of thin air while
1238        experimenting with different constants, and turns out to be a prime.
1239        this is one of the algorithms used in berkeley db (see sleepycat) and
1240        elsewhere. */
1241     KU32 uHash = 0;
1242     KU32 uChar;
1243     while ((uChar = (unsigned char)*pszString++) != 0)
1244         uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1245     return uHash;
1246 }
1247 
1248 
1249 /**
1250  * Hashes a string.
1251  *
1252  * @returns The string length.
1253  * @param   pszString           String to hash.
1254  * @param   puHash              Where to return the 32-bit string hash.
1255  */
kwStrHashEx(const char * pszString,KU32 * puHash)1256 static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1257 {
1258     const char * const pszStart = pszString;
1259     KU32 uHash = 0;
1260     KU32 uChar;
1261     while ((uChar = (unsigned char)*pszString) != 0)
1262     {
1263         uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1264         pszString++;
1265     }
1266     *puHash = uHash;
1267     return pszString - pszStart;
1268 }
1269 
1270 
1271 /**
1272  * Hashes a string.
1273  *
1274  * @returns The string length in wchar_t units.
1275  * @param   pwszString          String to hash.
1276  * @param   puHash              Where to return the 32-bit string hash.
1277  */
kwUtf16HashEx(const wchar_t * pwszString,KU32 * puHash)1278 static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1279 {
1280     const wchar_t * const pwszStart = pwszString;
1281     KU32 uHash = 0;
1282     KU32 uChar;
1283     while ((uChar = *pwszString) != 0)
1284     {
1285         uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1286         pwszString++;
1287     }
1288     *puHash = uHash;
1289     return pwszString - pwszStart;
1290 }
1291 
1292 
1293 /**
1294  * Converts the given string to unicode.
1295  *
1296  * @returns Length of the resulting string in wchar_t's.
1297  * @param   pszSrc              The source string.
1298  * @param   pwszDst             The destination buffer.
1299  * @param   cwcDst              The size of the destination buffer in wchar_t's.
1300  */
kwStrToUtf16(const char * pszSrc,wchar_t * pwszDst,KSIZE cwcDst)1301 static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1302 {
1303     /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time.  */
1304     KSIZE offDst = 0;
1305     while (offDst < cwcDst)
1306     {
1307         char ch = *pszSrc++;
1308         pwszDst[offDst++] = ch;
1309         if (!ch)
1310             return offDst - 1;
1311         kHlpAssert((unsigned)ch < 127);
1312     }
1313 
1314     pwszDst[offDst - 1] = '\0';
1315     return offDst;
1316 }
1317 
1318 
1319 /**
1320  * Converts the given string to UTF-16, allocating the buffer.
1321  *
1322  * @returns Pointer to the new heap allocation containing the UTF-16 version of
1323  *          the source string.
1324  * @param   pchSrc              The source string.
1325  * @param   cchSrc              The length of the source string.
1326  */
kwStrToUtf16AllocN(const char * pchSrc,KSIZE cchSrc)1327 static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1328 {
1329     DWORD const dwErrSaved = GetLastError();
1330     KSIZE       cwcBuf     = cchSrc + 1;
1331     wchar_t    *pwszBuf    = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1332     if (pwszBuf)
1333     {
1334         if (cchSrc > 0)
1335         {
1336             int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1337             if (cwcRet > 0)
1338             {
1339                 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1340                 pwszBuf[cwcRet] = '\0';
1341             }
1342             else
1343             {
1344                 kHlpFree(pwszBuf);
1345 
1346                 /* Figure the length and allocate the right buffer size. */
1347                 SetLastError(NO_ERROR);
1348                 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1349                 if (cwcRet)
1350                 {
1351                     cwcBuf = cwcRet + 2;
1352                     pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1353                     if (pwszBuf)
1354                     {
1355                         SetLastError(NO_ERROR);
1356                         cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1357                         if (cwcRet)
1358                         {
1359                             kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1360                             pwszBuf[cwcRet] = '\0';
1361                         }
1362                         else
1363                         {
1364                             kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1365                             kHlpFree(pwszBuf);
1366                             pwszBuf = NULL;
1367                         }
1368                     }
1369                 }
1370                 else
1371                 {
1372                     kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1373                     pwszBuf = NULL;
1374                 }
1375             }
1376         }
1377         else
1378             pwszBuf[0] = '\0';
1379     }
1380     SetLastError(dwErrSaved);
1381     return pwszBuf;
1382 }
1383 
1384 
1385 /**
1386  * Converts the given UTF-16 to a normal string.
1387  *
1388  * @returns Length of the resulting string.
1389  * @param   pwszSrc             The source UTF-16 string.
1390  * @param   pszDst              The destination buffer.
1391  * @param   cbDst               The size of the destination buffer in bytes.
1392  */
kwUtf16ToStr(const wchar_t * pwszSrc,char * pszDst,KSIZE cbDst)1393 static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1394 {
1395     /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time.  */
1396     KSIZE offDst = 0;
1397     while (offDst < cbDst)
1398     {
1399         wchar_t wc = *pwszSrc++;
1400         pszDst[offDst++] = (char)wc;
1401         if (!wc)
1402             return offDst - 1;
1403         kHlpAssert((unsigned)wc < 127);
1404     }
1405 
1406     pszDst[offDst - 1] = '\0';
1407     return offDst;
1408 }
1409 
1410 
1411 /**
1412  * Converts the given UTF-16 to ASSI, allocating the buffer.
1413  *
1414  * @returns Pointer to the new heap allocation containing the ANSI version of
1415  *          the source string.
1416  * @param   pwcSrc              The source string.
1417  * @param   cwcSrc              The length of the source string.
1418  */
kwUtf16ToStrAllocN(const wchar_t * pwcSrc,KSIZE cwcSrc)1419 static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1420 {
1421     DWORD const dwErrSaved = GetLastError();
1422     KSIZE       cbBuf      = cwcSrc + (cwcSrc >> 1) + 1;
1423     char       *pszBuf     = (char *)kHlpAlloc(cbBuf);
1424     if (pszBuf)
1425     {
1426         if (cwcSrc > 0)
1427         {
1428             int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1429             if (cchRet > 0)
1430             {
1431                 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1432                 pszBuf[cchRet] = '\0';
1433             }
1434             else
1435             {
1436                 kHlpFree(pszBuf);
1437 
1438                 /* Figure the length and allocate the right buffer size. */
1439                 SetLastError(NO_ERROR);
1440                 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1441                 if (cchRet)
1442                 {
1443                     cbBuf = cchRet + 2;
1444                     pszBuf = (char *)kHlpAlloc(cbBuf);
1445                     if (pszBuf)
1446                     {
1447                         SetLastError(NO_ERROR);
1448                         cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1449                         if (cchRet)
1450                         {
1451                             kHlpAssert(cchRet < (KSSIZE)cbBuf);
1452                             pszBuf[cchRet] = '\0';
1453                         }
1454                         else
1455                         {
1456                             kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1457                             kHlpFree(pszBuf);
1458                             pszBuf = NULL;
1459                         }
1460                     }
1461                 }
1462                 else
1463                 {
1464                     kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1465                     pszBuf = NULL;
1466                 }
1467             }
1468         }
1469         else
1470             pszBuf[0] = '\0';
1471     }
1472     SetLastError(dwErrSaved);
1473     return pszBuf;
1474 }
1475 
1476 
1477 
1478 /** UTF-16 string length.  */
kwUtf16Len(wchar_t const * pwsz)1479 static KSIZE kwUtf16Len(wchar_t const *pwsz)
1480 {
1481     KSIZE cwc = 0;
1482     while (*pwsz != '\0')
1483         cwc++, pwsz++;
1484     return cwc;
1485 }
1486 
1487 /**
1488  * Copy out the UTF-16 string following the convension of GetModuleFileName
1489  */
kwUtf16CopyStyle1(wchar_t const * pwszSrc,wchar_t * pwszDst,KSIZE cwcDst)1490 static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1491 {
1492     KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1493     if (cwcSrc + 1 <= cwcDst)
1494     {
1495         kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1496         return (DWORD)cwcSrc;
1497     }
1498     if (cwcDst > 0)
1499     {
1500         KSIZE cwcDstTmp = cwcDst - 1;
1501         pwszDst[cwcDstTmp] = '\0';
1502         if (cwcDstTmp > 0)
1503             kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1504     }
1505     SetLastError(ERROR_INSUFFICIENT_BUFFER);
1506     return (DWORD)cwcDst;
1507 }
1508 
1509 
1510 /**
1511  * Copy out the ANSI string following the convension of GetModuleFileName
1512  */
kwStrCopyStyle1(char const * pszSrc,char * pszDst,KSIZE cbDst)1513 static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1514 {
1515     KSIZE cchSrc = kHlpStrLen(pszSrc);
1516     if (cchSrc + 1 <= cbDst)
1517     {
1518         kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1519         return (DWORD)cchSrc;
1520     }
1521     if (cbDst > 0)
1522     {
1523         KSIZE cbDstTmp = cbDst - 1;
1524         pszDst[cbDstTmp] = '\0';
1525         if (cbDstTmp > 0)
1526             kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1527     }
1528     SetLastError(ERROR_INSUFFICIENT_BUFFER);
1529     return (DWORD)cbDst;
1530 }
1531 
1532 
1533 /**
1534  * Normalizes the path so we get a consistent hash.
1535  *
1536  * @returns status code.
1537  * @param   pszPath             The path.
1538  * @param   pszNormPath         The output buffer.
1539  * @param   cbNormPath          The size of the output buffer.
1540  */
kwPathNormalize(const char * pszPath,char * pszNormPath,KSIZE cbNormPath)1541 static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1542 {
1543     KFSLOOKUPERROR enmError;
1544     PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1545     if (pFsObj)
1546     {
1547         KBOOL fRc;
1548         fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1549         kFsCacheObjRelease(g_pFsCache, pFsObj);
1550         if (fRc)
1551             return 0;
1552         return KERR_BUFFER_OVERFLOW;
1553     }
1554     return KERR_FILE_NOT_FOUND;
1555 }
1556 
1557 
1558 /**
1559  * Get the pointer to the filename part of the path.
1560  *
1561  * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1562  * @returns Pointer to the terminator char if no filename.
1563  * @param   pszPath     The path to parse.
1564  */
kwPathGetFilenameW(const wchar_t * pwszPath)1565 static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1566 {
1567     const wchar_t *pwszLast = NULL;
1568     for (;;)
1569     {
1570         wchar_t wc = *pwszPath;
1571 #if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1572         if (wc == '/' || wc == '\\' || wc == ':')
1573         {
1574             while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1575                 /* nothing */;
1576             pwszLast = pwszPath;
1577         }
1578 #else
1579         if (wc == '/')
1580         {
1581             while ((wc = *++pszFilename) == '/')
1582                 /* betsuni */;
1583             pwszLast = pwszPath;
1584         }
1585 #endif
1586         if (!wc)
1587             return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1588         pwszPath++;
1589     }
1590 }
1591 
1592 
1593 
1594 /**
1595  * Retains a new reference to the given module
1596  * @returns pMod
1597  * @param   pMod                The module to retain.
1598  */
kwLdrModuleRetain(PKWMODULE pMod)1599 static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1600 {
1601     kHlpAssert(pMod->cRefs > 0);
1602     kHlpAssert(pMod->cRefs < 64);
1603     pMod->cRefs++;
1604     return pMod;
1605 }
1606 
1607 
1608 /**
1609  * Releases a module reference.
1610  *
1611  * @param   pMod                The module to release.
1612  */
kwLdrModuleRelease(PKWMODULE pMod)1613 static void kwLdrModuleRelease(PKWMODULE pMod)
1614 {
1615     if (--pMod->cRefs == 0)
1616     {
1617         /* Unlink it. */
1618         if (!pMod->fExe)
1619         {
1620             PKWMODULE pPrev = NULL;
1621             unsigned  idx   = pMod->uHashPath % K_ELEMENTS(g_apModules);
1622             if (g_apModules[idx] == pMod)
1623                 g_apModules[idx] = pMod->pNext;
1624             else
1625             {
1626                 PKWMODULE pPrev = g_apModules[idx];
1627                 kHlpAssert(pPrev != NULL);
1628                 while (pPrev->pNext != pMod)
1629                 {
1630                     pPrev = pPrev->pNext;
1631                     kHlpAssert(pPrev != NULL);
1632                 }
1633                 pPrev->pNext = pMod->pNext;
1634             }
1635         }
1636 
1637         /* Release import modules. */
1638         if (!pMod->fNative)
1639         {
1640             KSIZE idx = pMod->u.Manual.cImpMods;
1641             while (idx-- > 0)
1642                 if (pMod->u.Manual.apImpMods[idx])
1643                 {
1644                     kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1645                     pMod->u.Manual.apImpMods[idx] = NULL;
1646                 }
1647         }
1648 
1649         /* Free our resources. */
1650         kLdrModClose(pMod->pLdrMod);
1651         pMod->pLdrMod = NULL;
1652 
1653         if (!pMod->fNative)
1654         {
1655             kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1656             kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1657         }
1658 
1659         kHlpFree(pMod);
1660     }
1661     else
1662         kHlpAssert(pMod->cRefs < 64);
1663 }
1664 
1665 
1666 /**
1667  * Links the module into the module hash table.
1668  *
1669  * @returns pMod
1670  * @param   pMod                The module to link.
1671  */
kwLdrModuleLink(PKWMODULE pMod)1672 static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
1673 {
1674     unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1675     pMod->pNext = g_apModules[idx];
1676     g_apModules[idx] = pMod;
1677     return pMod;
1678 }
1679 
1680 
1681 /**
1682  * Replaces imports for this module according to g_aSandboxNativeReplacements.
1683  *
1684  * @param   pMod                The natively loaded module to process.
1685  */
kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)1686 static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
1687 {
1688     KSIZE const                 cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
1689     KU8 const * const           pbImage = (KU8 const *)pMod->hOurMod;
1690     IMAGE_DOS_HEADER const     *pMzHdr  = (IMAGE_DOS_HEADER const *)pbImage;
1691     IMAGE_NT_HEADERS const     *pNtHdrs;
1692     IMAGE_DATA_DIRECTORY const *pDirEnt;
1693 
1694     kHlpAssert(pMod->fNative);
1695 
1696     /*
1697      * Locate the export descriptors.
1698      */
1699     /* MZ header. */
1700     if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
1701     {
1702         kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
1703         pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
1704     }
1705     else
1706         pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
1707 
1708     /* Check PE header. */
1709     kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
1710     kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
1711 
1712     /* Locate the import descriptor array. */
1713     pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
1714     if (   pDirEnt->Size > 0
1715         && pDirEnt->VirtualAddress != 0)
1716     {
1717         const IMAGE_IMPORT_DESCRIPTOR  *pImpDesc    = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
1718         KU32                            cLeft       = pDirEnt->Size / sizeof(*pImpDesc);
1719         MEMORY_BASIC_INFORMATION        ProtInfo    = { NULL, NULL, 0, 0, 0, 0, 0 };
1720         KU8                            *pbProtRange = NULL;
1721         SIZE_T                          cbProtRange = 0;
1722         DWORD                           fOldProt    = 0;
1723         KU32 const                      cbPage      = 0x1000;
1724         BOOL                            fRc;
1725 
1726 
1727         kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
1728         kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
1729         kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
1730 
1731         /*
1732          * Walk the import descriptor array.
1733          * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
1734          */
1735         while (   cLeft-- > 0
1736                && pImpDesc->Name > 0
1737                && pImpDesc->FirstThunk > 0)
1738         {
1739             KU32                iThunk;
1740             const char * const  pszImport   = (const char *)&pbImage[pImpDesc->Name];
1741             PIMAGE_THUNK_DATA   paThunks    = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
1742             PIMAGE_THUNK_DATA   paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
1743             kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
1744             kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
1745             kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
1746             kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
1747             kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
1748 
1749             /* Iterate the thunks. */
1750             for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
1751             {
1752                 KUPTR const off = paOrgThunks[iThunk].u1.Function;
1753                 kHlpAssertReturnVoid(off < cbImage);
1754                 if (!IMAGE_SNAP_BY_ORDINAL(off))
1755                 {
1756                     IMAGE_IMPORT_BY_NAME const *pName     = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
1757                     KSIZE const                 cchSymbol = kHlpStrLen(pName->Name);
1758                     KU32                        i         = g_cSandboxNativeReplacements;
1759                     while (i-- > 0)
1760                         if (   g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
1761                             && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
1762                         {
1763                             if (   !g_aSandboxNativeReplacements[i].pszModule
1764                                 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
1765                             {
1766                                 KW_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
1767 
1768                                 /* The .rdata section is normally read-only, so we need to make it writable first. */
1769                                 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
1770                                 {
1771                                     /* Restore previous .rdata page. */
1772                                     if (fOldProt)
1773                                     {
1774                                         fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
1775                                         kHlpAssert(fRc);
1776                                         fOldProt = 0;
1777                                     }
1778 
1779                                     /* Query attributes for the current .rdata page. */
1780                                     pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
1781                                     cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
1782                                     kHlpAssert(cbProtRange);
1783                                     if (cbProtRange)
1784                                     {
1785                                         switch (ProtInfo.Protect)
1786                                         {
1787                                             case PAGE_READWRITE:
1788                                             case PAGE_WRITECOPY:
1789                                             case PAGE_EXECUTE_READWRITE:
1790                                             case PAGE_EXECUTE_WRITECOPY:
1791                                                 /* Already writable, nothing to do. */
1792                                                 break;
1793 
1794                                             default:
1795                                                 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
1796                                             case PAGE_READONLY:
1797                                                 cbProtRange = cbPage;
1798                                                 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
1799                                                 break;
1800 
1801                                             case PAGE_EXECUTE:
1802                                             case PAGE_EXECUTE_READ:
1803                                                 cbProtRange = cbPage;
1804                                                 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
1805                                                 break;
1806                                         }
1807                                         kHlpAssertStmt(fRc, fOldProt = 0);
1808                                     }
1809                                 }
1810 
1811                                 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
1812                                 break;
1813                             }
1814                         }
1815                 }
1816             }
1817 
1818 
1819             /* Next import descriptor. */
1820             pImpDesc++;
1821         }
1822 
1823 
1824         if (fOldProt)
1825         {
1826             DWORD fIgnore = 0;
1827             fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
1828             kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
1829         }
1830     }
1831 
1832 }
1833 
1834 
1835 /**
1836  * Creates a module from a native kLdr module handle.
1837  *
1838  * @returns Module w/ 1 reference on success, NULL on failure.
1839  * @param   pLdrMod             The native kLdr module.
1840  * @param   pszPath             The normalized path to the module.
1841  * @param   cbPath              The module path length with terminator.
1842  * @param   uHashPath           The module path hash.
1843  * @param   fDoReplacements     Whether to do import replacements on this
1844  *                              module.
1845  */
kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod,const char * pszPath,KSIZE cbPath,KU32 uHashPath,KBOOL fDoReplacements)1846 static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
1847                                                       KBOOL fDoReplacements)
1848 {
1849     /*
1850      * Create the entry.
1851      */
1852     PKWMODULE pMod   = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
1853     if (pMod)
1854     {
1855         pMod->pwszPath      = (wchar_t *)(pMod + 1);
1856         kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
1857         pMod->pszPath       = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
1858         pMod->uHashPath     = uHashPath;
1859         pMod->cRefs         = 1;
1860         pMod->offFilename   = (KU16)(kHlpGetFilename(pszPath) - pszPath);
1861         pMod->fExe          = K_FALSE;
1862         pMod->fNative       = K_TRUE;
1863         pMod->pLdrMod       = pLdrMod;
1864         pMod->hOurMod       = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
1865         pMod->cbImage       = (KSIZE)kLdrModSize(pLdrMod);
1866 
1867         if (fDoReplacements)
1868         {
1869             DWORD const dwSavedErr = GetLastError();
1870             kwLdrModuleDoNativeImportReplacements(pMod);
1871             SetLastError(dwSavedErr);
1872         }
1873 
1874         KW_LOG(("New module: %p LB %#010x %s (native)\n",
1875                 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath));
1876         g_cModules++;
1877         return kwLdrModuleLink(pMod);
1878     }
1879     return NULL;
1880 }
1881 
1882 
1883 
1884 /**
1885  * Creates a module using the native loader.
1886  *
1887  * @returns Module w/ 1 reference on success, NULL on failure.
1888  * @param   pszPath             The normalized path to the module.
1889  * @param   uHashPath           The module path hash.
1890  * @param   fDoReplacements     Whether to do import replacements on this
1891  *                              module.
1892  */
kwLdrModuleCreateNative(const char * pszPath,KU32 uHashPath,KBOOL fDoReplacements)1893 static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
1894 {
1895     /*
1896      * Open the module and check the type.
1897      */
1898     PKLDRMOD pLdrMod;
1899     int rc = kLdrModOpenNative(pszPath, &pLdrMod);
1900     if (rc == 0)
1901     {
1902         PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, kHlpStrLen(pszPath) + 1,
1903                                                               uHashPath, fDoReplacements);
1904         if (pMod)
1905             return pMod;
1906         kLdrModClose(pLdrMod);
1907     }
1908     return NULL;
1909 }
1910 
1911 
1912 /**
1913  * Sets up the quick zero & copy tables for the non-native module.
1914  *
1915  * This is a worker for kwLdrModuleCreateNonNative.
1916  *
1917  * @param   pMod                The module.
1918  */
kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)1919 static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
1920 {
1921     PCKLDRSEG   paSegs = pMod->pLdrMod->aSegments;
1922     KU32        cSegs  = pMod->pLdrMod->cSegments;
1923     KU32        iSeg;
1924 
1925     KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
1926     pMod->u.Manual.cQuickCopyChunks = 0;
1927     pMod->u.Manual.cQuickZeroChunks = 0;
1928 
1929     for (iSeg = 0; iSeg < cSegs; iSeg++)
1930         switch (paSegs[iSeg].enmProt)
1931         {
1932             case KPROT_READWRITE:
1933             case KPROT_WRITECOPY:
1934             case KPROT_EXECUTE_READWRITE:
1935             case KPROT_EXECUTE_WRITECOPY:
1936                 if (paSegs[iSeg].cbMapped)
1937                 {
1938                     KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
1939                     if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
1940                     {
1941                         /*
1942                          * Check for trailing zero words.
1943                          */
1944                         KSIZE cbTrailingZeros;
1945                         if (   paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
1946                             && (paSegs[iSeg].cbMapped & 7) == 0
1947                             && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
1948                         {
1949                             KSIZE const *pauNatural   = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
1950                             KSIZE        cNatural     = paSegs[iSeg].cbMapped / sizeof(KSIZE);
1951                             KSIZE        idxFirstZero = cNatural;
1952                             while (idxFirstZero > 0)
1953                                 if (pauNatural[--idxFirstZero] == 0)
1954                                 { /* likely */ }
1955                                 else
1956                                 {
1957                                     idxFirstZero++;
1958                                     break;
1959                                 }
1960                             cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
1961                             if (cbTrailingZeros < 128)
1962                                 cbTrailingZeros = 0;
1963                         }
1964                         else
1965                             cbTrailingZeros = 0;
1966 
1967                         /*
1968                          * Add quick copy entry.
1969                          */
1970                         if (cbTrailingZeros < paSegs[iSeg].cbMapped)
1971                         {
1972                             pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst    = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
1973                             pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc    = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
1974                             pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
1975                             pMod->u.Manual.cQuickCopyChunks = iChunk + 1;
1976                             KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
1977                                        pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
1978                                        pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
1979                                        pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
1980                                        paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
1981                         }
1982 
1983                         /*
1984                          * Add quick zero entry.
1985                          */
1986                         if (cbTrailingZeros)
1987                         {
1988                             KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
1989                             pMod->u.Manual.aQuickZeroChunks[iZero].pbDst    = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
1990                                                                             + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
1991                             pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
1992                             pMod->u.Manual.cQuickZeroChunks = iZero + 1;
1993                             KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
1994                                        pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
1995                                        pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
1996                                        paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
1997                         }
1998                     }
1999                     else
2000                     {
2001                         /*
2002                          * We're out of quick copy table entries, so just copy the whole darn thing.
2003                          * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2004                          */
2005                         kHlpAssertFailed();
2006                         pMod->u.Manual.aQuickCopyChunks[0].pbDst    = pMod->u.Manual.pbLoad;
2007                         pMod->u.Manual.aQuickCopyChunks[0].pbSrc    = pMod->u.Manual.pbCopy;
2008                         pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2009                         pMod->u.Manual.cQuickCopyChunks = 1;
2010                         KWLDR_LOG(("Quick copy not possible!\n"));
2011                         return;
2012                     }
2013                 }
2014                 break;
2015 
2016             default:
2017                 break;
2018         }
2019 }
2020 
2021 
2022 /**
2023  * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2024  *
2025  * @param   hDll            The DLL handle.
2026  * @param   idxTls          The allocated TLS index.
2027  * @param   ppfnTlsCallback Pointer to the TLS callback table entry.
2028  */
kwLdrTlsAllocationHook(void * hDll,ULONG idxTls,PIMAGE_TLS_CALLBACK * ppfnTlsCallback)2029 __declspec(dllexport) void kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, PIMAGE_TLS_CALLBACK *ppfnTlsCallback)
2030 {
2031     /*
2032      * Do the module initialization thing first.
2033      */
2034     PKWMODULE pMod = g_pModPendingTlsAlloc;
2035     if (pMod)
2036     {
2037         PPEB        pPeb = kwSandboxGetProcessEnvironmentBlock();
2038         LIST_ENTRY *pHead;
2039         LIST_ENTRY *pCur;
2040 
2041         pMod->u.Manual.idxTls = idxTls;
2042         KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2043 
2044         /*
2045          * Try sabotage the DLL name so we can load this module again.
2046          */
2047         pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2048         for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2049         {
2050             LDR_DATA_TABLE_ENTRY *pMte;
2051             pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2052             if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2053             {
2054                 PUNICODE_STRING pStr = &pMte->FullDllName;
2055                 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2056                 pStr->Buffer[--off]++;
2057                 pStr->Buffer[--off]++;
2058                 pStr->Buffer[--off]++;
2059                 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2060                 break;
2061             }
2062         }
2063     }
2064 }
2065 
2066 
2067 /**
2068  * Allocates and initializes TLS variables.
2069  *
2070  * @returns 0 on success, non-zero failure.
2071  * @param   pMod                The module.
2072  */
kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)2073 static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2074 {
2075     KU8                        *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2076     IMAGE_NT_HEADERS const     *pNtHdrs;
2077     IMAGE_DATA_DIRECTORY const *pTlsDir;
2078 
2079     if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2080         pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2081     else
2082         pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2083     kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2084 
2085     pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2086     if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2087     {
2088         PIMAGE_TLS_DIRECTORY const  paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2089         KU32 const                  cEntries  = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2090         KU32                        iEntry;
2091         KUPTR                       offIndex;
2092         KUPTR                       offCallbacks;
2093         KUPTR const                *puCallbacks;
2094         KSIZE                       cbData;
2095         const wchar_t              *pwszTlsDll;
2096         HMODULE                     hmodTlsDll;
2097 
2098         /*
2099          * Check and log.
2100          */
2101         for (iEntry = 0; iEntry < cEntries; iEntry++)
2102         {
2103             KUPTR        offIndex     = (KUPTR)paEntries[iEntry].AddressOfIndex     - (KUPTR)pMod->u.Manual.pbLoad;
2104             KUPTR        offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2105             KUPTR const *puCallbacks  = (KUPTR const *)&pbImg[offCallbacks];
2106             KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2107                        iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2108                        paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2109                        paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2110 
2111             if (offIndex >= pMod->cbImage)
2112             {
2113                 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2114                             iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2115                 return -1;
2116             }
2117             if (offCallbacks >= pMod->cbImage)
2118             {
2119                 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2120                             iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2121                 return -1;
2122             }
2123             while (*puCallbacks != 0)
2124             {
2125                 KWLDR_LOG(("TLS DIR #%u:   callback %p, RVA %#x\n",
2126                             iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2127                 puCallbacks++;
2128             }
2129             if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2130             {
2131                 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2132                             iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2133                 return -1;
2134             }
2135         }
2136 
2137         if (cEntries > 1)
2138         {
2139             kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2140             return -1;
2141         }
2142 
2143         /*
2144          * Make the allocation by loading a new instance of one of the TLS dlls.
2145          * The DLL will make a call to
2146          */
2147         offIndex     = (KUPTR)paEntries[0].AddressOfIndex     - (KUPTR)pMod->u.Manual.pbLoad;
2148         offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2149         puCallbacks  = (KUPTR const *)&pbImg[offCallbacks];
2150         cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2151         if (cbData <= 1024)
2152             pwszTlsDll = L"kWorkerTls1K.dll";
2153         else if (cbData <= 65536)
2154             pwszTlsDll = L"kWorkerTls64K.dll";
2155         else if (cbData <= 524288)
2156             pwszTlsDll = L"kWorkerTls512K.dll";
2157         else
2158         {
2159             kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2160             return -1;
2161         }
2162 
2163         pMod->u.Manual.idxTls         = KU32_MAX;
2164         pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2165         pMod->u.Manual.cbTlsInitData  = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2166         pMod->u.Manual.cbTlsAlloc     = (KU32)cbData;
2167         pMod->u.Manual.cTlsCallbacks  = 0;
2168         while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2169             pMod->u.Manual.cTlsCallbacks++;
2170         pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2171 
2172         g_pModPendingTlsAlloc = pMod;
2173         hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2174         g_pModPendingTlsAlloc = NULL;
2175         if (hmodTlsDll == NULL)
2176         {
2177             kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2178             return -1;
2179         }
2180         if (pMod->u.Manual.idxTls == KU32_MAX)
2181         {
2182             kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2183             return -1;
2184         }
2185 
2186         *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2187         KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x\n",
2188                    pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData));
2189     }
2190     return 0;
2191 }
2192 
2193 
2194 /**
2195  * Creates a module using the our own loader.
2196  *
2197  * @returns Module w/ 1 reference on success, NULL on failure.
2198  * @param   pszPath             The normalized path to the module.
2199  * @param   uHashPath           The module path hash.
2200  * @param   fExe                K_TRUE if this is an executable image, K_FALSE
2201  *                              if not.  Executable images does not get entered
2202  *                              into the global module table.
2203  * @param   pExeMod             The executable module of the process (for
2204  *                              resolving imports).  NULL if fExe is set.
2205  * @param   pszSearchPath       The PATH to search for imports.  Can be NULL.
2206  */
kwLdrModuleCreateNonNative(const char * pszPath,KU32 uHashPath,KBOOL fExe,PKWMODULE pExeMod,const char * pszSearchPath)2207 static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2208                                             PKWMODULE pExeMod, const char *pszSearchPath)
2209 {
2210     /*
2211      * Open the module and check the type.
2212      */
2213     PKLDRMOD pLdrMod;
2214     int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2215     if (rc == 0)
2216     {
2217         switch (pLdrMod->enmType)
2218         {
2219             case KLDRTYPE_EXECUTABLE_FIXED:
2220             case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2221             case KLDRTYPE_EXECUTABLE_PIC:
2222                 if (!fExe)
2223                     rc = KERR_GENERAL_FAILURE;
2224                 break;
2225 
2226             case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2227             case KLDRTYPE_SHARED_LIBRARY_PIC:
2228             case KLDRTYPE_SHARED_LIBRARY_FIXED:
2229                 if (fExe)
2230                     rc = KERR_GENERAL_FAILURE;
2231                 break;
2232 
2233             default:
2234                 rc = KERR_GENERAL_FAILURE;
2235                 break;
2236         }
2237         if (rc == 0)
2238         {
2239             KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2240             if (cImports >= 0)
2241             {
2242                 /*
2243                  * Create the entry.
2244                  */
2245                 KSIZE     cbPath = kHlpStrLen(pszPath) + 1;
2246                 PKWMODULE pMod   = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2247                                                          + sizeof(pMod) * cImports
2248                                                          + cbPath
2249                                                          + cbPath * 2 * sizeof(wchar_t));
2250                 if (pMod)
2251                 {
2252                     KBOOL fFixed;
2253 
2254                     pMod->cRefs         = 1;
2255                     pMod->offFilename   = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2256                     pMod->uHashPath     = uHashPath;
2257                     pMod->fExe          = fExe;
2258                     pMod->fNative       = K_FALSE;
2259                     pMod->pLdrMod       = pLdrMod;
2260                     pMod->u.Manual.cImpMods = (KU32)cImports;
2261 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2262                     pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2263 #endif
2264                     pMod->u.Manual.fUseLdBuf        = K_FALSE;
2265                     pMod->u.Manual.fCanDoQuick      = K_FALSE;
2266                     pMod->u.Manual.cQuickZeroChunks = 0;
2267                     pMod->u.Manual.cQuickCopyChunks = 0;
2268                     pMod->u.Manual.idxTls           = KU32_MAX;
2269                     pMod->u.Manual.offTlsInitData   = KU32_MAX;
2270                     pMod->u.Manual.cbTlsInitData    = 0;
2271                     pMod->u.Manual.cbTlsAlloc       = 0;
2272                     pMod->u.Manual.cTlsCallbacks    = 0;
2273                     pMod->u.Manual.offTlsCallbacks  = 0;
2274                     pMod->pszPath       = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2275                     pMod->pwszPath      = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2276                     kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2277 
2278                     /*
2279                      * Figure out where to load it and get memory there.
2280                      */
2281                     fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2282                           || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2283                     pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2284                     pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2285                     if (   !fFixed
2286                         || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2287                         || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2288                         || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2289                         rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2290                     else
2291                         pMod->u.Manual.fUseLdBuf = K_TRUE;
2292                     if (rc == 0)
2293                     {
2294                         rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2295                         if (rc == 0)
2296                         {
2297                             KI32 iImp;
2298 
2299                             /*
2300                              * Link the module (unless it's an executable image) and process the imports.
2301                              */
2302                             pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2303                             if (!fExe)
2304                                 kwLdrModuleLink(pMod);
2305                             KW_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2306                                     pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2307                             KW_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2308                             kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2309 
2310                             for (iImp = 0; iImp < cImports; iImp++)
2311                             {
2312                                 char szName[1024];
2313                                 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2314                                 if (rc == 0)
2315                                 {
2316                                     rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2317                                                                      &pMod->u.Manual.apImpMods[iImp]);
2318                                     if (rc == 0)
2319                                         continue;
2320                                 }
2321                                 break;
2322                             }
2323 
2324                             if (rc == 0)
2325                             {
2326                                 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2327                                                     kwLdrModuleGetImportCallback, pMod);
2328                                 if (rc == 0)
2329                                 {
2330 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2331                                     /*
2332                                      * Find the function table.  No validation here because the
2333                                      * loader did that already, right...
2334                                      */
2335                                     KU8                        *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2336                                     IMAGE_NT_HEADERS const     *pNtHdrs;
2337                                     IMAGE_DATA_DIRECTORY const *pXcptDir;
2338                                     if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2339                                         pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2340                                     else
2341                                         pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2342                                     pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2343                                     kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2344                                     if (pXcptDir->Size > 0)
2345                                     {
2346                                         pMod->u.Manual.cFunctions  = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2347                                         kHlpAssert(   pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2348                                                    == pXcptDir->Size);
2349                                         pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2350                                     }
2351                                     else
2352                                     {
2353                                         pMod->u.Manual.cFunctions  = 0;
2354                                         pMod->u.Manual.paFunctions = NULL;
2355                                     }
2356 #endif
2357 
2358                                     kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2359 
2360                                     rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2361                                     if (rc == 0)
2362                                     {
2363                                         /*
2364                                          * Final finish.
2365                                          */
2366                                         pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2367                                         pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2368                                         g_cModules++;
2369                                         g_cNonNativeModules++;
2370                                         return pMod;
2371                                     }
2372                                 }
2373                                 else
2374                                     kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
2375                             }
2376 
2377                             kwLdrModuleRelease(pMod);
2378                             return NULL;
2379                         }
2380 
2381                         kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2382                         kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2383                     }
2384                     else if (fFixed)
2385                         kwErrPrintf("Failed to allocate %#x bytes at %p\n",
2386                                     pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
2387                     else
2388                         kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2389                 }
2390             }
2391         }
2392         kLdrModClose(pLdrMod);
2393     }
2394     else
2395         kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
2396     return NULL;
2397 }
2398 
2399 
2400 /** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
kwLdrModuleGetImportCallback(PKLDRMOD pMod,KU32 iImport,KU32 iSymbol,const char * pchSymbol,KSIZE cchSymbol,const char * pszVersion,PKLDRADDR puValue,KU32 * pfKind,void * pvUser)2401 static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
2402                                         const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
2403 {
2404     PKWMODULE pCurMod = (PKWMODULE)pvUser;
2405     PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
2406     int rc;
2407     K_NOREF(pMod);
2408 
2409     if (pImpMod->fNative)
2410         rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
2411                                 iSymbol, pchSymbol, cchSymbol, pszVersion,
2412                                 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2413                                 puValue, pfKind);
2414     else
2415         rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
2416                                 iSymbol, pchSymbol, cchSymbol, pszVersion,
2417                                 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2418                                 puValue, pfKind);
2419     if (rc == 0)
2420     {
2421         KU32 i = g_cSandboxReplacements;
2422         while (i-- > 0)
2423             if (   g_aSandboxReplacements[i].cchFunction == cchSymbol
2424                 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
2425             {
2426                 if (   !g_aSandboxReplacements[i].pszModule
2427                     || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
2428                 {
2429                     if (   pCurMod->fExe
2430                         || !g_aSandboxReplacements[i].fOnlyExe)
2431                     {
2432                         KW_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
2433                         *puValue = g_aSandboxReplacements[i].pfnReplacement;
2434                     }
2435                     break;
2436                 }
2437             }
2438     }
2439 
2440     //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
2441     KW_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
2442     return rc;
2443 
2444 }
2445 
2446 
2447 /**
2448  * Gets the main entrypoint for a module.
2449  *
2450  * @returns 0 on success, KERR on failure
2451  * @param   pMod                The module.
2452  * @param   puAddrMain          Where to return the address.
2453  */
kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod,KUPTR * puAddrMain)2454 static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
2455 {
2456     KLDRADDR uLdrAddrMain;
2457     int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
2458     if (rc == 0)
2459     {
2460         *puAddrMain = (KUPTR)uLdrAddrMain;
2461         return 0;
2462     }
2463     return rc;
2464 }
2465 
2466 
2467 /**
2468  * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
2469  *
2470  * @returns K_TRUE/K_FALSE.
2471  * @param   pszFilename         The filename (no path).
2472  * @param   enmLocation         The location.
2473  */
kwLdrModuleShouldDoNativeReplacements(const char * pszFilename,KWLOCATION enmLocation)2474 static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
2475 {
2476     if (enmLocation != KWLOCATION_SYSTEM32)
2477         return K_TRUE;
2478     return kHlpStrNICompAscii(pszFilename, TUPLE("msvc"))   == 0
2479         || kHlpStrNICompAscii(pszFilename, TUPLE("msdis"))  == 0
2480         || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb"))  == 0;
2481 }
2482 
2483 
2484 /**
2485  * Lazily initializes the g_pWinSys32 variable.
2486  */
kwLdrResolveWinSys32(void)2487 static PKFSDIR kwLdrResolveWinSys32(void)
2488 {
2489     KFSLOOKUPERROR  enmError;
2490     PKFSDIR         pWinSys32;
2491 
2492     /* Get the path first. */
2493     char            szSystem32[MAX_PATH];
2494     if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
2495     {
2496         kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
2497         strcpy(szSystem32, "C:\\Windows\\System32");
2498     }
2499 
2500     /* Look it up and verify it. */
2501     pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
2502     if (pWinSys32)
2503     {
2504         if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
2505         {
2506             g_pWinSys32 = pWinSys32;
2507             return pWinSys32;
2508         }
2509 
2510         kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
2511     }
2512     else
2513         kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
2514     return NULL;
2515 }
2516 
2517 
2518 /**
2519  * Whether we can load this DLL natively or not.
2520  *
2521  * @returns K_TRUE/K_FALSE.
2522  * @param   pszFilename         The filename (no path).
2523  * @param   enmLocation         The location.
2524  * @param   pszFullPath         The full filename and path.
2525  */
kwLdrModuleCanLoadNatively(const char * pszFilename,KWLOCATION enmLocation,const char * pszFullPath)2526 static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
2527 {
2528     if (enmLocation == KWLOCATION_SYSTEM32)
2529         return K_TRUE;
2530     if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
2531         return K_TRUE;
2532 
2533     /* If the location is unknown, we must check if it's some dynamic loading
2534        of a SYSTEM32 DLL with a full path.  We do not want to load these ourselves! */
2535     if (enmLocation == KWLOCATION_UNKNOWN)
2536     {
2537         PKFSDIR pWinSys32 = g_pWinSys32;
2538         if (!pWinSys32)
2539             pWinSys32 = kwLdrResolveWinSys32();
2540         if (pWinSys32)
2541         {
2542             KFSLOOKUPERROR enmError;
2543             PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
2544             if (pFsObj)
2545             {
2546                 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
2547                 kFsCacheObjRelease(g_pFsCache, pFsObj);
2548                 if (fInWinSys32)
2549                     return K_TRUE;
2550             }
2551         }
2552     }
2553 
2554     return kHlpStrNICompAscii(pszFilename, TUPLE("msvc"))   == 0
2555         || kHlpStrNICompAscii(pszFilename, TUPLE("msdis"))  == 0
2556         || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb"))  == 0;
2557 }
2558 
2559 
2560 /**
2561  * Check if the path leads to a regular file (that exists).
2562  *
2563  * @returns K_TRUE / K_FALSE
2564  * @param   pszPath             Path to the file to check out.
2565  */
kwLdrModuleIsRegularFile(const char * pszPath)2566 static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
2567 {
2568     /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
2569     KSIZE cchPath = kHlpStrLen(pszPath);
2570     if (   cchPath > 3
2571         && pszPath[cchPath - 4] == '.'
2572         && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
2573         && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
2574         && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
2575     {
2576         KFSLOOKUPERROR enmError;
2577         PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
2578         if (pFsObj)
2579         {
2580             KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
2581             kFsCacheObjRelease(g_pFsCache, pFsObj);
2582             return fRc;
2583         }
2584     }
2585     else
2586     {
2587         BirdStat_T Stat;
2588         int rc = birdStatFollowLink(pszPath, &Stat);
2589         if (rc == 0)
2590         {
2591             if (S_ISREG(Stat.st_mode))
2592                 return K_TRUE;
2593         }
2594     }
2595     return K_FALSE;
2596 }
2597 
2598 
2599 /**
2600  * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
2601  *
2602  * If the file exists, we consult the module hash table before trying to load it
2603  * off the disk.
2604  *
2605  * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
2606  *          failure.
2607  * @param   pszPath             The name of the import module.
2608  * @param   enmLocation         The location we're searching.  This is used in
2609  *                              the heuristics for determining if we can use the
2610  *                              native loader or need to sandbox the DLL.
2611  * @param   pExe                The executable (optional).
2612  * @param   pszSearchPath       The PATH to search (optional).
2613  */
kwLdrModuleTryLoadDll(const char * pszPath,KWLOCATION enmLocation,PKWMODULE pExeMod,const char * pszSearchPath)2614 static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
2615 {
2616     /*
2617      * Does the file exists and is it a regular file?
2618      */
2619     if (kwLdrModuleIsRegularFile(pszPath))
2620     {
2621         /*
2622          * Yes! Normalize it and look it up in the hash table.
2623          */
2624         char szNormPath[1024];
2625         int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
2626         if (rc == 0)
2627         {
2628             const char *pszName;
2629             KU32 const  uHashPath = kwStrHash(szNormPath);
2630             unsigned    idxHash   = uHashPath % K_ELEMENTS(g_apModules);
2631             PKWMODULE   pMod      = g_apModules[idxHash];
2632             if (pMod)
2633             {
2634                 do
2635                 {
2636                     if (   pMod->uHashPath == uHashPath
2637                         && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
2638                         return kwLdrModuleRetain(pMod);
2639                     pMod = pMod->pNext;
2640                 } while (pMod);
2641             }
2642 
2643             /*
2644              * Not in the hash table, so we have to load it from scratch.
2645              */
2646             pszName = kHlpGetFilename(szNormPath);
2647             if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
2648                 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
2649                                                kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
2650             else
2651                 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
2652             if (pMod)
2653                 return pMod;
2654             return (PKWMODULE)~(KUPTR)0;
2655         }
2656     }
2657     return NULL;
2658 }
2659 
2660 
2661 /**
2662  * Gets a reference to the module by the given name.
2663  *
2664  * We must do the search path thing, as our hash table may multiple DLLs with
2665  * the same base name due to different tools version and similar.  We'll use a
2666  * modified search sequence, though.  No point in searching the current
2667  * directory for instance.
2668  *
2669  * @returns 0 on success, KERR on failure.
2670  * @param   pszName             The name of the import module.
2671  * @param   pExe                The executable (optional).
2672  * @param   pImporter           The module doing the importing (optional).
2673  * @param   pszSearchPath       The PATH to search (optional).
2674  * @param   ppMod               Where to return the module pointer w/ reference.
2675  */
kwLdrModuleResolveAndLookup(const char * pszName,PKWMODULE pExe,PKWMODULE pImporter,const char * pszSearchPath,PKWMODULE * ppMod)2676 static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
2677                                        const char *pszSearchPath, PKWMODULE *ppMod)
2678 {
2679     KSIZE const cchName = kHlpStrLen(pszName);
2680     char        szPath[1024];
2681     char       *psz;
2682     PKWMODULE   pMod = NULL;
2683     KBOOL       fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
2684     KSIZE       cchSuffix   = fNeedSuffix ? 4 : 0;
2685 
2686 
2687     /* The import path. */
2688     if (pMod == NULL && pImporter != NULL)
2689     {
2690         if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
2691             return KERR_BUFFER_OVERFLOW;
2692 
2693         psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
2694         if (fNeedSuffix)
2695             kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2696         pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
2697     }
2698 
2699     /* Application directory first. */
2700     if (pMod == NULL && pExe != NULL && pExe != pImporter)
2701     {
2702         if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
2703             return KERR_BUFFER_OVERFLOW;
2704         psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
2705         if (fNeedSuffix)
2706             kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2707         pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
2708     }
2709 
2710     /* The windows directory. */
2711     if (pMod == NULL)
2712     {
2713         UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
2714         if (   cchDir <= 2
2715             || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
2716             return KERR_BUFFER_OVERFLOW;
2717         szPath[cchDir++] = '\\';
2718         psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
2719         if (fNeedSuffix)
2720             kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2721         pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2722     }
2723 
2724     /* The path. */
2725     if (   pMod == NULL
2726         && pszSearchPath)
2727     {
2728         const char *pszCur = pszSearchPath;
2729         while (*pszCur != '\0')
2730         {
2731             /* Find the end of the component */
2732             KSIZE cch = 0;
2733             while (pszCur[cch] != ';' && pszCur[cch] != '\0')
2734                 cch++;
2735 
2736             if (   cch > 0 /* wrong, but whatever */
2737                 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
2738             {
2739                 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
2740                 if (   szPath[cch - 1] != ':'
2741                     && szPath[cch - 1] != '/'
2742                     && szPath[cch - 1] != '\\')
2743                     *pszDst++ = '\\';
2744                 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
2745                 if (fNeedSuffix)
2746                     pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
2747                 *pszDst = '\0';
2748 
2749                 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2750                 if (pMod)
2751                     break;
2752             }
2753 
2754             /* Advance */
2755             pszCur += cch;
2756             while (*pszCur == ';')
2757                 pszCur++;
2758         }
2759     }
2760 
2761     /* Return. */
2762     if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
2763     {
2764         *ppMod = pMod;
2765         return 0;
2766     }
2767     *ppMod = NULL;
2768     return KERR_GENERAL_FAILURE;
2769 }
2770 
2771 
2772 /**
2773  * Does the TLS memory initialization for a module on the current thread.
2774  *
2775  * @returns 0 on success, error on failure.
2776  * @param   pMod                The module.
2777  */
kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)2778 static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
2779 {
2780     if (pMod->u.Manual.idxTls != KU32_MAX)
2781     {
2782         PTEB pTeb = NtCurrentTeb();
2783         void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
2784         KU8   *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
2785         KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
2786                    pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
2787         if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
2788             kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
2789         if (pMod->u.Manual.cbTlsInitData)
2790             kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
2791     }
2792     return 0;
2793 }
2794 
2795 
2796 /**
2797  * Does the TLS callbacks for a module.
2798  *
2799  * @param   pMod                The module.
2800  * @param   dwReason            The callback reason.
2801  */
kwLdrCallTlsCallbacks(PKWMODULE pMod,DWORD dwReason)2802 static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
2803 {
2804     if (pMod->u.Manual.cTlsCallbacks)
2805     {
2806         PIMAGE_TLS_CALLBACK *pCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
2807         do
2808         {
2809             KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *pCallback, pMod->hOurMod, dwReason));
2810             (*pCallback)(pMod->hOurMod, dwReason, 0);
2811         } while (*++pCallback);
2812     }
2813 }
2814 
2815 
2816 /**
2817  * Does module initialization starting at @a pMod.
2818  *
2819  * This is initially used on the executable.  Later it is used by the
2820  * LoadLibrary interceptor.
2821  *
2822  * @returns 0 on success, error on failure.
2823  * @param   pMod                The module to initialize.
2824  */
kwLdrModuleInitTree(PKWMODULE pMod)2825 static int kwLdrModuleInitTree(PKWMODULE pMod)
2826 {
2827     int rc = 0;
2828     if (!pMod->fNative)
2829     {
2830         KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
2831                    pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
2832 
2833         /*
2834          * Need to copy bits?
2835          */
2836         if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
2837         {
2838             if (pMod->u.Manual.fUseLdBuf)
2839             {
2840 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2841                 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
2842                 {
2843                     BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
2844                     kHlpAssert(fRc); K_NOREF(fRc);
2845                 }
2846 #endif
2847                 g_pModPrevInLdBuf = g_pModInLdBuf;
2848                 g_pModInLdBuf = pMod;
2849             }
2850 
2851             /* Do quick zeroing and copying when we can. */
2852             pMod->u.Manual.fCanDoQuick = K_FALSE;
2853             if (   pMod->u.Manual.fCanDoQuick
2854                 && (   !pMod->u.Manual.fUseLdBuf
2855                     || g_pModPrevInLdBuf == pMod))
2856             {
2857                 /* Zero first. */
2858                 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
2859                 switch (pMod->u.Manual.cQuickZeroChunks)
2860                 {
2861                     case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
2862                     case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
2863                     case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
2864                     case 0: break;
2865                 }
2866 
2867                 /* Then copy. */
2868                 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
2869                 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
2870                 switch (pMod->u.Manual.cQuickCopyChunks)
2871                 {
2872                     case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
2873                                         pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
2874                     case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
2875                                         pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
2876                     case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
2877                                         pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
2878                     case 0: break;
2879                 }
2880             }
2881             /* Must copy the whole image. */
2882             else
2883             {
2884                 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
2885                 pMod->u.Manual.fCanDoQuick = K_TRUE;
2886             }
2887             pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
2888         }
2889 
2890 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2891         /*
2892          * Need to register function table?
2893          */
2894         if (   !pMod->u.Manual.fRegisteredFunctionTable
2895             && pMod->u.Manual.cFunctions > 0)
2896         {
2897             pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
2898                                                                           pMod->u.Manual.cFunctions,
2899                                                                           (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
2900             kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
2901         }
2902 #endif
2903 
2904 
2905         if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
2906         {
2907             /*
2908              * Must do imports first, but mark our module as being initialized to avoid
2909              * endless recursion should there be a dependency loop.
2910              */
2911             KSIZE iImp;
2912             pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
2913 
2914             for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
2915             {
2916                 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
2917                 if (rc != 0)
2918                     return rc;
2919             }
2920 
2921             /* Do TLS allocations for module init? */
2922             rc = kwLdrCallTlsAllocateAndInit(pMod);
2923             if (rc != 0)
2924                 return rc;
2925             if (pMod->u.Manual.cTlsCallbacks > 0)
2926                 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
2927 
2928             /* Finally call the entry point. */
2929             rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
2930             if (rc == 0)
2931                 pMod->u.Manual.enmState = KWMODSTATE_READY;
2932             else
2933                 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
2934         }
2935     }
2936     return rc;
2937 }
2938 
2939 
2940 /**
2941  * Looks up a module handle for a tool.
2942  *
2943  * @returns Referenced loader module on success, NULL on if not found.
2944  * @param   pTool               The tool.
2945  * @param   hmod                The module handle.
2946  */
kwToolLocateModuleByHandle(PKWTOOL pTool,HMODULE hmod)2947 static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
2948 {
2949     KUPTR const     uHMod = (KUPTR)hmod;
2950     PKWMODULE      *papMods;
2951     KU32            iEnd;
2952     KU32            i;
2953     PKWDYNLOAD      pDynLoad;
2954 
2955     /* The executable. */
2956     if (   hmod == NULL
2957         || pTool->u.Sandboxed.pExe->hOurMod == hmod)
2958         return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
2959 
2960     /*
2961      * Binary lookup using the module table.
2962      */
2963     papMods = pTool->u.Sandboxed.papModules;
2964     iEnd    = pTool->u.Sandboxed.cModules;
2965     if (iEnd)
2966     {
2967         KU32 iStart  = 0;
2968         i = iEnd / 2;
2969         for (;;)
2970         {
2971             KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
2972             if (uHMod < uHModThis)
2973             {
2974                 iEnd = i--;
2975                 if (iStart <= i)
2976                 { }
2977                 else
2978                     break;
2979             }
2980             else if (uHMod != uHModThis)
2981             {
2982                 iStart = ++i;
2983                 if (i < iEnd)
2984                 { }
2985                 else
2986                     break;
2987             }
2988             else
2989                 return kwLdrModuleRetain(papMods[i]);
2990 
2991             i = iStart + (iEnd - iStart) / 2;
2992         }
2993 
2994 #ifndef NDEBUG
2995         iStart = pTool->u.Sandboxed.cModules;
2996         while (--iStart > 0)
2997             kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
2998         kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
2999         kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod > uHMod);
3000 #endif
3001     }
3002 
3003     /*
3004      * Dynamically loaded images.
3005      */
3006     for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3007         if (pDynLoad->hmod == hmod)
3008         {
3009             if (pDynLoad->pMod)
3010                 return kwLdrModuleRetain(pDynLoad->pMod);
3011             KWFS_TODO();
3012             return NULL;
3013         }
3014 
3015     return NULL;
3016 }
3017 
3018 /**
3019  * Adds the given module to the tool import table.
3020  *
3021  * @returns 0 on success, non-zero on failure.
3022  * @param   pTool               The tool.
3023  * @param   pMod                The module.
3024  */
kwToolAddModule(PKWTOOL pTool,PKWMODULE pMod)3025 static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3026 {
3027     /*
3028      * Binary lookup. Locating the right slot for it, return if already there.
3029      */
3030     KUPTR const     uHMod   = (KUPTR)pMod->hOurMod;
3031     PKWMODULE      *papMods = pTool->u.Sandboxed.papModules;
3032     KU32            iEnd    = pTool->u.Sandboxed.cModules;
3033     KU32            i;
3034     if (iEnd)
3035     {
3036         KU32        iStart  = 0;
3037         i = iEnd / 2;
3038         for (;;)
3039         {
3040             KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3041             if (uHMod < uHModThis)
3042             {
3043                 iEnd = i;
3044                 if (iStart < i)
3045                 { }
3046                 else
3047                     break;
3048             }
3049             else if (uHMod != uHModThis)
3050             {
3051                 iStart = ++i;
3052                 if (i < iEnd)
3053                 { }
3054                 else
3055                     break;
3056             }
3057             else
3058             {
3059                 /* Already there in the table. */
3060                 return 0;
3061             }
3062 
3063             i = iStart + (iEnd - iStart) / 2;
3064         }
3065 #ifndef NDEBUG
3066         iStart = pTool->u.Sandboxed.cModules;
3067         while (--iStart > 0)
3068         {
3069             kHlpAssert(papMods[iStart] != pMod);
3070             kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3071         }
3072         kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3073         kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod > uHMod);
3074 #endif
3075     }
3076     else
3077         i = 0;
3078 
3079     /*
3080      * Grow the table?
3081      */
3082     if ((pTool->u.Sandboxed.cModules % 16) == 0)
3083     {
3084         void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3085         if (!pvNew)
3086             return KERR_NO_MEMORY;
3087         pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3088     }
3089 
3090     /* Insert it. */
3091     if (i != pTool->u.Sandboxed.cModules)
3092         kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3093     papMods[i] = kwLdrModuleRetain(pMod);
3094     pTool->u.Sandboxed.cModules++;
3095     KW_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3096     return 0;
3097 }
3098 
3099 
3100 /**
3101  * Adds the given module and all its imports to the
3102  *
3103  * @returns 0 on success, non-zero on failure.
3104  * @param   pTool               The tool.
3105  * @param   pMod                The module.
3106  */
kwToolAddModuleAndImports(PKWTOOL pTool,PKWMODULE pMod)3107 static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
3108 {
3109     int rc = kwToolAddModule(pTool, pMod);
3110     if (!pMod->fNative && rc == 0)
3111     {
3112         KSIZE iImp = pMod->u.Manual.cImpMods;
3113         while (iImp-- > 0)
3114         {
3115             rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
3116             if (rc == 0)
3117             { }
3118             else
3119                 break;
3120         }
3121     }
3122     return 0;
3123 }
3124 
3125 
3126 /**
3127  * Creates a tool entry and inserts it.
3128  *
3129  * @returns Pointer to the tool entry.  NULL on failure.
3130  * @param   pToolFsObj          The file object of the tool.  The created tool
3131  *                              will be associated with it.
3132  *
3133  *                              A reference is donated by the caller and must be
3134  *                              released.
3135  * @param   pszSearchPath       The PATH environment variable value, or NULL.
3136  */
kwToolEntryCreate(PKFSOBJ pToolFsObj,const char * pszSearchPath)3137 static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
3138 {
3139     KSIZE   cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
3140     KSIZE   cbPath  = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
3141     PKWTOOL pTool   = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
3142                                                       sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
3143     if (pTool)
3144     {
3145         KBOOL fRc;
3146         pTool->pwszPath = (wchar_t const *)(pTool + 1);
3147         fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
3148         kHlpAssert(fRc); K_NOREF(fRc);
3149 
3150         pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
3151         fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
3152         kHlpAssert(fRc);
3153 
3154         pTool->enmType = KWTOOLTYPE_SANDBOXED;
3155         pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
3156                                                              NULL /*pEexeMod*/, pszSearchPath);
3157         if (pTool->u.Sandboxed.pExe)
3158         {
3159             int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
3160             if (rc == 0)
3161             {
3162                 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
3163                     pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
3164                 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
3165                     pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
3166                 else
3167                     pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
3168                 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
3169             }
3170             else
3171             {
3172                 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
3173                 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
3174                 pTool->u.Sandboxed.pExe = NULL;
3175                 pTool->enmType = KWTOOLTYPE_EXEC;
3176             }
3177         }
3178         else
3179             pTool->enmType = KWTOOLTYPE_EXEC;
3180 
3181         kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3182         g_cTools++;
3183         return pTool;
3184     }
3185     kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3186     return NULL;
3187 }
3188 
3189 
3190 /**
3191  * Looks up the given tool, creating a new tool table entry if necessary.
3192  *
3193  * @returns Pointer to the tool entry.  NULL on failure (fully bitched).
3194  * @param   pszExe              The executable for the tool (not normalized).
3195  * @param   cEnvVars            Number of environment varibles.
3196  * @param   papszEnvVars        Environment variables.  For getting the PATH.
3197  */
kwToolLookup(const char * pszExe,KU32 cEnvVars,const char ** papszEnvVars)3198 static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
3199 {
3200     /*
3201      * We associate the tools instances with the file system objects.
3202      *
3203      * We'd like to do the lookup without invaliding the volatile parts of the
3204      * cache, thus the double lookup here.  The cache gets invalidate later on.
3205      */
3206     KFSLOOKUPERROR  enmError;
3207     PKFSOBJ         pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3208     if (   !pToolFsObj
3209         || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
3210     {
3211         kFsCacheInvalidateCustomBoth(g_pFsCache);
3212         pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3213     }
3214     if (pToolFsObj)
3215     {
3216         if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
3217         {
3218             const char *pszSearchPath;
3219             PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
3220             if (pTool)
3221             {
3222                 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3223                 return pTool;
3224             }
3225 
3226             /*
3227              * Need to create a new tool.
3228              */
3229             pszSearchPath = NULL;
3230             while (cEnvVars-- > 0)
3231                 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
3232                 {
3233                     pszSearchPath = &papszEnvVars[cEnvVars][5];
3234                     break;
3235                 }
3236 
3237             pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
3238             if (pTool)
3239                 return pTool;
3240 
3241             kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
3242         }
3243         else
3244         {
3245             kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3246             kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
3247                         pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
3248                         g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
3249         }
3250     }
3251     else
3252         kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
3253     return NULL;
3254 }
3255 
3256 
3257 
3258 /*
3259  *
3260  * File system cache.
3261  * File system cache.
3262  * File system cache.
3263  *
3264  */
3265 
3266 
3267 /**
3268  * This is for kDep.
3269  */
kwFsPathExists(const char * pszPath)3270 int kwFsPathExists(const char *pszPath)
3271 {
3272     BirdTimeSpec_T  TsIgnored;
3273     KFSLOOKUPERROR  enmError;
3274     PKFSOBJ         pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3275     if (pFsObj)
3276     {
3277         kFsCacheObjRelease(g_pFsCache, pFsObj);
3278         return 1;
3279     }
3280     return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
3281 }
3282 
3283 
3284 /* duplicated in dir-nt-bird.c */
nt_fullpath_cached(const char * pszPath,char * pszFull,size_t cbFull)3285 void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
3286 {
3287     KFSLOOKUPERROR  enmError;
3288     PKFSOBJ         pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
3289     if (pPathObj)
3290     {
3291         KSIZE off = pPathObj->cchParent;
3292         if (off > 0)
3293         {
3294             KSIZE offEnd = off + pPathObj->cchName;
3295             if (offEnd < cbFull)
3296             {
3297                 PKFSDIR pAncestor;
3298 
3299                 pszFull[off + pPathObj->cchName] = '\0';
3300                 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
3301 
3302                 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3303                 {
3304                     kHlpAssert(off > 1);
3305                     kHlpAssert(pAncestor != NULL);
3306                     kHlpAssert(pAncestor->Obj.cchName > 0);
3307                     pszFull[--off] = '/';
3308                     off -= pAncestor->Obj.cchName;
3309                     kHlpAssert(pAncestor->Obj.cchParent == off);
3310                     memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3311                 }
3312                 kFsCacheObjRelease(g_pFsCache, pPathObj);
3313                 return;
3314             }
3315         }
3316         else
3317         {
3318             if ((size_t)pPathObj->cchName + 1 < cbFull)
3319             {
3320                 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
3321                 pszFull[pPathObj->cchName] = '/';
3322                 pszFull[pPathObj->cchName + 1] = '\0';
3323 
3324                 kFsCacheObjRelease(g_pFsCache, pPathObj);
3325                 return;
3326             }
3327         }
3328 
3329         /* do fallback. */
3330         kHlpAssertFailed();
3331         kFsCacheObjRelease(g_pFsCache, pPathObj);
3332     }
3333 
3334     nt_fullpath(pszPath, pszFull, cbFull);
3335 }
3336 
3337 
3338 /**
3339  * Helper for getting the extension of a UTF-16 path.
3340  *
3341  * @returns Pointer to the extension or the terminator.
3342  * @param   pwszPath        The path.
3343  * @param   pcwcExt         Where to return the length of the extension.
3344  */
kwFsPathGetExtW(wchar_t const * pwszPath,KSIZE * pcwcExt)3345 static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
3346 {
3347     wchar_t const *pwszName = pwszPath;
3348     wchar_t const *pwszExt  = NULL;
3349     for (;;)
3350     {
3351         wchar_t const wc = *pwszPath++;
3352         if (wc == '.')
3353             pwszExt = pwszPath;
3354         else if (wc == '/' || wc == '\\' || wc == ':')
3355         {
3356             pwszName = pwszPath;
3357             pwszExt = NULL;
3358         }
3359         else if (wc == '\0')
3360         {
3361             if (pwszExt)
3362             {
3363                 *pcwcExt = pwszPath - pwszExt - 1;
3364                 return pwszExt;
3365             }
3366             *pcwcExt = 0;
3367             return pwszPath - 1;
3368         }
3369     }
3370 }
3371 
3372 
3373 
3374 /**
3375  * Parses the argument string passed in as pszSrc.
3376  *
3377  * @returns size of the processed arguments.
3378  * @param   pszSrc  Pointer to the commandline that's to be parsed.
3379  * @param   pcArgs  Where to return the number of arguments.
3380  * @param   argv    Pointer to argument vector to put argument pointers in. NULL allowed.
3381  * @param   pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
3382  *
3383  * @remarks Lifted from startuphacks-win.c
3384  */
parse_args(const char * pszSrc,int * pcArgs,char ** argv,char * pchPool)3385 static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
3386 {
3387     int   bs;
3388     char  chQuote;
3389     char *pfFlags;
3390     int   cbArgs;
3391     int   cArgs;
3392 
3393 #define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
3394 #define PUTV    do { ++cArgs;  if (argv != NULL) *argv++ = pchPool; } while (0)
3395 #define WHITE(c) ((c) == ' ' || (c) == '\t')
3396 
3397 #define _ARG_DQUOTE   0x01          /* Argument quoted (")                  */
3398 #define _ARG_RESPONSE 0x02          /* Argument read from response file     */
3399 #define _ARG_WILDCARD 0x04          /* Argument expanded from wildcard      */
3400 #define _ARG_ENV      0x08          /* Argument from environment            */
3401 #define _ARG_NONZERO  0x80          /* Always set, to avoid end of string   */
3402 
3403     cArgs  = 0;
3404     cbArgs = 0;
3405 
3406 #if 0
3407     /* argv[0] */
3408     PUTC((char)_ARG_NONZERO);
3409     PUTV;
3410     for (;;)
3411     {
3412         PUTC(*pszSrc);
3413         if (*pszSrc == 0)
3414             break;
3415         ++pszSrc;
3416     }
3417     ++pszSrc;
3418 #endif
3419 
3420     for (;;)
3421     {
3422         while (WHITE(*pszSrc))
3423             ++pszSrc;
3424         if (*pszSrc == 0)
3425             break;
3426         pfFlags = pchPool;
3427         PUTC((char)_ARG_NONZERO);
3428         PUTV;
3429         bs = 0; chQuote = 0;
3430         for (;;)
3431         {
3432             if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
3433             {
3434                 while (bs >= 2)
3435                 {
3436                     PUTC('\\');
3437                     bs -= 2;
3438                 }
3439                 if (bs & 1)
3440                     PUTC(*pszSrc);
3441                 else
3442                 {
3443                     chQuote = chQuote ? 0 : *pszSrc;
3444                     if (pfFlags != NULL)
3445                         *pfFlags |= _ARG_DQUOTE;
3446                 }
3447                 bs = 0;
3448             }
3449             else if (*pszSrc == '\\')
3450                 ++bs;
3451             else
3452             {
3453                 while (bs != 0)
3454                 {
3455                     PUTC('\\');
3456                     --bs;
3457                 }
3458                 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
3459                     break;
3460                 PUTC(*pszSrc);
3461             }
3462             ++pszSrc;
3463         }
3464         PUTC(0);
3465     }
3466 
3467     *pcArgs = cArgs;
3468     return cbArgs;
3469 }
3470 
3471 
3472 
3473 
3474 /*
3475  *
3476  * Process and thread related APIs.
3477  * Process and thread related APIs.
3478  * Process and thread related APIs.
3479  *
3480  */
3481 
3482 /** Common worker for ExitProcess(), exit() and friends.  */
kwSandboxDoExit(int uExitCode)3483 static void WINAPI kwSandboxDoExit(int uExitCode)
3484 {
3485     if (g_Sandbox.idMainThread == GetCurrentThreadId())
3486     {
3487         PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
3488 
3489         g_Sandbox.rcExitCode = (int)uExitCode;
3490 
3491         /* Before we jump, restore the TIB as we're not interested in any
3492            exception chain stuff installed by the sandboxed executable. */
3493         *pTib = g_Sandbox.TibMainThread;
3494         pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
3495 
3496         longjmp(g_Sandbox.JmpBuf, 1);
3497     }
3498     KWFS_TODO();
3499 }
3500 
3501 
3502 /** ExitProcess replacement.  */
kwSandbox_Kernel32_ExitProcess(UINT uExitCode)3503 static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
3504 {
3505     KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
3506     kwSandboxDoExit((int)uExitCode);
3507 }
3508 
3509 
3510 /** ExitProcess replacement.  */
kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess,UINT uExitCode)3511 static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
3512 {
3513     if (hProcess == GetCurrentProcess())
3514         kwSandboxDoExit(uExitCode);
3515     KWFS_TODO();
3516     return TerminateProcess(hProcess, uExitCode);
3517 }
3518 
3519 
3520 /** Normal CRT exit(). */
kwSandbox_msvcrt_exit(int rcExitCode)3521 static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
3522 {
3523     KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
3524     kwSandboxDoExit(rcExitCode);
3525 }
3526 
3527 
3528 /** Quick CRT _exit(). */
kwSandbox_msvcrt__exit(int rcExitCode)3529 static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
3530 {
3531     /* Quick. */
3532     KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
3533     kwSandboxDoExit(rcExitCode);
3534 }
3535 
3536 
3537 /** Return to caller CRT _cexit(). */
kwSandbox_msvcrt__cexit(int rcExitCode)3538 static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
3539 {
3540     KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
3541     kwSandboxDoExit(rcExitCode);
3542 }
3543 
3544 
3545 /** Quick return to caller CRT _c_exit(). */
kwSandbox_msvcrt__c_exit(int rcExitCode)3546 static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
3547 {
3548     KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
3549     kwSandboxDoExit(rcExitCode);
3550 }
3551 
3552 
3553 /** Runtime error and exit _amsg_exit(). */
kwSandbox_msvcrt__amsg_exit(int iMsgNo)3554 static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
3555 {
3556     KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
3557     kwSandboxDoExit(255);
3558 }
3559 
3560 
3561 /** CRT - terminate().  */
kwSandbox_msvcrt_terminate(void)3562 static void __cdecl kwSandbox_msvcrt_terminate(void)
3563 {
3564     KW_LOG(("\nRuntime - terminate!\n"));
3565     kwSandboxDoExit(254);
3566 }
3567 
3568 
3569 /** CRT - _onexit   */
kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)3570 static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
3571 {
3572     //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3573     {
3574         PKWEXITCALLACK pCallback;
3575         KW_LOG(("_onexit(%p)\n", pfnFunc));
3576         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3577 
3578         pCallback = kHlpAlloc(sizeof(*pCallback));
3579         if (pCallback)
3580         {
3581             pCallback->pfnCallback = pfnFunc;
3582             pCallback->fAtExit     = K_FALSE;
3583             pCallback->pNext       = g_Sandbox.pExitCallbackHead;
3584             g_Sandbox.pExitCallbackHead = pCallback;
3585             return pfnFunc;
3586         }
3587         return NULL;
3588     }
3589     KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
3590     return pfnFunc;
3591 }
3592 
3593 
3594 /** CRT - atexit   */
kwSandbox_msvcrt_atexit(int (__cdecl * pfnFunc)(void))3595 static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
3596 {
3597     //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3598     {
3599         PKWEXITCALLACK pCallback;
3600         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3601         KW_LOG(("atexit(%p)\n", pfnFunc));
3602 
3603         pCallback = kHlpAlloc(sizeof(*pCallback));
3604         if (pCallback)
3605         {
3606             pCallback->pfnCallback = (_onexit_t)pfnFunc;
3607             pCallback->fAtExit     = K_TRUE;
3608             pCallback->pNext       = g_Sandbox.pExitCallbackHead;
3609             g_Sandbox.pExitCallbackHead = pCallback;
3610             return 0;
3611         }
3612         return -1;
3613     }
3614     KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
3615     return 0;
3616 }
3617 
3618 
3619 /** Kernel32 - SetConsoleCtrlHandler(). */
kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler,BOOL fAdd)3620 static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
3621 {
3622     KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
3623     return TRUE;
3624 }
3625 
3626 
3627 /** The CRT internal __getmainargs() API. */
kwSandbox_msvcrt___getmainargs(int * pargc,char *** pargv,char *** penvp,int dowildcard,int const * piNewMode)3628 static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
3629                                                   int dowildcard, int const *piNewMode)
3630 {
3631     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3632     *pargc = g_Sandbox.cArgs;
3633     *pargv = g_Sandbox.papszArgs;
3634     *penvp = g_Sandbox.environ;
3635 
3636     /** @todo startinfo points at a newmode (setmode) value.   */
3637     return 0;
3638 }
3639 
3640 
3641 /** The CRT internal __wgetmainargs() API. */
kwSandbox_msvcrt___wgetmainargs(int * pargc,wchar_t *** pargv,wchar_t *** penvp,int dowildcard,int const * piNewMode)3642 static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
3643                                                    int dowildcard, int const *piNewMode)
3644 {
3645     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3646     *pargc = g_Sandbox.cArgs;
3647     *pargv = g_Sandbox.papwszArgs;
3648     *penvp = g_Sandbox.wenviron;
3649 
3650     /** @todo startinfo points at a newmode (setmode) value.   */
3651     return 0;
3652 }
3653 
3654 
3655 
3656 /** Kernel32 - GetCommandLineA()  */
kwSandbox_Kernel32_GetCommandLineA(VOID)3657 static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
3658 {
3659     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3660     return g_Sandbox.pszCmdLine;
3661 }
3662 
3663 
3664 /** Kernel32 - GetCommandLineW()  */
kwSandbox_Kernel32_GetCommandLineW(VOID)3665 static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
3666 {
3667     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3668     return g_Sandbox.pwszCmdLine;
3669 }
3670 
3671 
3672 /** Kernel32 - GetStartupInfoA()  */
kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)3673 static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
3674 {
3675     KW_LOG(("GetStartupInfoA\n"));
3676     GetStartupInfoA(pStartupInfo);
3677     pStartupInfo->lpReserved  = NULL;
3678     pStartupInfo->lpTitle     = NULL;
3679     pStartupInfo->lpReserved2 = NULL;
3680     pStartupInfo->cbReserved2 = 0;
3681 }
3682 
3683 
3684 /** Kernel32 - GetStartupInfoW()  */
kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)3685 static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
3686 {
3687     KW_LOG(("GetStartupInfoW\n"));
3688     GetStartupInfoW(pStartupInfo);
3689     pStartupInfo->lpReserved  = NULL;
3690     pStartupInfo->lpTitle     = NULL;
3691     pStartupInfo->lpReserved2 = NULL;
3692     pStartupInfo->cbReserved2 = 0;
3693 }
3694 
3695 
3696 /** CRT - __p___argc().  */
kwSandbox_msvcrt___p___argc(void)3697 static int * __cdecl kwSandbox_msvcrt___p___argc(void)
3698 {
3699     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3700     return &g_Sandbox.cArgs;
3701 }
3702 
3703 
3704 /** CRT - __p___argv().  */
kwSandbox_msvcrt___p___argv(void)3705 static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
3706 {
3707     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3708     return &g_Sandbox.papszArgs;
3709 }
3710 
3711 
3712 /** CRT - __p___sargv().  */
kwSandbox_msvcrt___p___wargv(void)3713 static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
3714 {
3715     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3716     return &g_Sandbox.papwszArgs;
3717 }
3718 
3719 
3720 /** CRT - __p__acmdln().  */
kwSandbox_msvcrt___p__acmdln(void)3721 static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
3722 {
3723     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3724     return (char **)&g_Sandbox.pszCmdLine;
3725 }
3726 
3727 
3728 /** CRT - __p__acmdln().  */
kwSandbox_msvcrt___p__wcmdln(void)3729 static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
3730 {
3731     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3732     return &g_Sandbox.pwszCmdLine;
3733 }
3734 
3735 
3736 /** CRT - __p__pgmptr().  */
kwSandbox_msvcrt___p__pgmptr(void)3737 static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
3738 {
3739     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3740     return &g_Sandbox.pgmptr;
3741 }
3742 
3743 
3744 /** CRT - __p__wpgmptr().  */
kwSandbox_msvcrt___p__wpgmptr(void)3745 static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
3746 {
3747     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3748     return &g_Sandbox.wpgmptr;
3749 }
3750 
3751 
3752 /** CRT - _get_pgmptr().  */
kwSandbox_msvcrt__get_pgmptr(char ** ppszValue)3753 static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
3754 {
3755     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3756     *ppszValue = g_Sandbox.pgmptr;
3757     return 0;
3758 }
3759 
3760 
3761 /** CRT - _get_wpgmptr().  */
kwSandbox_msvcrt__get_wpgmptr(wchar_t ** ppwszValue)3762 static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
3763 {
3764     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3765     *ppwszValue = g_Sandbox.wpgmptr;
3766     return 0;
3767 }
3768 
3769 /** Just in case. */
kwSandbox_msvcrt__wincmdln(void)3770 static void kwSandbox_msvcrt__wincmdln(void)
3771 {
3772     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3773     KWFS_TODO();
3774 }
3775 
3776 
3777 /** Just in case. */
kwSandbox_msvcrt__wwincmdln(void)3778 static void kwSandbox_msvcrt__wwincmdln(void)
3779 {
3780     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3781     KWFS_TODO();
3782 }
3783 
3784 /** CreateThread interceptor. */
kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr,SIZE_T cbStack,PTHREAD_START_ROUTINE pfnThreadProc,PVOID pvUser,DWORD fFlags,PDWORD pidThread)3785 static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
3786                                                      PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
3787                                                      DWORD fFlags, PDWORD pidThread)
3788 {
3789     HANDLE hThread = NULL;
3790     KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
3791             pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
3792     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3793     if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3794     {
3795         /* Allow link::DbgThread. */
3796         hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
3797         KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
3798     }
3799     else
3800         KWFS_TODO();
3801     return hThread;
3802 }
3803 
3804 
3805 /** _beginthread - create a new thread. */
kwSandbox_msvcrt__beginthread(void (__cdecl * pfnThreadProc)(void *),unsigned cbStack,void * pvUser)3806 static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
3807 {
3808     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3809     KWFS_TODO();
3810     return 0;
3811 }
3812 
3813 
3814 /** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
kwSandbox_msvcr120__beginthreadex(void * pvSecAttr,unsigned cbStack,unsigned (__stdcall * pfnThreadProc)(void *),void * pvUser,unsigned fCreate,unsigned * pidThread)3815 static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
3816                                                            unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
3817                                                            unsigned fCreate, unsigned *pidThread)
3818 {
3819     /*
3820      * The VC++ 12 (VS 2013) compiler pass two is now threaded.  Let it do
3821      * whatever it needs to.
3822      */
3823     KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
3824             pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
3825             pfnThreadProc, pvUser, fCreate, pidThread));
3826     if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
3827     {
3828         uintptr_t rcRet;
3829         static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
3830         if (!s_pfnReal)
3831         {
3832             *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
3833             if (!s_pfnReal)
3834             {
3835                 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
3836                 __debugbreak();
3837             }
3838         }
3839         rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
3840         KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
3841         return rcRet;
3842     }
3843 
3844     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3845     KWFS_TODO();
3846     return 0;
3847 }
3848 
3849 
3850 /** _beginthreadex - create a new thread. */
kwSandbox_msvcrt__beginthreadex(void * pvSecAttr,unsigned cbStack,unsigned (__stdcall * pfnThreadProc)(void *),void * pvUser,unsigned fCreate,unsigned * pidThread)3851 static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex(void *pvSecAttr, unsigned cbStack,
3852                                                          unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
3853                                                          unsigned fCreate, unsigned *pidThread)
3854 {
3855     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3856     KWFS_TODO();
3857     return 0;
3858 }
3859 
3860 
3861 /*
3862  *
3863  * Environment related APIs.
3864  * Environment related APIs.
3865  * Environment related APIs.
3866  *
3867  */
3868 
3869 /** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
kwSandbox_Kernel32_GetEnvironmentStringsA(void)3870 static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
3871 {
3872     char *pszzEnv;
3873     char *pszCur;
3874     KSIZE cbNeeded = 1;
3875     KSIZE iVar = 0;
3876 
3877     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3878 
3879     /* Figure how space much we need first.  */
3880     while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
3881         cbNeeded += kHlpStrLen(pszCur) + 1;
3882 
3883     /* Allocate it. */
3884     pszzEnv = kHlpAlloc(cbNeeded);
3885     if (pszzEnv)
3886     {
3887         char *psz = pszzEnv;
3888         iVar = 0;
3889         while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
3890         {
3891             KSIZE cbCur = kHlpStrLen(pszCur) + 1;
3892             kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
3893             psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
3894         }
3895         *psz++ = '\0';
3896         kHlpAssert(psz - pszzEnv == cbNeeded);
3897     }
3898 
3899     KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
3900 #if 0
3901     fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
3902     pszCur = pszzEnv;
3903     iVar = 0;
3904     while (*pszCur)
3905     {
3906         fprintf(stderr, "  %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
3907         iVar++;
3908         pszCur += kHlpStrLen(pszCur) + 1;
3909     }
3910     fprintf(stderr, "  %u:%p=<eos>\n\n", iVar, pszCur);
3911     pszCur++;
3912     fprintf(stderr, "ended at %p, after %u bytes (exepcted %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
3913 #endif
3914     return pszzEnv;
3915 }
3916 
3917 
3918 /** Kernel32 - GetEnvironmentStrings */
kwSandbox_Kernel32_GetEnvironmentStrings(void)3919 static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
3920 {
3921     KW_LOG(("GetEnvironmentStrings!\n"));
3922     return kwSandbox_Kernel32_GetEnvironmentStringsA();
3923 }
3924 
3925 
3926 /** Kernel32 - GetEnvironmentStringsW */
kwSandbox_Kernel32_GetEnvironmentStringsW(void)3927 static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
3928 {
3929     wchar_t *pwszzEnv;
3930     wchar_t *pwszCur;
3931     KSIZE    cwcNeeded = 1;
3932     KSIZE    iVar = 0;
3933 
3934     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3935 
3936     /* Figure how space much we need first.  */
3937     while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
3938         cwcNeeded += kwUtf16Len(pwszCur) + 1;
3939 
3940     /* Allocate it. */
3941     pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
3942     if (pwszzEnv)
3943     {
3944         wchar_t *pwsz = pwszzEnv;
3945         iVar = 0;
3946         while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
3947         {
3948             KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
3949             kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
3950             pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
3951         }
3952         *pwsz++ = '\0';
3953         kHlpAssert(pwsz - pwszzEnv == cwcNeeded);
3954     }
3955 
3956     KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
3957     return pwszzEnv;
3958 }
3959 
3960 
3961 /** Kernel32 - FreeEnvironmentStringsA   */
kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)3962 static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
3963 {
3964     KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
3965     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3966     kHlpFree(pszzEnv);
3967     return TRUE;
3968 }
3969 
3970 
3971 /** Kernel32 - FreeEnvironmentStringsW   */
kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)3972 static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
3973 {
3974     KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
3975     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3976     kHlpFree(pwszzEnv);
3977     return TRUE;
3978 }
3979 
3980 
3981 /**
3982  * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
3983  * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
3984  *
3985  * @returns 0 on success, non-zero on failure.
3986  * @param   pSandbox            The sandbox.
3987  * @param   cMin                Minimum size, including terminator.
3988  */
kwSandboxGrowEnv(PKWSANDBOX pSandbox,KSIZE cMin)3989 static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
3990 {
3991     void       *pvNew;
3992     KSIZE const cOld = pSandbox->cEnvVarsAllocated;
3993     KSIZE       cNew = cOld + 256;
3994     while (cNew < cMin)
3995         cNew += 256;
3996 
3997     pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
3998     if (pvNew)
3999     {
4000         pSandbox->environ = (char **)pvNew;
4001         pSandbox->environ[cOld] = NULL;
4002 
4003         pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4004         if (pvNew)
4005         {
4006             pSandbox->papszEnvVars = (char **)pvNew;
4007             pSandbox->papszEnvVars[cOld] = NULL;
4008 
4009             pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4010             if (pvNew)
4011             {
4012                 pSandbox->wenviron = (wchar_t **)pvNew;
4013                 pSandbox->wenviron[cOld] = NULL;
4014 
4015                 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4016                 if (pvNew)
4017                 {
4018                     pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4019                     pSandbox->papwszEnvVars[cOld] = NULL;
4020 
4021                     pSandbox->cEnvVarsAllocated = cNew;
4022                     KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4023                             cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4024                     return 0;
4025                 }
4026             }
4027         }
4028     }
4029     kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4030     return KERR_NO_MEMORY;
4031 }
4032 
4033 
4034 /**
4035  * Sets an environment variable, ANSI style.
4036  *
4037  * @returns 0 on success, non-zero on failure.
4038  * @param   pSandbox            The sandbox.
4039  * @param   pchVar              The variable name.
4040  * @param   cchVar              The length of the name.
4041  * @param   pszValue            The value.
4042  */
kwSandboxDoSetEnvA(PKWSANDBOX pSandbox,const char * pchVar,KSIZE cchVar,const char * pszValue)4043 static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4044 {
4045     /* Allocate and construct the new strings. */
4046     KSIZE  cchTmp = kHlpStrLen(pszValue);
4047     char  *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4048     if (pszNew)
4049     {
4050         wchar_t *pwszNew;
4051         kHlpMemCopy(pszNew, pchVar, cchVar);
4052         pszNew[cchVar] = '=';
4053         kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4054         cchTmp += cchVar + 1;
4055         pszNew[cchTmp] = '\0';
4056 
4057         pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4058         if (pwszNew)
4059         {
4060             /* Look it up. */
4061             KSIZE   iVar = 0;
4062             char   *pszEnv;
4063             while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4064             {
4065                 if (   _strnicmp(pszEnv, pchVar, cchVar) == 0
4066                     && pszEnv[cchVar] == '=')
4067                 {
4068                     KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4069                             "                              iVar=%d: %p='%s' and %p='%ls'\n",
4070                             iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4071                             pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4072                             iVar, pszNew, pszNew, pwszNew, pwszNew));
4073 
4074                     kHlpFree(pSandbox->papszEnvVars[iVar]);
4075                     pSandbox->papszEnvVars[iVar]  = pszNew;
4076                     pSandbox->environ[iVar]       = pszNew;
4077 
4078                     kHlpFree(pSandbox->papwszEnvVars[iVar]);
4079                     pSandbox->papwszEnvVars[iVar] = pwszNew;
4080                     pSandbox->wenviron[iVar]      = pwszNew;
4081                     return 0;
4082                 }
4083                 iVar++;
4084             }
4085 
4086             /* Not found, do we need to grow the table first? */
4087             if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4088                 kwSandboxGrowEnv(pSandbox, iVar + 2);
4089             if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4090             {
4091                 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4092 
4093                 pSandbox->papszEnvVars[iVar + 1]  = NULL;
4094                 pSandbox->papszEnvVars[iVar]      = pszNew;
4095                 pSandbox->environ[iVar + 1]       = NULL;
4096                 pSandbox->environ[iVar]           = pszNew;
4097 
4098                 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4099                 pSandbox->papwszEnvVars[iVar]     = pwszNew;
4100                 pSandbox->wenviron[iVar + 1]      = NULL;
4101                 pSandbox->wenviron[iVar]          = pwszNew;
4102                 return 0;
4103             }
4104 
4105             kHlpFree(pwszNew);
4106         }
4107         kHlpFree(pszNew);
4108     }
4109     KW_LOG(("Out of memory!\n"));
4110     return 0;
4111 }
4112 
4113 
4114 /**
4115  * Sets an environment variable, UTF-16 style.
4116  *
4117  * @returns 0 on success, non-zero on failure.
4118  * @param   pSandbox            The sandbox.
4119  * @param   pwcVar              The variable name.
4120  * @param   cwcVar              The length of the name.
4121  * @param   pwszValue           The value.
4122  */
kwSandboxDoSetEnvW(PKWSANDBOX pSandbox,const wchar_t * pwchVar,KSIZE cwcVar,const wchar_t * pwszValue)4123 static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4124 {
4125     /* Allocate and construct the new strings. */
4126     KSIZE    cwcTmp = kwUtf16Len(pwszValue);
4127     wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4128     if (pwszNew)
4129     {
4130         char *pszNew;
4131         kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4132         pwszNew[cwcVar] = '=';
4133         kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4134         cwcTmp += cwcVar + 1;
4135         pwszNew[cwcVar] = '\0';
4136 
4137         pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4138         if (pszNew)
4139         {
4140             /* Look it up. */
4141             KSIZE    iVar = 0;
4142             wchar_t *pwszEnv;
4143             while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4144             {
4145                 if (   _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4146                     && pwszEnv[cwcVar] == '=')
4147                 {
4148                     KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4149                             "                              iVar=%d: %p='%s' and %p='%ls'\n",
4150                             iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4151                             pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4152                             iVar, pszNew, pszNew, pwszNew, pwszNew));
4153 
4154                     kHlpFree(pSandbox->papszEnvVars[iVar]);
4155                     pSandbox->papszEnvVars[iVar]  = pszNew;
4156                     pSandbox->environ[iVar]       = pszNew;
4157 
4158                     kHlpFree(pSandbox->papwszEnvVars[iVar]);
4159                     pSandbox->papwszEnvVars[iVar] = pwszNew;
4160                     pSandbox->wenviron[iVar]      = pwszNew;
4161                     return 0;
4162                 }
4163                 iVar++;
4164             }
4165 
4166             /* Not found, do we need to grow the table first? */
4167             if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4168                 kwSandboxGrowEnv(pSandbox, iVar + 2);
4169             if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4170             {
4171                 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4172 
4173                 pSandbox->papszEnvVars[iVar + 1]  = NULL;
4174                 pSandbox->papszEnvVars[iVar]      = pszNew;
4175                 pSandbox->environ[iVar + 1]       = NULL;
4176                 pSandbox->environ[iVar]           = pszNew;
4177 
4178                 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4179                 pSandbox->papwszEnvVars[iVar]     = pwszNew;
4180                 pSandbox->wenviron[iVar + 1]      = NULL;
4181                 pSandbox->wenviron[iVar]          = pwszNew;
4182                 return 0;
4183             }
4184 
4185             kHlpFree(pwszNew);
4186         }
4187         kHlpFree(pszNew);
4188     }
4189     KW_LOG(("Out of memory!\n"));
4190     return 0;
4191 }
4192 
4193 
4194 /** ANSI unsetenv worker. */
kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox,const char * pchVar,KSIZE cchVar)4195 static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4196 {
4197     KSIZE   iVar   = 0;
4198     char   *pszEnv;
4199     while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4200     {
4201         if (   _strnicmp(pszEnv, pchVar, cchVar) == 0
4202             && pszEnv[cchVar] == '=')
4203         {
4204             KSIZE cVars = iVar;
4205             while (pSandbox->papszEnvVars[cVars])
4206                 cVars++;
4207             kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4208             kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4209 
4210             KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4211                     pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4212                     pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4213 
4214             kHlpFree(pSandbox->papszEnvVars[iVar]);
4215             pSandbox->papszEnvVars[iVar]    = pSandbox->papszEnvVars[cVars];
4216             pSandbox->environ[iVar]         = pSandbox->papszEnvVars[cVars];
4217             pSandbox->papszEnvVars[cVars]   = NULL;
4218             pSandbox->environ[cVars]        = NULL;
4219 
4220             kHlpFree(pSandbox->papwszEnvVars[iVar]);
4221             pSandbox->papwszEnvVars[iVar]   = pSandbox->papwszEnvVars[cVars];
4222             pSandbox->wenviron[iVar]        = pSandbox->papwszEnvVars[cVars];
4223             pSandbox->papwszEnvVars[cVars]  = NULL;
4224             pSandbox->wenviron[cVars]       = NULL;
4225             return 0;
4226         }
4227         iVar++;
4228     }
4229     return KERR_ENVVAR_NOT_FOUND;
4230 }
4231 
4232 
4233 /** UTF-16 unsetenv worker. */
kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox,const wchar_t * pwcVar,KSIZE cwcVar)4234 static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4235 {
4236     KSIZE    iVar   = 0;
4237     wchar_t *pwszEnv;
4238     while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4239     {
4240         if (   _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4241             && pwszEnv[cwcVar] == '=')
4242         {
4243             KSIZE cVars = iVar;
4244             while (pSandbox->papwszEnvVars[cVars])
4245                 cVars++;
4246             kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
4247             kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
4248 
4249             KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4250                     pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4251                     pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4252 
4253             kHlpFree(pSandbox->papszEnvVars[iVar]);
4254             pSandbox->papszEnvVars[iVar]    = pSandbox->papszEnvVars[cVars];
4255             pSandbox->environ[iVar]         = pSandbox->papszEnvVars[cVars];
4256             pSandbox->papszEnvVars[cVars]   = NULL;
4257             pSandbox->environ[cVars]        = NULL;
4258 
4259             kHlpFree(pSandbox->papwszEnvVars[iVar]);
4260             pSandbox->papwszEnvVars[iVar]   = pSandbox->papwszEnvVars[cVars];
4261             pSandbox->wenviron[iVar]        = pSandbox->papwszEnvVars[cVars];
4262             pSandbox->papwszEnvVars[cVars]  = NULL;
4263             pSandbox->wenviron[cVars]       = NULL;
4264             return 0;
4265         }
4266         iVar++;
4267     }
4268     return KERR_ENVVAR_NOT_FOUND;
4269 }
4270 
4271 
4272 
4273 /** ANSI getenv worker. */
kwSandboxDoGetEnvA(PKWSANDBOX pSandbox,const char * pchVar,KSIZE cchVar)4274 static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4275 {
4276     KSIZE   iVar   = 0;
4277     char   *pszEnv;
4278     while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
4279         if (   _strnicmp(pszEnv, pchVar, cchVar) == 0
4280             && pszEnv[cchVar] == '=')
4281             return &pszEnv[cchVar + 1];
4282     return NULL;
4283 }
4284 
4285 
4286 /** UTF-16 getenv worker. */
kwSandboxDoGetEnvW(PKWSANDBOX pSandbox,const wchar_t * pwcVar,KSIZE cwcVar)4287 static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4288 {
4289     KSIZE    iVar   = 0;
4290     wchar_t *pwszEnv;
4291     while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
4292         if (   _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4293             && pwszEnv[cwcVar] == '=')
4294             return &pwszEnv[cwcVar + 1];
4295     return NULL;
4296 }
4297 
4298 
4299 /** Kernel32 - GetEnvironmentVariableA()  */
kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar,LPSTR pszValue,DWORD cbValue)4300 static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
4301 {
4302     char *pszFoundValue;
4303     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4304 
4305     pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4306     if (pszFoundValue)
4307     {
4308         DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
4309         KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
4310         return cchRet;
4311     }
4312     KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
4313     SetLastError(ERROR_ENVVAR_NOT_FOUND);
4314     return 0;
4315 }
4316 
4317 
4318 /** Kernel32 - GetEnvironmentVariableW()  */
kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar,LPWSTR pwszValue,DWORD cwcValue)4319 static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
4320 {
4321     wchar_t *pwszFoundValue;
4322     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4323 
4324     pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4325     if (pwszFoundValue)
4326     {
4327         DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
4328         KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
4329         return cchRet;
4330     }
4331     KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
4332     SetLastError(ERROR_ENVVAR_NOT_FOUND);
4333     return 0;
4334 }
4335 
4336 
4337 /** Kernel32 - SetEnvironmentVariableA()  */
kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar,LPCSTR pszValue)4338 static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
4339 {
4340     int rc;
4341     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4342 
4343     if (pszValue)
4344         rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4345     else
4346     {
4347         kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4348         rc = 0; //??
4349     }
4350     if (rc == 0)
4351     {
4352         KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
4353         return TRUE;
4354     }
4355     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4356     KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
4357     return FALSE;
4358 }
4359 
4360 
4361 /** Kernel32 - SetEnvironmentVariableW()  */
kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar,LPCWSTR pwszValue)4362 static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
4363 {
4364     int rc;
4365     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4366 
4367     if (pwszValue)
4368         rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4369     else
4370     {
4371         kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4372         rc = 0; //??
4373     }
4374     if (rc == 0)
4375     {
4376         KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
4377         return TRUE;
4378     }
4379     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4380     KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
4381     return FALSE;
4382 }
4383 
4384 
4385 /** Kernel32 - ExpandEnvironmentStringsA()  */
kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc,LPSTR pwszDst,DWORD cbDst)4386 static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
4387 {
4388     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4389     KWFS_TODO();
4390     return 0;
4391 }
4392 
4393 
4394 /** Kernel32 - ExpandEnvironmentStringsW()  */
kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc,LPWSTR pwszDst,DWORD cbDst)4395 static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
4396 {
4397     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4398     KWFS_TODO();
4399     return 0;
4400 }
4401 
4402 
4403 /** CRT - _putenv(). */
kwSandbox_msvcrt__putenv(const char * pszVarEqualValue)4404 static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
4405 {
4406     int rc;
4407     char const *pszEqual;
4408     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4409 
4410     pszEqual = kHlpStrChr(pszVarEqualValue, '=');
4411     if (pszEqual)
4412     {
4413         rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
4414         if (rc == 0)
4415         { }
4416         else
4417             rc = -1;
4418     }
4419     else
4420     {
4421         kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
4422         rc = 0;
4423     }
4424     KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
4425     return rc;
4426 }
4427 
4428 
4429 /** CRT - _wputenv(). */
kwSandbox_msvcrt__wputenv(const wchar_t * pwszVarEqualValue)4430 static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
4431 {
4432     int rc;
4433     wchar_t const *pwszEqual;
4434     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4435 
4436     pwszEqual = wcschr(pwszVarEqualValue, '=');
4437     if (pwszEqual)
4438     {
4439         rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
4440         if (rc == 0)
4441         { }
4442         else
4443             rc = -1;
4444     }
4445     else
4446     {
4447         kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
4448         rc = 0;
4449     }
4450     KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
4451     return rc;
4452 }
4453 
4454 
4455 /** CRT - _putenv_s(). */
kwSandbox_msvcrt__putenv_s(const char * pszVar,const char * pszValue)4456 static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
4457 {
4458     char const *pszEqual;
4459     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4460 
4461     pszEqual = kHlpStrChr(pszVar, '=');
4462     if (pszEqual == NULL)
4463     {
4464         if (pszValue)
4465         {
4466             int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4467             if (rc == 0)
4468             {
4469                 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
4470                 return 0;
4471             }
4472         }
4473         else
4474         {
4475             kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4476             KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
4477             return 0;
4478         }
4479         KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
4480         return ENOMEM;
4481     }
4482     KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
4483     return EINVAL;
4484 }
4485 
4486 
4487 /** CRT - _wputenv_s(). */
kwSandbox_msvcrt__wputenv_s(const wchar_t * pwszVar,const wchar_t * pwszValue)4488 static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
4489 {
4490     wchar_t const *pwszEqual;
4491     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4492 
4493     pwszEqual = wcschr(pwszVar, '=');
4494     if (pwszEqual == NULL)
4495     {
4496         if (pwszValue)
4497         {
4498             int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4499             if (rc == 0)
4500             {
4501                 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
4502                 return 0;
4503             }
4504         }
4505         else
4506         {
4507             kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4508             KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
4509             return 0;
4510         }
4511         KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
4512         return ENOMEM;
4513     }
4514     KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
4515     return EINVAL;
4516 }
4517 
4518 
4519 /** CRT - get pointer to the __initenv variable (initial environment).   */
kwSandbox_msvcrt___p___initenv(void)4520 static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
4521 {
4522     KW_LOG(("__p___initenv\n"));
4523     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4524     KWFS_TODO();
4525     return &g_Sandbox.initenv;
4526 }
4527 
4528 
4529 /** CRT - get pointer to the __winitenv variable (initial environment).   */
kwSandbox_msvcrt___p___winitenv(void)4530 static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
4531 {
4532     KW_LOG(("__p___winitenv\n"));
4533     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4534     KWFS_TODO();
4535     return &g_Sandbox.winitenv;
4536 }
4537 
4538 
4539 /** CRT - get pointer to the _environ variable (current environment).   */
kwSandbox_msvcrt___p__environ(void)4540 static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
4541 {
4542     KW_LOG(("__p__environ\n"));
4543     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4544     return &g_Sandbox.environ;
4545 }
4546 
4547 
4548 /** CRT - get pointer to the _wenviron variable (current environment).   */
kwSandbox_msvcrt___p__wenviron(void)4549 static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
4550 {
4551     KW_LOG(("__p__wenviron\n"));
4552     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4553     return &g_Sandbox.wenviron;
4554 }
4555 
4556 
4557 /** CRT - get the _environ variable (current environment).
4558  * @remarks Not documented or prototyped?  */
kwSandbox_msvcrt__get_environ(char *** ppapszEnviron)4559 static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
4560 {
4561     KWFS_TODO(); /** @todo check the callers expectations! */
4562     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4563     *ppapszEnviron = g_Sandbox.environ;
4564     return 0;
4565 }
4566 
4567 
4568 /** CRT - get the _wenviron variable (current environment).
4569  * @remarks Not documented or prototyped? */
kwSandbox_msvcrt__get_wenviron(wchar_t *** ppapwszEnviron)4570 static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
4571 {
4572     KWFS_TODO(); /** @todo check the callers expectations! */
4573     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4574     *ppapwszEnviron = g_Sandbox.wenviron;
4575     return 0;
4576 }
4577 
4578 
4579 
4580 /*
4581  *
4582  * Loader related APIs
4583  * Loader related APIs
4584  * Loader related APIs
4585  *
4586  */
4587 
4588 /**
4589  * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
4590  */
kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad,DWORD fFlags)4591 static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
4592 {
4593     /* Load it first. */
4594     HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
4595     if (hmod)
4596     {
4597         pDynLoad->hmod = hmod;
4598         pDynLoad->pMod = NULL; /* indicates special  */
4599 
4600         pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
4601         g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
4602         KW_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
4603     }
4604     else
4605         kHlpFree(pDynLoad);
4606     return hmod;
4607 }
4608 
4609 
4610 /**
4611  * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
4612  */
kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad,DWORD fFlags)4613 static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
4614 {
4615     HMODULE     hmod;
4616     PKWMODULE   pMod;
4617     KU32        uHashPath;
4618     KSIZE       idxHash;
4619     char        szNormPath[256];
4620     KSIZE       cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
4621 
4622     /*
4623      * Lower case it.
4624      */
4625     if (cbFilename <= sizeof(szNormPath))
4626     {
4627         kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
4628         _strlwr(szNormPath);
4629     }
4630     else
4631     {
4632         SetLastError(ERROR_FILENAME_EXCED_RANGE);
4633         return NULL;
4634     }
4635 
4636     /*
4637      * Check if it has already been loaded so we don't create an unnecessary
4638      * loader module for it.
4639      */
4640     uHashPath = kwStrHash(szNormPath);
4641     idxHash   = uHashPath % K_ELEMENTS(g_apModules);
4642     pMod      = g_apModules[idxHash];
4643     if (pMod)
4644     {
4645         do
4646         {
4647             if (   pMod->uHashPath == uHashPath
4648                 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
4649             {
4650                 pDynLoad->pMod = kwLdrModuleRetain(pMod);
4651                 pDynLoad->hmod = pMod->hOurMod;
4652 
4653                 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
4654                 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
4655                 KW_LOG(("LoadLibraryExA(%s,,) -> %p [already loaded]\n", pDynLoad->szRequest, pDynLoad->hmod));
4656                 return pDynLoad->hmod;
4657             }
4658             pMod = pMod->pNext;
4659         } while (pMod);
4660     }
4661 
4662 
4663     /*
4664      * Try load it and make a kLdr module for it.
4665      */
4666     hmod = LoadLibraryExA(szNormPath, NULL /*hFile*/, fFlags);
4667     if (hmod)
4668     {
4669         PKLDRMOD pLdrMod;
4670         int rc = kLdrModOpenNativeByHandle((KUPTR)hmod, &pLdrMod);
4671         if (rc == 0)
4672         {
4673             PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cbFilename, uHashPath,
4674                                                                   K_FALSE /*fDoReplacements*/);
4675             if (pMod)
4676             {
4677                 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
4678 
4679                 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cbFilename + cbFilename * sizeof(wchar_t));
4680                 if (pDynLoad)
4681                 {
4682                     pDynLoad->pMod = pMod;
4683                     pDynLoad->hmod = hmod;
4684 
4685                     pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
4686                     g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
4687                     KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
4688                     return hmod;
4689                 }
4690 
4691                 KWFS_TODO();
4692             }
4693             else
4694                 KWFS_TODO();
4695         }
4696         else
4697             KWFS_TODO();
4698     }
4699     kHlpFree(pDynLoad);
4700     return hmod;
4701 }
4702 
4703 
4704 /** Kernel32 - LoadLibraryExA() */
kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename,HANDLE hFile,DWORD fFlags)4705 static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
4706 {
4707     KSIZE       cchFilename = kHlpStrLen(pszFilename);
4708     const char *pszSearchPath;
4709     PKWDYNLOAD  pDynLoad;
4710     PKWMODULE   pMod;
4711     int         rc;
4712     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4713 
4714     /*
4715      * Deal with a couple of extremely unlikely special cases right away.
4716      */
4717     if (   (   !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
4718             || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
4719         && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
4720     { /* likely */ }
4721     else
4722     {
4723         KWFS_TODO();
4724         return LoadLibraryExA(pszFilename, hFile, fFlags);
4725     }
4726 
4727     /*
4728      * Check if we've already got a dynload entry for this one.
4729      */
4730     for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
4731         if (   pDynLoad->cchRequest == cchFilename
4732             && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
4733         {
4734             if (pDynLoad->pMod)
4735                 rc = kwLdrModuleInitTree(pDynLoad->pMod);
4736             else
4737                 rc = 0;
4738             if (rc == 0)
4739             {
4740                 KW_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
4741                 return pDynLoad->hmod;
4742             }
4743             SetLastError(ERROR_DLL_INIT_FAILED);
4744             return NULL;
4745         }
4746 
4747     /*
4748      * Allocate a dynload entry for the request.
4749      */
4750     pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
4751     if (pDynLoad)
4752     {
4753         pDynLoad->cchRequest = cchFilename;
4754         kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
4755     }
4756     else
4757     {
4758         KW_LOG(("LoadLibraryExA: Out of memory!\n"));
4759         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4760         return NULL;
4761     }
4762 
4763     /*
4764      * Deal with resource / data DLLs.
4765      */
4766     if (fFlags & (  DONT_RESOLVE_DLL_REFERENCES
4767                   | LOAD_LIBRARY_AS_DATAFILE
4768                   | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
4769                   | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
4770         return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
4771 
4772     /*
4773      * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
4774      */
4775     if (   strnicmp(pszFilename, TUPLE("api-ms-")) == 0
4776         && kHlpIsFilenameOnly(pszFilename))
4777         return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
4778 
4779     /*
4780      * Normal library loading.
4781      * We start by being very lazy and reusing the code for resolving imports.
4782      */
4783     pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
4784     if (!kHlpIsFilenameOnly(pszFilename))
4785         pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
4786     else
4787     {
4788         rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
4789         if (rc != 0)
4790             pMod = NULL;
4791     }
4792     if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
4793     {
4794         /* Enter it into the tool module table and dynamic link request cache. */
4795         kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
4796 
4797         pDynLoad->pMod = pMod;
4798         pDynLoad->hmod = pMod->hOurMod;
4799 
4800         pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
4801         g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
4802 
4803         /*
4804          * Make sure it's initialized (need to link it first since DllMain may
4805          * use loader APIs).
4806          */
4807         rc = kwLdrModuleInitTree(pMod);
4808         if (rc == 0)
4809         {
4810             KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
4811             return pDynLoad->hmod;
4812         }
4813 
4814         SetLastError(ERROR_DLL_INIT_FAILED);
4815     }
4816     else
4817     {
4818         KWFS_TODO();
4819         kHlpFree(pDynLoad);
4820         SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
4821     }
4822     return NULL;
4823 }
4824 
4825 
4826 /** Kernel32 - LoadLibraryExA() for native overloads */
kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename,HANDLE hFile,DWORD fFlags)4827 static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
4828 {
4829     char szPath[1024];
4830     KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
4831 
4832     /*
4833      * We may have to help resolved unqualified DLLs living in the executable directory.
4834      */
4835     if (kHlpIsFilenameOnly(pszFilename))
4836     {
4837         KSIZE cchFilename = kHlpStrLen(pszFilename);
4838         KSIZE cchExePath  = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
4839         if (cchExePath + cchFilename + 1 <= sizeof(szPath))
4840         {
4841             kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
4842             kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
4843             if (kwFsPathExists(szPath))
4844             {
4845                 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
4846                 pszFilename = szPath;
4847             }
4848         }
4849 
4850         if (pszFilename != szPath)
4851         {
4852             KSIZE cchSuffix = 0;
4853             KBOOL fNeedSuffix = K_FALSE;
4854             const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
4855             while (*pszCur != '\0')
4856             {
4857                 /* Find the end of the component */
4858                 KSIZE cch = 0;
4859                 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
4860                     cch++;
4861 
4862                 if (   cch > 0 /* wrong, but whatever */
4863                     && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
4864                 {
4865                     char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
4866                     if (   szPath[cch - 1] != ':'
4867                         && szPath[cch - 1] != '/'
4868                         && szPath[cch - 1] != '\\')
4869                         *pszDst++ = '\\';
4870                     pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
4871                     if (fNeedSuffix)
4872                         pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
4873                     *pszDst = '\0';
4874 
4875                     if (kwFsPathExists(szPath))
4876                     {
4877                         KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
4878                         pszFilename = szPath;
4879                         break;
4880                     }
4881                 }
4882 
4883                 /* Advance */
4884                 pszCur += cch;
4885                 while (*pszCur == ';')
4886                     pszCur++;
4887             }
4888         }
4889     }
4890 
4891     return LoadLibraryExA(pszFilename, hFile, fFlags);
4892 }
4893 
4894 
4895 /** Kernel32 - LoadLibraryExW()   */
kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename,HANDLE hFile,DWORD fFlags)4896 static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
4897 {
4898     char szTmp[4096];
4899     KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
4900     if (cchTmp < sizeof(szTmp))
4901         return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
4902 
4903     KWFS_TODO();
4904     SetLastError(ERROR_FILENAME_EXCED_RANGE);
4905     return NULL;
4906 }
4907 
4908 /** Kernel32 - LoadLibraryA()   */
kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)4909 static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
4910 {
4911     return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
4912 }
4913 
4914 
4915 /** Kernel32 - LoadLibraryW()   */
kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)4916 static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
4917 {
4918     char szTmp[4096];
4919     KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
4920     if (cchTmp < sizeof(szTmp))
4921         return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
4922     KWFS_TODO();
4923     SetLastError(ERROR_FILENAME_EXCED_RANGE);
4924     return NULL;
4925 }
4926 
4927 
4928 /** Kernel32 - FreeLibrary()   */
kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)4929 static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
4930 {
4931     /* Ignored, we like to keep everything loaded. */
4932     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4933     return TRUE;
4934 }
4935 
4936 
4937 /** Kernel32 - GetModuleHandleA()   */
kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)4938 static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
4939 {
4940     KSIZE i;
4941     KSIZE cchModule;
4942     PKWDYNLOAD pDynLoad;
4943     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4944 
4945     /*
4946      * The executable.
4947      */
4948     if (pszModule == NULL)
4949         return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
4950 
4951     /*
4952      * Cache of system modules we've seen queried.
4953      */
4954     cchModule = kHlpStrLen(pszModule);
4955     for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
4956         if (   g_aGetModuleHandleCache[i].cchName == cchModule
4957             && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
4958         {
4959             if (g_aGetModuleHandleCache[i].hmod != NULL)
4960                 return g_aGetModuleHandleCache[i].hmod;
4961             return g_aGetModuleHandleCache[i].hmod = GetModuleHandleA(pszModule);
4962         }
4963 
4964     /*
4965      * Modules we've dynamically loaded.
4966      */
4967     for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
4968         if (   pDynLoad->pMod
4969             && (   stricmp(pDynLoad->pMod->pszPath, pszModule) == 0
4970                 || stricmp(&pDynLoad->pMod->pszPath[pDynLoad->pMod->offFilename], pszModule) == 0) )
4971         {
4972             if (   pDynLoad->pMod->fNative
4973                 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
4974             {
4975                 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
4976                 return pDynLoad->hmod;
4977             }
4978             SetLastError(ERROR_MOD_NOT_FOUND);
4979             return NULL;
4980         }
4981 
4982     kwErrPrintf("pszModule=%s\n", pszModule);
4983     KWFS_TODO();
4984     SetLastError(ERROR_MOD_NOT_FOUND);
4985     return NULL;
4986 }
4987 
4988 
4989 /** Kernel32 - GetModuleHandleW()   */
kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)4990 static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
4991 {
4992     KSIZE i;
4993     KSIZE cwcModule;
4994     PKWDYNLOAD pDynLoad;
4995     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4996 
4997     /*
4998      * The executable.
4999      */
5000     if (pwszModule == NULL)
5001         return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5002 
5003     /*
5004      * Cache of system modules we've seen queried.
5005      */
5006     cwcModule = kwUtf16Len(pwszModule);
5007     for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5008         if (   g_aGetModuleHandleCache[i].cwcName == cwcModule
5009             && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5010         {
5011             if (g_aGetModuleHandleCache[i].hmod != NULL)
5012                 return g_aGetModuleHandleCache[i].hmod;
5013             return g_aGetModuleHandleCache[i].hmod = GetModuleHandleW(pwszModule);
5014         }
5015 
5016     /*
5017      * Modules we've dynamically loaded.
5018      */
5019     for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5020         if (   pDynLoad->pMod
5021             && (   _wcsicmp(pDynLoad->pMod->pwszPath, pwszModule) == 0
5022                 || _wcsicmp(&pDynLoad->pMod->pwszPath[pDynLoad->pMod->offFilename], pwszModule) == 0) ) /** @todo wrong offset */
5023         {
5024             if (   pDynLoad->pMod->fNative
5025                 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5026             {
5027                 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5028                 return pDynLoad->hmod;
5029             }
5030             SetLastError(ERROR_MOD_NOT_FOUND);
5031             return NULL;
5032         }
5033 
5034     kwErrPrintf("pwszModule=%ls\n", pwszModule);
5035     KWFS_TODO();
5036     SetLastError(ERROR_MOD_NOT_FOUND);
5037     return NULL;
5038 }
5039 
5040 
5041 /** Used to debug dynamically resolved procedures. */
kwSandbox_BreakIntoDebugger(void * pv1,void * pv2,void * pv3,void * pv4)5042 static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5043 {
5044 #ifdef _MSC_VER
5045     __debugbreak();
5046 #else
5047     KWFS_TODO();
5048 #endif
5049     return -1;
5050 }
5051 
5052 
5053 #ifndef NDEBUG
5054 /*
5055  * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5056  */
5057 # if K_ARCH == K_ARCH_X86_32
5058 static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5059 # else
5060 static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5061 # endif
5062 typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5063 typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5064 typedef struct KWCXINTERCEPTORENTRY
5065 {
5066     PFNINVOKECOMPILERPASSW  pfnOrg;
5067     PKWMODULE               pModule;
5068     PFNINVOKECOMPILERPASSW  pfnWrap;
5069 } KWCXINTERCEPTORENTRY;
5070 
kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs,wchar_t ** papwszArgs,KUPTR fFlags,void ** phCluiInstance,KWCXINTERCEPTORENTRY * pEntry)5071 static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5072                                                      KWCXINTERCEPTORENTRY *pEntry)
5073 {
5074     int i;
5075     KIPTR rcExit;
5076     KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5077             &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5078     for (i = 0; i < cArgs; i++)
5079         KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5080 
5081     rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5082 
5083     KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5084     return rcExit;
5085 }
5086 
5087 static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
5088 static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
5089 static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
5090 
5091 static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
5092 {
5093     { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
5094     { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
5095     { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
5096 };
5097 
kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs,wchar_t ** papwszArgs,KUPTR fFlags,void ** phCluiInstance)5098 static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5099 {
5100     return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
5101 }
5102 
kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs,wchar_t ** papwszArgs,KUPTR fFlags,void ** phCluiInstance)5103 static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5104 {
5105     return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
5106 }
5107 
kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs,wchar_t ** papwszArgs,KUPTR fFlags,void ** phCluiInstance)5108 static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5109 {
5110     return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
5111 }
5112 
5113 #endif /* !NDEBUG */
5114 
5115 
5116 /** Kernel32 - GetProcAddress()   */
kwSandbox_Kernel32_GetProcAddress(HMODULE hmod,LPCSTR pszProc)5117 static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
5118 {
5119     KSIZE i;
5120     PKWMODULE pMod;
5121     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5122 
5123     /*
5124      * Try locate the module.
5125      */
5126     pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5127     if (pMod)
5128     {
5129         KLDRADDR uValue;
5130         int rc = kLdrModQuerySymbol(pMod->pLdrMod,
5131                                     pMod->fNative ? NULL : pMod->u.Manual.pvBits,
5132                                     pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
5133                                     KU32_MAX /*iSymbol*/,
5134                                     pszProc,
5135                                     kHlpStrLen(pszProc),
5136                                     NULL /*pszVersion*/,
5137                                     NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
5138                                     &uValue,
5139                                     NULL /*pfKind*/);
5140         if (rc == 0)
5141         {
5142             //static int s_cDbgGets = 0;
5143             KU32 cchProc = (KU32)kHlpStrLen(pszProc);
5144             KU32 i = g_cSandboxGetProcReplacements;
5145             while (i-- > 0)
5146                 if (   g_aSandboxGetProcReplacements[i].cchFunction == cchProc
5147                     && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
5148                 {
5149                     if (   !g_aSandboxGetProcReplacements[i].pszModule
5150                         || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
5151                     {
5152                         if (   !g_aSandboxGetProcReplacements[i].fOnlyExe
5153                             || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
5154                                < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
5155                         {
5156                             uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
5157                             KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5158                         }
5159                         kwLdrModuleRelease(pMod);
5160                         return (FARPROC)(KUPTR)uValue;
5161                     }
5162                 }
5163 
5164 #ifndef NDEBUG
5165             /* Intercept the compiler pass method, dumping arguments. */
5166             if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
5167             {
5168                 KU32 i;
5169                 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
5170                     if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
5171                     {
5172                         uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5173                         KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
5174                         break;
5175                     }
5176                 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
5177                     while (i-- > 0)
5178                         if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
5179                         {
5180                             g_aCxInterceptorEntries[i].pfnOrg  = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
5181                             g_aCxInterceptorEntries[i].pModule = pMod;
5182                             uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5183                             KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
5184                             break;
5185                         }
5186             }
5187 #endif
5188             KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5189             kwLdrModuleRelease(pMod);
5190             //s_cDbgGets++;
5191             //if (s_cGets >= 3)
5192             //    return (FARPROC)kwSandbox_BreakIntoDebugger;
5193             return (FARPROC)(KUPTR)uValue;
5194         }
5195 
5196         KWFS_TODO();
5197         SetLastError(ERROR_PROC_NOT_FOUND);
5198         kwLdrModuleRelease(pMod);
5199         return NULL;
5200     }
5201 
5202     /*
5203      * Hmm... could be a cached module-by-name.
5204      */
5205     for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5206         if (g_aGetModuleHandleCache[i].hmod == hmod)
5207             return GetProcAddress(hmod, pszProc);
5208 
5209     KWFS_TODO();
5210     return GetProcAddress(hmod, pszProc);
5211 }
5212 
5213 
5214 /** Kernel32 - GetModuleFileNameA()   */
kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod,LPSTR pszFilename,DWORD cbFilename)5215 static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
5216 {
5217     PKWMODULE pMod;
5218     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5219 
5220     pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5221     if (pMod != NULL)
5222     {
5223         DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
5224         kwLdrModuleRelease(pMod);
5225         return cbRet;
5226     }
5227     KWFS_TODO();
5228     return 0;
5229 }
5230 
5231 
5232 /** Kernel32 - GetModuleFileNameW()   */
kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod,LPWSTR pwszFilename,DWORD cbFilename)5233 static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
5234 {
5235     PKWMODULE pMod;
5236     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5237 
5238     pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5239     if (pMod)
5240     {
5241         DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
5242         kwLdrModuleRelease(pMod);
5243         return cwcRet;
5244     }
5245 
5246     KWFS_TODO();
5247     return 0;
5248 }
5249 
5250 
5251 /** NtDll - RtlPcToFileHeader
5252  * This is necessary for msvcr100.dll!CxxThrowException.  */
kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC,PVOID * ppvImageBase)5253 static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
5254 {
5255     PVOID pvRet;
5256 
5257     /*
5258      * Do a binary lookup of the module table for the current tool.
5259      * This will give us a
5260      */
5261     if (g_Sandbox.fRunning)
5262     {
5263         KUPTR const     uPC     = (KUPTR)pvPC;
5264         PKWMODULE      *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
5265         KU32            iEnd    = g_Sandbox.pTool->u.Sandboxed.cModules;
5266         KU32            i;
5267         if (iEnd)
5268         {
5269             KU32        iStart  = 0;
5270             i = iEnd / 2;
5271             for (;;)
5272             {
5273                 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
5274                 if (uPC < uHModThis)
5275                 {
5276                     iEnd = i;
5277                     if (iStart < i)
5278                     { }
5279                     else
5280                         break;
5281                 }
5282                 else if (uPC != uHModThis)
5283                 {
5284                     iStart = ++i;
5285                     if (i < iEnd)
5286                     { }
5287                     else
5288                         break;
5289                 }
5290                 else
5291                 {
5292                     /* This isn't supposed to happen. */
5293                     break;
5294                 }
5295 
5296                 i = iStart + (iEnd - iStart) / 2;
5297             }
5298 
5299             /* For reasons of simplicity (= copy & paste), we end up with the
5300                module after the one we're interested in here.  */
5301             i--;
5302             if (i < g_Sandbox.pTool->u.Sandboxed.cModules
5303                 && papMods[i]->pLdrMod)
5304             {
5305                 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
5306                 if (uRvaPC < papMods[i]->cbImage)
5307                 {
5308                     *ppvImageBase = papMods[i]->hOurMod;
5309                     pvRet = papMods[i]->hOurMod;
5310                     KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
5311                     return pvRet;
5312                 }
5313             }
5314         }
5315         else
5316             i = 0;
5317     }
5318 
5319     /*
5320      * Call the regular API.
5321      */
5322     pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
5323     KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
5324     return pvRet;
5325 }
5326 
5327 
5328 /*
5329  *
5330  * File access APIs (for speeding them up).
5331  * File access APIs (for speeding them up).
5332  * File access APIs (for speeding them up).
5333  *
5334  */
5335 
5336 
5337 /**
5338  * Converts a lookup error to a windows error code.
5339  *
5340  * @returns The windows error code.
5341  * @param   enmError            The lookup error.
5342  */
kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)5343 static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
5344 {
5345     switch (enmError)
5346     {
5347         case KFSLOOKUPERROR_NOT_FOUND:
5348         case KFSLOOKUPERROR_NOT_DIR:
5349             return ERROR_FILE_NOT_FOUND;
5350 
5351         case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
5352         case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
5353             return ERROR_PATH_NOT_FOUND;
5354 
5355         case KFSLOOKUPERROR_PATH_TOO_LONG:
5356             return ERROR_FILENAME_EXCED_RANGE;
5357 
5358         case KFSLOOKUPERROR_OUT_OF_MEMORY:
5359             return ERROR_NOT_ENOUGH_MEMORY;
5360 
5361         default:
5362             return ERROR_PATH_NOT_FOUND;
5363     }
5364 }
5365 
5366 #ifdef WITH_TEMP_MEMORY_FILES
5367 
5368 /**
5369  * Checks for a cl.exe temporary file.
5370  *
5371  * There are quite a bunch of these.  They seems to be passing data between the
5372  * first and second compiler pass.  Since they're on disk, they get subjected to
5373  * AV software screening and normal file consistency rules.  So, not necessarily
5374  * a very efficient way of handling reasonably small amounts of data.
5375  *
5376  * We make the files live in virtual memory by intercepting their  opening,
5377  * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
5378  *
5379  * @returns K_TRUE / K_FALSE
5380  * @param   pwszFilename    The file name being accessed.
5381  */
kwFsIsClTempFileW(const wchar_t * pwszFilename)5382 static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
5383 {
5384     wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
5385     if (pwszName)
5386     {
5387         /* The name starts with _CL_... */
5388         if (   pwszName[0] == '_'
5389             && pwszName[1] == 'C'
5390             && pwszName[2] == 'L'
5391             && pwszName[3] == '_' )
5392         {
5393             /* ... followed by 8 xdigits and ends with a two letter file type.  Simplify
5394                this check by just checking that it's alpha numerical ascii from here on. */
5395             wchar_t wc;
5396             pwszName += 4;
5397             while ((wc = *pwszName++) != '\0')
5398             {
5399                 if (wc < 127 && iswalnum(wc))
5400                 { /* likely */ }
5401                 else
5402                     return K_FALSE;
5403             }
5404             return K_TRUE;
5405         }
5406     }
5407     return K_FALSE;
5408 }
5409 
5410 
5411 /**
5412  * Creates a handle to a temporary file.
5413  *
5414  * @returns The handle on success.
5415  *          INVALID_HANDLE_VALUE and SetLastError on failure.
5416  * @param   pTempFile           The temporary file.
5417  * @param   dwDesiredAccess     The desired access to the handle.
5418  * @param   fMapping            Whether this is a mapping (K_TRUE) or file
5419  *                              (K_FALSE) handle type.
5420  */
kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile,DWORD dwDesiredAccess,KBOOL fMapping)5421 static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
5422 {
5423     /*
5424      * Create a handle to the temporary file.
5425      */
5426     HANDLE hFile = INVALID_HANDLE_VALUE;
5427     HANDLE hProcSelf = GetCurrentProcess();
5428     if (DuplicateHandle(hProcSelf, hProcSelf,
5429                         hProcSelf, &hFile,
5430                         SYNCHRONIZE, FALSE,
5431                         0 /*dwOptions*/))
5432     {
5433         PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
5434         if (pHandle)
5435         {
5436             pHandle->enmType            = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
5437             pHandle->cRefs              = 1;
5438             pHandle->offFile            = 0;
5439             pHandle->hHandle            = hFile;
5440             pHandle->dwDesiredAccess    = dwDesiredAccess;
5441             pHandle->u.pTempFile        = pTempFile;
5442             if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
5443             {
5444                 pTempFile->cActiveHandles++;
5445                 kHlpAssert(pTempFile->cActiveHandles >= 1);
5446                 kHlpAssert(pTempFile->cActiveHandles <= 2);
5447                 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
5448                 return hFile;
5449             }
5450 
5451             kHlpFree(pHandle);
5452         }
5453         else
5454             KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
5455         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5456     }
5457     else
5458         KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
5459     return INVALID_HANDLE_VALUE;
5460 }
5461 
5462 
kwFsTempFileCreateW(const wchar_t * pwszFilename,DWORD dwDesiredAccess,DWORD dwCreationDisposition)5463 static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
5464 {
5465     HANDLE hFile;
5466     DWORD  dwErr;
5467 
5468     /*
5469      * Check if we've got an existing temp file.
5470      * ASSUME exact same path for now.
5471      */
5472     KSIZE const   cwcFilename = kwUtf16Len(pwszFilename);
5473     PKWFSTEMPFILE pTempFile;
5474     for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
5475     {
5476         /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
5477         if (   pTempFile->cwcPath == cwcFilename
5478             && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
5479             && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
5480             && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
5481             break;
5482     }
5483 
5484     /*
5485      * Create a new temporary file instance if not found.
5486      */
5487     if (pTempFile == NULL)
5488     {
5489         KSIZE cbFilename;
5490 
5491         switch (dwCreationDisposition)
5492         {
5493             case CREATE_ALWAYS:
5494             case OPEN_ALWAYS:
5495                 dwErr = NO_ERROR;
5496                 break;
5497 
5498             case CREATE_NEW:
5499                 kHlpAssertFailed();
5500                 SetLastError(ERROR_ALREADY_EXISTS);
5501                 return INVALID_HANDLE_VALUE;
5502 
5503             case OPEN_EXISTING:
5504             case TRUNCATE_EXISTING:
5505                 kHlpAssertFailed();
5506                 SetLastError(ERROR_FILE_NOT_FOUND);
5507                 return INVALID_HANDLE_VALUE;
5508 
5509             default:
5510                 kHlpAssertFailed();
5511                 SetLastError(ERROR_INVALID_PARAMETER);
5512                 return INVALID_HANDLE_VALUE;
5513         }
5514 
5515         cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
5516         pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
5517         if (pTempFile)
5518         {
5519             pTempFile->cwcPath          = (KU16)cwcFilename;
5520             pTempFile->cbFile           = 0;
5521             pTempFile->cbFileAllocated  = 0;
5522             pTempFile->cActiveHandles   = 0;
5523             pTempFile->cMappings        = 0;
5524             pTempFile->cSegs            = 0;
5525             pTempFile->paSegs           = NULL;
5526             pTempFile->pwszPath         = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
5527 
5528             pTempFile->pNext = g_Sandbox.pTempFileHead;
5529             g_Sandbox.pTempFileHead = pTempFile;
5530             KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
5531         }
5532         else
5533         {
5534             KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
5535             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5536             return INVALID_HANDLE_VALUE;
5537         }
5538     }
5539     else
5540     {
5541         switch (dwCreationDisposition)
5542         {
5543             case OPEN_EXISTING:
5544                 dwErr = NO_ERROR;
5545                 break;
5546             case OPEN_ALWAYS:
5547                 dwErr = ERROR_ALREADY_EXISTS ;
5548                 break;
5549 
5550             case TRUNCATE_EXISTING:
5551             case CREATE_ALWAYS:
5552                 kHlpAssertFailed();
5553                 pTempFile->cbFile = 0;
5554                 dwErr = ERROR_ALREADY_EXISTS;
5555                 break;
5556 
5557             case CREATE_NEW:
5558                 kHlpAssertFailed();
5559                 SetLastError(ERROR_FILE_EXISTS);
5560                 return INVALID_HANDLE_VALUE;
5561 
5562             default:
5563                 kHlpAssertFailed();
5564                 SetLastError(ERROR_INVALID_PARAMETER);
5565                 return INVALID_HANDLE_VALUE;
5566         }
5567     }
5568 
5569     /*
5570      * Create a handle to the temporary file.
5571      */
5572     hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
5573     if (hFile != INVALID_HANDLE_VALUE)
5574         SetLastError(dwErr);
5575     return hFile;
5576 }
5577 
5578 #endif /* WITH_TEMP_MEMORY_FILES */
5579 
5580 /**
5581  * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
5582  *
5583  * @returns K_TRUE if cacheable, K_FALSE if not.
5584  * @param   wcFirst             The first extension character.
5585  * @param   wcSecond            The second extension character.
5586  * @param   wcThird             The third extension character.
5587  * @param   fAttrQuery          Set if it's for an attribute query, clear if for
5588  *                              file creation.
5589  */
kwFsIsCacheableExtensionCommon(wchar_t wcFirst,wchar_t wcSecond,wchar_t wcThird,KBOOL fAttrQuery)5590 static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
5591 {
5592     /* C++ header without an extension or a directory. */
5593     if (wcFirst == '\0')
5594     {
5595         /** @todo exclude temporary files...  */
5596         return K_TRUE;
5597     }
5598 
5599     /* C Header: .h */
5600     if (wcFirst == 'h' || wcFirst == 'H')
5601     {
5602         if (wcSecond == '\0')
5603             return K_TRUE;
5604 
5605         /* C++ Header: .hpp, .hxx */
5606         if (   (wcSecond == 'p' || wcSecond == 'P')
5607             && (wcThird  == 'p' || wcThird  == 'P'))
5608             return K_TRUE;
5609         if (   (wcSecond == 'x' || wcSecond == 'X')
5610             && (wcThird  == 'x' || wcThird  == 'X'))
5611             return K_TRUE;
5612     }
5613     /* Misc starting with i. */
5614     else if (wcFirst == 'i' || wcFirst == 'I')
5615     {
5616         if (wcSecond != '\0')
5617         {
5618             if (wcSecond == 'n' || wcSecond == 'N')
5619             {
5620                 /* C++ inline header: .inl */
5621                 if (wcThird == 'l' || wcThird == 'L')
5622                     return K_TRUE;
5623 
5624                 /* Assembly include file: .inc */
5625                 if (wcThird == 'c' || wcThird == 'C')
5626                     return K_TRUE;
5627             }
5628         }
5629     }
5630     /* Assembly header: .mac */
5631     else if (wcFirst == 'm' || wcFirst == 'M')
5632     {
5633         if (wcSecond == 'a' || wcSecond == 'A')
5634         {
5635             if (wcThird == 'c' || wcThird == 'C')
5636                 return K_TRUE;
5637         }
5638     }
5639 #ifdef WITH_PCH_CACHING
5640     /* Precompiled header: .pch */
5641     else if (wcFirst == 'p' || wcFirst == 'P')
5642     {
5643         if (wcSecond == 'c' || wcSecond == 'C')
5644         {
5645             if (wcThird == 'h' || wcThird == 'H')
5646                 return !g_Sandbox.fNoPchCaching;
5647         }
5648     }
5649 #endif
5650 #if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done.  */
5651     /* Linker - Object file: .obj */
5652     if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
5653     {
5654         if (wcSecond == 'b' || wcSecond == 'B')
5655         {
5656             if (wcThird == 'j' || wcThird == 'J')
5657                 return K_TRUE;
5658         }
5659     }
5660 #endif
5661     else if (fAttrQuery)
5662     {
5663         /* Dynamic link library: .dll */
5664         if (wcFirst == 'd' || wcFirst == 'D')
5665         {
5666             if (wcSecond == 'l' || wcSecond == 'L')
5667             {
5668                 if (wcThird == 'l' || wcThird == 'L')
5669                     return K_TRUE;
5670             }
5671         }
5672         /* Executable file: .exe */
5673         else if (wcFirst == 'e' || wcFirst == 'E')
5674         {
5675             if (wcSecond == 'x' || wcSecond == 'X')
5676             {
5677                 if (wcThird == 'e' || wcThird == 'E')
5678                     return K_TRUE;
5679             }
5680         }
5681         /* Response file: .rsp */
5682         else if (wcFirst == 'r' || wcFirst == 'R')
5683         {
5684             if (wcSecond == 's' || wcSecond == 'S')
5685             {
5686                 if (wcThird == 'p' || wcThird == 'P')
5687                     return !g_Sandbox.fNoPchCaching;
5688             }
5689         }
5690         /* Linker: */
5691         if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
5692         {
5693             /* Object file: .obj */
5694             if (wcFirst == 'o' || wcFirst == 'O')
5695             {
5696                 if (wcSecond == 'b' || wcSecond == 'B')
5697                 {
5698                     if (wcThird == 'j' || wcThird == 'J')
5699                         return K_TRUE;
5700                 }
5701             }
5702             /* Library file: .lib */
5703             else if (wcFirst == 'l' || wcFirst == 'L')
5704             {
5705                 if (wcSecond == 'i' || wcSecond == 'I')
5706                 {
5707                     if (wcThird == 'b' || wcThird == 'B')
5708                         return K_TRUE;
5709                 }
5710             }
5711             /* Linker definition file: .def */
5712             else if (wcFirst == 'd' || wcFirst == 'D')
5713             {
5714                 if (wcSecond == 'e' || wcSecond == 'E')
5715                 {
5716                     if (wcThird == 'f' || wcThird == 'F')
5717                         return K_TRUE;
5718                 }
5719             }
5720         }
5721     }
5722 
5723     return K_FALSE;
5724 }
5725 
5726 
5727 /**
5728  * Checks if the file extension indicates that the file/dir is something we
5729  * ought to cache.
5730  *
5731  * @returns K_TRUE if cachable, K_FALSE if not.
5732  * @param   pszExt              The kHlpGetExt result.
5733  * @param   fAttrQuery          Set if it's for an attribute query, clear if for
5734  *                              file creation.
5735  */
kwFsIsCacheableExtensionA(const char * pszExt,KBOOL fAttrQuery)5736 static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
5737 {
5738     wchar_t const wcFirst = *pszExt;
5739     if (wcFirst)
5740     {
5741         wchar_t const wcSecond = pszExt[1];
5742         if (wcSecond)
5743         {
5744             wchar_t const wcThird = pszExt[2];
5745             if (pszExt[3] == '\0')
5746                 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
5747             return K_FALSE;
5748         }
5749         return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
5750     }
5751     return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
5752 }
5753 
5754 
5755 /**
5756  * Checks if the extension of the given UTF-16 path indicates that the file/dir
5757  * should be cached.
5758  *
5759  * @returns K_TRUE if cachable, K_FALSE if not.
5760  * @param   pwszPath            The UTF-16 path to examine.
5761  * @param   fAttrQuery          Set if it's for an attribute query, clear if for
5762  *                              file creation.
5763  */
kwFsIsCacheablePathExtensionW(const wchar_t * pwszPath,KBOOL fAttrQuery)5764 static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
5765 {
5766     KSIZE           cwcExt;
5767     wchar_t const  *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
5768     switch (cwcExt)
5769     {
5770         case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
5771         case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0,          fAttrQuery);
5772         case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0,          0,          fAttrQuery);
5773         case 0: return kwFsIsCacheableExtensionCommon(0,          0,          0,          fAttrQuery);
5774     }
5775     return K_FALSE;
5776 }
5777 
5778 
5779 
5780 /**
5781  * Creates a new
5782  *
5783  * @returns
5784  * @param   pFsObj          .
5785  * @param   pwszFilename    .
5786  */
kwFsObjCacheNewFile(PKFSOBJ pFsObj)5787 static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
5788 {
5789     HANDLE                  hFile;
5790     MY_IO_STATUS_BLOCK      Ios;
5791     MY_OBJECT_ATTRIBUTES    ObjAttr;
5792     MY_UNICODE_STRING       UniStr;
5793     MY_NTSTATUS             rcNt;
5794     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5795 
5796     /*
5797      * Open the file relative to the parent directory.
5798      */
5799     kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
5800     kHlpAssert(pFsObj->pParent);
5801     kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
5802 
5803     Ios.Information = -1;
5804     Ios.u.Status    = -1;
5805 
5806     UniStr.Buffer        = (wchar_t *)pFsObj->pwszName;
5807     UniStr.Length        = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
5808     UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
5809 
5810     MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
5811 
5812     rcNt = g_pfnNtCreateFile(&hFile,
5813                              GENERIC_READ | SYNCHRONIZE,
5814                              &ObjAttr,
5815                              &Ios,
5816                              NULL, /*cbFileInitialAlloc */
5817                              FILE_ATTRIBUTE_NORMAL,
5818                              FILE_SHARE_READ,
5819                              FILE_OPEN,
5820                              FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
5821                              NULL, /*pEaBuffer*/
5822                              0);   /*cbEaBuffer*/
5823     if (MY_NT_SUCCESS(rcNt))
5824     {
5825         /*
5826          * Read the whole file into memory.
5827          */
5828         LARGE_INTEGER cbFile;
5829         if (GetFileSizeEx(hFile, &cbFile))
5830         {
5831             if (   cbFile.QuadPart >= 0
5832 #ifdef WITH_PCH_CACHING
5833                 && (   cbFile.QuadPart < 16*1024*1024
5834                     || (   cbFile.QuadPart < 96*1024*1024
5835                         && pFsObj->cchName > 4
5836                         && !g_Sandbox.fNoPchCaching
5837                         && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
5838 #endif
5839                )
5840             {
5841                 KU32 cbCache = (KU32)cbFile.QuadPart;
5842                 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/,  PAGE_READONLY,
5843                                                      0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
5844                 if (hMapping != NULL)
5845                 {
5846                     KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
5847                     if (pbCache)
5848                     {
5849                         /*
5850                          * Create the cached file object.
5851                          */
5852                         PKFSWCACHEDFILE pCachedFile;
5853                         KU32            cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
5854                         pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
5855                                                                               sizeof(*pCachedFile) + cbPath);
5856                         if (pCachedFile)
5857                         {
5858                             pCachedFile->hCached  = hFile;
5859                             pCachedFile->hSection = hMapping;
5860                             pCachedFile->cbCached = cbCache;
5861                             pCachedFile->pbCached = pbCache;
5862                             pCachedFile->pFsObj   = pFsObj;
5863                             kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
5864                             kFsCacheObjRetain(pFsObj);
5865 
5866                             g_cReadCachedFiles++;
5867                             g_cbReadCachedFiles += cbCache;
5868 
5869                             KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
5870                             return pCachedFile;
5871                         }
5872 
5873                         KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
5874                     }
5875                     else
5876                         KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
5877                     CloseHandle(hMapping);
5878                 }
5879                 else
5880                     KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
5881             }
5882             else
5883                 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
5884         }
5885         else
5886             KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
5887         g_pfnNtClose(hFile);
5888     }
5889     else
5890         KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
5891     return NULL;
5892 }
5893 
5894 
5895 /**
5896  * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
5897  */
kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile,DWORD dwDesiredAccess,BOOL fInheritHandle,KBOOL fIsFileHandle,HANDLE * phFile)5898 static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
5899                                           KBOOL fIsFileHandle, HANDLE *phFile)
5900 {
5901     HANDLE hProcSelf = GetCurrentProcess();
5902     if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
5903                         hProcSelf, phFile,
5904                         dwDesiredAccess, fInheritHandle,
5905                         0 /*dwOptions*/))
5906     {
5907         /*
5908          * Create handle table entry for the duplicate handle.
5909          */
5910         PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
5911         if (pHandle)
5912         {
5913             pHandle->enmType            = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
5914             pHandle->cRefs              = 1;
5915             pHandle->offFile            = 0;
5916             pHandle->hHandle            = *phFile;
5917             pHandle->dwDesiredAccess    = dwDesiredAccess;
5918             pHandle->u.pCachedFile      = pCachedFile;
5919             if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
5920                 return K_TRUE;
5921 
5922             kHlpFree(pHandle);
5923         }
5924         else
5925             KWFS_LOG(("Out of memory for handle!\n"));
5926 
5927         CloseHandle(*phFile);
5928         *phFile = INVALID_HANDLE_VALUE;
5929     }
5930     else
5931         KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
5932     return K_FALSE;
5933 }
5934 
5935 
5936 /**
5937  * Kernel32 - Common code for CreateFileW and CreateFileA.
5938  */
kwFsObjCacheCreateFile(PKFSOBJ pFsObj,DWORD dwDesiredAccess,BOOL fInheritHandle,HANDLE * phFile)5939 static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
5940 {
5941     *phFile = INVALID_HANDLE_VALUE;
5942 
5943     /*
5944      * At the moment we only handle existing files.
5945      */
5946     if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
5947     {
5948         PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
5949         kHlpAssert(pFsObj->fHaveStats);
5950         if (   pCachedFile != NULL
5951             || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
5952         {
5953             if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
5954                 return K_TRUE;
5955         }
5956     }
5957     /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
5958 
5959     /* Do fallback, please. */
5960     return K_FALSE;
5961 }
5962 
5963 
5964 /** Kernel32 - CreateFileA */
kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES pSecAttrs,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile)5965 static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
5966                                                     LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
5967                                                     DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
5968 {
5969     HANDLE hFile;
5970 
5971     /*
5972      * Check for include files and similar that we do read-only caching of.
5973      */
5974     if (dwCreationDisposition == FILE_OPEN_IF)
5975     {
5976         if (   dwDesiredAccess == GENERIC_READ
5977             || dwDesiredAccess == FILE_GENERIC_READ)
5978         {
5979             if (dwShareMode & FILE_SHARE_READ)
5980             {
5981                 if (   !pSecAttrs
5982                     || (   pSecAttrs->nLength == sizeof(*pSecAttrs)
5983                         && pSecAttrs->lpSecurityDescriptor == NULL ) )
5984                 {
5985                     const char *pszExt = kHlpGetExt(pszFilename);
5986                     if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
5987                     {
5988                         KFSLOOKUPERROR enmError;
5989                         PKFSOBJ pFsObj;
5990                         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5991 
5992                         pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
5993                         if (pFsObj)
5994                         {
5995                             KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
5996                                                                &hFile);
5997                             kFsCacheObjRelease(g_pFsCache, pFsObj);
5998                             if (fRc)
5999                             {
6000                                 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6001                                 return hFile;
6002                             }
6003                         }
6004                         /* These are for nasm and yasm header searching.  Cache will already
6005                            have checked the directories for the file, no need to call
6006                            CreateFile to do it again. */
6007                         else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6008                         {
6009                             KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6010                             return INVALID_HANDLE_VALUE;
6011                         }
6012                         else if (   enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6013                                  || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6014                         {
6015                             KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6016                             return INVALID_HANDLE_VALUE;
6017                         }
6018 
6019                         /* fallback */
6020                         hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6021                                             dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6022                         KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
6023                         return hFile;
6024                     }
6025                 }
6026             }
6027         }
6028     }
6029 
6030     /*
6031      * Okay, normal.
6032      */
6033     hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6034                         dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6035     if (hFile != INVALID_HANDLE_VALUE)
6036     {
6037         kHlpAssert(   KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6038                    || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6039     }
6040     KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
6041     return hFile;
6042 }
6043 
6044 
6045 /** Kernel32 - CreateFileW */
kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES pSecAttrs,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile)6046 static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6047                                                     LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6048                                                     DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6049 {
6050     HANDLE hFile;
6051 
6052 #ifdef WITH_TEMP_MEMORY_FILES
6053     /*
6054      * Check for temporary files (cl.exe only).
6055      */
6056     if (   g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6057         && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
6058         && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
6059         && kwFsIsClTempFileW(pwszFilename))
6060     {
6061         hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition);
6062         KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
6063         return hFile;
6064     }
6065 #endif
6066 
6067     /*
6068      * Check for include files and similar that we do read-only caching of.
6069      */
6070     if (dwCreationDisposition == FILE_OPEN_IF)
6071     {
6072         if (   dwDesiredAccess == GENERIC_READ
6073             || dwDesiredAccess == FILE_GENERIC_READ)
6074         {
6075             if (dwShareMode & FILE_SHARE_READ)
6076             {
6077                 if (   !pSecAttrs
6078                     || (   pSecAttrs->nLength == sizeof(*pSecAttrs)
6079                         && pSecAttrs->lpSecurityDescriptor == NULL ) )
6080                 {
6081                     if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
6082                     {
6083                         KFSLOOKUPERROR enmError;
6084                         PKFSOBJ pFsObj;
6085                         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6086 
6087                         pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6088                         if (pFsObj)
6089                         {
6090                             KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6091                                                                &hFile);
6092                             kFsCacheObjRelease(g_pFsCache, pFsObj);
6093                             if (fRc)
6094                             {
6095                                 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
6096                                 return hFile;
6097                             }
6098                         }
6099                         /* These are for nasm and yasm style header searching.  Cache will
6100                            already have checked the directories for the file, no need to call
6101                            CreateFile to do it again. */
6102                         else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6103                         {
6104                             KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
6105                             return INVALID_HANDLE_VALUE;
6106                         }
6107                         else if (   enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6108                                  || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6109                         {
6110                             KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
6111                             return INVALID_HANDLE_VALUE;
6112                         }
6113 
6114                         /* fallback */
6115                         hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6116                                             dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6117                         KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
6118                         return hFile;
6119                     }
6120                 }
6121                 else
6122                     KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
6123                               pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
6124             }
6125             else
6126                 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
6127         }
6128         else
6129             KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
6130     }
6131     else
6132         KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
6133 
6134     /*
6135      * Okay, normal.
6136      */
6137     hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6138                         dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6139     if (hFile != INVALID_HANDLE_VALUE)
6140     {
6141         kHlpAssert(   KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6142                    || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6143     }
6144     KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
6145     return hFile;
6146 }
6147 
6148 
6149 /** Kernel32 - SetFilePointer */
kwSandbox_Kernel32_SetFilePointer(HANDLE hFile,LONG cbMove,PLONG pcbMoveHi,DWORD dwMoveMethod)6150 static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
6151 {
6152     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6153     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6154     if (idxHandle < g_Sandbox.cHandles)
6155     {
6156         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6157         if (pHandle != NULL)
6158         {
6159             KU32 cbFile;
6160             KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
6161             switch (pHandle->enmType)
6162             {
6163                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6164                     cbFile = pHandle->u.pCachedFile->cbCached;
6165                     break;
6166 #ifdef WITH_TEMP_MEMORY_FILES
6167                 case KWHANDLETYPE_TEMP_FILE:
6168                     cbFile = pHandle->u.pTempFile->cbFile;
6169                     break;
6170 #endif
6171                 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6172                 case KWHANDLETYPE_OUTPUT_BUF:
6173                 default:
6174                     kHlpAssertFailed();
6175                     SetLastError(ERROR_INVALID_FUNCTION);
6176                     return INVALID_SET_FILE_POINTER;
6177             }
6178 
6179             switch (dwMoveMethod)
6180             {
6181                 case FILE_BEGIN:
6182                     break;
6183                 case FILE_CURRENT:
6184                     offMove += pHandle->offFile;
6185                     break;
6186                 case FILE_END:
6187                     offMove += cbFile;
6188                     break;
6189                 default:
6190                     KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6191                     SetLastError(ERROR_INVALID_PARAMETER);
6192                     return INVALID_SET_FILE_POINTER;
6193             }
6194             if (offMove >= 0)
6195             {
6196                 if (offMove >= (KSSIZE)cbFile)
6197                 {
6198                     /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6199                     if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6200                         offMove = (KSSIZE)cbFile;
6201                     /* For writable files, seeking beyond the end is fine, but check that we've got
6202                        the type range for the request. */
6203                     else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
6204                     {
6205                         kHlpAssertMsgFailed(("%#llx\n", offMove));
6206                         SetLastError(ERROR_SEEK);
6207                         return INVALID_SET_FILE_POINTER;
6208                     }
6209                 }
6210                 pHandle->offFile = (KU32)offMove;
6211             }
6212             else
6213             {
6214                 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
6215                 SetLastError(ERROR_NEGATIVE_SEEK);
6216                 return INVALID_SET_FILE_POINTER;
6217             }
6218             if (pcbMoveHi)
6219                 *pcbMoveHi = (KU64)offMove >> 32;
6220             KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
6221                       pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6222             SetLastError(NO_ERROR);
6223             return (KU32)offMove;
6224         }
6225     }
6226     KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
6227     return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
6228 }
6229 
6230 
6231 /** Kernel32 - SetFilePointerEx */
kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile,LARGE_INTEGER offMove,PLARGE_INTEGER poffNew,DWORD dwMoveMethod)6232 static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
6233                                                        DWORD dwMoveMethod)
6234 {
6235     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6236     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6237     if (idxHandle < g_Sandbox.cHandles)
6238     {
6239         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6240         if (pHandle != NULL)
6241         {
6242             KI64 offMyMove = offMove.QuadPart;
6243             KU32 cbFile;
6244             switch (pHandle->enmType)
6245             {
6246                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6247                     cbFile = pHandle->u.pCachedFile->cbCached;
6248                     break;
6249 #ifdef WITH_TEMP_MEMORY_FILES
6250                 case KWHANDLETYPE_TEMP_FILE:
6251                     cbFile = pHandle->u.pTempFile->cbFile;
6252                     break;
6253 #endif
6254                 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6255                 case KWHANDLETYPE_OUTPUT_BUF:
6256                 default:
6257                     kHlpAssertFailed();
6258                     SetLastError(ERROR_INVALID_FUNCTION);
6259                     return INVALID_SET_FILE_POINTER;
6260             }
6261 
6262             switch (dwMoveMethod)
6263             {
6264                 case FILE_BEGIN:
6265                     break;
6266                 case FILE_CURRENT:
6267                     offMyMove += pHandle->offFile;
6268                     break;
6269                 case FILE_END:
6270                     offMyMove += cbFile;
6271                     break;
6272                 default:
6273                     KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6274                     SetLastError(ERROR_INVALID_PARAMETER);
6275                     return INVALID_SET_FILE_POINTER;
6276             }
6277             if (offMyMove >= 0)
6278             {
6279                 if (offMyMove >= (KSSIZE)cbFile)
6280                 {
6281                     /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6282                     if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6283                         offMyMove = (KSSIZE)cbFile;
6284                     /* For writable files, seeking beyond the end is fine, but check that we've got
6285                        the type range for the request. */
6286                     else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
6287                     {
6288                         kHlpAssertMsgFailed(("%#llx\n", offMyMove));
6289                         SetLastError(ERROR_SEEK);
6290                         return INVALID_SET_FILE_POINTER;
6291                     }
6292                 }
6293                 pHandle->offFile = (KU32)offMyMove;
6294             }
6295             else
6296             {
6297                 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
6298                 SetLastError(ERROR_NEGATIVE_SEEK);
6299                 return INVALID_SET_FILE_POINTER;
6300             }
6301             if (poffNew)
6302                 poffNew->QuadPart = offMyMove;
6303             KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
6304                       pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6305             return TRUE;
6306         }
6307     }
6308     KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
6309     return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
6310 }
6311 
6312 
6313 /** Kernel32 - ReadFile */
kwSandbox_Kernel32_ReadFile(HANDLE hFile,LPVOID pvBuffer,DWORD cbToRead,LPDWORD pcbActuallyRead,LPOVERLAPPED pOverlapped)6314 static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
6315                                                LPOVERLAPPED pOverlapped)
6316 {
6317     BOOL        fRet;
6318     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6319     g_cReadFileCalls++;
6320     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6321     if (idxHandle < g_Sandbox.cHandles)
6322     {
6323         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6324         if (pHandle != NULL)
6325         {
6326             switch (pHandle->enmType)
6327             {
6328                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6329                 {
6330                     PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
6331                     KU32            cbActually = pCachedFile->cbCached - pHandle->offFile;
6332                     if (cbActually > cbToRead)
6333                         cbActually = cbToRead;
6334 
6335 #ifdef WITH_HASH_MD5_CACHE
6336                     if (g_Sandbox.pHashHead)
6337                     {
6338                         g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
6339                         g_Sandbox.LastHashRead.offRead     = pHandle->offFile;
6340                         g_Sandbox.LastHashRead.cbRead      = cbActually;
6341                         g_Sandbox.LastHashRead.pvRead      = pvBuffer;
6342                     }
6343 #endif
6344 
6345                     kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
6346                     pHandle->offFile += cbActually;
6347 
6348                     kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6349                     *pcbActuallyRead = cbActually;
6350 
6351                     g_cbReadFileFromReadCached += cbActually;
6352                     g_cbReadFileTotal          += cbActually;
6353                     g_cReadFileFromReadCached++;
6354 
6355                     KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
6356                     return TRUE;
6357                 }
6358 
6359 #ifdef WITH_TEMP_MEMORY_FILES
6360                 case KWHANDLETYPE_TEMP_FILE:
6361                 {
6362                     PKWFSTEMPFILE   pTempFile  = pHandle->u.pTempFile;
6363                     KU32            cbActually;
6364                     if (pHandle->offFile < pTempFile->cbFile)
6365                     {
6366                         cbActually = pTempFile->cbFile - pHandle->offFile;
6367                         if (cbActually > cbToRead)
6368                             cbActually = cbToRead;
6369 
6370                         /* Copy the data. */
6371                         if (cbActually > 0)
6372                         {
6373                             KU32                    cbLeft;
6374                             KU32                    offSeg;
6375                             KWFSTEMPFILESEG const  *paSegs = pTempFile->paSegs;
6376 
6377                             /* Locate the segment containing the byte at offFile. */
6378                             KU32 iSeg   = pTempFile->cSegs - 1;
6379                             kHlpAssert(pTempFile->cSegs > 0);
6380                             while (paSegs[iSeg].offData > pHandle->offFile)
6381                                 iSeg--;
6382 
6383                             /* Copy out the data. */
6384                             cbLeft = cbActually;
6385                             offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6386                             for (;;)
6387                             {
6388                                 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6389                                 if (cbAvail >= cbLeft)
6390                                 {
6391                                     kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
6392                                     break;
6393                                 }
6394 
6395                                 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
6396                                 cbLeft  -= cbAvail;
6397                                 offSeg   = 0;
6398                                 iSeg++;
6399                                 kHlpAssert(iSeg < pTempFile->cSegs);
6400                             }
6401 
6402                             /* Update the file offset. */
6403                             pHandle->offFile += cbActually;
6404                         }
6405                     }
6406                     /* Read does not commit file space, so return zero bytes. */
6407                     else
6408                         cbActually = 0;
6409 
6410                     kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6411                     *pcbActuallyRead = cbActually;
6412 
6413                     g_cbReadFileTotal         += cbActually;
6414                     g_cbReadFileFromInMemTemp += cbActually;
6415                     g_cReadFileFromInMemTemp++;
6416 
6417                     KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
6418                     return TRUE;
6419                 }
6420 #endif /* WITH_TEMP_MEMORY_FILES */
6421 
6422                 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6423                 case KWHANDLETYPE_OUTPUT_BUF:
6424                 default:
6425                     kHlpAssertFailed();
6426                     SetLastError(ERROR_INVALID_FUNCTION);
6427                     *pcbActuallyRead = 0;
6428                     return FALSE;
6429             }
6430         }
6431     }
6432 
6433     fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
6434     if (fRet && pcbActuallyRead)
6435         g_cbReadFileTotal += *pcbActuallyRead;
6436     KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
6437     return fRet;
6438 }
6439 
6440 
6441 /** Kernel32 - ReadFileEx */
kwSandbox_Kernel32_ReadFileEx(HANDLE hFile,LPVOID pvBuffer,DWORD cbToRead,LPOVERLAPPED pOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)6442 static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
6443                                                  LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
6444 {
6445     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6446     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6447     if (idxHandle < g_Sandbox.cHandles)
6448     {
6449         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6450         if (pHandle != NULL)
6451         {
6452             kHlpAssertFailed();
6453         }
6454     }
6455 
6456     KWFS_LOG(("ReadFile(%p)\n", hFile));
6457     return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
6458 }
6459 
6460 #ifdef WITH_STD_OUT_ERR_BUFFERING
6461 
6462 /**
6463  * Write something to a handle, making sure everything is actually written.
6464  *
6465  * @param   hHandle         Where to write it to.
6466  * @param   pchBuf          What to write
6467  * @param   cchToWrite      How much to write.
6468  */
kwSandboxOutBufWriteIt(HANDLE hFile,char const * pchBuf,KU32 cchToWrite)6469 static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
6470 {
6471     if (cchToWrite > 0)
6472     {
6473         DWORD cchWritten = 0;
6474         if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
6475         {
6476             if (cchWritten == cchToWrite)
6477             { /* likely */ }
6478             else
6479             {
6480                 do
6481                 {
6482                     pchBuf += cchWritten;
6483                     cchToWrite -= cchWritten;
6484                     cchWritten = 0;
6485                 } while (   cchToWrite > 0
6486                          && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
6487             }
6488         }
6489         else
6490             kHlpAssertFailed();
6491     }
6492 }
6493 
6494 
6495 /**
6496  * Worker for WriteFile when the output isn't going to the console.
6497  *
6498  * @param   pSandbox            The sandbox.
6499  * @param   pOutBuf             The output buffer.
6500  * @param   pchBuffer           What to write.
6501  * @param   cchToWrite          How much to write.
6502  */
kwSandboxOutBufWrite(PKWSANDBOX pSandbox,PKWOUTPUTSTREAMBUF pOutBuf,const char * pchBuffer,KU32 cchToWrite)6503 static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
6504 {
6505     if (pOutBuf->u.Fully.cchBufAlloc > 0)
6506     { /* likely */ }
6507     else
6508     {
6509         /* No realloc, max size is 64KB. */
6510         pOutBuf->u.Fully.cchBufAlloc = 0x10000;
6511         pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
6512         if (!pOutBuf->u.Fully.pchBuf)
6513         {
6514             while (   !pOutBuf->u.Fully.pchBuf
6515                    && pOutBuf->u.Fully.cchBufAlloc > 64)
6516             {
6517                 pOutBuf->u.Fully.cchBufAlloc /= 2;
6518                 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
6519             }
6520             if (!pOutBuf->u.Fully.pchBuf)
6521             {
6522                 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
6523                 pOutBuf->u.Fully.pchBuf      = pOutBuf->abPadding;
6524             }
6525         }
6526     }
6527 
6528     /*
6529      * Special case: Output ends with newline and fits in the buffer.
6530      */
6531     if (   cchToWrite > 1
6532         && pchBuffer[cchToWrite - 1] == '\n'
6533         && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
6534     {
6535         kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
6536         pOutBuf->u.Fully.cchBuf += cchToWrite;
6537     }
6538     else
6539     {
6540         /*
6541          * Work thru the text line by line, flushing the buffer when
6542          * appropriate.  The buffer is not a line buffer here, it's a
6543          * full buffer.
6544          */
6545         while (cchToWrite > 0)
6546         {
6547             char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
6548             KU32        cchLine    = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
6549             if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
6550             {
6551                 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
6552                 pOutBuf->u.Fully.cchBuf += cchLine;
6553             }
6554             /*
6555              * Option one: Flush the buffer and the current line.
6556              *
6557              * We choose this one when the line won't ever fit, or when we have
6558              * an incomplete line in the buffer.
6559              */
6560             else if (   cchLine >= pOutBuf->u.Fully.cchBufAlloc
6561                      || pOutBuf->u.Fully.cchBuf == 0
6562                      || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
6563             {
6564                 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
6565                 if (pOutBuf->u.Fully.cchBuf > 0)
6566                 {
6567                     kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
6568                     pOutBuf->u.Fully.cchBuf = 0;
6569                 }
6570                 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
6571             }
6572             /*
6573              * Option two: Only flush the lines in the buffer.
6574              */
6575             else
6576             {
6577                 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
6578                 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
6579                 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
6580                 pOutBuf->u.Fully.cchBuf = cchLine;
6581             }
6582 
6583             /* advance */
6584             pchBuffer  += cchLine;
6585             cchToWrite -= cchLine;
6586         }
6587     }
6588 }
6589 
6590 #endif  /* WITH_STD_OUT_ERR_BUFFERING */
6591 
6592 #ifdef WITH_TEMP_MEMORY_FILES
kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile,KU32 offFile,KU32 cbNeeded)6593 static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
6594 {
6595     KU32 cbMinFile = offFile + cbNeeded;
6596     if (cbMinFile >= offFile)
6597     {
6598         /* Calc how much space we've already allocated and  */
6599         if (cbMinFile <= pTempFile->cbFileAllocated)
6600             return K_TRUE;
6601 
6602         /* Grow the file. */
6603         if (cbMinFile <= KWFS_TEMP_FILE_MAX)
6604         {
6605             int  rc;
6606             KU32 cSegs    = pTempFile->cSegs;
6607             KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
6608             do
6609             {
6610                 /* grow the segment array? */
6611                 if ((cSegs % 16) == 0)
6612                 {
6613                     void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
6614                     if (!pvNew)
6615                         return K_FALSE;
6616                     pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
6617                 }
6618 
6619                 /* Use page alloc here to simplify mapping later. */
6620                 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
6621                 if (rc == 0)
6622                 { /* likely */ }
6623                 else
6624                 {
6625                     cbNewSeg = 64*1024;
6626                     rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
6627                     if (rc != 0)
6628                     {
6629                         kHlpAssertFailed();
6630                         return K_FALSE;
6631                     }
6632                 }
6633                 pTempFile->paSegs[cSegs].offData     = pTempFile->cbFileAllocated;
6634                 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
6635                 pTempFile->cbFileAllocated          += cbNewSeg;
6636                 pTempFile->cSegs                     = ++cSegs;
6637 
6638             } while (pTempFile->cbFileAllocated < cbMinFile);
6639 
6640             return K_TRUE;
6641         }
6642     }
6643 
6644     kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
6645     return K_FALSE;
6646 }
6647 #endif /* WITH_TEMP_MEMORY_FILES */
6648 
6649 
6650 #if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
6651 /** Kernel32 - WriteFile */
kwSandbox_Kernel32_WriteFile(HANDLE hFile,LPCVOID pvBuffer,DWORD cbToWrite,LPDWORD pcbActuallyWritten,LPOVERLAPPED pOverlapped)6652 static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
6653                                                 LPOVERLAPPED pOverlapped)
6654 {
6655     BOOL        fRet;
6656     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6657     g_cWriteFileCalls++;
6658     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_fCtrlC);
6659     if (idxHandle < g_Sandbox.cHandles)
6660     {
6661         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6662         if (pHandle != NULL)
6663         {
6664             switch (pHandle->enmType)
6665             {
6666 # ifdef WITH_TEMP_MEMORY_FILES
6667                 case KWHANDLETYPE_TEMP_FILE:
6668                 {
6669                     PKWFSTEMPFILE   pTempFile  = pHandle->u.pTempFile;
6670 
6671                     kHlpAssert(!pOverlapped);
6672                     kHlpAssert(pcbActuallyWritten);
6673 
6674                     if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
6675                     {
6676                         KU32                    cbLeft;
6677                         KU32                    offSeg;
6678 
6679                         /* Locate the segment containing the byte at offFile. */
6680                         KWFSTEMPFILESEG const  *paSegs = pTempFile->paSegs;
6681                         KU32                    iSeg   = pTempFile->cSegs - 1;
6682                         kHlpAssert(pTempFile->cSegs > 0);
6683                         while (paSegs[iSeg].offData > pHandle->offFile)
6684                             iSeg--;
6685 
6686                         /* Copy in the data. */
6687                         cbLeft = cbToWrite;
6688                         offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6689                         for (;;)
6690                         {
6691                             KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6692                             if (cbAvail >= cbLeft)
6693                             {
6694                                 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
6695                                 break;
6696                             }
6697 
6698                             kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
6699                             pvBuffer = (KU8 const *)pvBuffer + cbAvail;
6700                             cbLeft  -= cbAvail;
6701                             offSeg   = 0;
6702                             iSeg++;
6703                             kHlpAssert(iSeg < pTempFile->cSegs);
6704                         }
6705 
6706                         /* Update the file offset. */
6707                         pHandle->offFile += cbToWrite;
6708                         if (pHandle->offFile > pTempFile->cbFile)
6709                             pTempFile->cbFile = pHandle->offFile;
6710 
6711                         *pcbActuallyWritten = cbToWrite;
6712 
6713                         g_cbWriteFileTotal += cbToWrite;
6714                         g_cbWriteFileToInMemTemp += cbToWrite;
6715                         g_cWriteFileToInMemTemp++;
6716 
6717                         KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
6718                         return TRUE;
6719                     }
6720 
6721                     kHlpAssertFailed();
6722                     *pcbActuallyWritten = 0;
6723                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6724                     return FALSE;
6725                 }
6726 # endif
6727 
6728                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6729                     kHlpAssertFailed();
6730                     SetLastError(ERROR_ACCESS_DENIED);
6731                     *pcbActuallyWritten = 0;
6732                     return FALSE;
6733 
6734 # if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
6735                 /*
6736                  * Standard output & error.
6737                  */
6738                 case KWHANDLETYPE_OUTPUT_BUF:
6739                 {
6740                     PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
6741                     if (pOutBuf->fIsConsole)
6742                     {
6743                         kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
6744                         KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
6745                     }
6746                     else
6747                     {
6748 #  ifdef WITH_STD_OUT_ERR_BUFFERING
6749                         kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
6750                         KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
6751                                    pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
6752 #  else
6753                         kHlpAssertFailed();
6754 #  endif
6755                     }
6756                     if (pcbActuallyWritten)
6757                         *pcbActuallyWritten = cbToWrite;
6758                     g_cbWriteFileTotal += cbToWrite;
6759                     return TRUE;
6760                 }
6761 # endif
6762 
6763                 default:
6764                 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6765                     kHlpAssertFailed();
6766                     SetLastError(ERROR_INVALID_FUNCTION);
6767                     *pcbActuallyWritten = 0;
6768                     return FALSE;
6769             }
6770         }
6771     }
6772 
6773     fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
6774     if (fRet && pcbActuallyWritten)
6775         g_cbWriteFileTotal += *pcbActuallyWritten;
6776     KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
6777     return fRet;
6778 }
6779 
6780 
6781 /** Kernel32 - WriteFileEx */
kwSandbox_Kernel32_WriteFileEx(HANDLE hFile,LPCVOID pvBuffer,DWORD cbToWrite,LPOVERLAPPED pOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)6782 static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
6783                                                   LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
6784 {
6785     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6786     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6787     if (idxHandle < g_Sandbox.cHandles)
6788     {
6789         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6790         if (pHandle != NULL)
6791         {
6792             kHlpAssertFailed();
6793         }
6794     }
6795 
6796     KWFS_LOG(("WriteFileEx(%p)\n", hFile));
6797     return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
6798 }
6799 
6800 #endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
6801 
6802 #ifdef WITH_TEMP_MEMORY_FILES
6803 
6804 /** Kernel32 - SetEndOfFile; */
kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)6805 static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
6806 {
6807     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6808     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6809     if (idxHandle < g_Sandbox.cHandles)
6810     {
6811         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6812         if (pHandle != NULL)
6813         {
6814             switch (pHandle->enmType)
6815             {
6816                 case KWHANDLETYPE_TEMP_FILE:
6817                 {
6818                     PKWFSTEMPFILE   pTempFile  = pHandle->u.pTempFile;
6819                     if (   pHandle->offFile > pTempFile->cbFile
6820                         && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
6821                     {
6822                         kHlpAssertFailed();
6823                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6824                         return FALSE;
6825                     }
6826 
6827                     pTempFile->cbFile = pHandle->offFile;
6828                     KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
6829                     return TRUE;
6830                 }
6831 
6832                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6833                     kHlpAssertFailed();
6834                     SetLastError(ERROR_ACCESS_DENIED);
6835                     return FALSE;
6836 
6837                 case KWHANDLETYPE_OUTPUT_BUF:
6838                     kHlpAssertFailed();
6839                     SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
6840                     return FALSE;
6841 
6842                 default:
6843                 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6844                     kHlpAssertFailed();
6845                     SetLastError(ERROR_INVALID_FUNCTION);
6846                     return FALSE;
6847             }
6848         }
6849     }
6850 
6851     KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
6852     return SetEndOfFile(hFile);
6853 }
6854 
6855 
6856 /** Kernel32 - GetFileType  */
kwSandbox_Kernel32_GetFileType(HANDLE hFile)6857 static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
6858 {
6859     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6860     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6861     if (idxHandle < g_Sandbox.cHandles)
6862     {
6863         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6864         if (pHandle != NULL)
6865         {
6866             switch (pHandle->enmType)
6867             {
6868                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6869                     KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
6870                     return FILE_TYPE_DISK;
6871 
6872                 case KWHANDLETYPE_TEMP_FILE:
6873                     KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
6874                     return FILE_TYPE_DISK;
6875 
6876                 case KWHANDLETYPE_OUTPUT_BUF:
6877                 {
6878                     PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
6879                     DWORD fRet;
6880                     if (pOutBuf->fFileType != KU8_MAX)
6881                     {
6882                         fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
6883                         KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
6884                     }
6885                     else
6886                     {
6887                         fRet = GetFileType(hFile);
6888                         KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
6889                     }
6890                     return fRet;
6891                 }
6892 
6893             }
6894         }
6895     }
6896 
6897     KWFS_LOG(("GetFileType(%p)\n", hFile));
6898     return GetFileType(hFile);
6899 }
6900 
6901 
6902 /** Kernel32 - GetFileSize  */
kwSandbox_Kernel32_GetFileSize(HANDLE hFile,LPDWORD pcbHighDword)6903 static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
6904 {
6905     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6906     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6907     if (idxHandle < g_Sandbox.cHandles)
6908     {
6909         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6910         if (pHandle != NULL)
6911         {
6912             if (pcbHighDword)
6913                 *pcbHighDword = 0;
6914             SetLastError(NO_ERROR);
6915             switch (pHandle->enmType)
6916             {
6917                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6918                     KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
6919                     return pHandle->u.pCachedFile->cbCached;
6920 
6921                 case KWHANDLETYPE_TEMP_FILE:
6922                     KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
6923                     return pHandle->u.pTempFile->cbFile;
6924 
6925                 case KWHANDLETYPE_OUTPUT_BUF:
6926                     /* do default */
6927                     break;
6928 
6929                 default:
6930                     kHlpAssertFailed();
6931                     SetLastError(ERROR_INVALID_FUNCTION);
6932                     return INVALID_FILE_SIZE;
6933             }
6934         }
6935     }
6936 
6937     KWFS_LOG(("GetFileSize(%p,)\n", hFile));
6938     return GetFileSize(hFile, pcbHighDword);
6939 }
6940 
6941 
6942 /** Kernel32 - GetFileSizeEx  */
kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile,PLARGE_INTEGER pcbFile)6943 static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
6944 {
6945     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6946     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6947     if (idxHandle < g_Sandbox.cHandles)
6948     {
6949         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6950         if (pHandle != NULL)
6951         {
6952             switch (pHandle->enmType)
6953             {
6954                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6955                     KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
6956                     pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
6957                     return TRUE;
6958 
6959                 case KWHANDLETYPE_TEMP_FILE:
6960                     KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
6961                     pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
6962                     return TRUE;
6963 
6964                 case KWHANDLETYPE_OUTPUT_BUF:
6965                     /* do default */
6966                     break;
6967 
6968                 default:
6969                     kHlpAssertFailed();
6970                     SetLastError(ERROR_INVALID_FUNCTION);
6971                     return INVALID_FILE_SIZE;
6972             }
6973         }
6974     }
6975 
6976     KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
6977     return GetFileSizeEx(hFile, pcbFile);
6978 }
6979 
6980 
6981 /** Kernel32 - CreateFileMappingW  */
kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile,LPSECURITY_ATTRIBUTES pSecAttrs,DWORD fProtect,DWORD dwMaximumSizeHigh,DWORD dwMaximumSizeLow,LPCWSTR pwszName)6982 static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
6983                                                            DWORD fProtect, DWORD dwMaximumSizeHigh,
6984                                                            DWORD dwMaximumSizeLow, LPCWSTR pwszName)
6985 {
6986     HANDLE      hMapping;
6987     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6988     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6989     if (idxHandle < g_Sandbox.cHandles)
6990     {
6991         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6992         if (pHandle != NULL)
6993         {
6994             switch (pHandle->enmType)
6995             {
6996                 case KWHANDLETYPE_TEMP_FILE:
6997                 {
6998                     PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6999                     if (   (   fProtect == PAGE_READONLY
7000                             || fProtect == PAGE_EXECUTE_READ)
7001                         && dwMaximumSizeHigh == 0
7002                         &&  (   dwMaximumSizeLow == 0
7003                              || dwMaximumSizeLow == pTempFile->cbFile)
7004                         && pwszName == NULL)
7005                     {
7006                         hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
7007                         KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
7008                         return hMapping;
7009                     }
7010                     kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
7011                                          fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7012                     SetLastError(ERROR_ACCESS_DENIED);
7013                     return INVALID_HANDLE_VALUE;
7014                 }
7015 
7016                 /* moc.exe benefits from this. */
7017                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7018                 {
7019                     PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7020                     if (   (   fProtect == PAGE_READONLY
7021                             || fProtect == PAGE_EXECUTE_READ)
7022                         && dwMaximumSizeHigh == 0
7023                         &&  (   dwMaximumSizeLow == 0
7024                              || dwMaximumSizeLow == pCachedFile->cbCached)
7025                         && pwszName == NULL)
7026                     {
7027                         if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
7028                                                          K_FALSE /*fIsFileHandle*/, &hMapping))
7029                         { /* likely */ }
7030                         else
7031                             hMapping = NULL;
7032                         KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
7033                         return hMapping;
7034                     }
7035 
7036                     /* Do fallback (for .pch). */
7037                     kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
7038                                   ("fProtect=%#x cb=%#x'%08x name=%p\n",
7039                                    fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7040 
7041                     hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7042                     KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
7043                               hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7044                     return hMapping;
7045                 }
7046 
7047                 /** @todo read cached memory mapped files too for moc.   */
7048             }
7049         }
7050     }
7051 
7052     hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7053     KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
7054               hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7055     return hMapping;
7056 }
7057 
7058 
7059 /** Kernel32 - MapViewOfFile  */
kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection,DWORD dwDesiredAccess,DWORD offFileHigh,DWORD offFileLow,SIZE_T cbToMap)7060 static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
7061                                                      DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
7062 {
7063     PVOID       pvRet;
7064     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7065     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7066     if (idxHandle < g_Sandbox.cHandles)
7067     {
7068         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7069         if (pHandle != NULL)
7070         {
7071             KU32 idxMapping;
7072 
7073             /*
7074              * Ensure one free entry in the mapping tracking table first,
7075              * since this is common to both temporary and cached files.
7076              */
7077             if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
7078             { /* likely */ }
7079             else
7080             {
7081                 void *pvNew;
7082                 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
7083                 if (cNew)
7084                     cNew *= 2;
7085                 else
7086                     cNew = 32;
7087                 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings));
7088                 if (pvNew)
7089                     g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
7090                 else
7091                 {
7092                     kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
7093                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7094                     return NULL;
7095                 }
7096                 g_Sandbox.cMemMappingsAlloc = cNew;
7097             }
7098 
7099             /*
7100              * Type specific work.
7101              */
7102             switch (pHandle->enmType)
7103             {
7104                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7105                 case KWHANDLETYPE_TEMP_FILE:
7106                 case KWHANDLETYPE_OUTPUT_BUF:
7107                 default:
7108                     kHlpAssertFailed();
7109                     SetLastError(ERROR_INVALID_OPERATION);
7110                     return NULL;
7111 
7112                 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7113                 {
7114                     PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7115                     if (   dwDesiredAccess == FILE_MAP_READ
7116                         && offFileHigh == 0
7117                         && offFileLow  == 0
7118                         && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
7119                     {
7120                         kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
7121                         if (pTempFile->cSegs != 1)
7122                         {
7123                             KU32    iSeg;
7124                             KU32    cbLeft;
7125                             KU32    cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
7126                             KU8    *pbAll = NULL;
7127                             int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
7128                             if (rc != 0)
7129                             {
7130                                 kHlpAssertFailed();
7131                                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7132                                 return NULL;
7133                             }
7134 
7135                             cbLeft = pTempFile->cbFile;
7136                             for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
7137                             {
7138                                 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
7139                                 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
7140                                 cbLeft -= cbToCopy;
7141                             }
7142 
7143                             for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
7144                             {
7145                                 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
7146                                 pTempFile->paSegs[iSeg].pbData = NULL;
7147                                 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
7148                             }
7149 
7150                             pTempFile->cSegs                 = 1;
7151                             pTempFile->cbFileAllocated       = cbAll;
7152                             pTempFile->paSegs[0].cbDataAlloc = cbAll;
7153                             pTempFile->paSegs[0].pbData      = pbAll;
7154                             pTempFile->paSegs[0].offData     = 0;
7155                         }
7156 
7157                         pTempFile->cMappings++;
7158                         kHlpAssert(pTempFile->cMappings == 1);
7159 
7160                         pvRet = pTempFile->paSegs[0].pbData;
7161                         KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
7162                         break;
7163                     }
7164 
7165                     kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7166                                          dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
7167                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7168                     return NULL;
7169                 }
7170 
7171                 /*
7172                  * This is simple in comparison to the above temporary file code.
7173                  */
7174                 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7175                 {
7176                     PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7177                     if (   dwDesiredAccess == FILE_MAP_READ
7178                         && offFileHigh == 0
7179                         && offFileLow  == 0
7180                         && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
7181                     {
7182                         pvRet = pCachedFile->pbCached;
7183                         KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
7184                         break;
7185                     }
7186                     kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7187                                          dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
7188                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7189                     return NULL;
7190                 }
7191             }
7192 
7193             /*
7194              * Insert into the mapping tracking table.  This is common
7195              * and we should only get here with a non-NULL pvRet.
7196              *
7197              * Note! We could look for duplicates and do ref counting, but it's
7198              *       easier to just append for now.
7199              */
7200             kHlpAssert(pvRet != NULL);
7201             idxMapping = g_Sandbox.cMemMappings;
7202             kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
7203 
7204             g_Sandbox.paMemMappings[idxMapping].cRefs         = 1;
7205             g_Sandbox.paMemMappings[idxMapping].pvMapping     = pvRet;
7206             g_Sandbox.paMemMappings[idxMapping].enmType       = pHandle->enmType;
7207             g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
7208             g_Sandbox.cMemMappings++;
7209 
7210             return pvRet;
7211         }
7212     }
7213 
7214     pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7215     KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
7216               hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
7217     return pvRet;
7218 }
7219 
7220 
7221 /** Kernel32 - MapViewOfFileEx  */
kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection,DWORD dwDesiredAccess,DWORD offFileHigh,DWORD offFileLow,SIZE_T cbToMap,PVOID pvMapAddr)7222 static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
7223                                                        DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
7224 {
7225     PVOID       pvRet;
7226     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7227     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7228     if (idxHandle < g_Sandbox.cHandles)
7229     {
7230         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7231         if (pHandle != NULL)
7232         {
7233             switch (pHandle->enmType)
7234             {
7235                 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7236                     KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
7237                               hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7238                     if (!pvMapAddr)
7239                         return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7240                     kHlpAssertFailed();
7241                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7242                     return NULL;
7243 
7244                 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7245                     KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
7246                               hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7247                     if (!pvMapAddr)
7248                         return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7249                     /* We can use fallback here as the handle is an actual section handle. */
7250                     break;
7251 
7252                 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7253                 case KWHANDLETYPE_TEMP_FILE:
7254                 case KWHANDLETYPE_OUTPUT_BUF:
7255                 default:
7256                     kHlpAssertFailed();
7257                     SetLastError(ERROR_INVALID_OPERATION);
7258                     return NULL;
7259             }
7260         }
7261     }
7262 
7263     pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
7264     KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
7265               hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
7266     return pvRet;
7267 
7268 }
7269 
7270 /** Kernel32 - UnmapViewOfFile  */
kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)7271 static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
7272 {
7273     /*
7274      * Consult the memory mapping tracker.
7275      */
7276     PKWMEMMAPPING   paMemMappings = g_Sandbox.paMemMappings;
7277     KU32            idxMapping    = g_Sandbox.cMemMappings;
7278     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7279     while (idxMapping-- > 0)
7280         if (paMemMappings[idxMapping].pvMapping == pvBase)
7281         {
7282             /* Type specific stuff. */
7283             if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
7284             {
7285                 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
7286                 paMemMappings[idxMapping].u.pTempFile->cMappings--;
7287             }
7288             else
7289                 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
7290 
7291             /* Deref and probably free it. */
7292             if (--paMemMappings[idxMapping].cRefs == 0)
7293             {
7294                 g_Sandbox.cMemMappings--;
7295                 if (idxMapping != g_Sandbox.cMemMappings)
7296                     paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
7297             }
7298             return TRUE;
7299         }
7300 
7301     KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
7302     return UnmapViewOfFile(pvBase);
7303 }
7304 
7305 /** @todo UnmapViewOfFileEx */
7306 
7307 #endif /* WITH_TEMP_MEMORY_FILES */
7308 
7309 
7310 /** Kernel32 - DuplicateHandle */
kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc,HANDLE hSrc,HANDLE hDstProc,PHANDLE phNew,DWORD dwDesiredAccess,BOOL fInheritHandle,DWORD dwOptions)7311 static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
7312                                                       DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
7313 {
7314     BOOL fRet;
7315 
7316     /*
7317      * We must catch our handles being duplicated.
7318      */
7319     if (hSrcProc == GetCurrentProcess())
7320     {
7321         KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
7322         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7323         if (idxHandle < g_Sandbox.cHandles)
7324         {
7325             PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7326             if (pHandle)
7327             {
7328                 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7329                 if (fRet)
7330                 {
7331                     if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
7332                     {
7333                         pHandle->cRefs++;
7334                         KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
7335                                   hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
7336                                   pHandle->enmType, pHandle->cRefs));
7337                     }
7338                     else
7339                     {
7340                         fRet = FALSE;
7341                         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7342                         KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
7343                                   hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
7344                     }
7345                 }
7346                 else
7347                     KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
7348                               hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
7349                 return fRet;
7350             }
7351         }
7352     }
7353 
7354     /*
7355      * Not one of ours, just do what the caller asks and log it.
7356      */
7357     fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7358     KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
7359               fInheritHandle, dwOptions, fRet, *phNew));
7360     return fRet;
7361 }
7362 
7363 
7364 /** Kernel32 - CloseHandle */
kwSandbox_Kernel32_CloseHandle(HANDLE hObject)7365 static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
7366 {
7367     BOOL        fRet;
7368     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
7369     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_fCtrlC);
7370     if (idxHandle < g_Sandbox.cHandles)
7371     {
7372         PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7373         if (pHandle)
7374         {
7375             /* Prevent the closing of the standard output and error handles. */
7376             if (   pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
7377                 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
7378             {
7379                 fRet = CloseHandle(hObject);
7380                 if (fRet)
7381                 {
7382                     PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7383                     g_Sandbox.papHandles[idxHandle] = NULL;
7384                     g_Sandbox.cActiveHandles--;
7385                     kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
7386                     if (--pHandle->cRefs == 0)
7387                     {
7388 #ifdef WITH_TEMP_MEMORY_FILES
7389                         if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
7390                         {
7391                             kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
7392                             pHandle->u.pTempFile->cActiveHandles--;
7393                         }
7394 #endif
7395                         kHlpFree(pHandle);
7396                         KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
7397                     }
7398                     else
7399                         KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
7400                 }
7401                 else
7402                     KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
7403             }
7404             else
7405             {
7406                 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
7407                           hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
7408                 fRet = TRUE;
7409             }
7410             return fRet;
7411         }
7412     }
7413 
7414     fRet = CloseHandle(hObject);
7415     KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
7416     return fRet;
7417 }
7418 
7419 
7420 /** Kernel32 - GetFileAttributesA. */
kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)7421 static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
7422 {
7423     DWORD       fRet;
7424     const char *pszExt = kHlpGetExt(pszFilename);
7425     if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
7426     {
7427         KFSLOOKUPERROR enmError;
7428         PKFSOBJ pFsObj;
7429         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7430 
7431         pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
7432         if (pFsObj)
7433         {
7434             kHlpAssert(pFsObj->fHaveStats);
7435             fRet = pFsObj->Stats.st_attribs;
7436             kFsCacheObjRelease(g_pFsCache, pFsObj);
7437         }
7438         else
7439         {
7440             SetLastError(kwFsLookupErrorToWindowsError(enmError));
7441             fRet = INVALID_FILE_ATTRIBUTES;
7442         }
7443 
7444         KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
7445         return fRet;
7446     }
7447 
7448     fRet = GetFileAttributesA(pszFilename);
7449     KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
7450     return fRet;
7451 }
7452 
7453 
7454 /** Kernel32 - GetFileAttributesW. */
kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)7455 static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
7456 {
7457     DWORD fRet;
7458     if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
7459     {
7460         KFSLOOKUPERROR enmError;
7461         PKFSOBJ pFsObj;
7462         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7463 
7464         pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
7465         if (pFsObj)
7466         {
7467             kHlpAssert(pFsObj->fHaveStats);
7468             fRet = pFsObj->Stats.st_attribs;
7469             kFsCacheObjRelease(g_pFsCache, pFsObj);
7470         }
7471         else
7472         {
7473             SetLastError(kwFsLookupErrorToWindowsError(enmError));
7474             fRet = INVALID_FILE_ATTRIBUTES;
7475         }
7476 
7477         KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
7478         return fRet;
7479     }
7480 
7481     fRet = GetFileAttributesW(pwszFilename);
7482     KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
7483     return fRet;
7484 }
7485 
7486 
7487 /** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
7488  * directory containing each include file.  We cache the result to speed
7489  * things up a little. */
kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath,LPWSTR pwszShortPath,DWORD cwcShortPath)7490 static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
7491 {
7492     DWORD cwcRet;
7493     if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
7494     {
7495         KFSLOOKUPERROR enmError;
7496         PKFSOBJ pObj;
7497         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7498 
7499         pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
7500         if (pObj)
7501         {
7502             if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
7503             {
7504                 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
7505                 {
7506                     cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
7507 
7508                     /* Should preserve trailing slash on directory paths. */
7509                     if (pObj->bObjType == KFSOBJ_TYPE_DIR)
7510                     {
7511                         if (   cwcRet + 1 < cwcShortPath
7512                             && pwszShortPath[cwcRet - 1] != '\\')
7513                         {
7514                             KSIZE cwcIn = kwUtf16Len(pwszLongPath);
7515                             if (   cwcIn > 0
7516                                 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
7517                             {
7518                                 pwszShortPath[cwcRet++] = '\\';
7519                                 pwszShortPath[cwcRet]   = '\0';
7520                             }
7521                         }
7522                     }
7523 
7524                     KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
7525                               pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
7526                     kFsCacheObjRelease(g_pFsCache, pObj);
7527                     return cwcRet;
7528                 }
7529 
7530                 /* fall back for complicated cases. */
7531             }
7532             kFsCacheObjRelease(g_pFsCache, pObj);
7533         }
7534     }
7535     cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
7536     KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
7537               pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
7538     return cwcRet;
7539 }
7540 
7541 
7542 #ifdef WITH_TEMP_MEMORY_FILES
7543 /** Kernel32 - DeleteFileW
7544  * Skip deleting the in-memory files. */
kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)7545 static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
7546 {
7547     BOOL fRc;
7548     if (   g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7549         && kwFsIsClTempFileW(pwszFilename))
7550     {
7551         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7552         KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
7553         fRc = TRUE;
7554     }
7555     else
7556     {
7557         fRc = DeleteFileW(pwszFilename);
7558         KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
7559     }
7560     return fRc;
7561 }
7562 #endif /* WITH_TEMP_MEMORY_FILES */
7563 
7564 
7565 
7566 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
7567 
7568 /*
7569  *
7570  * Console output buffering.
7571  * Console output buffering.
7572  * Console output buffering.
7573  *
7574  */
7575 
7576 
7577 /**
7578  * Write a wide char string to the console.
7579  *
7580  * @param   pSandbox            The sandbox which output buffer to flush.
7581  */
kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox,wchar_t const * pwcBuf,KU32 cwcToWrite)7582 static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
7583 {
7584     if (cwcToWrite > 0)
7585     {
7586         DWORD cwcWritten = 0;
7587         if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
7588         {
7589             if (cwcWritten == cwcToWrite)
7590             { /* likely */ }
7591             else
7592             {
7593                 DWORD off = 0;
7594                 do
7595                 {
7596                     off += cwcWritten;
7597                     cwcWritten = 0;
7598                 } while (   off < cwcToWrite
7599                          && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
7600                 kHlpAssert(off == cwcWritten);
7601             }
7602         }
7603         else
7604             kHlpAssertFailed();
7605         pSandbox->Combined.cFlushes++;
7606     }
7607 }
7608 
7609 
7610 /**
7611  * Flushes the combined console output buffer.
7612  *
7613  * @param   pSandbox            The sandbox which output buffer to flush.
7614  */
kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)7615 static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
7616 {
7617     if (pSandbox->Combined.cwcBuf > 0)
7618     {
7619         KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
7620         kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
7621         pSandbox->Combined.cwcBuf = 0;
7622     }
7623 }
7624 
7625 
7626 /**
7627  * For handling combined buffer overflow cases line by line.
7628  *
7629  * @param   pSandbox            The sandbox.
7630  * @param   pwcBuf              What to add to the combined buffer.  Usually a
7631  *                              line, unless we're really low on buffer space.
7632  * @param   cwcBuf              The length of what to add.
7633  * @param   fBrokenLine         Whether this is a broken line.
7634  */
kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox,wchar_t const * pwcBuf,KU32 cwcBuf,KBOOL fBrokenLine)7635 static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
7636 {
7637     if (fBrokenLine)
7638         kwSandboxConsoleFlushCombined(pSandbox);
7639     if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
7640     {
7641         kwSandboxConsoleFlushCombined(pSandbox);
7642         kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
7643     }
7644     else
7645     {
7646         kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
7647         pSandbox->Combined.cwcBuf += cwcBuf;
7648     }
7649 }
7650 
7651 
7652 /**
7653  * Called to final flush a line buffer via the combined buffer (if applicable).
7654  *
7655  * @param   pSandbox            The sandbox.
7656  * @param   pLineBuf            The line buffer.
7657  * @param   pszName             The line buffer name (for logging)
7658  */
kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox,PKWOUTPUTSTREAMBUF pLineBuf,const char * pszName)7659 static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
7660 {
7661     if (pLineBuf->fIsConsole)
7662     {
7663         if (pLineBuf->u.Con.cwcBuf > 0)
7664         {
7665             KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
7666 
7667             if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
7668             {
7669                 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
7670                 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
7671             }
7672             else
7673             {
7674                 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
7675                 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
7676             }
7677             pLineBuf->u.Con.cwcBuf = 0;
7678         }
7679     }
7680 #ifdef WITH_STD_OUT_ERR_BUFFERING
7681     else if (pLineBuf->u.Fully.cchBuf > 0)
7682     {
7683         KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
7684 
7685         kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
7686         pLineBuf->u.Fully.cchBuf = 0;
7687     }
7688 #endif
7689 }
7690 
7691 
7692 /**
7693  * Called at the end of sandboxed execution to flush both stream buffers.
7694  *
7695  * @param   pSandbox            The sandbox.
7696  */
kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)7697 static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
7698 {
7699     /*
7700      * First do the cl.exe source file supression trick, if applicable.
7701      * The output ends up on CONOUT$ if either StdOut or StdErr is a console
7702      * handle.
7703      */
7704     if (   pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7705         && pSandbox->Combined.cFlushes == 0)
7706     {
7707         if (   pSandbox->StdOut.fIsConsole
7708             || pSandbox->StdErr.fIsConsole)
7709         {
7710             if (   pSandbox->Combined.cwcBuf >= 3
7711                 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
7712                 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
7713             {
7714                 KI32    off = pSandbox->Combined.cwcBuf - 1;
7715                 if (pSandbox->Combined.wszBuf[off] == '\n')
7716                 {
7717                     KBOOL fOk = K_TRUE;
7718                     while (off-- > 0)
7719                     {
7720                         wchar_t const wc = pSandbox->Combined.wszBuf[off];
7721                         if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
7722                         { /* likely */ }
7723                         else
7724                         {
7725                             fOk = K_FALSE;
7726                             break;
7727                         }
7728                     }
7729                     if (fOk)
7730                     {
7731                         KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
7732                                    pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
7733                         pSandbox->Combined.cwcBuf = 0;
7734                         return;
7735                     }
7736                 }
7737                 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
7738                            pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
7739             }
7740         }
7741 #ifdef WITH_STD_OUT_ERR_BUFFERING
7742         /*
7743          * Otherwise, it goes to standard output (redirected).
7744          */
7745         else if (   pSandbox->StdErr.u.Fully.cchBuf == 0
7746                  && pSandbox->StdOut.u.Fully.cchBuf >= 3)
7747         {
7748             char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
7749             KI32        off    = pSandbox->StdOut.u.Fully.cchBuf - 1;
7750             kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
7751 
7752             if (pchBuf[off] == '\n')
7753             {
7754                 KBOOL fOk = K_TRUE;
7755                 if (pchBuf[off - 1] == '\r')
7756                     off--;
7757                 while (off-- > 0)
7758                 {
7759                     char const ch = pchBuf[off];
7760                     if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
7761                     { /* likely */ }
7762                     else
7763                     {
7764                         fOk = K_FALSE;
7765                         break;
7766                     }
7767                 }
7768                 if (fOk)
7769                 {
7770                     KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
7771                                pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
7772                     pSandbox->StdOut.u.Fully.cchBuf = 0;
7773                     return;
7774                 }
7775             }
7776             KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
7777                        pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
7778         }
7779 #endif
7780     }
7781 
7782     /*
7783      * Flush the two line buffer, the the combined buffer.
7784      */
7785     kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
7786     kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
7787     kwSandboxConsoleFlushCombined(pSandbox);
7788 }
7789 
7790 
7791 /**
7792  * Writes a string to the given output stream.
7793  *
7794  * @param   pSandbox            The sandbox.
7795  * @param   pLineBuf            The line buffer for the output stream.
7796  * @param   pwcBuffer           The buffer to write.
7797  * @param   cwcToWrite          The number of wchar_t's in the buffer.
7798  */
kwSandboxConsoleWriteW(PKWSANDBOX pSandbox,PKWOUTPUTSTREAMBUF pLineBuf,wchar_t const * pwcBuffer,KU32 cwcToWrite)7799 static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
7800 {
7801     kHlpAssert(pLineBuf->fIsConsole);
7802     if (cwcToWrite > 0)
7803     {
7804         /*
7805          * First, find the start of the last incomplete line so we can figure
7806          * out how much line buffering we need to do.
7807          */
7808         KU32 cchLastIncompleteLine;
7809         KU32 offLastIncompleteLine = cwcToWrite;
7810         while (   offLastIncompleteLine > 0
7811                && pwcBuffer[offLastIncompleteLine - 1] != '\n')
7812             offLastIncompleteLine--;
7813         cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
7814 
7815         /* Was there anything to line buffer? */
7816         if (offLastIncompleteLine < cwcToWrite)
7817         {
7818             /* Need to grow the line buffer? */
7819             KU32 cwcNeeded = offLastIncompleteLine == 0
7820                            ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
7821                            : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
7822             if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
7823             {
7824                 void *pvNew;
7825                 KU32  cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
7826                 while (cwcNew < cwcNeeded)
7827                     cwcNew *= 2;
7828                 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
7829                 if (pvNew)
7830                 {
7831                     pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
7832                     pLineBuf->u.Con.cwcBufAlloc = cwcNew;
7833                 }
7834                 else
7835                 {
7836                     pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
7837                     if (pvNew)
7838                     {
7839                         pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
7840                         pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
7841                     }
7842                     else
7843                     {
7844                         /* This isn't perfect, but it will have to do for now. */
7845                         if (pLineBuf->u.Con.cwcBuf > 0)
7846                         {
7847                             kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
7848                                                           K_TRUE /*fBrokenLine*/);
7849                             pLineBuf->u.Con.cwcBuf = 0;
7850                         }
7851                         kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
7852                         return;
7853                     }
7854                 }
7855             }
7856 
7857             /*
7858              * Handle the case where we only add to the line buffer.
7859              */
7860             if (offLastIncompleteLine == 0)
7861             {
7862                 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
7863                 pLineBuf->u.Con.cwcBuf += cwcToWrite;
7864                 return;
7865             }
7866         }
7867 
7868         /*
7869          * If there is sufficient combined buffer to handle this request, this is rather simple.
7870          */
7871         kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
7872         if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
7873         {
7874             if (pLineBuf->u.Con.cwcBuf > 0)
7875             {
7876                 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7877                             pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
7878                 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
7879                 pLineBuf->u.Con.cwcBuf = 0;
7880             }
7881 
7882             kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7883                         pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
7884             pSandbox->Combined.cwcBuf += offLastIncompleteLine;
7885         }
7886         else
7887         {
7888             /*
7889              * Do line-by-line processing of the input, flusing the combined buffer
7890              * when it becomes necessary.  We may have to write lines
7891              */
7892             KU32 off = 0;
7893             KU32 offNextLine = 0;
7894 
7895             /* If there are buffered chars, we handle the first line outside the
7896                main loop.  We must try our best outputting it as a complete line. */
7897             if (pLineBuf->u.Con.cwcBuf > 0)
7898             {
7899                 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
7900                     offNextLine++;
7901                 offNextLine++;
7902                 kHlpAssert(offNextLine <= offLastIncompleteLine);
7903 
7904                 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
7905                 {
7906                     kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7907                                 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
7908                     pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
7909                     pLineBuf->u.Con.cwcBuf = 0;
7910 
7911                     kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
7912                     pSandbox->Combined.cwcBuf += offNextLine;
7913                 }
7914                 else
7915                 {
7916                     KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
7917                     if (cwcLeft > 0)
7918                     {
7919                         KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
7920                         kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
7921                         pLineBuf->u.Con.cwcBuf += cwcCopy;
7922                         off += cwcCopy;
7923                     }
7924                     if (pLineBuf->u.Con.cwcBuf > 0)
7925                     {
7926                         kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
7927                                                       K_TRUE /*fBrokenLine*/);
7928                         pLineBuf->u.Con.cwcBuf = 0;
7929                     }
7930                     if (off < offNextLine)
7931                         kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
7932                 }
7933                 off = offNextLine;
7934             }
7935 
7936             /* Deal with the remaining lines */
7937             while (off < offLastIncompleteLine)
7938             {
7939                 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
7940                     offNextLine++;
7941                 offNextLine++;
7942                 kHlpAssert(offNextLine <= offLastIncompleteLine);
7943                 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
7944                 off = offNextLine;
7945             }
7946         }
7947 
7948         /*
7949          * Buffer any remaining incomplete line chars.
7950          */
7951         if (cchLastIncompleteLine)
7952         {
7953             kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
7954             pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
7955         }
7956     }
7957 }
7958 
7959 
7960 /**
7961  * Worker for WriteConsoleA and WriteFile.
7962  *
7963  * @param   pSandbox            The sandbox.
7964  * @param   pLineBuf            The line buffer.
7965  * @param   pchBuffer           What to write.
7966  * @param   cchToWrite          How much to write.
7967  */
kwSandboxConsoleWriteA(PKWSANDBOX pSandbox,PKWOUTPUTSTREAMBUF pLineBuf,const char * pchBuffer,KU32 cchToWrite)7968 static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
7969 {
7970     /*
7971      * Convert it to wide char and use the 'W' to do the work.
7972      */
7973     int         cwcRet;
7974     KU32        cwcBuf = cchToWrite * 2 + 1;
7975     wchar_t    *pwcBufFree = NULL;
7976     wchar_t    *pwcBuf;
7977     kHlpAssert(pLineBuf->fIsConsole);
7978 
7979     if (cwcBuf <= 4096)
7980         pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
7981     else
7982         pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
7983 
7984     cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
7985     if (cwcRet > 0)
7986          kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
7987     else
7988     {
7989         DWORD cchWritten;
7990         kHlpAssertFailed();
7991 
7992         /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
7993         if (pLineBuf->u.Con.cwcBuf > 0)
7994         {
7995             kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
7996             pLineBuf->u.Con.cwcBuf = 0;
7997         }
7998         kwSandboxConsoleFlushCombined(pSandbox);
7999 
8000         if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
8001         {
8002             if (cchWritten >= cchToWrite)
8003             { /* likely */ }
8004             else
8005             {
8006                 KU32 off = 0;
8007                 do
8008                 {
8009                     off += cchWritten;
8010                     cchWritten = 0;
8011                 } while (   off < cchToWrite
8012                          && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
8013             }
8014         }
8015     }
8016 
8017     if (pwcBufFree)
8018         kHlpFree(pwcBufFree);
8019 }
8020 
8021 
8022 /** Kernel32 - WriteConsoleA  */
kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput,CONST VOID * pvBuffer,DWORD cbToWrite,PDWORD pcbWritten,PVOID pvReserved)8023 BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
8024                                              PVOID pvReserved)
8025 {
8026     BOOL                fRc;
8027     PKWOUTPUTSTREAMBUF  pLineBuf;
8028     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8029 
8030     if (hConOutput == g_Sandbox.StdErr.hOutput)
8031         pLineBuf = &g_Sandbox.StdErr;
8032     else
8033         pLineBuf = &g_Sandbox.StdOut;
8034     if (pLineBuf->fIsConsole)
8035     {
8036         kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
8037 
8038         KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
8039                    hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
8040         if (pcbWritten)
8041             *pcbWritten = cbToWrite;
8042         fRc = TRUE;
8043     }
8044     else
8045     {
8046         fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
8047         KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
8048                    hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
8049     }
8050     return fRc;
8051 }
8052 
8053 
8054 /** Kernel32 - WriteConsoleW  */
kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput,CONST VOID * pvBuffer,DWORD cwcToWrite,PDWORD pcwcWritten,PVOID pvReserved)8055 BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
8056                                              PVOID pvReserved)
8057 {
8058     BOOL                fRc;
8059     PKWOUTPUTSTREAMBUF  pLineBuf;
8060     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8061 
8062     if (hConOutput == g_Sandbox.StdErr.hOutput)
8063         pLineBuf = &g_Sandbox.StdErr;
8064     else if (hConOutput == g_Sandbox.StdOut.hOutput)
8065         pLineBuf = &g_Sandbox.StdOut;
8066     else
8067         pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
8068     if (pLineBuf->fIsConsole)
8069     {
8070         kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
8071 
8072         KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
8073                    hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
8074         if (pcwcWritten)
8075             *pcwcWritten = cwcToWrite;
8076         fRc = TRUE;
8077     }
8078     else
8079     {
8080         fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
8081         KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
8082                    hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
8083     }
8084     return fRc;
8085 }
8086 
8087 #endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8088 
8089 
8090 
8091 /*
8092  *
8093  * Virtual memory leak prevension.
8094  * Virtual memory leak prevension.
8095  * Virtual memory leak prevension.
8096  *
8097  */
8098 
8099 #ifdef WITH_FIXED_VIRTUAL_ALLOCS
8100 
8101 /** For debug logging.  */
8102 # ifndef NDEBUG
kwSandboxLogFixedAllocation(KU32 idxFixed,const char * pszWhere)8103 static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
8104 {
8105     MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
8106     SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
8107     kHlpAssert(cbMemInfo == sizeof(MemInfo));
8108     if (cbMemInfo != 0)
8109         KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
8110                 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
8111                 MemInfo.BaseAddress,
8112                 MemInfo.AllocationBase,
8113                 MemInfo.RegionSize,
8114                 MemInfo.State,
8115                 MemInfo.Protect,
8116                 MemInfo.Type));
8117 }
8118 # else
8119 #  define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
8120 # endif
8121 
8122 /**
8123  * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
8124  *
8125  * @param   idxFixed        The fixed allocation index to "free".
8126  */
kwSandboxResetFixedAllocation(KU32 idxFixed)8127 static void kwSandboxResetFixedAllocation(KU32 idxFixed)
8128 {
8129     BOOL fRc;
8130     kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
8131     fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
8132     kHlpAssert(fRc); K_NOREF(fRc);
8133     kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
8134     g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
8135 }
8136 
8137 #endif /* WITH_FIXED_VIRTUAL_ALLOCS */
8138 
8139 
8140 /** Kernel32 - VirtualAlloc - for managing  cl.exe / c1[xx].dll heap with fixed
8141  * location (~78MB in 32-bit 2010 compiler). */
kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr,SIZE_T cb,DWORD fAllocType,DWORD fProt)8142 static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
8143 {
8144     PVOID pvMem;
8145     if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8146     {
8147         KU32 idxPreAllocated = KU32_MAX;
8148 
8149 #ifdef WITH_FIXED_VIRTUAL_ALLOCS
8150         /*
8151          * Look for a pre-reserved CL.exe heap allocation.
8152          */
8153         pvMem = NULL;
8154         if (   pvAddr != 0
8155             && (fAllocType & MEM_RESERVE))
8156         {
8157             KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8158             kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
8159             while (idxFixed-- > 0)
8160                 if (   g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
8161                     && g_aFixedVirtualAllocs[idxFixed].pvReserved)
8162                 {
8163                     if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
8164                     {
8165                         if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
8166                         {
8167                             g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
8168                             pvMem                           = pvAddr;
8169                             idxPreAllocated                 = idxFixed;
8170                             KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
8171                                     pvAddr, cb, fAllocType, fProt, pvMem));
8172                             kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
8173                             SetLastError(NO_ERROR);
8174                             break;
8175                         }
8176                         kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
8177                     }
8178                     else
8179                         kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
8180                                      pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
8181                 }
8182         }
8183         if (!pvMem)
8184 #endif
8185         {
8186             pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8187             KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8188                     pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8189             if (pvAddr && pvAddr != pvMem)
8190                 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
8191                             pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
8192         }
8193 
8194         if (pvMem)
8195         {
8196             /*
8197              * Track it.
8198              */
8199             PKWVIRTALLOC pTracker;
8200             kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8201 
8202             pTracker = g_Sandbox.pVirtualAllocHead;
8203             while (   pTracker
8204                    && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
8205                 pTracker = pTracker->pNext;
8206             if (!pTracker)
8207             {
8208                 DWORD dwErr = GetLastError();
8209                 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
8210                 if (pTracker)
8211                 {
8212                     pTracker->pvAlloc           = pvMem;
8213                     pTracker->cbAlloc           = cb;
8214                     pTracker->idxPreAllocated   = idxPreAllocated;
8215                     pTracker->pNext             = g_Sandbox.pVirtualAllocHead;
8216                     g_Sandbox.pVirtualAllocHead = pTracker;
8217                 }
8218                 SetLastError(dwErr);
8219             }
8220         }
8221     }
8222     else
8223         pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8224     KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8225             pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8226     return pvMem;
8227 }
8228 
8229 
8230 /** Kernel32 - VirtualFree.   */
kwSandbox_Kernel32_VirtualFree(PVOID pvAddr,SIZE_T cb,DWORD dwFreeType)8231 static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
8232 {
8233     BOOL fRc;
8234     if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8235     {
8236         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8237         if (dwFreeType & MEM_RELEASE)
8238         {
8239             PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
8240             if (pTracker)
8241             {
8242                 if (pTracker->pvAlloc == pvAddr)
8243                     g_Sandbox.pVirtualAllocHead = pTracker->pNext;
8244                 else
8245                 {
8246                     PKWVIRTALLOC pPrev;
8247                     do
8248                     {
8249                         pPrev = pTracker;
8250                         pTracker = pTracker->pNext;
8251                     } while (pTracker && pTracker->pvAlloc != pvAddr);
8252                     if (pTracker)
8253                         pPrev->pNext = pTracker->pNext;
8254                 }
8255                 if (pTracker)
8256                 {
8257 #ifdef WITH_FIXED_VIRTUAL_ALLOCS
8258                     if (pTracker->idxPreAllocated != KU32_MAX)
8259                     {
8260                         kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
8261                         KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
8262                                 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
8263                         kHlpFree(pTracker);
8264                         return TRUE;
8265                     }
8266 #endif
8267 
8268                     fRc = VirtualFree(pvAddr, cb, dwFreeType);
8269                     if (fRc)
8270                         kHlpFree(pTracker);
8271                     else
8272                     {
8273                         pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8274                         g_Sandbox.pVirtualAllocHead = pTracker;
8275                     }
8276                     KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8277                     return fRc;
8278                 }
8279 
8280                 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
8281             }
8282         }
8283     }
8284 
8285 #ifdef WITH_FIXED_VIRTUAL_ALLOCS
8286     /*
8287      * Protect our fixed allocations (this isn't just paranoia, btw.).
8288      */
8289     if (dwFreeType & MEM_RELEASE)
8290     {
8291         KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8292         while (idxFixed-- > 0)
8293             if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
8294             {
8295                 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
8296                         idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
8297                 return TRUE;
8298             }
8299     }
8300 #endif
8301 
8302     /*
8303      * Not tracker or not actually free the virtual range.
8304      */
8305     fRc = VirtualFree(pvAddr, cb, dwFreeType);
8306     KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8307     return fRc;
8308 }
8309 
8310 
8311 /** Kernel32 - HeapCreate / NtDll - RTlCreateHeap  */
kwSandbox_Kernel32_HeapCreate(DWORD fOptions,SIZE_T cbInitial,SIZE_T cbMax)8312 HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
8313 {
8314     HANDLE hHeap;
8315     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8316 
8317     hHeap = HeapCreate(fOptions, cbInitial, cbMax);
8318     if (hHeap != NULL)
8319     {
8320         DWORD dwErr = GetLastError();
8321         PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
8322         if (pTracker)
8323         {
8324             pTracker->hHeap = hHeap;
8325             pTracker->pNext = g_Sandbox.pHeapHead;
8326             g_Sandbox.pHeapHead = pTracker;
8327         }
8328 
8329         SetLastError(dwErr);
8330     }
8331     return hHeap;
8332 
8333 }
8334 
8335 
8336 /** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)8337 BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
8338 {
8339     BOOL fRc = HeapDestroy(hHeap);
8340     KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
8341     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8342     if (fRc)
8343     {
8344         PKWHEAP pTracker = g_Sandbox.pHeapHead;
8345         if (pTracker)
8346         {
8347             if (pTracker->hHeap == hHeap)
8348                 g_Sandbox.pHeapHead = pTracker->pNext;
8349             else
8350             {
8351                 PKWHEAP pPrev;
8352                 do
8353                 {
8354                     pPrev = pTracker;
8355                     pTracker = pTracker->pNext;
8356                 } while (pTracker && pTracker->hHeap == hHeap);
8357                 if (pTracker)
8358                     pPrev->pNext = pTracker->pNext;
8359             }
8360             if (pTracker)
8361                 kHlpFree(pTracker);
8362             else
8363                 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
8364         }
8365     }
8366 
8367     return fRc;
8368 }
8369 
8370 
8371 
8372 /*
8373  *
8374  * Thread/Fiber local storage leak prevention.
8375  * Thread/Fiber local storage leak prevention.
8376  * Thread/Fiber local storage leak prevention.
8377  *
8378  * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
8379  *       linked VS2010 code like VBoxBs3ObjConverter.exe.  One thing is that
8380  *       we're leaking these indexes, but more importantely we crash during
8381  *       worker exit since the callback is triggered multiple times.
8382  */
8383 
8384 
8385 /** Kernel32 - FlsAlloc  */
kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)8386 DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
8387 {
8388     DWORD idxFls = FlsAlloc(pfnCallback);
8389     KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
8390     if (idxFls != FLS_OUT_OF_INDEXES)
8391     {
8392         PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
8393         if (pTracker)
8394         {
8395             kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8396             pTracker->idx = idxFls;
8397             pTracker->pNext = g_Sandbox.pFlsAllocHead;
8398             g_Sandbox.pFlsAllocHead = pTracker;
8399         }
8400     }
8401 
8402     return idxFls;
8403 }
8404 
8405 /** Kernel32 - FlsFree */
kwSandbox_Kernel32_FlsFree(DWORD idxFls)8406 BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
8407 {
8408     BOOL fRc = FlsFree(idxFls);
8409     KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
8410     if (fRc)
8411     {
8412         PKWLOCALSTORAGE pTracker;
8413         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8414 
8415         pTracker = g_Sandbox.pFlsAllocHead;
8416         if (pTracker)
8417         {
8418             if (pTracker->idx == idxFls)
8419                 g_Sandbox.pFlsAllocHead = pTracker->pNext;
8420             else
8421             {
8422                 PKWLOCALSTORAGE pPrev;
8423                 do
8424                 {
8425                     pPrev = pTracker;
8426                     pTracker = pTracker->pNext;
8427                 } while (pTracker && pTracker->idx != idxFls);
8428                 if (pTracker)
8429                     pPrev->pNext = pTracker->pNext;
8430             }
8431             if (pTracker)
8432             {
8433                 pTracker->idx   = FLS_OUT_OF_INDEXES;
8434                 pTracker->pNext = NULL;
8435                 kHlpFree(pTracker);
8436             }
8437         }
8438     }
8439     return fRc;
8440 }
8441 
8442 
8443 /** Kernel32 - TlsAlloc  */
kwSandbox_Kernel32_TlsAlloc(VOID)8444 DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
8445 {
8446     DWORD idxTls = TlsAlloc();
8447     KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
8448     if (idxTls != TLS_OUT_OF_INDEXES)
8449     {
8450         PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
8451         if (pTracker)
8452         {
8453             kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8454             pTracker->idx = idxTls;
8455             pTracker->pNext = g_Sandbox.pTlsAllocHead;
8456             g_Sandbox.pTlsAllocHead = pTracker;
8457         }
8458     }
8459 
8460     return idxTls;
8461 }
8462 
8463 /** Kernel32 - TlsFree */
kwSandbox_Kernel32_TlsFree(DWORD idxTls)8464 BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
8465 {
8466     BOOL fRc = TlsFree(idxTls);
8467     KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
8468     if (fRc)
8469     {
8470         PKWLOCALSTORAGE pTracker;
8471         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8472 
8473         pTracker = g_Sandbox.pTlsAllocHead;
8474         if (pTracker)
8475         {
8476             if (pTracker->idx == idxTls)
8477                 g_Sandbox.pTlsAllocHead = pTracker->pNext;
8478             else
8479             {
8480                 PKWLOCALSTORAGE pPrev;
8481                 do
8482                 {
8483                     pPrev = pTracker;
8484                     pTracker = pTracker->pNext;
8485                 } while (pTracker && pTracker->idx != idxTls);
8486                 if (pTracker)
8487                     pPrev->pNext = pTracker->pNext;
8488             }
8489             if (pTracker)
8490             {
8491                 pTracker->idx   = TLS_OUT_OF_INDEXES;
8492                 pTracker->pNext = NULL;
8493                 kHlpFree(pTracker);
8494             }
8495         }
8496     }
8497     return fRc;
8498 }
8499 
8500 
8501 
8502 /*
8503  *
8504  * Header file hashing.
8505  * Header file hashing.
8506  * Header file hashing.
8507  *
8508  * c1.dll / c1XX.dll hashes the input files.  The Visual C++ 2010 profiler
8509  * indicated that ~12% of the time was spent doing MD5 caluclation when
8510  * rebuiling openssl.  The hashing it done right after reading the source
8511  * via ReadFile, same buffers and sizes.
8512  */
8513 
8514 #ifdef WITH_HASH_MD5_CACHE
8515 
8516 /** AdvApi32 - CryptCreateHash */
kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv,ALG_ID idAlg,HCRYPTKEY hKey,DWORD dwFlags,HCRYPTHASH * phHash)8517 static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
8518                                                       HCRYPTHASH *phHash)
8519 {
8520     BOOL fRc;
8521 
8522     /*
8523      * Only do this for cl.exe when it request normal MD5.
8524      */
8525     if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8526     {
8527         if (idAlg == CALG_MD5)
8528         {
8529             if (hKey == 0)
8530             {
8531                 if (dwFlags == 0)
8532                 {
8533                     PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
8534                     if (pHash)
8535                     {
8536                         kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8537                         pHash->uMagic        = KWHASHMD5_MAGIC;
8538                         pHash->cbHashed      = 0;
8539                         pHash->fGoneBad      = K_FALSE;
8540                         pHash->fFallbackMode = K_FALSE;
8541                         pHash->fFinal        = K_FALSE;
8542 
8543                         /* link it. */
8544                         pHash->pNext         = g_Sandbox.pHashHead;
8545                         g_Sandbox.pHashHead  = pHash;
8546 
8547                         *phHash = (KUPTR)pHash;
8548                         KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
8549                                      hProv, *phHash, TRUE));
8550                         return TRUE;
8551                     }
8552 
8553                     kwErrPrintf("CryptCreateHash: out of memory!\n");
8554                 }
8555                 else
8556                     kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
8557             }
8558             else
8559                 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
8560         }
8561         else
8562             kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
8563     }
8564 
8565     /*
8566      * Fallback.
8567      */
8568     fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
8569     KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
8570                  hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
8571     return fRc;
8572 }
8573 
8574 
8575 /** AdvApi32 - CryptHashData */
kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash,CONST BYTE * pbData,DWORD cbData,DWORD dwFlags)8576 static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
8577 {
8578     BOOL        fRc;
8579     PKWHASHMD5  pHash = g_Sandbox.pHashHead;
8580     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8581     while (pHash && (KUPTR)pHash != hHash)
8582         pHash = pHash->pNext;
8583     KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
8584                  hHash, pHash, pbData, cbData, dwFlags));
8585     if (pHash)
8586     {
8587         /*
8588          * Validate the state.
8589          */
8590         if (   pHash->uMagic == KWHASHMD5_MAGIC
8591             && !pHash->fFinal)
8592         {
8593             if (!pHash->fFallbackMode)
8594             {
8595                 /*
8596                  * Does this match the previous ReadFile call to a cached file?
8597                  * If it doesn't, try falling back.
8598                  */
8599                 if (   g_Sandbox.LastHashRead.cbRead == cbData
8600                     && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
8601                 {
8602                     PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
8603                     if (   pCachedFile
8604                         && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
8605                     {
8606 
8607                         if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
8608                         {
8609                             if (   pHash->pCachedFile == NULL
8610                                 && pHash->cbHashed == 0)
8611                                 pHash->pCachedFile = pCachedFile;
8612                             if (pHash->pCachedFile == pCachedFile)
8613                             {
8614                                 pHash->cbHashed += cbData;
8615                                 g_Sandbox.LastHashRead.pCachedFile = NULL;
8616                                 g_Sandbox.LastHashRead.pvRead      = NULL;
8617                                 g_Sandbox.LastHashRead.cbRead      = 0;
8618                                 g_Sandbox.LastHashRead.offRead     = 0;
8619                                 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
8620                                              hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
8621                                 return TRUE;
8622                             }
8623 
8624                             /* Note! it's possible to fall back here too, if necessary. */
8625                             kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
8626                                         pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
8627                         }
8628                         else
8629                             kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
8630                                         pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
8631                     }
8632                     else if (!pCachedFile)
8633                         kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
8634                     else
8635                         kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
8636                 }
8637                 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
8638                     kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
8639                                 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
8640                 if (pHash->cbHashed == 0)
8641                     pHash->fFallbackMode = K_TRUE;
8642                 if (pHash->fFallbackMode)
8643                 {
8644                     /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
8645                     pHash->fFallbackMode = K_TRUE;
8646                     MD5Init(&pHash->Md5Ctx);
8647                     MD5Update(&pHash->Md5Ctx, pbData, cbData);
8648                     pHash->cbHashed = cbData;
8649                     KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
8650                                  hHash, pbData, cbData, dwFlags));
8651                     return TRUE;
8652                 }
8653                 pHash->fGoneBad = K_TRUE;
8654                 SetLastError(ERROR_INVALID_PARAMETER);
8655                 fRc = FALSE;
8656             }
8657             else
8658             {
8659                 /* fallback. */
8660                 MD5Update(&pHash->Md5Ctx, pbData, cbData);
8661                 pHash->cbHashed += cbData;
8662                 fRc = TRUE;
8663                 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
8664                              hHash, pbData, cbData, dwFlags));
8665             }
8666         }
8667         /*
8668          * Bad handle state.
8669          */
8670         else
8671         {
8672             if (pHash->uMagic != KWHASHMD5_MAGIC)
8673                 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
8674             else
8675                 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
8676             SetLastError(NTE_BAD_HASH);
8677             fRc = FALSE;
8678         }
8679     }
8680     else
8681     {
8682 
8683         fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
8684         KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
8685     }
8686     return fRc;
8687 }
8688 
8689 
8690 /** AdvApi32 - CryptGetHashParam */
kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash,DWORD dwParam,BYTE * pbData,DWORD * pcbData,DWORD dwFlags)8691 static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
8692                                                         BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
8693 {
8694     BOOL        fRc;
8695     PKWHASHMD5  pHash = g_Sandbox.pHashHead;
8696     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8697     while (pHash && (KUPTR)pHash != hHash)
8698         pHash = pHash->pNext;
8699     if (pHash)
8700     {
8701         if (pHash->uMagic == KWHASHMD5_MAGIC)
8702         {
8703             if (dwFlags == 0)
8704             {
8705                 DWORD cbRet;
8706                 void *pvRet;
8707                 union
8708                 {
8709                     DWORD dw;
8710                 } uBuf;
8711 
8712                 switch (dwParam)
8713                 {
8714                     case HP_HASHVAL:
8715                     {
8716                         /* Check the hash progress. */
8717                         PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
8718                         if (pCachedFile)
8719                         {
8720                             if (   pCachedFile->cbCached == pHash->cbHashed
8721                                 && !pHash->fGoneBad)
8722                             {
8723                                 if (pCachedFile->fValidMd5)
8724                                     KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
8725                                 else
8726                                 {
8727                                     MD5Init(&pHash->Md5Ctx);
8728                                     MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
8729                                     MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
8730                                     pCachedFile->fValidMd5 = K_TRUE;
8731                                     KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
8732                                 }
8733                                 pvRet = pCachedFile->abMd5Digest;
8734                             }
8735                             else
8736                             {
8737                                 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
8738                                    from what I can tell, so just deal with it. */
8739                                 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
8740                                              pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
8741                                              pHash, pCachedFile, pCachedFile->szPath));
8742                                 pHash->fFallbackMode = K_TRUE;
8743                                 pHash->pCachedFile   = NULL;
8744                                 MD5Init(&pHash->Md5Ctx);
8745                                 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
8746                                 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
8747                                 pvRet = pHash->abDigest;
8748                             }
8749                             pHash->fFinal = K_TRUE;
8750                             cbRet = 16;
8751                             break;
8752                         }
8753                         else if (pHash->fFallbackMode)
8754                         {
8755                             if (!pHash->fFinal)
8756                             {
8757                                 pHash->fFinal = K_TRUE;
8758                                 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
8759                             }
8760                             pvRet = pHash->abDigest;
8761                             cbRet = 16;
8762                             break;
8763                         }
8764                         else
8765                         {
8766                             kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
8767                             SetLastError(ERROR_INVALID_SERVER_STATE);
8768                         }
8769                         return FALSE;
8770                     }
8771 
8772                     case HP_HASHSIZE:
8773                         uBuf.dw = 16;
8774                         pvRet = &uBuf;
8775                         cbRet = sizeof(DWORD);
8776                         break;
8777 
8778                     case HP_ALGID:
8779                         uBuf.dw = CALG_MD5;
8780                         pvRet = &uBuf;
8781                         cbRet = sizeof(DWORD);
8782                         break;
8783 
8784                     default:
8785                         kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
8786                         SetLastError(NTE_BAD_TYPE);
8787                         return FALSE;
8788                 }
8789 
8790                 /*
8791                  * Copy out cbRet from pvRet.
8792                  */
8793                 if (pbData)
8794                 {
8795                     if (*pcbData >= cbRet)
8796                     {
8797                         *pcbData = cbRet;
8798                         kHlpMemCopy(pbData, pvRet, cbRet);
8799                         if (cbRet == 4)
8800                             KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
8801                                          dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
8802                         else if (cbRet == 16)
8803                             KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x [cached]\n",
8804                                          dwParam, pHash, pHash->pCachedFile, cbRet,
8805                                          pbData[0],  pbData[1],  pbData[2],  pbData[3],
8806                                          pbData[4],  pbData[5],  pbData[6],  pbData[7],
8807                                          pbData[8],  pbData[9],  pbData[10], pbData[11],
8808                                          pbData[12], pbData[13], pbData[14], pbData[15]));
8809                         else
8810                             KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
8811                                          dwParam, pHash, pHash->pCachedFile, cbRet));
8812                         return TRUE;
8813                     }
8814 
8815                     kHlpMemCopy(pbData, pvRet, *pcbData);
8816                 }
8817                 SetLastError(ERROR_MORE_DATA);
8818                 *pcbData = cbRet;
8819                 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
8820             }
8821             else
8822             {
8823                 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
8824                 SetLastError(NTE_BAD_FLAGS);
8825             }
8826         }
8827         else
8828         {
8829             kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
8830             SetLastError(NTE_BAD_HASH);
8831         }
8832         fRc = FALSE;
8833     }
8834     /*
8835      * Regular handle.
8836      */
8837     else
8838     {
8839         fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
8840         KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
8841                      hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
8842     }
8843 
8844     return fRc;
8845 }
8846 
8847 
8848 /** AdvApi32 - CryptDestroyHash */
kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)8849 static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
8850 {
8851     BOOL        fRc;
8852     PKWHASHMD5  pPrev = NULL;
8853     PKWHASHMD5  pHash = g_Sandbox.pHashHead;
8854     kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8855     while (pHash && (KUPTR)pHash != hHash)
8856     {
8857         pPrev = pHash;
8858         pHash = pHash->pNext;
8859     }
8860     if (pHash)
8861     {
8862         if (pHash->uMagic == KWHASHMD5_MAGIC)
8863         {
8864             pHash->uMagic = 0;
8865             if (!pPrev)
8866                 g_Sandbox.pHashHead = pHash->pNext;
8867             else
8868                 pPrev->pNext = pHash->pNext;
8869             kHlpFree(pHash);
8870             KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
8871             fRc = TRUE;
8872         }
8873         else
8874         {
8875             kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
8876             KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
8877             SetLastError(ERROR_INVALID_HANDLE);
8878             fRc = FALSE;
8879         }
8880     }
8881     /*
8882      * Regular handle.
8883      */
8884     else
8885     {
8886         fRc = CryptDestroyHash(hHash);
8887         KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
8888     }
8889     return fRc;
8890 }
8891 
8892 #endif /* WITH_HASH_MD5_CACHE */
8893 
8894 
8895 /*
8896  *
8897  * Reuse crypt context.
8898  * Reuse crypt context.
8899  * Reuse crypt context.
8900  *
8901  *
8902  * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
8903  *
8904  */
8905 
8906 #ifdef WITH_CRYPT_CTX_REUSE
8907 
8908 /** AdvApi32 - CryptAcquireContextW.  */
kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV * phProv,LPCWSTR pwszContainer,LPCWSTR pwszProvider,DWORD dwProvType,DWORD dwFlags)8909 static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
8910                                                            DWORD dwProvType,  DWORD dwFlags)
8911 {
8912     BOOL fRet;
8913 
8914     /*
8915      * Lookup reusable context based on the input.
8916      */
8917     KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
8918     KSIZE const cwcProvider  = pwszProvider  ? kwUtf16Len(pwszProvider) : 0;
8919     KU32        iCtx = g_Sandbox.cCryptCtxs;
8920     while (iCtx-- > 0)
8921     {
8922         if (   g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
8923             && g_Sandbox.aCryptCtxs[iCtx].cwcProvider  == cwcProvider
8924             && g_Sandbox.aCryptCtxs[iCtx].dwProvType   == dwProvType
8925             && g_Sandbox.aCryptCtxs[iCtx].dwFlags      == dwFlags
8926             && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
8927             && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider,  pwszProvider,  cwcProvider  * sizeof(wchar_t)) == 0)
8928         {
8929             if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
8930             {
8931                 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
8932                 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
8933                              pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8934                 return TRUE;
8935             }
8936         }
8937     }
8938 
8939     /*
8940      * Create it and enter it into the reused array if possible.
8941      */
8942     fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
8943     if (fRet)
8944     {
8945         iCtx = g_Sandbox.cCryptCtxs;
8946         if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
8947         {
8948             /* Try duplicate the input strings. */
8949             g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
8950                                                                (cwcContainer + 1) * sizeof(wchar_t));
8951             if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
8952             {
8953                 g_Sandbox.aCryptCtxs[iCtx].pwszProvider  = kHlpDup(pwszProvider ? pwszProvider : L"",
8954                                                                    (cwcProvider + 1) * sizeof(wchar_t));
8955                 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
8956                 {
8957                     /* Add a couple of references just to be on the safe side and all that. */
8958                     HCRYPTPROV hProv = *phProv;
8959                     if (CryptContextAddRef(hProv, NULL, 0))
8960                     {
8961                         if (CryptContextAddRef(hProv, NULL, 0))
8962                         {
8963                             /* Okay, finish the entry and return success */
8964                             g_Sandbox.aCryptCtxs[iCtx].hProv      = hProv;
8965                             g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
8966                             g_Sandbox.aCryptCtxs[iCtx].dwFlags    = dwFlags;
8967                             g_Sandbox.cCryptCtxs = iCtx + 1;
8968 
8969                             KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
8970                                          pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8971                             return TRUE;
8972                         }
8973                         CryptReleaseContext(hProv, 0);
8974                     }
8975                     KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
8976 
8977                     kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
8978                     g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
8979                 }
8980                 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
8981                 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
8982             }
8983         }
8984         else
8985             KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
8986     }
8987 
8988     KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
8989                  pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8990     return fRet;
8991 }
8992 
8993 
8994 /** AdvApi32 - CryptReleaseContext */
kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv,DWORD dwFlags)8995 static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
8996 {
8997     BOOL fRet = CryptReleaseContext(hProv, dwFlags);
8998     KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
8999     return fRet;
9000 }
9001 
9002 
9003 /** AdvApi32 - CryptContextAddRef  */
kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv,DWORD * pdwReserved,DWORD dwFlags)9004 static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
9005 {
9006     BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
9007     KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
9008     return fRet;
9009 }
9010 
9011 #endif /* WITH_CRYPT_CTX_REUSE */
9012 
9013 /*
9014  *
9015  * Structured exception handling.
9016  * Structured exception handling.
9017  * Structured exception handling.
9018  *
9019  */
9020 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9021 
9022 # define EH_NONCONTINUABLE      KU32_C(0x00000001)
9023 # define EH_UNWINDING           KU32_C(0x00000002)
9024 # define EH_EXIT_UNWIND         KU32_C(0x00000004)
9025 # define EH_STACK_INVALID       KU32_C(0x00000008)
9026 # define EH_NESTED_CALL         KU32_C(0x00000010)
9027 
9028 typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
9029                                                  struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
9030 typedef struct _EXCEPTION_REGISTRATION_RECORD
9031 {
9032     struct _EXCEPTION_REGISTRATION_RECORD * volatile    pPrevRegRec;
9033     PFNXCPTHANDLER                                      pfnXcptHandler;
9034 };
9035 
9036 
9037 /**
9038  * Calls @a pfnHandler.
9039  */
kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec,struct _EXCEPTION_REGISTRATION_RECORD * pRegRec,PCONTEXT pXcptCtx,struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,PFNXCPTHANDLER pfnHandler)9040 static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
9041                                      PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
9042                                      PFNXCPTHANDLER pfnHandler)
9043 {
9044 # if 1
9045     /* This is a more robust version that isn't subject to calling
9046        convension cleanup disputes and such. */
9047     KU32 uSavedEdi;
9048     KU32 uSavedEsi;
9049     KU32 uSavedEbx;
9050     KU32 rcHandler;
9051 
9052     __asm
9053     {
9054         mov     [uSavedEdi], edi
9055         mov     [uSavedEsi], esi
9056         mov     [uSavedEbx], ebx
9057         mov     esi, esp
9058         mov     edi, esp
9059         mov     edi, [pXcptRec]
9060         mov     edx, [pRegRec]
9061         mov     eax, [pXcptCtx]
9062         mov     ebx, [ppRegRec]
9063         mov     ecx, [pfnHandler]
9064         sub     esp, 16
9065         and     esp, 0fffffff0h
9066         mov     [esp     ], edi
9067         mov     [esp +  4], edx
9068         mov     [esp +  8], eax
9069         mov     [esp + 12], ebx
9070         mov     edi, esi
9071         call    ecx
9072         mov     esp, esi
9073         cmp     esp, edi
9074         je      stack_ok
9075         int     3
9076     stack_ok:
9077         mov     edi, [uSavedEdi]
9078         mov     esi, [uSavedEsi]
9079         mov     ebx, [uSavedEbx]
9080         mov     [rcHandler], eax
9081     }
9082     return rcHandler;
9083 # else
9084     return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
9085 # endif
9086 }
9087 
9088 
9089 /**
9090  * Vectored exception handler that emulates x86 chained exception handler.
9091  *
9092  * This is necessary because the RtlIsValidHandler check fails for self loaded
9093  * code and prevents cl.exe from working.  (On AMD64 we can register function
9094  * tables, but on X86 cooking your own handling seems to be the only viabke
9095  * alternative.)
9096  *
9097  * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
9098  * @param   pXcptPtrs           The exception details.
9099  */
kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)9100 static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
9101 {
9102     PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9103     KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
9104     if (g_Sandbox.fRunning)
9105     {
9106         HANDLE const                                      hCurProc = GetCurrentProcess();
9107         PEXCEPTION_RECORD                                 pXcptRec = pXcptPtrs->ExceptionRecord;
9108         PCONTEXT                                          pXcptCtx = pXcptPtrs->ContextRecord;
9109         struct _EXCEPTION_REGISTRATION_RECORD *           pRegRec  = pTib->ExceptionList;
9110         while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
9111         {
9112             /* Read the exception record in a safe manner. */
9113             struct _EXCEPTION_REGISTRATION_RECORD   RegRec;
9114             DWORD                                   cbActuallyRead = 0;
9115             if (   ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9116                 && cbActuallyRead == sizeof(RegRec))
9117             {
9118                 struct _EXCEPTION_REGISTRATION_RECORD * volatile    pDispRegRec = NULL;
9119                 KU32                                                rcHandler;
9120                 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9121                         RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9122                 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9123                 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9124                 if (rcHandler == ExceptionContinueExecution)
9125                 {
9126                     kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
9127                     KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
9128                     return EXCEPTION_CONTINUE_EXECUTION;
9129                 }
9130 
9131                 if (rcHandler == ExceptionContinueSearch)
9132                     kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9133                 else if (rcHandler == ExceptionNestedException)
9134                     kHlpAssertMsgFailed(("Nested exceptions.\n"));
9135                 else
9136                     kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9137             }
9138             else
9139             {
9140                 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9141                 break;
9142             }
9143 
9144             /*
9145              * Next.
9146              */
9147             pRegRec = RegRec.pPrevRegRec;
9148         }
9149     }
9150     return EXCEPTION_CONTINUE_SEARCH;
9151 }
9152 
9153 
9154 /** NtDll,Kernel32 - RtlUnwind */
kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD * pStopXcptRec,PVOID pvTargetIp,PEXCEPTION_RECORD pXcptRec,PVOID pvReturnValue)9155 static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
9156                                              PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
9157 {
9158     PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9159     KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
9160             pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
9161     if (g_Sandbox.fRunning)
9162     {
9163         HANDLE const                                      hCurProc = GetCurrentProcess();
9164         PCONTEXT                                          pXcptCtx = NULL;
9165         struct _EXCEPTION_REGISTRATION_RECORD *           pRegRec  = pTib->ExceptionList;
9166 
9167         /*
9168          * Update / create an exception record.
9169          */
9170         if (pXcptRec)
9171             pXcptRec->ExceptionFlags |= EH_UNWINDING;
9172         else
9173         {
9174             pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
9175             kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
9176             pXcptRec->ExceptionCode  = STATUS_UNWIND;
9177             pXcptRec->ExceptionFlags = EH_UNWINDING;
9178         }
9179         if (!pStopXcptRec)
9180             pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
9181 
9182         /*
9183          * Walk the chain till we find pStopXctpRec.
9184          */
9185         while (   ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
9186                && pRegRec != NULL
9187                && pRegRec != pStopXcptRec)
9188         {
9189             /* Read the exception record in a safe manner. */
9190             struct _EXCEPTION_REGISTRATION_RECORD   RegRec;
9191             DWORD                                   cbActuallyRead = 0;
9192             if (   ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9193                 && cbActuallyRead == sizeof(RegRec))
9194             {
9195                 struct _EXCEPTION_REGISTRATION_RECORD * volatile    pDispRegRec = NULL;
9196                 KU32                                                rcHandler;
9197                 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9198                         RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9199                 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9200                 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9201 
9202                 if (rcHandler == ExceptionContinueSearch)
9203                     kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9204                 else if (rcHandler == ExceptionCollidedUnwind)
9205                     kHlpAssertMsgFailed(("Implement collided unwind!\n"));
9206                 else
9207                     kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9208             }
9209             else
9210             {
9211                 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9212                 break;
9213             }
9214 
9215             /*
9216              * Pop next.
9217              */
9218             pTib->ExceptionList = RegRec.pPrevRegRec;
9219             pRegRec = RegRec.pPrevRegRec;
9220         }
9221         return;
9222     }
9223 
9224     RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
9225 }
9226 
9227 #endif /* WINDOWS + X86 */
9228 
9229 
9230 /*
9231  *
9232  * Misc function only intercepted while debugging.
9233  * Misc function only intercepted while debugging.
9234  * Misc function only intercepted while debugging.
9235  *
9236  */
9237 
9238 #ifndef NDEBUG
9239 
9240 /** CRT - memcpy   */
kwSandbox_msvcrt_memcpy(void * pvDst,void const * pvSrc,size_t cb)9241 static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
9242 {
9243     KU8 const *pbSrc = (KU8 const *)pvSrc;
9244     KU8       *pbDst = (KU8 *)pvDst;
9245     KSIZE      cbLeft = cb;
9246     while (cbLeft-- > 0)
9247         *pbDst++ = *pbSrc++;
9248     return pvDst;
9249 }
9250 
9251 
9252 /** CRT - memset   */
kwSandbox_msvcrt_memset(void * pvDst,int bFiller,size_t cb)9253 static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
9254 {
9255     KU8       *pbDst = (KU8 *)pvDst;
9256     KSIZE      cbLeft = cb;
9257     while (cbLeft-- > 0)
9258         *pbDst++ = bFiller;
9259     return pvDst;
9260 }
9261 
9262 #endif /* NDEBUG */
9263 
9264 
9265 
9266 /**
9267  * Functions that needs replacing for sandboxed execution.
9268  */
9269 KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
9270 {
9271     /*
9272      * Kernel32.dll and friends.
9273      */
9274     { TUPLE("ExitProcess"),                 NULL,       (KUPTR)kwSandbox_Kernel32_ExitProcess },
9275     { TUPLE("TerminateProcess"),            NULL,       (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9276 
9277     { TUPLE("LoadLibraryA"),                NULL,       (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
9278     { TUPLE("LoadLibraryW"),                NULL,       (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
9279     { TUPLE("LoadLibraryExA"),              NULL,       (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
9280     { TUPLE("LoadLibraryExW"),              NULL,       (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
9281     { TUPLE("FreeLibrary"),                 NULL,       (KUPTR)kwSandbox_Kernel32_FreeLibrary },
9282     { TUPLE("GetModuleHandleA"),            NULL,       (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
9283     { TUPLE("GetModuleHandleW"),            NULL,       (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
9284     { TUPLE("GetProcAddress"),              NULL,       (KUPTR)kwSandbox_Kernel32_GetProcAddress },
9285     { TUPLE("GetModuleFileNameA"),          NULL,       (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
9286     { TUPLE("GetModuleFileNameW"),          NULL,       (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
9287     { TUPLE("RtlPcToFileHeader"),           NULL,       (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9288 
9289     { TUPLE("GetCommandLineA"),             NULL,       (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
9290     { TUPLE("GetCommandLineW"),             NULL,       (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
9291     { TUPLE("GetStartupInfoA"),             NULL,       (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
9292     { TUPLE("GetStartupInfoW"),             NULL,       (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
9293 
9294     { TUPLE("CreateThread"),                NULL,       (KUPTR)kwSandbox_Kernel32_CreateThread },
9295 
9296     { TUPLE("GetEnvironmentStrings"),       NULL,       (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
9297     { TUPLE("GetEnvironmentStringsA"),      NULL,       (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
9298     { TUPLE("GetEnvironmentStringsW"),      NULL,       (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
9299     { TUPLE("FreeEnvironmentStringsA"),     NULL,       (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
9300     { TUPLE("FreeEnvironmentStringsW"),     NULL,       (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
9301     { TUPLE("GetEnvironmentVariableA"),     NULL,       (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
9302     { TUPLE("GetEnvironmentVariableW"),     NULL,       (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
9303     { TUPLE("SetEnvironmentVariableA"),     NULL,       (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
9304     { TUPLE("SetEnvironmentVariableW"),     NULL,       (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
9305     { TUPLE("ExpandEnvironmentStringsA"),   NULL,       (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
9306     { TUPLE("ExpandEnvironmentStringsW"),   NULL,       (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
9307 
9308     { TUPLE("CreateFileA"),                 NULL,       (KUPTR)kwSandbox_Kernel32_CreateFileA },
9309     { TUPLE("CreateFileW"),                 NULL,       (KUPTR)kwSandbox_Kernel32_CreateFileW },
9310     { TUPLE("ReadFile"),                    NULL,       (KUPTR)kwSandbox_Kernel32_ReadFile },
9311     { TUPLE("ReadFileEx"),                  NULL,       (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9312 #ifdef WITH_TEMP_MEMORY_FILES
9313     { TUPLE("WriteFile"),                   NULL,       (KUPTR)kwSandbox_Kernel32_WriteFile },
9314     { TUPLE("WriteFileEx"),                 NULL,       (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9315     { TUPLE("SetEndOfFile"),                NULL,       (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9316     { TUPLE("GetFileType"),                 NULL,       (KUPTR)kwSandbox_Kernel32_GetFileType },
9317     { TUPLE("GetFileSize"),                 NULL,       (KUPTR)kwSandbox_Kernel32_GetFileSize },
9318     { TUPLE("GetFileSizeEx"),               NULL,       (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9319     { TUPLE("CreateFileMappingW"),          NULL,       (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9320     { TUPLE("MapViewOfFile"),               NULL,       (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9321     { TUPLE("MapViewOfFileEx"),             NULL,       (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9322     { TUPLE("UnmapViewOfFile"),             NULL,       (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9323 #endif
9324     { TUPLE("SetFilePointer"),              NULL,       (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9325     { TUPLE("SetFilePointerEx"),            NULL,       (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9326     { TUPLE("DuplicateHandle"),             NULL,       (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9327     { TUPLE("CloseHandle"),                 NULL,       (KUPTR)kwSandbox_Kernel32_CloseHandle },
9328     { TUPLE("GetFileAttributesA"),          NULL,       (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9329     { TUPLE("GetFileAttributesW"),          NULL,       (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9330     { TUPLE("GetShortPathNameW"),           NULL,       (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9331 #ifdef WITH_TEMP_MEMORY_FILES
9332     { TUPLE("DeleteFileW"),                 NULL,       (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9333 #endif
9334 
9335     { TUPLE("WriteConsoleA"),               NULL,       (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
9336     { TUPLE("WriteConsoleW"),               NULL,       (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
9337 
9338     { TUPLE("VirtualAlloc"),                NULL,       (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
9339     { TUPLE("VirtualFree"),                 NULL,       (KUPTR)kwSandbox_Kernel32_VirtualFree },
9340 
9341     { TUPLE("HeapCreate"),                  NULL,       (KUPTR)kwSandbox_Kernel32_HeapCreate,       K_TRUE /*fOnlyExe*/ },
9342     { TUPLE("HeapDestroy"),                 NULL,       (KUPTR)kwSandbox_Kernel32_HeapDestroy,      K_TRUE /*fOnlyExe*/ },
9343 
9344     { TUPLE("FlsAlloc"),                    NULL,       (KUPTR)kwSandbox_Kernel32_FlsAlloc,         K_TRUE /*fOnlyExe*/ },
9345     { TUPLE("FlsFree"),                     NULL,       (KUPTR)kwSandbox_Kernel32_FlsFree,          K_TRUE /*fOnlyExe*/ },
9346     { TUPLE("TlsAlloc"),                    NULL,       (KUPTR)kwSandbox_Kernel32_TlsAlloc,         K_TRUE /*fOnlyExe*/ },
9347     { TUPLE("TlsFree"),                     NULL,       (KUPTR)kwSandbox_Kernel32_TlsFree,          K_TRUE /*fOnlyExe*/ },
9348 
9349     { TUPLE("SetConsoleCtrlHandler"),       NULL,       (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
9350 
9351 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9352     { TUPLE("RtlUnwind"),                   NULL,       (KUPTR)kwSandbox_ntdll_RtlUnwind },
9353 #endif
9354 
9355 #ifdef WITH_HASH_MD5_CACHE
9356     { TUPLE("CryptCreateHash"),             NULL,       (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
9357     { TUPLE("CryptHashData"),               NULL,       (KUPTR)kwSandbox_Advapi32_CryptHashData },
9358     { TUPLE("CryptGetHashParam"),           NULL,       (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
9359     { TUPLE("CryptDestroyHash"),            NULL,       (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
9360 #endif
9361 
9362 #ifdef WITH_CRYPT_CTX_REUSE
9363     { TUPLE("CryptAcquireContextW"),        NULL,       (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
9364     { TUPLE("CryptReleaseContext"),         NULL,       (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
9365     { TUPLE("CryptContextAddRef"),          NULL,       (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
9366 #endif
9367 
9368     /*
9369      * MS Visual C++ CRTs.
9370      */
9371     { TUPLE("exit"),                        NULL,       (KUPTR)kwSandbox_msvcrt_exit },
9372     { TUPLE("_exit"),                       NULL,       (KUPTR)kwSandbox_msvcrt__exit },
9373     { TUPLE("_cexit"),                      NULL,       (KUPTR)kwSandbox_msvcrt__cexit },
9374     { TUPLE("_c_exit"),                     NULL,       (KUPTR)kwSandbox_msvcrt__c_exit },
9375     { TUPLE("_amsg_exit"),                  NULL,       (KUPTR)kwSandbox_msvcrt__amsg_exit },
9376     { TUPLE("terminate"),                   NULL,       (KUPTR)kwSandbox_msvcrt_terminate },
9377 
9378     { TUPLE("onexit"),                      NULL,       (KUPTR)kwSandbox_msvcrt__onexit,            K_TRUE /*fOnlyExe*/ },
9379     { TUPLE("_onexit"),                     NULL,       (KUPTR)kwSandbox_msvcrt__onexit,            K_TRUE /*fOnlyExe*/ },
9380     { TUPLE("atexit"),                      NULL,       (KUPTR)kwSandbox_msvcrt_atexit,             K_TRUE /*fOnlyExe*/ },
9381 
9382     { TUPLE("_beginthread"),                NULL,       (KUPTR)kwSandbox_msvcrt__beginthread },
9383     { TUPLE("_beginthreadex"),              NULL,       (KUPTR)kwSandbox_msvcrt__beginthreadex },
9384     { TUPLE("_beginthreadex"),          "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
9385 
9386     { TUPLE("__argc"),                      NULL,       (KUPTR)&g_Sandbox.cArgs },
9387     { TUPLE("__argv"),                      NULL,       (KUPTR)&g_Sandbox.papszArgs },
9388     { TUPLE("__wargv"),                     NULL,       (KUPTR)&g_Sandbox.papwszArgs },
9389     { TUPLE("__p___argc"),                  NULL,       (KUPTR)kwSandbox_msvcrt___p___argc },
9390     { TUPLE("__p___argv"),                  NULL,       (KUPTR)kwSandbox_msvcrt___p___argv },
9391     { TUPLE("__p___wargv"),                 NULL,       (KUPTR)kwSandbox_msvcrt___p___wargv },
9392     { TUPLE("_acmdln"),                     NULL,       (KUPTR)&g_Sandbox.pszCmdLine },
9393     { TUPLE("_wcmdln"),                     NULL,       (KUPTR)&g_Sandbox.pwszCmdLine },
9394     { TUPLE("__p__acmdln"),                 NULL,       (KUPTR)kwSandbox_msvcrt___p__acmdln },
9395     { TUPLE("__p__wcmdln"),                 NULL,       (KUPTR)kwSandbox_msvcrt___p__wcmdln },
9396     { TUPLE("_pgmptr"),                     NULL,       (KUPTR)&g_Sandbox.pgmptr  },
9397     { TUPLE("_wpgmptr"),                    NULL,       (KUPTR)&g_Sandbox.wpgmptr },
9398     { TUPLE("_get_pgmptr"),                 NULL,       (KUPTR)kwSandbox_msvcrt__get_pgmptr },
9399     { TUPLE("_get_wpgmptr"),                NULL,       (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
9400     { TUPLE("__p__pgmptr"),                 NULL,       (KUPTR)kwSandbox_msvcrt___p__pgmptr },
9401     { TUPLE("__p__wpgmptr"),                NULL,       (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
9402     { TUPLE("_wincmdln"),                   NULL,       (KUPTR)kwSandbox_msvcrt__wincmdln },
9403     { TUPLE("_wwincmdln"),                  NULL,       (KUPTR)kwSandbox_msvcrt__wwincmdln },
9404     { TUPLE("__getmainargs"),               NULL,       (KUPTR)kwSandbox_msvcrt___getmainargs},
9405     { TUPLE("__wgetmainargs"),              NULL,       (KUPTR)kwSandbox_msvcrt___wgetmainargs},
9406 
9407     { TUPLE("_putenv"),                     NULL,       (KUPTR)kwSandbox_msvcrt__putenv},
9408     { TUPLE("_wputenv"),                    NULL,       (KUPTR)kwSandbox_msvcrt__wputenv},
9409     { TUPLE("_putenv_s"),                   NULL,       (KUPTR)kwSandbox_msvcrt__putenv_s},
9410     { TUPLE("_wputenv_s"),                  NULL,       (KUPTR)kwSandbox_msvcrt__wputenv_s},
9411     { TUPLE("__initenv"),                   NULL,       (KUPTR)&g_Sandbox.initenv },
9412     { TUPLE("__winitenv"),                  NULL,       (KUPTR)&g_Sandbox.winitenv },
9413     { TUPLE("__p___initenv"),               NULL,       (KUPTR)kwSandbox_msvcrt___p___initenv},
9414     { TUPLE("__p___winitenv"),              NULL,       (KUPTR)kwSandbox_msvcrt___p___winitenv},
9415     { TUPLE("_environ"),                    NULL,       (KUPTR)&g_Sandbox.environ },
9416     { TUPLE("_wenviron"),                   NULL,       (KUPTR)&g_Sandbox.wenviron },
9417     { TUPLE("_get_environ"),                NULL,       (KUPTR)kwSandbox_msvcrt__get_environ },
9418     { TUPLE("_get_wenviron"),               NULL,       (KUPTR)kwSandbox_msvcrt__get_wenviron },
9419     { TUPLE("__p__environ"),                NULL,       (KUPTR)kwSandbox_msvcrt___p__environ },
9420     { TUPLE("__p__wenviron"),               NULL,       (KUPTR)kwSandbox_msvcrt___p__wenviron },
9421 
9422 #ifndef NDEBUG
9423     { TUPLE("memcpy"),                      NULL,       (KUPTR)kwSandbox_msvcrt_memcpy },
9424     { TUPLE("memset"),                      NULL,       (KUPTR)kwSandbox_msvcrt_memset },
9425 #endif
9426 };
9427 /** Number of entries in g_aReplacements. */
9428 KU32 const                  g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
9429 
9430 
9431 /**
9432  * Functions that needs replacing in natively loaded DLLs when doing sandboxed
9433  * execution.
9434  */
9435 KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
9436 {
9437     /*
9438      * Kernel32.dll and friends.
9439      */
9440     { TUPLE("ExitProcess"),                 NULL,       (KUPTR)kwSandbox_Kernel32_ExitProcess },
9441     { TUPLE("TerminateProcess"),            NULL,       (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9442 
9443 #if 0
9444     { TUPLE("CreateThread"),                NULL,       (KUPTR)kwSandbox_Kernel32_CreateThread },
9445 #endif
9446 
9447     { TUPLE("CreateFileA"),                 NULL,       (KUPTR)kwSandbox_Kernel32_CreateFileA },
9448     { TUPLE("CreateFileW"),                 NULL,       (KUPTR)kwSandbox_Kernel32_CreateFileW },
9449     { TUPLE("ReadFile"),                    NULL,       (KUPTR)kwSandbox_Kernel32_ReadFile },
9450     { TUPLE("ReadFileEx"),                  NULL,       (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9451 #ifdef WITH_TEMP_MEMORY_FILES
9452     { TUPLE("WriteFile"),                   NULL,       (KUPTR)kwSandbox_Kernel32_WriteFile },
9453     { TUPLE("WriteFileEx"),                 NULL,       (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9454     { TUPLE("SetEndOfFile"),                NULL,       (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9455     { TUPLE("GetFileType"),                 NULL,       (KUPTR)kwSandbox_Kernel32_GetFileType },
9456     { TUPLE("GetFileSize"),                 NULL,       (KUPTR)kwSandbox_Kernel32_GetFileSize },
9457     { TUPLE("GetFileSizeEx"),               NULL,       (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9458     { TUPLE("CreateFileMappingW"),          NULL,       (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9459     { TUPLE("MapViewOfFile"),               NULL,       (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9460     { TUPLE("MapViewOfFileEx"),             NULL,       (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9461     { TUPLE("UnmapViewOfFile"),             NULL,       (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9462 #endif
9463     { TUPLE("SetFilePointer"),              NULL,       (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9464     { TUPLE("SetFilePointerEx"),            NULL,       (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9465     { TUPLE("DuplicateHandle"),             NULL,       (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9466     { TUPLE("CloseHandle"),                 NULL,       (KUPTR)kwSandbox_Kernel32_CloseHandle },
9467     { TUPLE("GetFileAttributesA"),          NULL,       (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9468     { TUPLE("GetFileAttributesW"),          NULL,       (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9469     { TUPLE("GetShortPathNameW"),           NULL,       (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9470 #ifdef WITH_TEMP_MEMORY_FILES
9471     { TUPLE("DeleteFileW"),                 NULL,       (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9472 #endif
9473     { TUPLE("SetConsoleCtrlHandler"),       NULL,       (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
9474     { TUPLE("LoadLibraryExA"),              NULL,       (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
9475 
9476     { TUPLE("WriteConsoleA"),               NULL,       (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
9477     { TUPLE("WriteConsoleW"),               NULL,       (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
9478 
9479 #ifdef WITH_HASH_MD5_CACHE
9480     { TUPLE("CryptCreateHash"),             NULL,       (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
9481     { TUPLE("CryptHashData"),               NULL,       (KUPTR)kwSandbox_Advapi32_CryptHashData },
9482     { TUPLE("CryptGetHashParam"),           NULL,       (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
9483     { TUPLE("CryptDestroyHash"),            NULL,       (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
9484 #endif
9485 
9486     { TUPLE("RtlPcToFileHeader"),           NULL,       (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9487 
9488 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9489     { TUPLE("RtlUnwind"),                   NULL,       (KUPTR)kwSandbox_ntdll_RtlUnwind },
9490 #endif
9491 
9492     /*
9493      * MS Visual C++ CRTs.
9494      */
9495     { TUPLE("exit"),                        NULL,       (KUPTR)kwSandbox_msvcrt_exit },
9496     { TUPLE("_exit"),                       NULL,       (KUPTR)kwSandbox_msvcrt__exit },
9497     { TUPLE("_cexit"),                      NULL,       (KUPTR)kwSandbox_msvcrt__cexit },
9498     { TUPLE("_c_exit"),                     NULL,       (KUPTR)kwSandbox_msvcrt__c_exit },
9499     { TUPLE("_amsg_exit"),                  NULL,       (KUPTR)kwSandbox_msvcrt__amsg_exit },
9500     { TUPLE("terminate"),                   NULL,       (KUPTR)kwSandbox_msvcrt_terminate },
9501 
9502 #if 0 /* used by mspdbXXX.dll */
9503     { TUPLE("_beginthread"),                NULL,       (KUPTR)kwSandbox_msvcrt__beginthread },
9504     { TUPLE("_beginthreadex"),              NULL,       (KUPTR)kwSandbox_msvcrt__beginthreadex },
9505 #endif
9506 };
9507 /** Number of entries in g_aSandboxNativeReplacements. */
9508 KU32 const                  g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
9509 
9510 
9511 /**
9512  * Functions that needs replacing when queried by GetProcAddress.
9513  */
9514 KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
9515 {
9516     /*
9517      * Kernel32.dll and friends.
9518      */
9519     { TUPLE("FlsAlloc"),                    NULL,       (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
9520     { TUPLE("FlsFree"),                     NULL,       (KUPTR)kwSandbox_Kernel32_FlsFree,  K_TRUE /*fOnlyExe*/ },
9521     { TUPLE("TlsAlloc"),                    NULL,       (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
9522     { TUPLE("TlsFree"),                     NULL,       (KUPTR)kwSandbox_Kernel32_TlsFree,  K_TRUE /*fOnlyExe*/ },
9523 };
9524 /** Number of entries in g_aSandboxGetProcReplacements. */
9525 KU32 const                  g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
9526 
9527 
9528 /**
9529  * Control handler.
9530  *
9531  * @returns TRUE if handled, FALSE if not.
9532  * @param   dwCtrlType          The signal.
9533  */
kwSandboxCtrlHandler(DWORD dwCtrlType)9534 static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
9535 {
9536     switch (dwCtrlType)
9537     {
9538         case CTRL_C_EVENT:
9539             fprintf(stderr, "kWorker: Ctrl-C\n");
9540             g_fCtrlC = K_TRUE;
9541             exit(9);
9542             break;
9543 
9544         case CTRL_BREAK_EVENT:
9545             fprintf(stderr, "kWorker: Ctrl-Break\n");
9546             g_fCtrlC = K_TRUE;
9547             exit(10);
9548             break;
9549 
9550         case CTRL_CLOSE_EVENT:
9551             fprintf(stderr, "kWorker: console closed\n");
9552             g_fCtrlC = K_TRUE;
9553             exit(11);
9554             break;
9555 
9556         case CTRL_LOGOFF_EVENT:
9557             fprintf(stderr, "kWorker: logoff event\n");
9558             g_fCtrlC = K_TRUE;
9559             exit(11);
9560             break;
9561 
9562         case CTRL_SHUTDOWN_EVENT:
9563             fprintf(stderr, "kWorker: shutdown event\n");
9564             g_fCtrlC = K_TRUE;
9565             exit(11);
9566             break;
9567 
9568         default:
9569             fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
9570             break;
9571     }
9572     return TRUE;
9573 }
9574 
9575 
9576 /**
9577  * Used by kwSandboxExec to reset the state of the module tree.
9578  *
9579  * This is done recursively.
9580  *
9581  * @param   pMod                The root of the tree to consider.
9582  */
kwSandboxResetModuleState(PKWMODULE pMod)9583 static void kwSandboxResetModuleState(PKWMODULE pMod)
9584 {
9585     if (   !pMod->fNative
9586         && pMod->u.Manual.enmState != KWMODSTATE_NEEDS_BITS)
9587     {
9588         KSIZE iImp;
9589         pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
9590         iImp = pMod->u.Manual.cImpMods;
9591         while (iImp-- > 0)
9592             kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
9593     }
9594 }
9595 
kwSandboxGetProcessEnvironmentBlock(void)9596 static PPEB kwSandboxGetProcessEnvironmentBlock(void)
9597 {
9598 #if K_ARCH == K_ARCH_X86_32
9599     return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
9600 #elif K_ARCH == K_ARCH_AMD64
9601     return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
9602 #else
9603 # error "Port me!"
9604 #endif
9605 }
9606 
9607 
9608 /**
9609  * Enters the given handle into the handle table.
9610  *
9611  * @returns K_TRUE on success, K_FALSE on failure.
9612  * @param   pSandbox            The sandbox.
9613  * @param   pHandle             The handle.
9614  * @param   hHandle             The handle value to enter it under (for the
9615  *                              duplicate handle API).
9616  */
kwSandboxHandleTableEnter(PKWSANDBOX pSandbox,PKWHANDLE pHandle,HANDLE hHandle)9617 static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
9618 {
9619     KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
9620     kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
9621 
9622     /*
9623      * Grow handle table.
9624      */
9625     if (idxHandle >= pSandbox->cHandles)
9626     {
9627         void *pvNew;
9628         KU32  cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
9629         while (cHandles <= idxHandle)
9630             cHandles *= 2;
9631         pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
9632         if (!pvNew)
9633         {
9634             KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
9635             return K_FALSE;
9636         }
9637         pSandbox->papHandles = (PKWHANDLE *)pvNew;
9638         kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
9639                    (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
9640         pSandbox->cHandles = cHandles;
9641     }
9642 
9643     /*
9644      * Check that the entry is unused then insert it.
9645      */
9646     kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
9647     pSandbox->papHandles[idxHandle] = pHandle;
9648     pSandbox->cActiveHandles++;
9649     return K_TRUE;
9650 }
9651 
9652 
9653 /**
9654  * Creates a correctly quoted ANSI command line string from the given argv.
9655  *
9656  * @returns Pointer to the command line.
9657  * @param   cArgs               Number of arguments.
9658  * @param   papszArgs           The argument vector.
9659  * @param   fWatcomBrainDamange Whether to apply watcom rules while quoting.
9660  * @param   pcbCmdLine          Where to return the command line length,
9661  *                              including one terminator.
9662  */
kwSandboxInitCmdLineFromArgv(KU32 cArgs,const char ** papszArgs,KBOOL fWatcomBrainDamange,KSIZE * pcbCmdLine)9663 static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
9664 {
9665     KU32    i;
9666     KSIZE   cbCmdLine;
9667     char   *pszCmdLine;
9668 
9669     /* Make a copy of the argument vector that we'll be quoting. */
9670     char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
9671     kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
9672 
9673     /* Quote the arguments that need it. */
9674     quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
9675 
9676     /* figure out cmd line length. */
9677     cbCmdLine = 0;
9678     for (i = 0; i < cArgs; i++)
9679         cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
9680     *pcbCmdLine = cbCmdLine;
9681 
9682     pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
9683     if (pszCmdLine)
9684     {
9685         char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
9686         if (papszQuotedArgs[0] != papszArgs[0])
9687             free(papszQuotedArgs[0]);
9688 
9689         for (i = 1; i < cArgs; i++)
9690         {
9691             *psz++ = ' ';
9692             psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
9693             if (papszQuotedArgs[i] != papszArgs[i])
9694                 free(papszQuotedArgs[i]);
9695         }
9696         kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
9697 
9698         *psz++ = '\0';
9699         *psz++ = '\0';
9700     }
9701 
9702     return pszCmdLine;
9703 }
9704 
9705 
9706 
kwSandboxInit(PKWSANDBOX pSandbox,PKWTOOL pTool,KU32 cArgs,const char ** papszArgs,KBOOL fWatcomBrainDamange,KU32 cEnvVars,const char ** papszEnvVars,KBOOL fNoPchCaching)9707 static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
9708                          KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
9709                          KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
9710 {
9711     PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
9712     PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
9713     wchar_t *pwcPool;
9714     KSIZE cbStrings;
9715     KSIZE cwc;
9716     KSIZE cbCmdLine;
9717     KU32 i;
9718 
9719     /* Simple stuff. */
9720     pSandbox->rcExitCode    = 256;
9721     pSandbox->pTool         = pTool;
9722     pSandbox->idMainThread  = GetCurrentThreadId();
9723     pSandbox->pgmptr        = (char *)pTool->pszPath;
9724     pSandbox->wpgmptr       = (wchar_t *)pTool->pwszPath;
9725 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
9726     if (pSandbox->StdOut.fIsConsole)
9727         pSandbox->StdOut.u.Con.cwcBuf   = 0;
9728     else
9729         pSandbox->StdOut.u.Fully.cchBuf = 0;
9730     if (pSandbox->StdErr.fIsConsole)
9731         pSandbox->StdErr.u.Con.cwcBuf   = 0;
9732     else
9733         pSandbox->StdErr.u.Fully.cchBuf = 0;
9734     pSandbox->Combined.cwcBuf   = 0;
9735     pSandbox->Combined.cFlushes = 0;
9736 #endif
9737     pSandbox->fNoPchCaching = fNoPchCaching;
9738     pSandbox->cArgs         = cArgs;
9739     pSandbox->papszArgs     = (char **)papszArgs;
9740     pSandbox->pszCmdLine    = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
9741     if (!pSandbox->pszCmdLine)
9742         return KERR_NO_MEMORY;
9743 
9744     /*
9745      * Convert command line and argv to UTF-16.
9746      * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
9747      */
9748     pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
9749     if (!pSandbox->papwszArgs)
9750         return KERR_NO_MEMORY;
9751     pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
9752     for (i = 0; i < cArgs; i++)
9753     {
9754         *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
9755         pSandbox->papwszArgs[i] = pwcPool;
9756         pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
9757         pwcPool++;
9758     }
9759     pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
9760     pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
9761 
9762     /*
9763      * Convert the commandline string to UTF-16, same pessimistic approach as above.
9764      */
9765     cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
9766     pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
9767     if (!pSandbox->pwszCmdLine)
9768         return KERR_NO_MEMORY;
9769     cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
9770 
9771     pSandbox->SavedCommandLine = pProcParams->CommandLine;
9772     pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
9773     pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
9774 
9775     /*
9776      * Setup the environment.
9777      */
9778     if (   cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
9779         || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
9780     {
9781         KU32 iDst = 0;
9782         for (i = 0; i < cEnvVars; i++)
9783         {
9784             const char *pszVar   = papszEnvVars[i];
9785             KSIZE       cchVar   = kHlpStrLen(pszVar);
9786             const char *pszEqual;
9787             if (   cchVar > 0
9788                 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
9789             {
9790                 char       *pszCopy  = kHlpDup(pszVar, cchVar + 1);
9791                 wchar_t    *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
9792                 if (pszCopy && pwszCopy)
9793                 {
9794                     pSandbox->papszEnvVars[iDst]  = pszCopy;
9795                     pSandbox->environ[iDst]       = pszCopy;
9796                     pSandbox->papwszEnvVars[iDst] = pwszCopy;
9797                     pSandbox->wenviron[iDst]      = pwszCopy;
9798 
9799                     /* When we see the path, we must tell the system or native exec and module loading won't work . */
9800                     if (   (pszEqual - pszVar) == 4
9801                         && (  pszCopy[0] == 'P' || pszCopy[0] == 'p')
9802                         && (  pszCopy[1] == 'A' || pszCopy[1] == 'a')
9803                         && (  pszCopy[2] == 'T' || pszCopy[2] == 't')
9804                         && (  pszCopy[3] == 'H' || pszCopy[3] == 'h'))
9805                         if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
9806                             kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
9807 
9808                     iDst++;
9809                 }
9810                 else
9811                 {
9812                     kHlpFree(pszCopy);
9813                     kHlpFree(pwszCopy);
9814                     return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
9815                 }
9816             }
9817             else
9818                 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
9819         }
9820         pSandbox->papszEnvVars[iDst]  = NULL;
9821         pSandbox->environ[iDst]       = NULL;
9822         pSandbox->papwszEnvVars[iDst] = NULL;
9823         pSandbox->wenviron[iDst]      = NULL;
9824     }
9825     else
9826         return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
9827 
9828     /*
9829      * Invalidate the volatile parts of cache (kBuild output directory,
9830      * temporary directory, whatever).
9831      */
9832     kFsCacheInvalidateCustomBoth(g_pFsCache);
9833 
9834 #ifdef WITH_HISTORY
9835     /*
9836      * Record command line in debug history.
9837      */
9838     kHlpFree(g_apszHistory[g_iHistoryNext]);
9839     g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
9840     g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
9841 #endif
9842 
9843     return 0;
9844 }
9845 
9846 
9847 /**
9848  * Does sandbox cleanup between jobs.
9849  *
9850  * We postpone whatever isn't externally visible (i.e. files) and doesn't
9851  * influence the result, so that kmk can get on with things ASAP.
9852  *
9853  * @param   pSandbox            The sandbox.
9854  */
kwSandboxCleanupLate(PKWSANDBOX pSandbox)9855 static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
9856 {
9857     PROCESS_MEMORY_COUNTERS     MemInfo;
9858     PKWVIRTALLOC                pTracker;
9859     PKWHEAP                     pHeap;
9860     PKWLOCALSTORAGE             pLocalStorage;
9861 #ifdef WITH_HASH_MD5_CACHE
9862     PKWHASHMD5                  pHash;
9863 #endif
9864 #ifdef WITH_TEMP_MEMORY_FILES
9865     PKWFSTEMPFILE               pTempFile;
9866 #endif
9867     PKWEXITCALLACK              pExitCallback;
9868 
9869     /*
9870      * First stuff that may cause code to run.
9871      */
9872 
9873     /* Do exit callback first. */
9874     pExitCallback = g_Sandbox.pExitCallbackHead;
9875     g_Sandbox.pExitCallbackHead = NULL;
9876     while (pExitCallback)
9877     {
9878         PKWEXITCALLACK  pNext = pExitCallback->pNext;
9879         KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
9880                 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
9881         __try
9882         {
9883             pExitCallback->pfnCallback();
9884         }
9885         __except (EXCEPTION_EXECUTE_HANDLER)
9886         {
9887             KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
9888                     pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
9889             kHlpAssertFailed();
9890         }
9891         kHlpFree(pExitCallback);
9892         pExitCallback = pNext;
9893     }
9894 
9895     /* Free left behind FlsAlloc leaks. */
9896     pLocalStorage = g_Sandbox.pFlsAllocHead;
9897     g_Sandbox.pFlsAllocHead = NULL;
9898     while (pLocalStorage)
9899     {
9900         PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
9901         KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
9902         FlsFree(pLocalStorage->idx);
9903         kHlpFree(pLocalStorage);
9904         pLocalStorage = pNext;
9905     }
9906 
9907     /* Free left behind TlsAlloc leaks. */
9908     pLocalStorage = g_Sandbox.pTlsAllocHead;
9909     g_Sandbox.pTlsAllocHead = NULL;
9910     while (pLocalStorage)
9911     {
9912         PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
9913         KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
9914         TlsFree(pLocalStorage->idx);
9915         kHlpFree(pLocalStorage);
9916         pLocalStorage = pNext;
9917     }
9918 
9919 
9920     /*
9921      * Then free resources associated with the sandbox run.
9922      */
9923 
9924     /* Open handles, except fixed handles (stdout and stderr). */
9925     if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
9926     {
9927         KU32 idxHandle = pSandbox->cHandles;
9928         while (idxHandle-- > 0)
9929             if (pSandbox->papHandles[idxHandle] == NULL)
9930             { /* likely */ }
9931             else
9932             {
9933                 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
9934                 if (   pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
9935                     || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
9936                 {
9937                     pSandbox->papHandles[idxHandle] = NULL;
9938                     pSandbox->cLeakedHandles++;
9939 
9940                     switch (pHandle->enmType)
9941                     {
9942                         case KWHANDLETYPE_FSOBJ_READ_CACHE:
9943                             KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
9944                                       idxHandle, pHandle->hHandle, pHandle->cRefs));
9945                             break;
9946                         case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
9947                             KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
9948                                       idxHandle, pHandle->hHandle, pHandle->cRefs));
9949                             break;
9950                         case KWHANDLETYPE_OUTPUT_BUF:
9951                             KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
9952                                       idxHandle, pHandle->hHandle, pHandle->cRefs));
9953                             break;
9954                         case KWHANDLETYPE_TEMP_FILE:
9955                             KWFS_LOG(("Closing leaked temp file  handle: %#x/%p cRefs=%d\n",
9956                                       idxHandle, pHandle->hHandle, pHandle->cRefs));
9957                             pHandle->u.pTempFile->cActiveHandles--;
9958                             break;
9959                         case KWHANDLETYPE_TEMP_FILE_MAPPING:
9960                             KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
9961                                       idxHandle, pHandle->hHandle, pHandle->cRefs));
9962                             pHandle->u.pTempFile->cActiveHandles--;
9963                             break;
9964                         default:
9965                             kHlpAssertFailed();
9966                     }
9967                     if (--pHandle->cRefs == 0)
9968                         kHlpFree(pHandle);
9969                     if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
9970                         break;
9971                 }
9972             }
9973         kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
9974     }
9975 
9976     /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
9977     g_Sandbox.cMemMappings = 0;
9978 
9979 #ifdef WITH_TEMP_MEMORY_FILES
9980     /* The temporary files aren't externally visible, they're all in memory. */
9981     pTempFile = pSandbox->pTempFileHead;
9982     pSandbox->pTempFileHead = NULL;
9983     while (pTempFile)
9984     {
9985         PKWFSTEMPFILE pNext = pTempFile->pNext;
9986         KU32          iSeg  = pTempFile->cSegs;
9987         while (iSeg-- > 0)
9988             kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
9989         kHlpFree(pTempFile->paSegs);
9990         pTempFile->pNext = NULL;
9991         kHlpFree(pTempFile);
9992 
9993         pTempFile = pNext;
9994     }
9995 #endif
9996 
9997     /* Free left behind HeapCreate leaks. */
9998     pHeap = g_Sandbox.pHeapHead;
9999     g_Sandbox.pHeapHead = NULL;
10000     while (pHeap != NULL)
10001     {
10002         PKWHEAP pNext = pHeap->pNext;
10003         KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
10004         HeapDestroy(pHeap->hHeap);
10005         pHeap = pNext;
10006     }
10007 
10008     /* Free left behind VirtualAlloc leaks. */
10009     pTracker = g_Sandbox.pVirtualAllocHead;
10010     g_Sandbox.pVirtualAllocHead = NULL;
10011     while (pTracker)
10012     {
10013         PKWVIRTALLOC pNext = pTracker->pNext;
10014         KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
10015 
10016 #ifdef WITH_FIXED_VIRTUAL_ALLOCS
10017         if (pTracker->idxPreAllocated != KU32_MAX)
10018             kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
10019         else
10020 #endif
10021             VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
10022         kHlpFree(pTracker);
10023         pTracker = pNext;
10024     }
10025 
10026     /* Free the environment. */
10027     if (pSandbox->papszEnvVars)
10028     {
10029         KU32 i;
10030         for (i = 0; pSandbox->papszEnvVars[i]; i++)
10031             kHlpFree(pSandbox->papszEnvVars[i]);
10032         pSandbox->environ[0]      = NULL;
10033         pSandbox->papszEnvVars[0] = NULL;
10034 
10035         for (i = 0; pSandbox->papwszEnvVars[i]; i++)
10036             kHlpFree(pSandbox->papwszEnvVars[i]);
10037         pSandbox->wenviron[0]      = NULL;
10038         pSandbox->papwszEnvVars[0] = NULL;
10039     }
10040 
10041 #ifdef WITH_HASH_MD5_CACHE
10042     /*
10043      * Hash handles.
10044      */
10045     pHash = pSandbox->pHashHead;
10046     pSandbox->pHashHead = NULL;
10047     while (pHash)
10048     {
10049         PKWHASHMD5 pNext = pHash->pNext;
10050         KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
10051         kHlpFree(pHash);
10052         pHash = pNext;
10053     }
10054 #endif
10055 
10056     /*
10057      * Check the memory usage.  If it's getting high, trigger a respawn
10058      * after the next job.
10059      */
10060     MemInfo.WorkingSetSize = 0;
10061     if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
10062     {
10063         /* The first time thru, we figure out approximately when to restart
10064            based on installed RAM and CPU threads. */
10065         static KU64 s_cbMaxWorkingSet = 0;
10066         if (s_cbMaxWorkingSet != 0)
10067         { /* likely */ }
10068         else
10069         {
10070             SYSTEM_INFO SysInfo;
10071             MEMORYSTATUSEX GlobalMemInfo;
10072             const char    *pszValue;
10073 
10074             /* Calculate a reasonable estimate. */
10075             kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
10076             GetNativeSystemInfo(&SysInfo);
10077 
10078             kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
10079             GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
10080             if (!GlobalMemoryStatusEx(&GlobalMemInfo))
10081 #if K_ARCH_BITS >= 64
10082                 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
10083 #else
10084                 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
10085 #endif
10086             s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
10087             KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10088 
10089             /* User limit. */
10090             pszValue = getenv("KWORKER_MEMORY_LIMIT");
10091             if (pszValue != NULL)
10092             {
10093                 char         *pszNext;
10094                 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
10095                 if (*pszNext == '\0' || *pszNext == 'M')
10096                     s_cbMaxWorkingSet = ulValue * (KU64)1048576;
10097                 else if (*pszNext == 'K')
10098                     s_cbMaxWorkingSet = ulValue * (KU64)1024;
10099                 else if (*pszNext == 'G')
10100                     s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
10101                 else
10102                     kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
10103                 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10104             }
10105 
10106             /* Clamp it a little. */
10107             if (s_cbMaxWorkingSet < 168*1024*1024)
10108                 s_cbMaxWorkingSet = 168*1024*1024;
10109 #if K_ARCH_BITS < 64
10110             else
10111                 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
10112                                           SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
10113                                           ?  512*1024*1024 /* Only got 2 or 3 GB VA */
10114                                           : 1536*1024*1024 /* got 4GB VA */);
10115 #endif
10116             if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
10117                 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
10118             KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10119         }
10120 
10121         /* Finally the check. */
10122         if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
10123         {
10124             KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
10125             g_fRestart = K_TRUE;
10126         }
10127     }
10128 
10129     /*
10130      * The CRT has a max of 8192 handles, so we better restart after a while if
10131      * someone is leaking handles or we risk running out of descriptors.
10132      *
10133      * Note! We only detect leaks for handles we intercept.  In the case of CL.EXE
10134      *       doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
10135      */
10136     if (pSandbox->cLeakedHandles > 6000)
10137     {
10138         KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
10139         g_fRestart = K_TRUE;
10140     }
10141 }
10142 
10143 
10144 /**
10145  * Does essential cleanups and restoring, anything externally visible.
10146  *
10147  * All cleanups that aren't externally visible are postponed till after we've
10148  * informed kmk of the result, so it can be done in the dead time between jobs.
10149  *
10150  * @param   pSandbox            The sandbox.
10151  */
kwSandboxCleanup(PKWSANDBOX pSandbox)10152 static void kwSandboxCleanup(PKWSANDBOX pSandbox)
10153 {
10154     /*
10155      * Restore the parent command line string.
10156      */
10157     PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10158     PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10159     pProcParams->CommandLine    = pSandbox->SavedCommandLine;
10160     pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
10161     pProcParams->StandardError  = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
10162 }
10163 
10164 
kwSandboxExec(PKWSANDBOX pSandbox,PKWTOOL pTool,KU32 cArgs,const char ** papszArgs,KBOOL fWatcomBrainDamange,KU32 cEnvVars,const char ** papszEnvVars,KBOOL fNoPchCaching)10165 static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10166                          KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10167 {
10168     int rcExit = 42;
10169     int rc;
10170 
10171     /*
10172      * Initialize the sandbox environment.
10173      */
10174     rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
10175     if (rc == 0)
10176     {
10177         /*
10178          * Do module initialization.
10179          */
10180         kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
10181         rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
10182         if (rc == 0)
10183         {
10184             /*
10185              * Call the main function.
10186              */
10187 #if K_ARCH == K_ARCH_AMD64
10188             int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
10189 #elif K_ARCH == K_ARCH_X86_32
10190             int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
10191 #else
10192 # error "Port me!"
10193 #endif
10194 
10195             /* Save the NT TIB first (should do that here, not in some other function). */
10196             PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10197             pSandbox->TibMainThread = *pTib;
10198 
10199             /* Make the call in a guarded fashion. */
10200 #if K_ARCH == K_ARCH_AMD64
10201             /* AMD64 */
10202             *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
10203             __try
10204             {
10205                 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10206                 if (setjmp(pSandbox->JmpBuf) == 0)
10207                 {
10208                     *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10209                     pSandbox->fRunning = K_TRUE;
10210                     rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
10211                     pSandbox->fRunning = K_FALSE;
10212                 }
10213                 else
10214                     rcExit = pSandbox->rcExitCode;
10215             }
10216 #elif K_ARCH == K_ARCH_X86_32
10217             /* x86 (see _tmainCRTStartup) */
10218             *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
10219             __try
10220             {
10221                 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10222                 if (setjmp(pSandbox->JmpBuf) == 0)
10223                 {
10224                     //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10225                     pSandbox->fRunning = K_TRUE;
10226                     rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
10227                     pSandbox->fRunning = K_FALSE;
10228                 }
10229                 else
10230                     rcExit = pSandbox->rcExitCode;
10231             }
10232 #endif
10233             __except (EXCEPTION_EXECUTE_HANDLER)
10234             {
10235                 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
10236 #ifdef WITH_HISTORY
10237                 {
10238                     KU32 cPrinted = 0;
10239                     while (cPrinted++ < 5)
10240                     {
10241                         KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
10242                         if (g_apszHistory[idx])
10243                             kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
10244                     }
10245                 }
10246 #endif
10247                 rcExit = 512;
10248             }
10249             pSandbox->fRunning = K_FALSE;
10250 
10251             /* Now, restore the NT TIB. */
10252             *pTib = pSandbox->TibMainThread;
10253         }
10254         else
10255             rcExit = 42 + 4;
10256 
10257         /*
10258          * Flush and clean up the essential bits only, postpone whatever we
10259          * can till after we've replied to kmk.
10260          */
10261 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10262         kwSandboxConsoleFlushAll(&g_Sandbox);
10263 #endif
10264         kwSandboxCleanup(&g_Sandbox);
10265     }
10266     else
10267         rcExit = 42 + 3;
10268 
10269     return rcExit;
10270 }
10271 
10272 
10273 /**
10274  * Does the post command part of a job (optional).
10275  *
10276  * @returns The exit code of the job.
10277  * @param   cPostCmdArgs        Number of post command arguments (includes cmd).
10278  * @param   papszPostCmdArgs    The post command and its argument.
10279  */
kSubmitHandleJobPostCmd(KU32 cPostCmdArgs,const char ** papszPostCmdArgs)10280 static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
10281 {
10282     const char *pszCmd = papszPostCmdArgs[0];
10283 
10284     /* Allow the kmk builtin prefix. */
10285     static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
10286     if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
10287         pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
10288 
10289     /* Command switch. */
10290     if (kHlpStrComp(pszCmd, "kDepObj") == 0)
10291         return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL);
10292 
10293     return kwErrPrintfRc(42 + 5 , "Unknown post command: '%s'\n", pszCmd);
10294 }
10295 
10296 
10297 /**
10298  * Part 2 of the "JOB" command handler.
10299  *
10300  * @returns The exit code of the job.
10301  * @param   pszExecutable       The executable to execute.
10302  * @param   pszCwd              The current working directory of the job.
10303  * @param   cArgs               The number of arguments.
10304  * @param   papszArgs           The argument vector.
10305  * @param   fWatcomBrainDamange Whether to apply watcom rules while quoting.
10306  * @param   cEnvVars            The number of environment variables.
10307  * @param   papszEnvVars        The environment vector.
10308  * @param   fNoPchCaching       Whether to disable precompiled header file
10309  *                              caching.  Avoid trouble when creating them.
10310  * @param   cPostCmdArgs        Number of post command arguments (includes cmd).
10311  * @param   papszPostCmdArgs    The post command and its argument.
10312  */
kSubmitHandleJobUnpacked(const char * pszExecutable,const char * pszCwd,KU32 cArgs,const char ** papszArgs,KBOOL fWatcomBrainDamange,KU32 cEnvVars,const char ** papszEnvVars,KBOOL fNoPchCaching,KU32 cPostCmdArgs,const char ** papszPostCmdArgs)10313 static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
10314                                     KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10315                                     KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching,
10316                                     KU32 cPostCmdArgs, const char **papszPostCmdArgs)
10317 {
10318     int rcExit;
10319     PKWTOOL pTool;
10320 
10321     KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
10322             pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
10323 #ifdef KW_LOG_ENABLED
10324     {
10325         KU32 i;
10326         for (i = 0; i < cArgs; i++)
10327             KW_LOG(("  papszArgs[%u]=%s\n", i, papszArgs[i]));
10328         for (i = 0; i < cPostCmdArgs; i++)
10329             KW_LOG(("  papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
10330     }
10331 #endif
10332     g_cJobs++;
10333 
10334     /*
10335      * Lookup the tool.
10336      */
10337     pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
10338     if (pTool)
10339     {
10340         /*
10341          * Change the directory if we're going to execute the job inside
10342          * this process.  Then invoke the tool type specific handler.
10343          */
10344         switch (pTool->enmType)
10345         {
10346             case KWTOOLTYPE_SANDBOXED:
10347             case KWTOOLTYPE_WATCOM:
10348             {
10349                 /* Change dir. */
10350                 KFSLOOKUPERROR  enmError;
10351                 PKFSOBJ         pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
10352                 if (   pNewCurDir           == g_pCurDirObj
10353                     && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
10354                     kFsCacheObjRelease(g_pFsCache, pNewCurDir);
10355                 else if (SetCurrentDirectoryA(pszCwd))
10356                 {
10357                     kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
10358                     g_pCurDirObj = pNewCurDir;
10359                 }
10360                 else
10361                 {
10362                     kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
10363                     kFsCacheObjRelease(g_pFsCache, pNewCurDir);
10364                     rcExit = 42 + 1;
10365                     break;
10366                 }
10367 
10368                 /* Call specific handler. */
10369                 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
10370                 {
10371                     KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
10372                     rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
10373                                            cEnvVars, papszEnvVars, fNoPchCaching);
10374                 }
10375                 else
10376                 {
10377                     kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
10378                     rcExit = 42 + 2;
10379                 }
10380                 break;
10381             }
10382 
10383             case KWTOOLTYPE_EXEC:
10384                 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
10385                 rcExit = 42 + 2;
10386                 break;
10387 
10388             default:
10389                 kHlpAssertFailed();
10390                 kwErrPrintf("Internal tool type corruption!!\n");
10391                 rcExit = 42 + 2;
10392                 g_fRestart = K_TRUE;
10393                 break;
10394         }
10395 
10396         /*
10397          * Do the post command, if present.
10398          */
10399         if (cPostCmdArgs && rcExit == 0)
10400             rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
10401     }
10402     else
10403         rcExit = 42 + 1;
10404     return rcExit;
10405 }
10406 
10407 
10408 /**
10409  * Handles a "JOB" command.
10410  *
10411  * @returns The exit code of the job.
10412  * @param   pszMsg              Points to the "JOB" command part of the message.
10413  * @param   cbMsg               Number of message bytes at @a pszMsg.  There are
10414  *                              4 more zero bytes after the message body to
10415  *                              simplify parsing.
10416  */
kSubmitHandleJob(const char * pszMsg,KSIZE cbMsg)10417 static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
10418 {
10419     int rcExit = 42;
10420 
10421     /*
10422      * Unpack the message.
10423      */
10424     const char     *pszExecutable;
10425     KSIZE           cbTmp;
10426 
10427     pszMsg += sizeof("JOB");
10428     cbMsg  -= sizeof("JOB");
10429 
10430     /* Executable name. */
10431     pszExecutable = pszMsg;
10432     cbTmp = kHlpStrLen(pszMsg) + 1;
10433     pszMsg += cbTmp;
10434     if (   cbTmp < cbMsg
10435         && cbTmp > 2)
10436     {
10437         const char *pszCwd;
10438         cbMsg -= cbTmp;
10439 
10440         /* Current working directory. */
10441         pszCwd = pszMsg;
10442         cbTmp = kHlpStrLen(pszMsg) + 1;
10443         pszMsg += cbTmp;
10444         if (   cbTmp + sizeof(KU32) < cbMsg
10445             && cbTmp >= 2)
10446         {
10447             KU32    cArgs;
10448             cbMsg  -= cbTmp;
10449 
10450             /* Argument count. */
10451             kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
10452             pszMsg += sizeof(cArgs);
10453             cbMsg  -= sizeof(cArgs);
10454 
10455             if (cArgs > 0 && cArgs < 4096)
10456             {
10457                 /* The argument vector. */
10458                 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
10459                 if (papszArgs)
10460                 {
10461                     KU32 i;
10462                     for (i = 0; i < cArgs; i++)
10463                     {
10464                         papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
10465                         cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
10466                         pszMsg += cbTmp;
10467                         if (cbTmp < cbMsg)
10468                             cbMsg -= cbTmp;
10469                         else
10470                         {
10471                             cbMsg = 0;
10472                             break;
10473                         }
10474 
10475                     }
10476                     papszArgs[cArgs] = 0;
10477 
10478                     /* Environment variable count. */
10479                     if (cbMsg > sizeof(KU32))
10480                     {
10481                         KU32    cEnvVars;
10482                         kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
10483                         pszMsg += sizeof(cEnvVars);
10484                         cbMsg  -= sizeof(cEnvVars);
10485 
10486                         if (cEnvVars >= 0 && cEnvVars < 4096)
10487                         {
10488                             /* The argument vector. */
10489                             char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
10490                             if (papszEnvVars)
10491                             {
10492                                 for (i = 0; i < cEnvVars; i++)
10493                                 {
10494                                     papszEnvVars[i] = pszMsg;
10495                                     cbTmp = kHlpStrLen(pszMsg) + 1;
10496                                     pszMsg += cbTmp;
10497                                     if (cbTmp < cbMsg)
10498                                         cbMsg -= cbTmp;
10499                                     else
10500                                     {
10501                                         cbMsg = 0;
10502                                         break;
10503                                     }
10504                                 }
10505                                 papszEnvVars[cEnvVars] = 0;
10506 
10507                                 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
10508                                 if (cbMsg >= sizeof(KU8) * 2)
10509                                 {
10510                                     KBOOL fWatcomBrainDamange = *pszMsg++;
10511                                     KBOOL fNoPchCaching = *pszMsg++;
10512                                     cbMsg -= 2;
10513 
10514                                     /* Post command argument count (can be zero). */
10515                                     if (cbMsg >= sizeof(KU32))
10516                                     {
10517                                         KU32 cPostCmdArgs;
10518                                         kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
10519                                         pszMsg += sizeof(cPostCmdArgs);
10520                                         cbMsg  -= sizeof(cPostCmdArgs);
10521 
10522                                         if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
10523                                         {
10524                                             char const *apszPostCmdArgs[32+1];
10525                                             for (i = 0; i < cPostCmdArgs; i++)
10526                                             {
10527                                                 apszPostCmdArgs[i] = pszMsg;
10528                                                 cbTmp = kHlpStrLen(pszMsg) + 1;
10529                                                 pszMsg += cbTmp;
10530                                                 if (   cbTmp < cbMsg
10531                                                     || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
10532                                                     cbMsg -= cbTmp;
10533                                                 else
10534                                                 {
10535                                                     cbMsg = KSIZE_MAX;
10536                                                     break;
10537                                                 }
10538                                             }
10539                                             if (cbMsg == 0)
10540                                             {
10541                                                 apszPostCmdArgs[cPostCmdArgs] = NULL;
10542 
10543                                                 /*
10544                                                  * The next step.
10545                                                  */
10546                                                 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
10547                                                                                   cArgs, papszArgs, fWatcomBrainDamange,
10548                                                                                   cEnvVars, papszEnvVars, fNoPchCaching,
10549                                                                                   cPostCmdArgs, apszPostCmdArgs);
10550                                             }
10551                                             else if (cbMsg == KSIZE_MAX)
10552                                                 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
10553                                             else
10554                                                 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
10555                                         }
10556                                         else
10557                                             kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
10558                                     }
10559                                     else
10560                                         kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
10561                                 }
10562                                 else
10563                                     kwErrPrintf("Detected bogus message unpacking environment variables!\n");
10564                                 kHlpFree((void *)papszEnvVars);
10565                             }
10566                             else
10567                                 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
10568                         }
10569                         else
10570                             kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
10571                     }
10572                     else
10573                         kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
10574                     kHlpFree((void *)papszArgs);
10575                 }
10576                 else
10577                     kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
10578             }
10579             else
10580                 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
10581         }
10582         else
10583             kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
10584     }
10585     else
10586         kwErrPrintf("Detected bogus message unpacking executable path!\n");
10587     return rcExit;
10588 }
10589 
10590 
10591 /**
10592  * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
10593  *
10594  * @retval  0 on success.
10595  * @retval  -1 on error (fully bitched).
10596  *
10597  * @param   hPipe               The pipe handle.
10598  * @param   pvBuf               The buffer to write out out.
10599  * @param   cbToWrite           The number of bytes to write.
10600  */
kSubmitWriteIt(HANDLE hPipe,const void * pvBuf,KU32 cbToWrite)10601 static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
10602 {
10603     KU8 const  *pbBuf  = (KU8 const *)pvBuf;
10604     KU32        cbLeft = cbToWrite;
10605     for (;;)
10606     {
10607         DWORD cbActuallyWritten = 0;
10608         if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
10609         {
10610             cbLeft -= cbActuallyWritten;
10611             if (!cbLeft)
10612                 return 0;
10613             pbBuf  += cbActuallyWritten;
10614         }
10615         else
10616         {
10617             DWORD dwErr = GetLastError();
10618             if (cbLeft == cbToWrite)
10619                 kwErrPrintf("WriteFile failed: %u\n", dwErr);
10620             else
10621                 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
10622             return -1;
10623         }
10624     }
10625 }
10626 
10627 
10628 /**
10629  * Wrapper around ReadFile / read that reads the whole @a cbToRead.
10630  *
10631  * @retval  0 on success.
10632  * @retval  1 on shut down (fShutdownOkay must be K_TRUE).
10633  * @retval  -1 on error (fully bitched).
10634  * @param   hPipe               The pipe handle.
10635  * @param   pvBuf               The buffer to read into.
10636  * @param   cbToRead            The number of bytes to read.
10637  * @param   fShutdownOkay       Whether connection shutdown while reading the
10638  *                              first byte is okay or not.
10639  */
kSubmitReadIt(HANDLE hPipe,void * pvBuf,KU32 cbToRead,KBOOL fMayShutdown)10640 static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
10641 {
10642     KU8 *pbBuf  = (KU8 *)pvBuf;
10643     KU32 cbLeft = cbToRead;
10644     for (;;)
10645     {
10646         DWORD cbActuallyRead = 0;
10647         if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
10648         {
10649             cbLeft -= cbActuallyRead;
10650             if (!cbLeft)
10651                 return 0;
10652             pbBuf  += cbActuallyRead;
10653         }
10654         else
10655         {
10656             DWORD dwErr = GetLastError();
10657             if (cbLeft == cbToRead)
10658             {
10659                 if (   fMayShutdown
10660                     && dwErr == ERROR_BROKEN_PIPE)
10661                     return 1;
10662                 kwErrPrintf("ReadFile failed: %u\n", dwErr);
10663             }
10664             else
10665                 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
10666             return -1;
10667         }
10668     }
10669 }
10670 
10671 
10672 /**
10673  * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
10674  *
10675  * @returns pszBuf
10676  * @param   pszBuf              The buffer (sufficiently large).
10677  * @param   uValue              The value.
10678  */
kwFmtU64(char * pszBuf,KU64 uValue)10679 static const char *kwFmtU64(char *pszBuf, KU64 uValue)
10680 {
10681     char  szTmp[64];
10682     char *psz = &szTmp[63];
10683     int   cch = 4;
10684 
10685     *psz-- = '\0';
10686     do
10687     {
10688         if (--cch == 0)
10689         {
10690             *psz-- = ' ';
10691             cch = 3;
10692         }
10693         *psz-- = (uValue % 10) + '0';
10694         uValue /= 10;
10695     } while (uValue != 0);
10696 
10697     return strcpy(pszBuf, psz + 1);
10698 }
10699 
10700 
10701 /**
10702  * Prints statistics.
10703  */
kwPrintStats(void)10704 static void kwPrintStats(void)
10705 {
10706     PROCESS_MEMORY_COUNTERS_EX MemInfo;
10707     MEMORYSTATUSEX MemStatus;
10708     IO_COUNTERS IoCounters;
10709     DWORD cHandles;
10710     KSIZE cMisses;
10711     char  szBuf[16*1024];
10712     int   off = 0;
10713     char  szPrf[24];
10714     char  sz1[64];
10715     char  sz2[64];
10716     char  sz3[64];
10717     char  sz4[64];
10718     extern size_t maybe_con_fwrite(void const *pvBuf, size_t cbUnit, size_t cUnits, FILE *pFile);
10719 
10720     sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
10721 
10722     szBuf[off++] = '\n';
10723 
10724     off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
10725                    kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
10726     off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
10727                    kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
10728                    kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
10729 
10730     off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
10731                    szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
10732 
10733     off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
10734                    kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
10735                    kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
10736 
10737     off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
10738                    kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
10739                    kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
10740 
10741     off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
10742                    kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
10743     off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
10744                    kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
10745                    (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
10746                    kwFmtU64(sz2, g_cWriteFileToInMemTemp),
10747                    (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
10748 
10749     off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
10750                    kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
10751     off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
10752                    kwFmtU64(sz1, g_pFsCache->cObjects),
10753                    kwFmtU64(sz2, g_pFsCache->cbObjects),
10754                    kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
10755     off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
10756                    kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
10757                    kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
10758                    kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
10759                    kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
10760 #ifdef KFSCACHE_CFG_UTF16
10761     off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
10762                    kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
10763                    kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
10764                    kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
10765                    kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
10766 #endif
10767     off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
10768                    kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
10769                    kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
10770                    kwFmtU64(sz3, g_pFsCache->cChildHashed),
10771                    kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
10772 
10773     cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
10774     off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
10775                    kwFmtU64(sz1, g_pFsCache->cLookups),
10776                    kwFmtU64(sz2, g_pFsCache->cPathHashHits),
10777                    (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
10778                    kwFmtU64(sz3, g_pFsCache->cWalkHits),
10779                    (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
10780                    kwFmtU64(sz4, cMisses),
10781                    (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
10782 
10783     off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
10784                    kwFmtU64(sz1, g_pFsCache->cChildSearches),
10785                    kwFmtU64(sz2, g_pFsCache->cChildHashHits),
10786                    (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
10787     off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
10788                    kwFmtU64(sz1, g_pFsCache->cNameChanges),
10789                    kwFmtU64(sz2, g_pFsCache->cNameGrowths),
10790                    (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
10791 
10792 
10793     /*
10794      * Process & Memory details.
10795      */
10796     if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
10797         cHandles = 0;
10798     MemInfo.cb = sizeof(MemInfo);
10799     if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
10800         memset(&MemInfo, 0, sizeof(MemInfo));
10801     off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
10802                    kwFmtU64(sz1, cHandles),
10803                    kwFmtU64(sz2, MemInfo.PageFaultCount),
10804                    kwFmtU64(sz3, MemInfo.PagefileUsage),
10805                    kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
10806     off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
10807                    kwFmtU64(sz1, MemInfo.WorkingSetSize),
10808                    kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
10809                    kwFmtU64(sz3, MemInfo.PrivateUsage));
10810     off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
10811                    szPrf,
10812                    kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
10813                    kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
10814                    kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
10815                    kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
10816 
10817     if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
10818         memset(&IoCounters, 0, sizeof(IoCounters));
10819     off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
10820                    kwFmtU64(sz1, IoCounters.ReadTransferCount),
10821                    kwFmtU64(sz2, IoCounters.ReadOperationCount));
10822     off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
10823                    kwFmtU64(sz1, IoCounters.WriteTransferCount),
10824                    kwFmtU64(sz2, IoCounters.WriteOperationCount));
10825     off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
10826                    kwFmtU64(sz1, IoCounters.OtherTransferCount),
10827                    kwFmtU64(sz2, IoCounters.OtherOperationCount));
10828 
10829     MemStatus.dwLength = sizeof(MemStatus);
10830     if (!GlobalMemoryStatusEx(&MemStatus))
10831         memset(&MemStatus, 0, sizeof(MemStatus));
10832     off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
10833                    kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
10834                    MemStatus.ullAvailVirtual);
10835     off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
10836 
10837     maybe_con_fwrite(szBuf, off, 1, stdout);
10838     fflush(stdout);
10839 }
10840 
10841 
10842 /**
10843  * Handles what comes after --test.
10844  *
10845  * @returns Exit code.
10846  * @param   argc                Number of arguments after --test.
10847  * @param   argv                Arguments after --test.
10848  */
kwTestRun(int argc,char ** argv)10849 static int kwTestRun(int argc, char **argv)
10850 {
10851     int         i;
10852     int         j;
10853     int         rcExit;
10854     int         cRepeats;
10855     char        szCwd[MAX_PATH];
10856     const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
10857     KU32        cEnvVars;
10858     KBOOL       fWatcomBrainDamange = K_FALSE;
10859     KBOOL       fNoPchCaching = K_FALSE;
10860 
10861     /*
10862      * Parse arguments.
10863      */
10864     /* Repeat count. */
10865     i = 0;
10866     if (i >= argc)
10867         return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
10868     if (strcmp(argv[i], "--") != 0)
10869     {
10870         cRepeats = atoi(argv[i]);
10871         if (cRepeats <= 0)
10872             return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
10873         i++;
10874 
10875         /* Optional directory change. */
10876         if (   i < argc
10877             && (   strcmp(argv[i], "--chdir") == 0
10878                 || strcmp(argv[i], "-C")      == 0 ) )
10879         {
10880             i++;
10881             if (i >= argc)
10882                 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
10883             pszCwd = argv[i++];
10884         }
10885 
10886         /* Optional watcom flag directory change. */
10887         if (   i < argc
10888             && (   strcmp(argv[i], "--wcc-brain-damage") == 0
10889                 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
10890         {
10891             fWatcomBrainDamange = K_TRUE;
10892             i++;
10893         }
10894 
10895         /* Optional watcom flag directory change. */
10896         if (   i < argc
10897             && strcmp(argv[i], "--no-pch-caching") == 0)
10898         {
10899             fNoPchCaching = K_TRUE;
10900             i++;
10901         }
10902 
10903         /* Trigger breakpoint */
10904         if (   i < argc
10905             && strcmp(argv[i], "--breakpoint") == 0)
10906         {
10907             __debugbreak();
10908             i++;
10909         }
10910 
10911         /* Check for '--'. */
10912         if (i >= argc)
10913             return kwErrPrintfRc(2, "Missing '--'\n");
10914         if (strcmp(argv[i], "--") != 0)
10915             return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
10916         i++;
10917     }
10918     else
10919     {
10920         cRepeats = 1;
10921         i++;
10922     }
10923     if (i >= argc)
10924         return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
10925 
10926     /*
10927      * Do the job.
10928      */
10929     cEnvVars = 0;
10930     while (environ[cEnvVars] != NULL)
10931         cEnvVars++;
10932 
10933     for (j = 0; j < cRepeats; j++)
10934     {
10935         rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
10936                                           argc - i, &argv[i], fWatcomBrainDamange,
10937                                           cEnvVars, environ, fNoPchCaching,
10938                                           0, NULL);
10939         KW_LOG(("rcExit=%d\n", rcExit));
10940         kwSandboxCleanupLate(&g_Sandbox);
10941     }
10942 
10943     if (getenv("KWORKER_STATS") != NULL)
10944         kwPrintStats();
10945 
10946 # ifdef WITH_LOG_FILE
10947     if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
10948         CloseHandle(g_hLogFile);
10949 # endif
10950     return rcExit;
10951 }
10952 
10953 
main(int argc,char ** argv)10954 int main(int argc, char **argv)
10955 {
10956     KSIZE                           cbMsgBuf = 0;
10957     KU8                            *pbMsgBuf = NULL;
10958     int                             i;
10959     HANDLE                          hPipe = INVALID_HANDLE_VALUE;
10960     const char                     *pszTmp;
10961     KFSLOOKUPERROR                  enmIgnored;
10962 #if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10963     PVOID                           pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
10964                                                                                    kwSandboxVecXcptEmulateChained);
10965 #endif
10966 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10967     HANDLE                          hCurProc       = GetCurrentProcess();
10968     PPEB                            pPeb           = kwSandboxGetProcessEnvironmentBlock();
10969     PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10970     DWORD                           dwType;
10971 #endif
10972 
10973 
10974 #ifdef WITH_FIXED_VIRTUAL_ALLOCS
10975     /*
10976      * Reserve memory for cl.exe
10977      */
10978     for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
10979     {
10980         g_aFixedVirtualAllocs[i].fInUse     = K_FALSE;
10981         g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
10982                                                            g_aFixedVirtualAllocs[i].cbFixed,
10983                                                            MEM_RESERVE, PAGE_READWRITE);
10984         if (   !g_aFixedVirtualAllocs[i].pvReserved
10985             || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
10986         {
10987             kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
10988                         GetLastError());
10989             if (g_aFixedVirtualAllocs[i].pvReserved)
10990             {
10991                 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
10992                 g_aFixedVirtualAllocs[i].pvReserved = NULL;
10993             }
10994         }
10995     }
10996 #endif
10997 
10998     /*
10999      * Register our Control-C and Control-Break handlers.
11000      */
11001     if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
11002         return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
11003 
11004     /*
11005      * Create the cache and mark the temporary directory as using the custom revision.
11006      */
11007     g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
11008     if (!g_pFsCache)
11009         return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
11010 
11011     pszTmp = getenv("TEMP");
11012     if (pszTmp && *pszTmp != '\0')
11013         kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
11014     pszTmp = getenv("TMP");
11015     if (pszTmp && *pszTmp != '\0')
11016         kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
11017     pszTmp = getenv("TMPDIR");
11018     if (pszTmp && *pszTmp != '\0')
11019         kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
11020 
11021     /*
11022      * Make g_abDefLdBuf executable.
11023      */
11024     if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
11025         return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
11026                              g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
11027 
11028 #ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11029     /*
11030      * Get and duplicate the console handles.
11031      */
11032     /* Standard output. */
11033     g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
11034     if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
11035                          GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
11036         kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
11037     dwType = GetFileType(g_Sandbox.StdOut.hOutput);
11038     g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
11039     g_Sandbox.StdOut.fFileType  = (dwType & ~FILE_TYPE_REMOTE) < 0xf
11040                                 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
11041     g_Sandbox.HandleStdOut.enmType         = KWHANDLETYPE_OUTPUT_BUF;
11042     g_Sandbox.HandleStdOut.cRefs           = 0x10001;
11043     g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
11044     g_Sandbox.HandleStdOut.u.pOutBuf       = &g_Sandbox.StdOut;
11045     g_Sandbox.HandleStdOut.hHandle         = g_Sandbox.StdOut.hOutput;
11046     if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
11047     {
11048         if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
11049             g_Sandbox.cFixedHandles++;
11050         else
11051             return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
11052     }
11053     KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
11054                g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
11055 
11056     /* Standard error. */
11057     g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
11058     if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
11059                          GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
11060         kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
11061     dwType = GetFileType(g_Sandbox.StdErr.hOutput);
11062     g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
11063     g_Sandbox.StdErr.fFileType  = (dwType & ~FILE_TYPE_REMOTE) < 0xf
11064                                 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
11065     g_Sandbox.HandleStdErr.enmType         = KWHANDLETYPE_OUTPUT_BUF;
11066     g_Sandbox.HandleStdErr.cRefs           = 0x10001;
11067     g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
11068     g_Sandbox.HandleStdErr.u.pOutBuf       = &g_Sandbox.StdErr;
11069     g_Sandbox.HandleStdErr.hHandle         = g_Sandbox.StdErr.hOutput;
11070     if (   g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
11071         && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
11072     {
11073         if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
11074             g_Sandbox.cFixedHandles++;
11075         else
11076             return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
11077     }
11078     KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
11079                g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
11080 
11081     /* Combined console buffer. */
11082     if (g_Sandbox.StdErr.fIsConsole)
11083     {
11084         g_Sandbox.Combined.hOutput   = g_Sandbox.StdErr.hBackup;
11085         g_Sandbox.Combined.uCodepage = GetConsoleCP();
11086     }
11087     else if (g_Sandbox.StdOut.fIsConsole)
11088     {
11089         g_Sandbox.Combined.hOutput   = g_Sandbox.StdOut.hBackup;
11090         g_Sandbox.Combined.uCodepage = GetConsoleCP();
11091     }
11092     else
11093     {
11094         g_Sandbox.Combined.hOutput   = INVALID_HANDLE_VALUE;
11095         g_Sandbox.Combined.uCodepage = CP_ACP;
11096     }
11097     KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
11098 #endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
11099 
11100 
11101     /*
11102      * Parse arguments.
11103      */
11104     for (i = 1; i < argc; i++)
11105     {
11106         if (strcmp(argv[i], "--pipe") == 0)
11107         {
11108             i++;
11109             if (i < argc)
11110             {
11111                 char *pszEnd = NULL;
11112                 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
11113                 if (   *argv[i]
11114                     && pszEnd != NULL
11115                     && *pszEnd == '\0'
11116                     && u64Value != 0
11117                     && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
11118                     && (uintptr_t)u64Value == u64Value)
11119                     hPipe = (HANDLE)(uintptr_t)u64Value;
11120                 else
11121                     return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
11122             }
11123             else
11124                 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
11125         }
11126         else if (strcmp(argv[i], "--volatile") == 0)
11127         {
11128             i++;
11129             if (i < argc)
11130                 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
11131             else
11132                 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
11133         }
11134         else if (strcmp(argv[i], "--test") == 0)
11135             return kwTestRun(argc - i - 1, &argv[i + 1]);
11136         else if (strcmp(argv[i], "--priority") == 0)
11137         {
11138             i++;
11139             if (i < argc)
11140             {
11141                 char *pszEnd = NULL;
11142                 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
11143                 if (   *argv[i]
11144                     && pszEnd != NULL
11145                     && *pszEnd == '\0'
11146                     && uValue >= 1
11147                     && uValue <= 5)
11148                 {
11149                     DWORD dwClass, dwPriority;
11150                     switch (uValue)
11151                     {
11152                         case 1: dwClass = IDLE_PRIORITY_CLASS;         dwPriority = THREAD_PRIORITY_IDLE; break;
11153                         case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
11154                         case 3: dwClass = NORMAL_PRIORITY_CLASS;       dwPriority = THREAD_PRIORITY_NORMAL; break;
11155                         case 4: dwClass = HIGH_PRIORITY_CLASS;         dwPriority = 0xffffffff; break;
11156                         case 5: dwClass = REALTIME_PRIORITY_CLASS;     dwPriority = 0xffffffff; break;
11157                     }
11158                     SetPriorityClass(GetCurrentProcess(), dwClass);
11159                     if (dwPriority != 0xffffffff)
11160                         SetThreadPriority(GetCurrentThread(), dwPriority);
11161                 }
11162                 else
11163                     return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
11164             }
11165             else
11166                 return kwErrPrintfRc(2, "--priority takes an argument!\n");
11167 
11168         }
11169         else if (   strcmp(argv[i], "--help") == 0
11170                  || strcmp(argv[i], "-h") == 0
11171                  || strcmp(argv[i], "-?") == 0)
11172         {
11173             printf("usage: kWorker [--volatile dir] [--priority <1-5>] --pipe <pipe-handle>\n"
11174                    "usage: kWorker <--help|-h>\n"
11175                    "usage: kWorker <--version|-V>\n"
11176                    "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
11177                    "\n"
11178                    "This is an internal kmk program that is used via the builtin_kSubmit.\n");
11179             return 0;
11180         }
11181         else if (   strcmp(argv[i], "--version") == 0
11182                  || strcmp(argv[i], "-V") == 0)
11183             return kbuild_version(argv[0]);
11184         else
11185             return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
11186     }
11187 
11188     if (hPipe == INVALID_HANDLE_VALUE)
11189         return kwErrPrintfRc(2, "Missing --pipe <pipe-handle> argument!\n");
11190 
11191     /*
11192      * Serve the pipe.
11193      */
11194     for (;;)
11195     {
11196         KU32 cbMsg = 0;
11197         int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
11198         if (rc == 0)
11199         {
11200             /* Make sure the message length is within sane bounds.  */
11201             if (   cbMsg > 4
11202                 && cbMsg <= 256*1024*1024)
11203             {
11204                 /* Reallocate the message buffer if necessary.  We add 4 zero bytes.  */
11205                 if (cbMsg + 4 <= cbMsgBuf)
11206                 { /* likely */ }
11207                 else
11208                 {
11209                     cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
11210                     pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
11211                     if (!pbMsgBuf)
11212                         return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
11213                 }
11214 
11215                 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
11216                 *(KU32 *)pbMsgBuf = cbMsg;
11217                 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
11218                 if (rc == 0)
11219                 {
11220                     const char *psz;
11221 
11222                     pbMsgBuf[cbMsg]     = '\0';
11223                     pbMsgBuf[cbMsg + 1] = '\0';
11224                     pbMsgBuf[cbMsg + 2] = '\0';
11225                     pbMsgBuf[cbMsg + 3] = '\0';
11226 
11227                     /* The first string after the header is the command. */
11228                     psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
11229                     if (strcmp(psz, "JOB") == 0)
11230                     {
11231                         struct
11232                         {
11233                             KI32 rcExitCode;
11234                             KU8  bExiting;
11235                             KU8  abZero[3];
11236                         } Reply;
11237                         Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
11238                         Reply.bExiting   = g_fRestart;
11239                         Reply.abZero[0]  = 0;
11240                         Reply.abZero[1]  = 0;
11241                         Reply.abZero[2]  = 0;
11242                         rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
11243                         if (   rc == 0
11244                             && !g_fRestart)
11245                         {
11246                             kwSandboxCleanupLate(&g_Sandbox);
11247                             continue;
11248                         }
11249                     }
11250                     else
11251                         rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
11252                 }
11253             }
11254             else
11255                 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
11256         }
11257 
11258         /*
11259          * If we're exitting because we're restarting, we need to delay till
11260          * kmk/kSubmit has read the result.  Windows documentation says it
11261          * immediately discards pipe buffers once the pipe is broken by the
11262          * server (us).  So, We flush the buffer and queues a 1 byte read
11263          * waiting for kSubmit to close the pipe when it receives the
11264          * bExiting = K_TRUE result.
11265          */
11266         if (g_fRestart)
11267         {
11268             KU8 b;
11269             FlushFileBuffers(hPipe);
11270             ReadFile(hPipe, &b, 1, &cbMsg, NULL);
11271         }
11272 
11273         CloseHandle(hPipe);
11274 #ifdef WITH_LOG_FILE
11275         if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
11276             CloseHandle(g_hLogFile);
11277 #endif
11278         if (getenv("KWORKER_STATS") != NULL)
11279             kwPrintStats();
11280         return rc > 0 ? 0 : 1;
11281     }
11282 }
11283 
11284 
11285 /** @page pg_kWorker    kSubmit / kWorker
11286  *
11287  * @section sec_kWorker_Motivation  Motivation / Inspiration
11288  *
11289  * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
11290  * builds on machines "infested" by Anti Virus protection and disk encryption
11291  * software.  Build times jumping from 35-40 min to 77-82 min after the machine
11292  * got "infected".
11293  *
11294  * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
11295  * mainly a bunch of tiny assembly and C files being compiler a million times.
11296  * As some of us OS/2 users maybe recalls, the Watcom make program can run its
11297  * own toolchain from within the same process, saving a lot of process creation
11298  * and teardown overhead.
11299  *
11300  *
11301  * @section sec_kWorker_kSubmit     About kSubmit
11302  *
11303  * When wanting to execute a job in a kWorker instance, it must be submitted
11304  * using the kmk_builtin_kSubmit command in kmk.  As the name suggest, this is
11305  * built into kmk and does not exist as an external program.  The reason for
11306  * this is that it keep track of the kWorker instances.
11307  *
11308  * The kSubmit command has the --32-bit and --64-bit options for selecting
11309  * between 32-bit and 64-bit worker instance.  We generally assume the user of
11310  * the command knows which bit count the executable has, so kSubmit is spared
11311  * the extra work of finding out.
11312  *
11313  * The kSubmit command shares a environment and current directory manipulation
11314  * with the kRedirect command, but not the file redirection.  So long no file
11315  * operation is involed, kSubmit is a drop in kRedirect replacement.  This is
11316  * hand for tools like OpenWatcom, NASM and YASM which all require environment
11317  * and/or current directory changes to work.
11318  *
11319  * Unlike the kRedirect command, the kSubmit command can also specify an
11320  * internall post command to be executed after the main command succeeds.
11321  * Currently only kmk_builtin_kDepObj is supported.  kDepObj gathers dependency
11322  * information from Microsoft COFF object files and Watcom OMF object files and
11323  * is scheduled to replace kDepIDB.
11324  *
11325  *
11326  * @section sec_kWorker_Interaction kSubmit / kWorker interaction
11327  *
11328  * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
11329  * A job request is written by kSubmit and kWorker read, unpacks it and executes
11330  * it.  When the job is completed, kWorker writes a short reply with the exit
11331  * code and an internal status indicating whether it is going to restart.
11332  *
11333  * The kWorker intance will reply to kSubmit before completing all the internal
11334  * cleanup work, so as not to delay the next job execution unnecessarily.  This
11335  * includes checking its own memory consumption and checking whether it needs
11336  * restarting.  So, a decision to restart unfortunately have to wait till after
11337  * the next job has completed.  This is a little bit unfortunate if the next job
11338  * requires a lot of memory and kWorker has already leaked/used a lot.
11339  *
11340  *
11341  * @section sec_kWorker_How_Works   How kWorker Works
11342  *
11343  * kWorker will load the executable specified by kSubmit into memory and call
11344  * it's entrypoint in a lightly sandbox'ed environment.
11345  *
11346  *
11347  * @subsection ssec_kWorker_Loaing      Image loading
11348  *
11349  * kWorker will manually load all the executable images into memory, fix them
11350  * up, and make a copy of the virgin image so it can be restored using memcpy
11351  * the next time it is used.
11352  *
11353  * Imported functions are monitored and replacements used for a few of them.
11354  * These replacements are serve the following purposes:
11355  *      - Provide a different command line.
11356  *      - Provide a different environment.
11357  *      - Intercept process termination.
11358  *      - Intercept thread creation (only linker is allowed to create threads).
11359  *      - Intercept file reading for caching (header files, ++) as file system
11360  *        access is made even slower by anti-virus software.
11361  *      - Intercept crypto hash APIs to cache MD5 digests of header files
11362  *        (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
11363  *      - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
11364  *        in memory as writing files grows expensive with encryption and
11365  *        anti-virus software active.
11366  *      - Intercept some file system queries to use the kFsCache instead of
11367  *        going to the kernel and slowly worm thru the AV filter driver.
11368  *      - Intercept standard output/error and console writes to aggressivly
11369  *        buffer the output.  The MS CRT does not buffer either when it goes to
11370  *        the console, resulting in terrible performance and mixing up output
11371  *        with other compile jobs.
11372  *        This also allows us to filter out the annoying source file announcements
11373  *        by cl.exe.
11374  *      - Intercept VirtualAlloc and VirtualFree to prevent
11375  *        CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
11376  *      - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
11377  *        the callbacks run after each job.
11378  *      - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
11379  *        executables and tools using custom heaps (like the microsoft linker).
11380  *        [exectuable images only]
11381  *      - Intercept atexit and _onexit registration to be able run them after
11382  *        each job instead of crashing as kWorker exits.  This also helps avoid
11383  *        some leaks. [executable image only]
11384  *
11385  * DLLs falls into two categories, system DLLs which we always load using the
11386  * native loader, and tool DLLs which can be handled like the executable or
11387  * optionally using the native loader.  We maintain a hardcoded white listing of
11388  * tool DLLs we trust to load using the native loader.
11389  *
11390  * Imports of natively loaded DLLs are processed too, but we only replace a
11391  * subset of the functions compared to natively loaded excutable and DLL images.
11392  *
11393  * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
11394  * This is to speed up job execution.
11395  *
11396  * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
11397  * for each job run, but so far this hasn't been necessary.
11398  *
11399  *
11400  * @subsection ssec_kWorker_Optimizing  Optimizing the Compiler
11401  *
11402  * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
11403  * files and uses a whole bunch of temporary files (in %TEMP%) for passing
11404  * intermediate representation between the first (c1/c1xx.dll) and second pass
11405  * (c2.dll).
11406  *
11407  * kWorker helps the compiler as best as it can.  Given a little knowledge about
11408  * stable and volatile file system areas, it can do a lot of caching that a
11409  * normal compiler driver cannot easily do when given a single file.
11410  *
11411  *
11412  * @subsubsection sssec_kWorker_Headers     Cache Headers Files and Searches
11413  *
11414  * The preprocessor part will open and process header files exactly as they are
11415  * encountered in the source files.  If string.h is included by the main source
11416  * and five other header files, it will be searched for (include path), opened,
11417  * read, MD5-summed, and pre-processed six times.  The last five times is just a
11418  * waste of time because of the guards or \#pragma once.  A smart compiler would
11419  * make a little extra effort and realize this.
11420  *
11421  * kWorker will cache help the preprocessor by remembering places where the
11422  * header was not found with help of kFsCache, and cache the file in memory when
11423  * found.  The first part is taken care of by intercepting GetFileAttributesW,
11424  * and the latter by intercepting CreateFileW, ReadFile and CloseFile.  Once
11425  * cached, the file is kept open and the CreateFileW call returns a duplicate of
11426  * that handle.  An internal handle table is used by ReadFile and CloseFile to
11427  * keep track of intercepted handles (also used for temporary file, temporary
11428  * file mappings, console buffering, and standard out/err buffering).
11429  *
11430  * PS. The header search optimization also comes in handy when cl.exe goes on
11431  *     thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
11432  *     c1/c1xx.dll and c2.dll.  My guess is that the compiler team can
11433  *     optionally compile the three pass DLLs as executables during development
11434  *     and problem analysis.
11435  *
11436  *
11437  * @subsubsection sssec_kWorker_Temp_Files  Temporary Files In Memory
11438  *
11439  * The issues of the temporary files is pretty severe on the Dell machine used
11440  * for benchmarking with full AV and encryption.  The synthetic benchmark
11441  * improved by 30% when kWorker implemented measures to keep them entirely in
11442  * memory.
11443  *
11444  * kWorker implement these by recognizing the filename pattern in CreateFileW
11445  * and creating/opening the given file as needed.  The handle returned is a
11446  * duplicate of the current process, thus giving us a good chance of catching
11447  * API calls we're not intercepting.
11448  *
11449  * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
11450  * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile.  The 2nd pass
11451  * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
11452  * UnmapViewOfFile.
11453  *
11454  *
11455  * @section sec_kWorker_Numbers     Some measurements.
11456  *
11457  *  - r2881 building src/VBox/Runtime:
11458  *     - without: 2m01.016388s = 120.016388 s
11459  *     - with:    1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
11460  *  - r2884 building vbox/debug (r110512):
11461  *     - without: 11m14.446609s = 674.446609 s
11462  *     - with:     9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
11463  *  - r2896 building vbox/debug (r110577):
11464  *     - with:     8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
11465  *  - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
11466  *     MS Defender as AV):
11467  *     - without: 10m24.990389s = 624.990389s
11468  *     - with:    08m04.738184s = 484.738184s
11469  *          - delta: 624.99s - 484.74s = 140.25s
11470  *          - saved: 140.25/624.99 = 22% faster
11471  *
11472  *
11473  * @subsection subsec_kWorker_Early_Numbers     Early Experiments
11474  *
11475  * These are some early experiments doing 1024 compilations of
11476  * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
11477  * main function:
11478  *
11479  * Skylake (W10/amd64, only stdandard MS defender):
11480  *  - cmd 1:  48    /1024 = 0x0 (0.046875)        [for /l %i in (1,1,1024) do ...]
11481  *  - kmk 1:  44    /1024 = 0x0 (0.04296875)      [all: ; 1024 x cl.exe]
11482  *  - run 1:  37    /1024 = 0x0 (0.0361328125)    [just process creation gain]
11483  *  - run 2:  34    /1024 = 0x0 (0.033203125)     [get file attribs]
11484  *  - run 3:  32.77 /1024 = 0x0 (0.032001953125)  [read caching of headers]
11485  *  - run 4:  32.67 /1024 = 0x0 (0.031904296875)  [loader tweaking]
11486  *  - run 5:  29.144/1024 = 0x0 (0.0284609375)    [with temp files in memory]
11487  *
11488  * Dell (W7/amd64, infected by mcafee):
11489  *  - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
11490  *  - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
11491  *  - run 2:  78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
11492  *
11493  * The command line:
11494  * @code{.cpp}
11495    "\"E:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/bin/amd64/cl.exe\" -c -c -TP -nologo -Zi -Zi -Zl -GR- -EHsc -GF -Zc:wchar_t- -Oy- -MT -W4 -Wall -wd4065 -wd4996 -wd4127 -wd4706 -wd4201 -wd4214 -wd4510 -wd4512 -wd4610 -wd4514 -wd4820 -wd4365 -wd4987 -wd4710 -wd4061 -wd4986 -wd4191 -wd4574 -wd4917 -wd4711 -wd4611 -wd4571 -wd4324 -wd4505 -wd4263 -wd4264 -wd4738 -wd4242 -wd4244 -WX -RTCsu -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/include -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/atlmfc/include -IE:/vbox/svn/trunk/tools/win.x86/sdk/v7.1/Include -IE:/vbox/svn/trunk/include -IE:/vbox/svn/trunk/out/win.amd64/debug -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/include -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/atlmfc/include -DVBOX -DVBOX_WITH_64_BITS_GUESTS -DVBOX_WITH_REM -DVBOX_WITH_RAW_MODE -DDEBUG -DDEBUG_bird -DDEBUG_USERNAME=bird -DRT_OS_WINDOWS -D__WIN__ -DRT_ARCH_AMD64 -D__AMD64__ -D__WIN64__ -DVBOX_WITH_DEBUGGER -DRT_LOCK_STRICT -DRT_LOCK_STRICT_ORDER -DIN_RING3 -DLOG_DISABLED -DIN_BLD_PROG -D_CRT_SECURE_NO_DEPRECATE -FdE:/vbox/svn/trunk/out/win.amd64/debug/obj/VBoxBs2Linker/VBoxBs2Linker-obj.pdb -FD -FoE:/vbox/svn/trunk/out/win.amd64/debug/obj/VBoxBs2Linker/VBoxBs2Linker.obj E:\\vbox\\svn\\trunk\\src\\VBox\\ValidationKit\\bootsectors\\VBoxBs2Linker.cpp"
11496  * @endcode
11497  */
11498 
11499