VirtualBox

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

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

kWorker: KWORKER_STATS env var triggers statistics to be dumped on exit.

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