VirtualBox

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

Last change on this file since 3080 was 3080, checked in by bird, 7 years ago

kWorker: More details on exit code 43.

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