VirtualBox

source: kBuild/trunk/src/kWorker/kWorker.c@ 2965

Last change on this file since 2965 was 2965, checked in by bird, 8 years ago

kWorker: Need to reserve more space for large precompiled header files. sigh

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette