VirtualBox

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

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

kWorker: More exit 43 details.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 425.6 KB
Line 
1/* $Id: kWorker.c 3089 2017-10-04 13:10:41Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <[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 (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
3247 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
3248 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
3249 }
3250 }
3251 else
3252 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
3253 return NULL;
3254}
3255
3256
3257
3258/*
3259 *
3260 * File system cache.
3261 * File system cache.
3262 * File system cache.
3263 *
3264 */
3265
3266
3267/**
3268 * This is for kDep.
3269 */
3270int kwFsPathExists(const char *pszPath)
3271{
3272 BirdTimeSpec_T TsIgnored;
3273 KFSLOOKUPERROR enmError;
3274 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3275 if (pFsObj)
3276 {
3277 kFsCacheObjRelease(g_pFsCache, pFsObj);
3278 return 1;
3279 }
3280 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
3281}
3282
3283
3284/* duplicated in dir-nt-bird.c */
3285void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
3286{
3287 KFSLOOKUPERROR enmError;
3288 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
3289 if (pPathObj)
3290 {
3291 KSIZE off = pPathObj->cchParent;
3292 if (off > 0)
3293 {
3294 KSIZE offEnd = off + pPathObj->cchName;
3295 if (offEnd < cbFull)
3296 {
3297 PKFSDIR pAncestor;
3298
3299 pszFull[off + pPathObj->cchName] = '\0';
3300 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
3301
3302 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3303 {
3304 kHlpAssert(off > 1);
3305 kHlpAssert(pAncestor != NULL);
3306 kHlpAssert(pAncestor->Obj.cchName > 0);
3307 pszFull[--off] = '/';
3308 off -= pAncestor->Obj.cchName;
3309 kHlpAssert(pAncestor->Obj.cchParent == off);
3310 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3311 }
3312 kFsCacheObjRelease(g_pFsCache, pPathObj);
3313 return;
3314 }
3315 }
3316 else
3317 {
3318 if ((size_t)pPathObj->cchName + 1 < cbFull)
3319 {
3320 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
3321 pszFull[pPathObj->cchName] = '/';
3322 pszFull[pPathObj->cchName + 1] = '\0';
3323
3324 kFsCacheObjRelease(g_pFsCache, pPathObj);
3325 return;
3326 }
3327 }
3328
3329 /* do fallback. */
3330 kHlpAssertFailed();
3331 kFsCacheObjRelease(g_pFsCache, pPathObj);
3332 }
3333
3334 nt_fullpath(pszPath, pszFull, cbFull);
3335}
3336
3337
3338/**
3339 * Helper for getting the extension of a UTF-16 path.
3340 *
3341 * @returns Pointer to the extension or the terminator.
3342 * @param pwszPath The path.
3343 * @param pcwcExt Where to return the length of the extension.
3344 */
3345static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
3346{
3347 wchar_t const *pwszName = pwszPath;
3348 wchar_t const *pwszExt = NULL;
3349 for (;;)
3350 {
3351 wchar_t const wc = *pwszPath++;
3352 if (wc == '.')
3353 pwszExt = pwszPath;
3354 else if (wc == '/' || wc == '\\' || wc == ':')
3355 {
3356 pwszName = pwszPath;
3357 pwszExt = NULL;
3358 }
3359 else if (wc == '\0')
3360 {
3361 if (pwszExt)
3362 {
3363 *pcwcExt = pwszPath - pwszExt - 1;
3364 return pwszExt;
3365 }
3366 *pcwcExt = 0;
3367 return pwszPath - 1;
3368 }
3369 }
3370}
3371
3372
3373
3374/**
3375 * Parses the argument string passed in as pszSrc.
3376 *
3377 * @returns size of the processed arguments.
3378 * @param pszSrc Pointer to the commandline that's to be parsed.
3379 * @param pcArgs Where to return the number of arguments.
3380 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
3381 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
3382 *
3383 * @remarks Lifted from startuphacks-win.c
3384 */
3385static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
3386{
3387 int bs;
3388 char chQuote;
3389 char *pfFlags;
3390 int cbArgs;
3391 int cArgs;
3392
3393#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
3394#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
3395#define WHITE(c) ((c) == ' ' || (c) == '\t')
3396
3397#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
3398#define _ARG_RESPONSE 0x02 /* Argument read from response file */
3399#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
3400#define _ARG_ENV 0x08 /* Argument from environment */
3401#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
3402
3403 cArgs = 0;
3404 cbArgs = 0;
3405
3406#if 0
3407 /* argv[0] */
3408 PUTC((char)_ARG_NONZERO);
3409 PUTV;
3410 for (;;)
3411 {
3412 PUTC(*pszSrc);
3413 if (*pszSrc == 0)
3414 break;
3415 ++pszSrc;
3416 }
3417 ++pszSrc;
3418#endif
3419
3420 for (;;)
3421 {
3422 while (WHITE(*pszSrc))
3423 ++pszSrc;
3424 if (*pszSrc == 0)
3425 break;
3426 pfFlags = pchPool;
3427 PUTC((char)_ARG_NONZERO);
3428 PUTV;
3429 bs = 0; chQuote = 0;
3430 for (;;)
3431 {
3432 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
3433 {
3434 while (bs >= 2)
3435 {
3436 PUTC('\\');
3437 bs -= 2;
3438 }
3439 if (bs & 1)
3440 PUTC(*pszSrc);
3441 else
3442 {
3443 chQuote = chQuote ? 0 : *pszSrc;
3444 if (pfFlags != NULL)
3445 *pfFlags |= _ARG_DQUOTE;
3446 }
3447 bs = 0;
3448 }
3449 else if (*pszSrc == '\\')
3450 ++bs;
3451 else
3452 {
3453 while (bs != 0)
3454 {
3455 PUTC('\\');
3456 --bs;
3457 }
3458 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
3459 break;
3460 PUTC(*pszSrc);
3461 }
3462 ++pszSrc;
3463 }
3464 PUTC(0);
3465 }
3466
3467 *pcArgs = cArgs;
3468 return cbArgs;
3469}
3470
3471
3472
3473
3474/*
3475 *
3476 * Process and thread related APIs.
3477 * Process and thread related APIs.
3478 * Process and thread related APIs.
3479 *
3480 */
3481
3482/** Common worker for ExitProcess(), exit() and friends. */
3483static void WINAPI kwSandboxDoExit(int uExitCode)
3484{
3485 if (g_Sandbox.idMainThread == GetCurrentThreadId())
3486 {
3487 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
3488
3489 g_Sandbox.rcExitCode = (int)uExitCode;
3490
3491 /* Before we jump, restore the TIB as we're not interested in any
3492 exception chain stuff installed by the sandboxed executable. */
3493 *pTib = g_Sandbox.TibMainThread;
3494 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
3495
3496 longjmp(g_Sandbox.JmpBuf, 1);
3497 }
3498 KWFS_TODO();
3499}
3500
3501
3502/** ExitProcess replacement. */
3503static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
3504{
3505 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
3506 kwSandboxDoExit((int)uExitCode);
3507}
3508
3509
3510/** ExitProcess replacement. */
3511static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
3512{
3513 if (hProcess == GetCurrentProcess())
3514 kwSandboxDoExit(uExitCode);
3515 KWFS_TODO();
3516 return TerminateProcess(hProcess, uExitCode);
3517}
3518
3519
3520/** Normal CRT exit(). */
3521static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
3522{
3523 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
3524 kwSandboxDoExit(rcExitCode);
3525}
3526
3527
3528/** Quick CRT _exit(). */
3529static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
3530{
3531 /* Quick. */
3532 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
3533 kwSandboxDoExit(rcExitCode);
3534}
3535
3536
3537/** Return to caller CRT _cexit(). */
3538static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
3539{
3540 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
3541 kwSandboxDoExit(rcExitCode);
3542}
3543
3544
3545/** Quick return to caller CRT _c_exit(). */
3546static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
3547{
3548 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
3549 kwSandboxDoExit(rcExitCode);
3550}
3551
3552
3553/** Runtime error and exit _amsg_exit(). */
3554static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
3555{
3556 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
3557 kwSandboxDoExit(255);
3558}
3559
3560
3561/** CRT - terminate(). */
3562static void __cdecl kwSandbox_msvcrt_terminate(void)
3563{
3564 KW_LOG(("\nRuntime - terminate!\n"));
3565 kwSandboxDoExit(254);
3566}
3567
3568
3569/** CRT - _onexit */
3570static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
3571{
3572 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3573 {
3574 PKWEXITCALLACK pCallback;
3575 KW_LOG(("_onexit(%p)\n", pfnFunc));
3576 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3577
3578 pCallback = kHlpAlloc(sizeof(*pCallback));
3579 if (pCallback)
3580 {
3581 pCallback->pfnCallback = pfnFunc;
3582 pCallback->fAtExit = K_FALSE;
3583 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3584 g_Sandbox.pExitCallbackHead = pCallback;
3585 return pfnFunc;
3586 }
3587 return NULL;
3588 }
3589 KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
3590 return pfnFunc;
3591}
3592
3593
3594/** CRT - atexit */
3595static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
3596{
3597 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3598 {
3599 PKWEXITCALLACK pCallback;
3600 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3601 KW_LOG(("atexit(%p)\n", pfnFunc));
3602
3603 pCallback = kHlpAlloc(sizeof(*pCallback));
3604 if (pCallback)
3605 {
3606 pCallback->pfnCallback = (_onexit_t)pfnFunc;
3607 pCallback->fAtExit = K_TRUE;
3608 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3609 g_Sandbox.pExitCallbackHead = pCallback;
3610 return 0;
3611 }
3612 return -1;
3613 }
3614 KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
3615 return 0;
3616}
3617
3618
3619/** Kernel32 - SetConsoleCtrlHandler(). */
3620static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
3621{
3622 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
3623 return TRUE;
3624}
3625
3626
3627/** The CRT internal __getmainargs() API. */
3628static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
3629 int dowildcard, int const *piNewMode)
3630{
3631 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3632 *pargc = g_Sandbox.cArgs;
3633 *pargv = g_Sandbox.papszArgs;
3634 *penvp = g_Sandbox.environ;
3635
3636 /** @todo startinfo points at a newmode (setmode) value. */
3637 return 0;
3638}
3639
3640
3641/** The CRT internal __wgetmainargs() API. */
3642static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
3643 int dowildcard, int const *piNewMode)
3644{
3645 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3646 *pargc = g_Sandbox.cArgs;
3647 *pargv = g_Sandbox.papwszArgs;
3648 *penvp = g_Sandbox.wenviron;
3649
3650 /** @todo startinfo points at a newmode (setmode) value. */
3651 return 0;
3652}
3653
3654
3655
3656/** Kernel32 - GetCommandLineA() */
3657static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
3658{
3659 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3660 return g_Sandbox.pszCmdLine;
3661}
3662
3663
3664/** Kernel32 - GetCommandLineW() */
3665static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
3666{
3667 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3668 return g_Sandbox.pwszCmdLine;
3669}
3670
3671
3672/** Kernel32 - GetStartupInfoA() */
3673static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
3674{
3675 KW_LOG(("GetStartupInfoA\n"));
3676 GetStartupInfoA(pStartupInfo);
3677 pStartupInfo->lpReserved = NULL;
3678 pStartupInfo->lpTitle = NULL;
3679 pStartupInfo->lpReserved2 = NULL;
3680 pStartupInfo->cbReserved2 = 0;
3681}
3682
3683
3684/** Kernel32 - GetStartupInfoW() */
3685static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
3686{
3687 KW_LOG(("GetStartupInfoW\n"));
3688 GetStartupInfoW(pStartupInfo);
3689 pStartupInfo->lpReserved = NULL;
3690 pStartupInfo->lpTitle = NULL;
3691 pStartupInfo->lpReserved2 = NULL;
3692 pStartupInfo->cbReserved2 = 0;
3693}
3694
3695
3696/** CRT - __p___argc(). */
3697static int * __cdecl kwSandbox_msvcrt___p___argc(void)
3698{
3699 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3700 return &g_Sandbox.cArgs;
3701}
3702
3703
3704/** CRT - __p___argv(). */
3705static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
3706{
3707 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3708 return &g_Sandbox.papszArgs;
3709}
3710
3711
3712/** CRT - __p___sargv(). */
3713static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
3714{
3715 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3716 return &g_Sandbox.papwszArgs;
3717}
3718
3719
3720/** CRT - __p__acmdln(). */
3721static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
3722{
3723 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3724 return (char **)&g_Sandbox.pszCmdLine;
3725}
3726
3727
3728/** CRT - __p__acmdln(). */
3729static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
3730{
3731 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3732 return &g_Sandbox.pwszCmdLine;
3733}
3734
3735
3736/** CRT - __p__pgmptr(). */
3737static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
3738{
3739 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3740 return &g_Sandbox.pgmptr;
3741}
3742
3743
3744/** CRT - __p__wpgmptr(). */
3745static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
3746{
3747 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3748 return &g_Sandbox.wpgmptr;
3749}
3750
3751
3752/** CRT - _get_pgmptr(). */
3753static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
3754{
3755 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3756 *ppszValue = g_Sandbox.pgmptr;
3757 return 0;
3758}
3759
3760
3761/** CRT - _get_wpgmptr(). */
3762static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
3763{
3764 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3765 *ppwszValue = g_Sandbox.wpgmptr;
3766 return 0;
3767}
3768
3769/** Just in case. */
3770static void kwSandbox_msvcrt__wincmdln(void)
3771{
3772 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3773 KWFS_TODO();
3774}
3775
3776
3777/** Just in case. */
3778static void kwSandbox_msvcrt__wwincmdln(void)
3779{
3780 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3781 KWFS_TODO();
3782}
3783
3784/** CreateThread interceptor. */
3785static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
3786 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
3787 DWORD fFlags, PDWORD pidThread)
3788{
3789 HANDLE hThread = NULL;
3790 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
3791 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
3792 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3793 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3794 {
3795 /* Allow link::DbgThread. */
3796 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
3797 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
3798 }
3799 else
3800 KWFS_TODO();
3801 return hThread;
3802}
3803
3804
3805/** _beginthread - create a new thread. */
3806static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
3807{
3808 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3809 KWFS_TODO();
3810 return 0;
3811}
3812
3813
3814/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
3815static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
3816 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
3817 unsigned fCreate, unsigned *pidThread)
3818{
3819 /*
3820 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
3821 * whatever it needs to.
3822 */
3823 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
3824 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
3825 pfnThreadProc, pvUser, fCreate, pidThread));
3826 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
3827 {
3828 uintptr_t rcRet;
3829 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
3830 if (!s_pfnReal)
3831 {
3832 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
3833 if (!s_pfnReal)
3834 {
3835 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
3836 __debugbreak();
3837 }
3838 }
3839 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
3840 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
3841 return rcRet;
3842 }
3843
3844 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3845 KWFS_TODO();
3846 return 0;
3847}
3848
3849
3850/** _beginthreadex - create a new thread. */
3851static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex(void *pvSecAttr, unsigned cbStack,
3852 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
3853 unsigned fCreate, unsigned *pidThread)
3854{
3855 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3856 KWFS_TODO();
3857 return 0;
3858}
3859
3860
3861/*
3862 *
3863 * Environment related APIs.
3864 * Environment related APIs.
3865 * Environment related APIs.
3866 *
3867 */
3868
3869/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
3870static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
3871{
3872 char *pszzEnv;
3873 char *pszCur;
3874 KSIZE cbNeeded = 1;
3875 KSIZE iVar = 0;
3876
3877 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3878
3879 /* Figure how space much we need first. */
3880 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
3881 cbNeeded += kHlpStrLen(pszCur) + 1;
3882
3883 /* Allocate it. */
3884 pszzEnv = kHlpAlloc(cbNeeded);
3885 if (pszzEnv)
3886 {
3887 char *psz = pszzEnv;
3888 iVar = 0;
3889 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
3890 {
3891 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
3892 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
3893 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
3894 }
3895 *psz++ = '\0';
3896 kHlpAssert(psz - pszzEnv == cbNeeded);
3897 }
3898
3899 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
3900#if 0
3901 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
3902 pszCur = pszzEnv;
3903 iVar = 0;
3904 while (*pszCur)
3905 {
3906 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
3907 iVar++;
3908 pszCur += kHlpStrLen(pszCur) + 1;
3909 }
3910 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
3911 pszCur++;
3912 fprintf(stderr, "ended at %p, after %u bytes (exepcted %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
3913#endif
3914 return pszzEnv;
3915}
3916
3917
3918/** Kernel32 - GetEnvironmentStrings */
3919static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
3920{
3921 KW_LOG(("GetEnvironmentStrings!\n"));
3922 return kwSandbox_Kernel32_GetEnvironmentStringsA();
3923}
3924
3925
3926/** Kernel32 - GetEnvironmentStringsW */
3927static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
3928{
3929 wchar_t *pwszzEnv;
3930 wchar_t *pwszCur;
3931 KSIZE cwcNeeded = 1;
3932 KSIZE iVar = 0;
3933
3934 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3935
3936 /* Figure how space much we need first. */
3937 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
3938 cwcNeeded += kwUtf16Len(pwszCur) + 1;
3939
3940 /* Allocate it. */
3941 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
3942 if (pwszzEnv)
3943 {
3944 wchar_t *pwsz = pwszzEnv;
3945 iVar = 0;
3946 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
3947 {
3948 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
3949 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
3950 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
3951 }
3952 *pwsz++ = '\0';
3953 kHlpAssert(pwsz - pwszzEnv == cwcNeeded);
3954 }
3955
3956 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
3957 return pwszzEnv;
3958}
3959
3960
3961/** Kernel32 - FreeEnvironmentStringsA */
3962static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
3963{
3964 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
3965 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3966 kHlpFree(pszzEnv);
3967 return TRUE;
3968}
3969
3970
3971/** Kernel32 - FreeEnvironmentStringsW */
3972static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
3973{
3974 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
3975 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3976 kHlpFree(pwszzEnv);
3977 return TRUE;
3978}
3979
3980
3981/**
3982 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
3983 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
3984 *
3985 * @returns 0 on success, non-zero on failure.
3986 * @param pSandbox The sandbox.
3987 * @param cMin Minimum size, including terminator.
3988 */
3989static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
3990{
3991 void *pvNew;
3992 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
3993 KSIZE cNew = cOld + 256;
3994 while (cNew < cMin)
3995 cNew += 256;
3996
3997 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
3998 if (pvNew)
3999 {
4000 pSandbox->environ = (char **)pvNew;
4001 pSandbox->environ[cOld] = NULL;
4002
4003 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4004 if (pvNew)
4005 {
4006 pSandbox->papszEnvVars = (char **)pvNew;
4007 pSandbox->papszEnvVars[cOld] = NULL;
4008
4009 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4010 if (pvNew)
4011 {
4012 pSandbox->wenviron = (wchar_t **)pvNew;
4013 pSandbox->wenviron[cOld] = NULL;
4014
4015 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4016 if (pvNew)
4017 {
4018 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4019 pSandbox->papwszEnvVars[cOld] = NULL;
4020
4021 pSandbox->cEnvVarsAllocated = cNew;
4022 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4023 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4024 return 0;
4025 }
4026 }
4027 }
4028 }
4029 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4030 return KERR_NO_MEMORY;
4031}
4032
4033
4034/**
4035 * Sets an environment variable, ANSI style.
4036 *
4037 * @returns 0 on success, non-zero on failure.
4038 * @param pSandbox The sandbox.
4039 * @param pchVar The variable name.
4040 * @param cchVar The length of the name.
4041 * @param pszValue The value.
4042 */
4043static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4044{
4045 /* Allocate and construct the new strings. */
4046 KSIZE cchTmp = kHlpStrLen(pszValue);
4047 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4048 if (pszNew)
4049 {
4050 wchar_t *pwszNew;
4051 kHlpMemCopy(pszNew, pchVar, cchVar);
4052 pszNew[cchVar] = '=';
4053 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4054 cchTmp += cchVar + 1;
4055 pszNew[cchTmp] = '\0';
4056
4057 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4058 if (pwszNew)
4059 {
4060 /* Look it up. */
4061 KSIZE iVar = 0;
4062 char *pszEnv;
4063 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4064 {
4065 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4066 && pszEnv[cchVar] == '=')
4067 {
4068 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4069 " iVar=%d: %p='%s' and %p='%ls'\n",
4070 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4071 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4072 iVar, pszNew, pszNew, pwszNew, pwszNew));
4073
4074 kHlpFree(pSandbox->papszEnvVars[iVar]);
4075 pSandbox->papszEnvVars[iVar] = pszNew;
4076 pSandbox->environ[iVar] = pszNew;
4077
4078 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4079 pSandbox->papwszEnvVars[iVar] = pwszNew;
4080 pSandbox->wenviron[iVar] = pwszNew;
4081 return 0;
4082 }
4083 iVar++;
4084 }
4085
4086 /* Not found, do we need to grow the table first? */
4087 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4088 kwSandboxGrowEnv(pSandbox, iVar + 2);
4089 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4090 {
4091 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4092
4093 pSandbox->papszEnvVars[iVar + 1] = NULL;
4094 pSandbox->papszEnvVars[iVar] = pszNew;
4095 pSandbox->environ[iVar + 1] = NULL;
4096 pSandbox->environ[iVar] = pszNew;
4097
4098 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4099 pSandbox->papwszEnvVars[iVar] = pwszNew;
4100 pSandbox->wenviron[iVar + 1] = NULL;
4101 pSandbox->wenviron[iVar] = pwszNew;
4102 return 0;
4103 }
4104
4105 kHlpFree(pwszNew);
4106 }
4107 kHlpFree(pszNew);
4108 }
4109 KW_LOG(("Out of memory!\n"));
4110 return 0;
4111}
4112
4113
4114/**
4115 * Sets an environment variable, UTF-16 style.
4116 *
4117 * @returns 0 on success, non-zero on failure.
4118 * @param pSandbox The sandbox.
4119 * @param pwcVar The variable name.
4120 * @param cwcVar The length of the name.
4121 * @param pwszValue The value.
4122 */
4123static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4124{
4125 /* Allocate and construct the new strings. */
4126 KSIZE cwcTmp = kwUtf16Len(pwszValue);
4127 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4128 if (pwszNew)
4129 {
4130 char *pszNew;
4131 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4132 pwszNew[cwcVar] = '=';
4133 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4134 cwcTmp += cwcVar + 1;
4135 pwszNew[cwcVar] = '\0';
4136
4137 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4138 if (pszNew)
4139 {
4140 /* Look it up. */
4141 KSIZE iVar = 0;
4142 wchar_t *pwszEnv;
4143 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4144 {
4145 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4146 && pwszEnv[cwcVar] == '=')
4147 {
4148 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4149 " iVar=%d: %p='%s' and %p='%ls'\n",
4150 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4151 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4152 iVar, pszNew, pszNew, pwszNew, pwszNew));
4153
4154 kHlpFree(pSandbox->papszEnvVars[iVar]);
4155 pSandbox->papszEnvVars[iVar] = pszNew;
4156 pSandbox->environ[iVar] = pszNew;
4157
4158 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4159 pSandbox->papwszEnvVars[iVar] = pwszNew;
4160 pSandbox->wenviron[iVar] = pwszNew;
4161 return 0;
4162 }
4163 iVar++;
4164 }
4165
4166 /* Not found, do we need to grow the table first? */
4167 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4168 kwSandboxGrowEnv(pSandbox, iVar + 2);
4169 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4170 {
4171 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4172
4173 pSandbox->papszEnvVars[iVar + 1] = NULL;
4174 pSandbox->papszEnvVars[iVar] = pszNew;
4175 pSandbox->environ[iVar + 1] = NULL;
4176 pSandbox->environ[iVar] = pszNew;
4177
4178 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4179 pSandbox->papwszEnvVars[iVar] = pwszNew;
4180 pSandbox->wenviron[iVar + 1] = NULL;
4181 pSandbox->wenviron[iVar] = pwszNew;
4182 return 0;
4183 }
4184
4185 kHlpFree(pwszNew);
4186 }
4187 kHlpFree(pszNew);
4188 }
4189 KW_LOG(("Out of memory!\n"));
4190 return 0;
4191}
4192
4193
4194/** ANSI unsetenv worker. */
4195static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4196{
4197 KSIZE iVar = 0;
4198 char *pszEnv;
4199 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4200 {
4201 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4202 && pszEnv[cchVar] == '=')
4203 {
4204 KSIZE cVars = iVar;
4205 while (pSandbox->papszEnvVars[cVars])
4206 cVars++;
4207 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4208 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4209
4210 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4211 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4212 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4213
4214 kHlpFree(pSandbox->papszEnvVars[iVar]);
4215 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4216 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4217 pSandbox->papszEnvVars[cVars] = NULL;
4218 pSandbox->environ[cVars] = NULL;
4219
4220 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4221 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4222 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4223 pSandbox->papwszEnvVars[cVars] = NULL;
4224 pSandbox->wenviron[cVars] = NULL;
4225 return 0;
4226 }
4227 iVar++;
4228 }
4229 return KERR_ENVVAR_NOT_FOUND;
4230}
4231
4232
4233/** UTF-16 unsetenv worker. */
4234static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4235{
4236 KSIZE iVar = 0;
4237 wchar_t *pwszEnv;
4238 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4239 {
4240 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4241 && pwszEnv[cwcVar] == '=')
4242 {
4243 KSIZE cVars = iVar;
4244 while (pSandbox->papwszEnvVars[cVars])
4245 cVars++;
4246 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
4247 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
4248
4249 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4250 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4251 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4252
4253 kHlpFree(pSandbox->papszEnvVars[iVar]);
4254 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4255 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4256 pSandbox->papszEnvVars[cVars] = NULL;
4257 pSandbox->environ[cVars] = NULL;
4258
4259 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4260 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4261 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4262 pSandbox->papwszEnvVars[cVars] = NULL;
4263 pSandbox->wenviron[cVars] = NULL;
4264 return 0;
4265 }
4266 iVar++;
4267 }
4268 return KERR_ENVVAR_NOT_FOUND;
4269}
4270
4271
4272
4273/** ANSI getenv worker. */
4274static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4275{
4276 KSIZE iVar = 0;
4277 char *pszEnv;
4278 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
4279 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4280 && pszEnv[cchVar] == '=')
4281 return &pszEnv[cchVar + 1];
4282 return NULL;
4283}
4284
4285
4286/** UTF-16 getenv worker. */
4287static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4288{
4289 KSIZE iVar = 0;
4290 wchar_t *pwszEnv;
4291 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
4292 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4293 && pwszEnv[cwcVar] == '=')
4294 return &pwszEnv[cwcVar + 1];
4295 return NULL;
4296}
4297
4298
4299/** Kernel32 - GetEnvironmentVariableA() */
4300static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
4301{
4302 char *pszFoundValue;
4303 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4304
4305 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4306 if (pszFoundValue)
4307 {
4308 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
4309 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
4310 return cchRet;
4311 }
4312 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
4313 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4314 return 0;
4315}
4316
4317
4318/** Kernel32 - GetEnvironmentVariableW() */
4319static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
4320{
4321 wchar_t *pwszFoundValue;
4322 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4323
4324 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4325 if (pwszFoundValue)
4326 {
4327 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
4328 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
4329 return cchRet;
4330 }
4331 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
4332 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4333 return 0;
4334}
4335
4336
4337/** Kernel32 - SetEnvironmentVariableA() */
4338static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
4339{
4340 int rc;
4341 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4342
4343 if (pszValue)
4344 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4345 else
4346 {
4347 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4348 rc = 0; //??
4349 }
4350 if (rc == 0)
4351 {
4352 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
4353 return TRUE;
4354 }
4355 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4356 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
4357 return FALSE;
4358}
4359
4360
4361/** Kernel32 - SetEnvironmentVariableW() */
4362static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
4363{
4364 int rc;
4365 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4366
4367 if (pwszValue)
4368 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4369 else
4370 {
4371 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4372 rc = 0; //??
4373 }
4374 if (rc == 0)
4375 {
4376 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
4377 return TRUE;
4378 }
4379 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4380 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
4381 return FALSE;
4382}
4383
4384
4385/** Kernel32 - ExpandEnvironmentStringsA() */
4386static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
4387{
4388 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4389 KWFS_TODO();
4390 return 0;
4391}
4392
4393
4394/** Kernel32 - ExpandEnvironmentStringsW() */
4395static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
4396{
4397 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4398 KWFS_TODO();
4399 return 0;
4400}
4401
4402
4403/** CRT - _putenv(). */
4404static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
4405{
4406 int rc;
4407 char const *pszEqual;
4408 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4409
4410 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
4411 if (pszEqual)
4412 {
4413 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
4414 if (rc == 0)
4415 { }
4416 else
4417 rc = -1;
4418 }
4419 else
4420 {
4421 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
4422 rc = 0;
4423 }
4424 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
4425 return rc;
4426}
4427
4428
4429/** CRT - _wputenv(). */
4430static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
4431{
4432 int rc;
4433 wchar_t const *pwszEqual;
4434 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4435
4436 pwszEqual = wcschr(pwszVarEqualValue, '=');
4437 if (pwszEqual)
4438 {
4439 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
4440 if (rc == 0)
4441 { }
4442 else
4443 rc = -1;
4444 }
4445 else
4446 {
4447 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
4448 rc = 0;
4449 }
4450 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
4451 return rc;
4452}
4453
4454
4455/** CRT - _putenv_s(). */
4456static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
4457{
4458 char const *pszEqual;
4459 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4460
4461 pszEqual = kHlpStrChr(pszVar, '=');
4462 if (pszEqual == NULL)
4463 {
4464 if (pszValue)
4465 {
4466 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4467 if (rc == 0)
4468 {
4469 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
4470 return 0;
4471 }
4472 }
4473 else
4474 {
4475 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4476 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
4477 return 0;
4478 }
4479 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
4480 return ENOMEM;
4481 }
4482 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
4483 return EINVAL;
4484}
4485
4486
4487/** CRT - _wputenv_s(). */
4488static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
4489{
4490 wchar_t const *pwszEqual;
4491 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4492
4493 pwszEqual = wcschr(pwszVar, '=');
4494 if (pwszEqual == NULL)
4495 {
4496 if (pwszValue)
4497 {
4498 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4499 if (rc == 0)
4500 {
4501 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
4502 return 0;
4503 }
4504 }
4505 else
4506 {
4507 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4508 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
4509 return 0;
4510 }
4511 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
4512 return ENOMEM;
4513 }
4514 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
4515 return EINVAL;
4516}
4517
4518
4519/** CRT - get pointer to the __initenv variable (initial environment). */
4520static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
4521{
4522 KW_LOG(("__p___initenv\n"));
4523 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4524 KWFS_TODO();
4525 return &g_Sandbox.initenv;
4526}
4527
4528
4529/** CRT - get pointer to the __winitenv variable (initial environment). */
4530static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
4531{
4532 KW_LOG(("__p___winitenv\n"));
4533 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4534 KWFS_TODO();
4535 return &g_Sandbox.winitenv;
4536}
4537
4538
4539/** CRT - get pointer to the _environ variable (current environment). */
4540static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
4541{
4542 KW_LOG(("__p__environ\n"));
4543 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4544 return &g_Sandbox.environ;
4545}
4546
4547
4548/** CRT - get pointer to the _wenviron variable (current environment). */
4549static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
4550{
4551 KW_LOG(("__p__wenviron\n"));
4552 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4553 return &g_Sandbox.wenviron;
4554}
4555
4556
4557/** CRT - get the _environ variable (current environment).
4558 * @remarks Not documented or prototyped? */
4559static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
4560{
4561 KWFS_TODO(); /** @todo check the callers expectations! */
4562 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4563 *ppapszEnviron = g_Sandbox.environ;
4564 return 0;
4565}
4566
4567
4568/** CRT - get the _wenviron variable (current environment).
4569 * @remarks Not documented or prototyped? */
4570static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
4571{
4572 KWFS_TODO(); /** @todo check the callers expectations! */
4573 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4574 *ppapwszEnviron = g_Sandbox.wenviron;
4575 return 0;
4576}
4577
4578
4579
4580/*
4581 *
4582 * Loader related APIs
4583 * Loader related APIs
4584 * Loader related APIs
4585 *
4586 */
4587
4588/**
4589 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
4590 */
4591static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
4592{
4593 /* Load it first. */
4594 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
4595 if (hmod)
4596 {
4597 pDynLoad->hmod = hmod;
4598 pDynLoad->pMod = NULL; /* indicates special */
4599
4600 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
4601 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
4602 KW_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
4603 }
4604 else
4605 kHlpFree(pDynLoad);
4606 return hmod;
4607}
4608
4609
4610/**
4611 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
4612 */
4613static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
4614{
4615 HMODULE hmod;
4616 PKWMODULE pMod;
4617 KU32 uHashPath;
4618 KSIZE idxHash;
4619 char szNormPath[256];
4620 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
4621
4622 /*
4623 * Lower case it.
4624 */
4625 if (cbFilename <= sizeof(szNormPath))
4626 {
4627 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
4628 _strlwr(szNormPath);
4629 }
4630 else
4631 {
4632 SetLastError(ERROR_FILENAME_EXCED_RANGE);
4633 return NULL;
4634 }
4635
4636 /*
4637 * Check if it has already been loaded so we don't create an unnecessary
4638 * loader module for it.
4639 */
4640 uHashPath = kwStrHash(szNormPath);
4641 idxHash = uHashPath % K_ELEMENTS(g_apModules);
4642 pMod = g_apModules[idxHash];
4643 if (pMod)
4644 {
4645 do
4646 {
4647 if ( pMod->uHashPath == uHashPath
4648 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
4649 {
4650 pDynLoad->pMod = kwLdrModuleRetain(pMod);
4651 pDynLoad->hmod = pMod->hOurMod;
4652
4653 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
4654 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
4655 KW_LOG(("LoadLibraryExA(%s,,) -> %p [already loaded]\n", pDynLoad->szRequest, pDynLoad->hmod));
4656 return pDynLoad->hmod;
4657 }
4658 pMod = pMod->pNext;
4659 } while (pMod);
4660 }
4661
4662
4663 /*
4664 * Try load it and make a kLdr module for it.
4665 */
4666 hmod = LoadLibraryExA(szNormPath, NULL /*hFile*/, fFlags);
4667 if (hmod)
4668 {
4669 PKLDRMOD pLdrMod;
4670 int rc = kLdrModOpenNativeByHandle((KUPTR)hmod, &pLdrMod);
4671 if (rc == 0)
4672 {
4673 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cbFilename, uHashPath,
4674 K_FALSE /*fDoReplacements*/);
4675 if (pMod)
4676 {
4677 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
4678
4679 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cbFilename + cbFilename * sizeof(wchar_t));
4680 if (pDynLoad)
4681 {
4682 pDynLoad->pMod = pMod;
4683 pDynLoad->hmod = hmod;
4684
4685 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
4686 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
4687 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
4688 return hmod;
4689 }
4690
4691 KWFS_TODO();
4692 }
4693 else
4694 KWFS_TODO();
4695 }
4696 else
4697 KWFS_TODO();
4698 }
4699 kHlpFree(pDynLoad);
4700 return hmod;
4701}
4702
4703
4704/** Kernel32 - LoadLibraryExA() */
4705static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
4706{
4707 KSIZE cchFilename = kHlpStrLen(pszFilename);
4708 const char *pszSearchPath;
4709 PKWDYNLOAD pDynLoad;
4710 PKWMODULE pMod;
4711 int rc;
4712 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4713
4714 /*
4715 * Deal with a couple of extremely unlikely special cases right away.
4716 */
4717 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
4718 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
4719 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
4720 { /* likely */ }
4721 else
4722 {
4723 KWFS_TODO();
4724 return LoadLibraryExA(pszFilename, hFile, fFlags);
4725 }
4726
4727 /*
4728 * Check if we've already got a dynload entry for this one.
4729 */
4730 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
4731 if ( pDynLoad->cchRequest == cchFilename
4732 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
4733 {
4734 if (pDynLoad->pMod)
4735 rc = kwLdrModuleInitTree(pDynLoad->pMod);
4736 else
4737 rc = 0;
4738 if (rc == 0)
4739 {
4740 KW_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
4741 return pDynLoad->hmod;
4742 }
4743 SetLastError(ERROR_DLL_INIT_FAILED);
4744 return NULL;
4745 }
4746
4747 /*
4748 * Allocate a dynload entry for the request.
4749 */
4750 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
4751 if (pDynLoad)
4752 {
4753 pDynLoad->cchRequest = cchFilename;
4754 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
4755 }
4756 else
4757 {
4758 KW_LOG(("LoadLibraryExA: Out of memory!\n"));
4759 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4760 return NULL;
4761 }
4762
4763 /*
4764 * Deal with resource / data DLLs.
4765 */
4766 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
4767 | LOAD_LIBRARY_AS_DATAFILE
4768 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
4769 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
4770 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
4771
4772 /*
4773 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
4774 */
4775 if ( strnicmp(pszFilename, TUPLE("api-ms-")) == 0
4776 && kHlpIsFilenameOnly(pszFilename))
4777 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
4778
4779 /*
4780 * Normal library loading.
4781 * We start by being very lazy and reusing the code for resolving imports.
4782 */
4783 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
4784 if (!kHlpIsFilenameOnly(pszFilename))
4785 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
4786 else
4787 {
4788 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
4789 if (rc != 0)
4790 pMod = NULL;
4791 }
4792 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
4793 {
4794 /* Enter it into the tool module table and dynamic link request cache. */
4795 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
4796
4797 pDynLoad->pMod = pMod;
4798 pDynLoad->hmod = pMod->hOurMod;
4799
4800 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
4801 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
4802
4803 /*
4804 * Make sure it's initialized (need to link it first since DllMain may
4805 * use loader APIs).
4806 */
4807 rc = kwLdrModuleInitTree(pMod);
4808 if (rc == 0)
4809 {
4810 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
4811 return pDynLoad->hmod;
4812 }
4813
4814 SetLastError(ERROR_DLL_INIT_FAILED);
4815 }
4816 else
4817 {
4818 KWFS_TODO();
4819 kHlpFree(pDynLoad);
4820 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
4821 }
4822 return NULL;
4823}
4824
4825
4826/** Kernel32 - LoadLibraryExA() for native overloads */
4827static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
4828{
4829 char szPath[1024];
4830 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
4831
4832 /*
4833 * We may have to help resolved unqualified DLLs living in the executable directory.
4834 */
4835 if (kHlpIsFilenameOnly(pszFilename))
4836 {
4837 KSIZE cchFilename = kHlpStrLen(pszFilename);
4838 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
4839 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
4840 {
4841 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
4842 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
4843 if (kwFsPathExists(szPath))
4844 {
4845 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
4846 pszFilename = szPath;
4847 }
4848 }
4849
4850 if (pszFilename != szPath)
4851 {
4852 KSIZE cchSuffix = 0;
4853 KBOOL fNeedSuffix = K_FALSE;
4854 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
4855 while (*pszCur != '\0')
4856 {
4857 /* Find the end of the component */
4858 KSIZE cch = 0;
4859 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
4860 cch++;
4861
4862 if ( cch > 0 /* wrong, but whatever */
4863 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
4864 {
4865 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
4866 if ( szPath[cch - 1] != ':'
4867 && szPath[cch - 1] != '/'
4868 && szPath[cch - 1] != '\\')
4869 *pszDst++ = '\\';
4870 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
4871 if (fNeedSuffix)
4872 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
4873 *pszDst = '\0';
4874
4875 if (kwFsPathExists(szPath))
4876 {
4877 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
4878 pszFilename = szPath;
4879 break;
4880 }
4881 }
4882
4883 /* Advance */
4884 pszCur += cch;
4885 while (*pszCur == ';')
4886 pszCur++;
4887 }
4888 }
4889 }
4890
4891 return LoadLibraryExA(pszFilename, hFile, fFlags);
4892}
4893
4894
4895/** Kernel32 - LoadLibraryExW() */
4896static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
4897{
4898 char szTmp[4096];
4899 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
4900 if (cchTmp < sizeof(szTmp))
4901 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
4902
4903 KWFS_TODO();
4904 SetLastError(ERROR_FILENAME_EXCED_RANGE);
4905 return NULL;
4906}
4907
4908/** Kernel32 - LoadLibraryA() */
4909static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
4910{
4911 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
4912}
4913
4914
4915/** Kernel32 - LoadLibraryW() */
4916static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
4917{
4918 char szTmp[4096];
4919 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
4920 if (cchTmp < sizeof(szTmp))
4921 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
4922 KWFS_TODO();
4923 SetLastError(ERROR_FILENAME_EXCED_RANGE);
4924 return NULL;
4925}
4926
4927
4928/** Kernel32 - FreeLibrary() */
4929static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
4930{
4931 /* Ignored, we like to keep everything loaded. */
4932 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4933 return TRUE;
4934}
4935
4936
4937/** Kernel32 - GetModuleHandleA() */
4938static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
4939{
4940 KSIZE i;
4941 KSIZE cchModule;
4942 PKWDYNLOAD pDynLoad;
4943 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4944
4945 /*
4946 * The executable.
4947 */
4948 if (pszModule == NULL)
4949 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
4950
4951 /*
4952 * Cache of system modules we've seen queried.
4953 */
4954 cchModule = kHlpStrLen(pszModule);
4955 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
4956 if ( g_aGetModuleHandleCache[i].cchName == cchModule
4957 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
4958 {
4959 if (g_aGetModuleHandleCache[i].hmod != NULL)
4960 return g_aGetModuleHandleCache[i].hmod;
4961 return g_aGetModuleHandleCache[i].hmod = GetModuleHandleA(pszModule);
4962 }
4963
4964 /*
4965 * Modules we've dynamically loaded.
4966 */
4967 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
4968 if ( pDynLoad->pMod
4969 && ( stricmp(pDynLoad->pMod->pszPath, pszModule) == 0
4970 || stricmp(&pDynLoad->pMod->pszPath[pDynLoad->pMod->offFilename], pszModule) == 0) )
4971 {
4972 if ( pDynLoad->pMod->fNative
4973 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
4974 {
4975 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
4976 return pDynLoad->hmod;
4977 }
4978 SetLastError(ERROR_MOD_NOT_FOUND);
4979 return NULL;
4980 }
4981
4982 kwErrPrintf("pszModule=%s\n", pszModule);
4983 KWFS_TODO();
4984 SetLastError(ERROR_MOD_NOT_FOUND);
4985 return NULL;
4986}
4987
4988
4989/** Kernel32 - GetModuleHandleW() */
4990static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
4991{
4992 KSIZE i;
4993 KSIZE cwcModule;
4994 PKWDYNLOAD pDynLoad;
4995 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4996
4997 /*
4998 * The executable.
4999 */
5000 if (pwszModule == NULL)
5001 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5002
5003 /*
5004 * Cache of system modules we've seen queried.
5005 */
5006 cwcModule = kwUtf16Len(pwszModule);
5007 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5008 if ( g_aGetModuleHandleCache[i].cwcName == cwcModule
5009 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5010 {
5011 if (g_aGetModuleHandleCache[i].hmod != NULL)
5012 return g_aGetModuleHandleCache[i].hmod;
5013 return g_aGetModuleHandleCache[i].hmod = GetModuleHandleW(pwszModule);
5014 }
5015
5016 /*
5017 * Modules we've dynamically loaded.
5018 */
5019 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5020 if ( pDynLoad->pMod
5021 && ( _wcsicmp(pDynLoad->pMod->pwszPath, pwszModule) == 0
5022 || _wcsicmp(&pDynLoad->pMod->pwszPath[pDynLoad->pMod->offFilename], pwszModule) == 0) ) /** @todo wrong offset */
5023 {
5024 if ( pDynLoad->pMod->fNative
5025 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5026 {
5027 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5028 return pDynLoad->hmod;
5029 }
5030 SetLastError(ERROR_MOD_NOT_FOUND);
5031 return NULL;
5032 }
5033
5034 kwErrPrintf("pwszModule=%ls\n", pwszModule);
5035 KWFS_TODO();
5036 SetLastError(ERROR_MOD_NOT_FOUND);
5037 return NULL;
5038}
5039
5040
5041/** Used to debug dynamically resolved procedures. */
5042static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5043{
5044#ifdef _MSC_VER
5045 __debugbreak();
5046#else
5047 KWFS_TODO();
5048#endif
5049 return -1;
5050}
5051
5052
5053#ifndef NDEBUG
5054/*
5055 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5056 */
5057# if K_ARCH == K_ARCH_X86_32
5058static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5059# else
5060static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5061# endif
5062typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5063typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5064typedef struct KWCXINTERCEPTORENTRY
5065{
5066 PFNINVOKECOMPILERPASSW pfnOrg;
5067 PKWMODULE pModule;
5068 PFNINVOKECOMPILERPASSW pfnWrap;
5069} KWCXINTERCEPTORENTRY;
5070
5071static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5072 KWCXINTERCEPTORENTRY *pEntry)
5073{
5074 int i;
5075 KIPTR rcExit;
5076 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5077 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5078 for (i = 0; i < cArgs; i++)
5079 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5080
5081 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5082
5083 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5084 return rcExit;
5085}
5086
5087static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
5088static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
5089static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
5090
5091static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
5092{
5093 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
5094 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
5095 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
5096};
5097
5098static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5099{
5100 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
5101}
5102
5103static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5104{
5105 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
5106}
5107
5108static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5109{
5110 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
5111}
5112
5113#endif /* !NDEBUG */
5114
5115
5116/** Kernel32 - GetProcAddress() */
5117static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
5118{
5119 KSIZE i;
5120 PKWMODULE pMod;
5121 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5122
5123 /*
5124 * Try locate the module.
5125 */
5126 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5127 if (pMod)
5128 {
5129 KLDRADDR uValue;
5130 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
5131 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
5132 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
5133 KU32_MAX /*iSymbol*/,
5134 pszProc,
5135 kHlpStrLen(pszProc),
5136 NULL /*pszVersion*/,
5137 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
5138 &uValue,
5139 NULL /*pfKind*/);
5140 if (rc == 0)
5141 {
5142 //static int s_cDbgGets = 0;
5143 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
5144 KU32 i = g_cSandboxGetProcReplacements;
5145 while (i-- > 0)
5146 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
5147 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
5148 {
5149 if ( !g_aSandboxGetProcReplacements[i].pszModule
5150 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
5151 {
5152 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
5153 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
5154 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
5155 {
5156 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
5157 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5158 }
5159 kwLdrModuleRelease(pMod);
5160 return (FARPROC)(KUPTR)uValue;
5161 }
5162 }
5163
5164#ifndef NDEBUG
5165 /* Intercept the compiler pass method, dumping arguments. */
5166 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
5167 {
5168 KU32 i;
5169 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
5170 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
5171 {
5172 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5173 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
5174 break;
5175 }
5176 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
5177 while (i-- > 0)
5178 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
5179 {
5180 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
5181 g_aCxInterceptorEntries[i].pModule = pMod;
5182 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5183 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
5184 break;
5185 }
5186 }
5187#endif
5188 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5189 kwLdrModuleRelease(pMod);
5190 //s_cDbgGets++;
5191 //if (s_cGets >= 3)
5192 // return (FARPROC)kwSandbox_BreakIntoDebugger;
5193 return (FARPROC)(KUPTR)uValue;
5194 }
5195
5196 KWFS_TODO();
5197 SetLastError(ERROR_PROC_NOT_FOUND);
5198 kwLdrModuleRelease(pMod);
5199 return NULL;
5200 }
5201
5202 /*
5203 * Hmm... could be a cached module-by-name.
5204 */
5205 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5206 if (g_aGetModuleHandleCache[i].hmod == hmod)
5207 return GetProcAddress(hmod, pszProc);
5208
5209 KWFS_TODO();
5210 return GetProcAddress(hmod, pszProc);
5211}
5212
5213
5214/** Kernel32 - GetModuleFileNameA() */
5215static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
5216{
5217 PKWMODULE pMod;
5218 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5219
5220 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5221 if (pMod != NULL)
5222 {
5223 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
5224 kwLdrModuleRelease(pMod);
5225 return cbRet;
5226 }
5227 KWFS_TODO();
5228 return 0;
5229}
5230
5231
5232/** Kernel32 - GetModuleFileNameW() */
5233static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
5234{
5235 PKWMODULE pMod;
5236 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5237
5238 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5239 if (pMod)
5240 {
5241 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
5242 kwLdrModuleRelease(pMod);
5243 return cwcRet;
5244 }
5245
5246 KWFS_TODO();
5247 return 0;
5248}
5249
5250
5251/** NtDll - RtlPcToFileHeader
5252 * This is necessary for msvcr100.dll!CxxThrowException. */
5253static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
5254{
5255 PVOID pvRet;
5256
5257 /*
5258 * Do a binary lookup of the module table for the current tool.
5259 * This will give us a
5260 */
5261 if (g_Sandbox.fRunning)
5262 {
5263 KUPTR const uPC = (KUPTR)pvPC;
5264 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
5265 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
5266 KU32 i;
5267 if (iEnd)
5268 {
5269 KU32 iStart = 0;
5270 i = iEnd / 2;
5271 for (;;)
5272 {
5273 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
5274 if (uPC < uHModThis)
5275 {
5276 iEnd = i;
5277 if (iStart < i)
5278 { }
5279 else
5280 break;
5281 }
5282 else if (uPC != uHModThis)
5283 {
5284 iStart = ++i;
5285 if (i < iEnd)
5286 { }
5287 else
5288 break;
5289 }
5290 else
5291 {
5292 /* This isn't supposed to happen. */
5293 break;
5294 }
5295
5296 i = iStart + (iEnd - iStart) / 2;
5297 }
5298
5299 /* For reasons of simplicity (= copy & paste), we end up with the
5300 module after the one we're interested in here. */
5301 i--;
5302 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
5303 && papMods[i]->pLdrMod)
5304 {
5305 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
5306 if (uRvaPC < papMods[i]->cbImage)
5307 {
5308 *ppvImageBase = papMods[i]->hOurMod;
5309 pvRet = papMods[i]->hOurMod;
5310 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
5311 return pvRet;
5312 }
5313 }
5314 }
5315 else
5316 i = 0;
5317 }
5318
5319 /*
5320 * Call the regular API.
5321 */
5322 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
5323 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
5324 return pvRet;
5325}
5326
5327
5328/*
5329 *
5330 * File access APIs (for speeding them up).
5331 * File access APIs (for speeding them up).
5332 * File access APIs (for speeding them up).
5333 *
5334 */
5335
5336
5337/**
5338 * Converts a lookup error to a windows error code.
5339 *
5340 * @returns The windows error code.
5341 * @param enmError The lookup error.
5342 */
5343static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
5344{
5345 switch (enmError)
5346 {
5347 case KFSLOOKUPERROR_NOT_FOUND:
5348 case KFSLOOKUPERROR_NOT_DIR:
5349 return ERROR_FILE_NOT_FOUND;
5350
5351 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
5352 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
5353 return ERROR_PATH_NOT_FOUND;
5354
5355 case KFSLOOKUPERROR_PATH_TOO_LONG:
5356 return ERROR_FILENAME_EXCED_RANGE;
5357
5358 case KFSLOOKUPERROR_OUT_OF_MEMORY:
5359 return ERROR_NOT_ENOUGH_MEMORY;
5360
5361 default:
5362 return ERROR_PATH_NOT_FOUND;
5363 }
5364}
5365
5366#ifdef WITH_TEMP_MEMORY_FILES
5367
5368/**
5369 * Checks for a cl.exe temporary file.
5370 *
5371 * There are quite a bunch of these. They seems to be passing data between the
5372 * first and second compiler pass. Since they're on disk, they get subjected to
5373 * AV software screening and normal file consistency rules. So, not necessarily
5374 * a very efficient way of handling reasonably small amounts of data.
5375 *
5376 * We make the files live in virtual memory by intercepting their opening,
5377 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
5378 *
5379 * @returns K_TRUE / K_FALSE
5380 * @param pwszFilename The file name being accessed.
5381 */
5382static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
5383{
5384 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
5385 if (pwszName)
5386 {
5387 /* The name starts with _CL_... */
5388 if ( pwszName[0] == '_'
5389 && pwszName[1] == 'C'
5390 && pwszName[2] == 'L'
5391 && pwszName[3] == '_' )
5392 {
5393 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
5394 this check by just checking that it's alpha numerical ascii from here on. */
5395 wchar_t wc;
5396 pwszName += 4;
5397 while ((wc = *pwszName++) != '\0')
5398 {
5399 if (wc < 127 && iswalnum(wc))
5400 { /* likely */ }
5401 else
5402 return K_FALSE;
5403 }
5404 return K_TRUE;
5405 }
5406 }
5407 return K_FALSE;
5408}
5409
5410
5411/**
5412 * Creates a handle to a temporary file.
5413 *
5414 * @returns The handle on success.
5415 * INVALID_HANDLE_VALUE and SetLastError on failure.
5416 * @param pTempFile The temporary file.
5417 * @param dwDesiredAccess The desired access to the handle.
5418 * @param fMapping Whether this is a mapping (K_TRUE) or file
5419 * (K_FALSE) handle type.
5420 */
5421static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
5422{
5423 /*
5424 * Create a handle to the temporary file.
5425 */
5426 HANDLE hFile = INVALID_HANDLE_VALUE;
5427 HANDLE hProcSelf = GetCurrentProcess();
5428 if (DuplicateHandle(hProcSelf, hProcSelf,
5429 hProcSelf, &hFile,
5430 SYNCHRONIZE, FALSE,
5431 0 /*dwOptions*/))
5432 {
5433 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
5434 if (pHandle)
5435 {
5436 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
5437 pHandle->cRefs = 1;
5438 pHandle->offFile = 0;
5439 pHandle->hHandle = hFile;
5440 pHandle->dwDesiredAccess = dwDesiredAccess;
5441 pHandle->u.pTempFile = pTempFile;
5442 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
5443 {
5444 pTempFile->cActiveHandles++;
5445 kHlpAssert(pTempFile->cActiveHandles >= 1);
5446 kHlpAssert(pTempFile->cActiveHandles <= 2);
5447 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
5448 return hFile;
5449 }
5450
5451 kHlpFree(pHandle);
5452 }
5453 else
5454 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
5455 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5456 }
5457 else
5458 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
5459 return INVALID_HANDLE_VALUE;
5460}
5461
5462
5463static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
5464{
5465 HANDLE hFile;
5466 DWORD dwErr;
5467
5468 /*
5469 * Check if we've got an existing temp file.
5470 * ASSUME exact same path for now.
5471 */
5472 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
5473 PKWFSTEMPFILE pTempFile;
5474 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
5475 {
5476 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
5477 if ( pTempFile->cwcPath == cwcFilename
5478 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
5479 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
5480 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
5481 break;
5482 }
5483
5484 /*
5485 * Create a new temporary file instance if not found.
5486 */
5487 if (pTempFile == NULL)
5488 {
5489 KSIZE cbFilename;
5490
5491 switch (dwCreationDisposition)
5492 {
5493 case CREATE_ALWAYS:
5494 case OPEN_ALWAYS:
5495 dwErr = NO_ERROR;
5496 break;
5497
5498 case CREATE_NEW:
5499 kHlpAssertFailed();
5500 SetLastError(ERROR_ALREADY_EXISTS);
5501 return INVALID_HANDLE_VALUE;
5502
5503 case OPEN_EXISTING:
5504 case TRUNCATE_EXISTING:
5505 kHlpAssertFailed();
5506 SetLastError(ERROR_FILE_NOT_FOUND);
5507 return INVALID_HANDLE_VALUE;
5508
5509 default:
5510 kHlpAssertFailed();
5511 SetLastError(ERROR_INVALID_PARAMETER);
5512 return INVALID_HANDLE_VALUE;
5513 }
5514
5515 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
5516 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
5517 if (pTempFile)
5518 {
5519 pTempFile->cwcPath = (KU16)cwcFilename;
5520 pTempFile->cbFile = 0;
5521 pTempFile->cbFileAllocated = 0;
5522 pTempFile->cActiveHandles = 0;
5523 pTempFile->cMappings = 0;
5524 pTempFile->cSegs = 0;
5525 pTempFile->paSegs = NULL;
5526 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
5527
5528 pTempFile->pNext = g_Sandbox.pTempFileHead;
5529 g_Sandbox.pTempFileHead = pTempFile;
5530 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
5531 }
5532 else
5533 {
5534 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
5535 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5536 return INVALID_HANDLE_VALUE;
5537 }
5538 }
5539 else
5540 {
5541 switch (dwCreationDisposition)
5542 {
5543 case OPEN_EXISTING:
5544 dwErr = NO_ERROR;
5545 break;
5546 case OPEN_ALWAYS:
5547 dwErr = ERROR_ALREADY_EXISTS ;
5548 break;
5549
5550 case TRUNCATE_EXISTING:
5551 case CREATE_ALWAYS:
5552 kHlpAssertFailed();
5553 pTempFile->cbFile = 0;
5554 dwErr = ERROR_ALREADY_EXISTS;
5555 break;
5556
5557 case CREATE_NEW:
5558 kHlpAssertFailed();
5559 SetLastError(ERROR_FILE_EXISTS);
5560 return INVALID_HANDLE_VALUE;
5561
5562 default:
5563 kHlpAssertFailed();
5564 SetLastError(ERROR_INVALID_PARAMETER);
5565 return INVALID_HANDLE_VALUE;
5566 }
5567 }
5568
5569 /*
5570 * Create a handle to the temporary file.
5571 */
5572 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
5573 if (hFile != INVALID_HANDLE_VALUE)
5574 SetLastError(dwErr);
5575 return hFile;
5576}
5577
5578#endif /* WITH_TEMP_MEMORY_FILES */
5579
5580/**
5581 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
5582 *
5583 * @returns K_TRUE if cacheable, K_FALSE if not.
5584 * @param wcFirst The first extension character.
5585 * @param wcSecond The second extension character.
5586 * @param wcThird The third extension character.
5587 * @param fAttrQuery Set if it's for an attribute query, clear if for
5588 * file creation.
5589 */
5590static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
5591{
5592 /* C++ header without an extension or a directory. */
5593 if (wcFirst == '\0')
5594 {
5595 /** @todo exclude temporary files... */
5596 return K_TRUE;
5597 }
5598
5599 /* C Header: .h */
5600 if (wcFirst == 'h' || wcFirst == 'H')
5601 {
5602 if (wcSecond == '\0')
5603 return K_TRUE;
5604
5605 /* C++ Header: .hpp, .hxx */
5606 if ( (wcSecond == 'p' || wcSecond == 'P')
5607 && (wcThird == 'p' || wcThird == 'P'))
5608 return K_TRUE;
5609 if ( (wcSecond == 'x' || wcSecond == 'X')
5610 && (wcThird == 'x' || wcThird == 'X'))
5611 return K_TRUE;
5612 }
5613 /* Misc starting with i. */
5614 else if (wcFirst == 'i' || wcFirst == 'I')
5615 {
5616 if (wcSecond != '\0')
5617 {
5618 if (wcSecond == 'n' || wcSecond == 'N')
5619 {
5620 /* C++ inline header: .inl */
5621 if (wcThird == 'l' || wcThird == 'L')
5622 return K_TRUE;
5623
5624 /* Assembly include file: .inc */
5625 if (wcThird == 'c' || wcThird == 'C')
5626 return K_TRUE;
5627 }
5628 }
5629 }
5630 /* Assembly header: .mac */
5631 else if (wcFirst == 'm' || wcFirst == 'M')
5632 {
5633 if (wcSecond == 'a' || wcSecond == 'A')
5634 {
5635 if (wcThird == 'c' || wcThird == 'C')
5636 return K_TRUE;
5637 }
5638 }
5639#ifdef WITH_PCH_CACHING
5640 /* Precompiled header: .pch */
5641 else if (wcFirst == 'p' || wcFirst == 'P')
5642 {
5643 if (wcSecond == 'c' || wcSecond == 'C')
5644 {
5645 if (wcThird == 'h' || wcThird == 'H')
5646 return !g_Sandbox.fNoPchCaching;
5647 }
5648 }
5649#endif
5650#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
5651 /* Linker - Object file: .obj */
5652 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
5653 {
5654 if (wcSecond == 'b' || wcSecond == 'B')
5655 {
5656 if (wcThird == 'j' || wcThird == 'J')
5657 return K_TRUE;
5658 }
5659 }
5660#endif
5661 else if (fAttrQuery)
5662 {
5663 /* Dynamic link library: .dll */
5664 if (wcFirst == 'd' || wcFirst == 'D')
5665 {
5666 if (wcSecond == 'l' || wcSecond == 'L')
5667 {
5668 if (wcThird == 'l' || wcThird == 'L')
5669 return K_TRUE;
5670 }
5671 }
5672 /* Executable file: .exe */
5673 else if (wcFirst == 'e' || wcFirst == 'E')
5674 {
5675 if (wcSecond == 'x' || wcSecond == 'X')
5676 {
5677 if (wcThird == 'e' || wcThird == 'E')
5678 return K_TRUE;
5679 }
5680 }
5681 /* Response file: .rsp */
5682 else if (wcFirst == 'r' || wcFirst == 'R')
5683 {
5684 if (wcSecond == 's' || wcSecond == 'S')
5685 {
5686 if (wcThird == 'p' || wcThird == 'P')
5687 return !g_Sandbox.fNoPchCaching;
5688 }
5689 }
5690 /* Linker: */
5691 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
5692 {
5693 /* Object file: .obj */
5694 if (wcFirst == 'o' || wcFirst == 'O')
5695 {
5696 if (wcSecond == 'b' || wcSecond == 'B')
5697 {
5698 if (wcThird == 'j' || wcThird == 'J')
5699 return K_TRUE;
5700 }
5701 }
5702 /* Library file: .lib */
5703 else if (wcFirst == 'l' || wcFirst == 'L')
5704 {
5705 if (wcSecond == 'i' || wcSecond == 'I')
5706 {
5707 if (wcThird == 'b' || wcThird == 'B')
5708 return K_TRUE;
5709 }
5710 }
5711 /* Linker definition file: .def */
5712 else if (wcFirst == 'd' || wcFirst == 'D')
5713 {
5714 if (wcSecond == 'e' || wcSecond == 'E')
5715 {
5716 if (wcThird == 'f' || wcThird == 'F')
5717 return K_TRUE;
5718 }
5719 }
5720 }
5721 }
5722
5723 return K_FALSE;
5724}
5725
5726
5727/**
5728 * Checks if the file extension indicates that the file/dir is something we
5729 * ought to cache.
5730 *
5731 * @returns K_TRUE if cachable, K_FALSE if not.
5732 * @param pszExt The kHlpGetExt result.
5733 * @param fAttrQuery Set if it's for an attribute query, clear if for
5734 * file creation.
5735 */
5736static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
5737{
5738 wchar_t const wcFirst = *pszExt;
5739 if (wcFirst)
5740 {
5741 wchar_t const wcSecond = pszExt[1];
5742 if (wcSecond)
5743 {
5744 wchar_t const wcThird = pszExt[2];
5745 if (pszExt[3] == '\0')
5746 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
5747 return K_FALSE;
5748 }
5749 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
5750 }
5751 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
5752}
5753
5754
5755/**
5756 * Checks if the extension of the given UTF-16 path indicates that the file/dir
5757 * should be cached.
5758 *
5759 * @returns K_TRUE if cachable, K_FALSE if not.
5760 * @param pwszPath The UTF-16 path to examine.
5761 * @param fAttrQuery Set if it's for an attribute query, clear if for
5762 * file creation.
5763 */
5764static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
5765{
5766 KSIZE cwcExt;
5767 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
5768 switch (cwcExt)
5769 {
5770 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
5771 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
5772 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
5773 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
5774 }
5775 return K_FALSE;
5776}
5777
5778
5779
5780/**
5781 * Creates a new
5782 *
5783 * @returns
5784 * @param pFsObj .
5785 * @param pwszFilename .
5786 */
5787static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
5788{
5789 HANDLE hFile;
5790 MY_IO_STATUS_BLOCK Ios;
5791 MY_OBJECT_ATTRIBUTES ObjAttr;
5792 MY_UNICODE_STRING UniStr;
5793 MY_NTSTATUS rcNt;
5794 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5795
5796 /*
5797 * Open the file relative to the parent directory.
5798 */
5799 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
5800 kHlpAssert(pFsObj->pParent);
5801 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
5802
5803 Ios.Information = -1;
5804 Ios.u.Status = -1;
5805
5806 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
5807 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
5808 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
5809
5810 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
5811
5812 rcNt = g_pfnNtCreateFile(&hFile,
5813 GENERIC_READ | SYNCHRONIZE,
5814 &ObjAttr,
5815 &Ios,
5816 NULL, /*cbFileInitialAlloc */
5817 FILE_ATTRIBUTE_NORMAL,
5818 FILE_SHARE_READ,
5819 FILE_OPEN,
5820 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
5821 NULL, /*pEaBuffer*/
5822 0); /*cbEaBuffer*/
5823 if (MY_NT_SUCCESS(rcNt))
5824 {
5825 /*
5826 * Read the whole file into memory.
5827 */
5828 LARGE_INTEGER cbFile;
5829 if (GetFileSizeEx(hFile, &cbFile))
5830 {
5831 if ( cbFile.QuadPart >= 0
5832#ifdef WITH_PCH_CACHING
5833 && ( cbFile.QuadPart < 16*1024*1024
5834 || ( cbFile.QuadPart < 96*1024*1024
5835 && pFsObj->cchName > 4
5836 && !g_Sandbox.fNoPchCaching
5837 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
5838#endif
5839 )
5840 {
5841 KU32 cbCache = (KU32)cbFile.QuadPart;
5842 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
5843 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
5844 if (hMapping != NULL)
5845 {
5846 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
5847 if (pbCache)
5848 {
5849 /*
5850 * Create the cached file object.
5851 */
5852 PKFSWCACHEDFILE pCachedFile;
5853 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
5854 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
5855 sizeof(*pCachedFile) + cbPath);
5856 if (pCachedFile)
5857 {
5858 pCachedFile->hCached = hFile;
5859 pCachedFile->hSection = hMapping;
5860 pCachedFile->cbCached = cbCache;
5861 pCachedFile->pbCached = pbCache;
5862 pCachedFile->pFsObj = pFsObj;
5863 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
5864 kFsCacheObjRetain(pFsObj);
5865
5866 g_cReadCachedFiles++;
5867 g_cbReadCachedFiles += cbCache;
5868
5869 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
5870 return pCachedFile;
5871 }
5872
5873 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
5874 }
5875 else
5876 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
5877 CloseHandle(hMapping);
5878 }
5879 else
5880 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
5881 }
5882 else
5883 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
5884 }
5885 else
5886 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
5887 g_pfnNtClose(hFile);
5888 }
5889 else
5890 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
5891 return NULL;
5892}
5893
5894
5895/**
5896 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
5897 */
5898static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
5899 KBOOL fIsFileHandle, HANDLE *phFile)
5900{
5901 HANDLE hProcSelf = GetCurrentProcess();
5902 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
5903 hProcSelf, phFile,
5904 dwDesiredAccess, fInheritHandle,
5905 0 /*dwOptions*/))
5906 {
5907 /*
5908 * Create handle table entry for the duplicate handle.
5909 */
5910 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
5911 if (pHandle)
5912 {
5913 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
5914 pHandle->cRefs = 1;
5915 pHandle->offFile = 0;
5916 pHandle->hHandle = *phFile;
5917 pHandle->dwDesiredAccess = dwDesiredAccess;
5918 pHandle->u.pCachedFile = pCachedFile;
5919 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
5920 return K_TRUE;
5921
5922 kHlpFree(pHandle);
5923 }
5924 else
5925 KWFS_LOG(("Out of memory for handle!\n"));
5926
5927 CloseHandle(*phFile);
5928 *phFile = INVALID_HANDLE_VALUE;
5929 }
5930 else
5931 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
5932 return K_FALSE;
5933}
5934
5935
5936/**
5937 * Kernel32 - Common code for CreateFileW and CreateFileA.
5938 */
5939static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
5940{
5941 *phFile = INVALID_HANDLE_VALUE;
5942
5943 /*
5944 * At the moment we only handle existing files.
5945 */
5946 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
5947 {
5948 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
5949 kHlpAssert(pFsObj->fHaveStats);
5950 if ( pCachedFile != NULL
5951 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
5952 {
5953 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
5954 return K_TRUE;
5955 }
5956 }
5957 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
5958
5959 /* Do fallback, please. */
5960 return K_FALSE;
5961}
5962
5963
5964/** Kernel32 - CreateFileA */
5965static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
5966 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
5967 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
5968{
5969 HANDLE hFile;
5970
5971 /*
5972 * Check for include files and similar that we do read-only caching of.
5973 */
5974 if (dwCreationDisposition == FILE_OPEN_IF)
5975 {
5976 if ( dwDesiredAccess == GENERIC_READ
5977 || dwDesiredAccess == FILE_GENERIC_READ)
5978 {
5979 if (dwShareMode & FILE_SHARE_READ)
5980 {
5981 if ( !pSecAttrs
5982 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
5983 && pSecAttrs->lpSecurityDescriptor == NULL ) )
5984 {
5985 const char *pszExt = kHlpGetExt(pszFilename);
5986 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
5987 {
5988 KFSLOOKUPERROR enmError;
5989 PKFSOBJ pFsObj;
5990 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5991
5992 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
5993 if (pFsObj)
5994 {
5995 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
5996 &hFile);
5997 kFsCacheObjRelease(g_pFsCache, pFsObj);
5998 if (fRc)
5999 {
6000 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6001 return hFile;
6002 }
6003 }
6004 /* These are for nasm and yasm header searching. Cache will already
6005 have checked the directories for the file, no need to call
6006 CreateFile to do it again. */
6007 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6008 {
6009 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6010 return INVALID_HANDLE_VALUE;
6011 }
6012 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6013 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6014 {
6015 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6016 return INVALID_HANDLE_VALUE;
6017 }
6018
6019 /* fallback */
6020 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6021 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6022 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
6023 return hFile;
6024 }
6025 }
6026 }
6027 }
6028 }
6029
6030 /*
6031 * Okay, normal.
6032 */
6033 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6034 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6035 if (hFile != INVALID_HANDLE_VALUE)
6036 {
6037 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6038 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6039 }
6040 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
6041 return hFile;
6042}
6043
6044
6045/** Kernel32 - CreateFileW */
6046static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6047 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6048 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6049{
6050 HANDLE hFile;
6051
6052#ifdef WITH_TEMP_MEMORY_FILES
6053 /*
6054 * Check for temporary files (cl.exe only).
6055 */
6056 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6057 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
6058 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
6059 && kwFsIsClTempFileW(pwszFilename))
6060 {
6061 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition);
6062 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
6063 return hFile;
6064 }
6065#endif
6066
6067 /*
6068 * Check for include files and similar that we do read-only caching of.
6069 */
6070 if (dwCreationDisposition == FILE_OPEN_IF)
6071 {
6072 if ( dwDesiredAccess == GENERIC_READ
6073 || dwDesiredAccess == FILE_GENERIC_READ)
6074 {
6075 if (dwShareMode & FILE_SHARE_READ)
6076 {
6077 if ( !pSecAttrs
6078 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6079 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6080 {
6081 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
6082 {
6083 KFSLOOKUPERROR enmError;
6084 PKFSOBJ pFsObj;
6085 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6086
6087 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6088 if (pFsObj)
6089 {
6090 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6091 &hFile);
6092 kFsCacheObjRelease(g_pFsCache, pFsObj);
6093 if (fRc)
6094 {
6095 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
6096 return hFile;
6097 }
6098 }
6099 /* These are for nasm and yasm style header searching. Cache will
6100 already have checked the directories for the file, no need to call
6101 CreateFile to do it again. */
6102 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6103 {
6104 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
6105 return INVALID_HANDLE_VALUE;
6106 }
6107 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6108 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6109 {
6110 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
6111 return INVALID_HANDLE_VALUE;
6112 }
6113
6114 /* fallback */
6115 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6116 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6117 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
6118 return hFile;
6119 }
6120 }
6121 else
6122 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
6123 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
6124 }
6125 else
6126 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
6127 }
6128 else
6129 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
6130 }
6131 else
6132 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
6133
6134 /*
6135 * Okay, normal.
6136 */
6137 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6138 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6139 if (hFile != INVALID_HANDLE_VALUE)
6140 {
6141 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6142 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6143 }
6144 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
6145 return hFile;
6146}
6147
6148
6149/** Kernel32 - SetFilePointer */
6150static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
6151{
6152 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6153 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6154 if (idxHandle < g_Sandbox.cHandles)
6155 {
6156 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6157 if (pHandle != NULL)
6158 {
6159 KU32 cbFile;
6160 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
6161 switch (pHandle->enmType)
6162 {
6163 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6164 cbFile = pHandle->u.pCachedFile->cbCached;
6165 break;
6166#ifdef WITH_TEMP_MEMORY_FILES
6167 case KWHANDLETYPE_TEMP_FILE:
6168 cbFile = pHandle->u.pTempFile->cbFile;
6169 break;
6170#endif
6171 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6172 case KWHANDLETYPE_OUTPUT_BUF:
6173 default:
6174 kHlpAssertFailed();
6175 SetLastError(ERROR_INVALID_FUNCTION);
6176 return INVALID_SET_FILE_POINTER;
6177 }
6178
6179 switch (dwMoveMethod)
6180 {
6181 case FILE_BEGIN:
6182 break;
6183 case FILE_CURRENT:
6184 offMove += pHandle->offFile;
6185 break;
6186 case FILE_END:
6187 offMove += cbFile;
6188 break;
6189 default:
6190 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6191 SetLastError(ERROR_INVALID_PARAMETER);
6192 return INVALID_SET_FILE_POINTER;
6193 }
6194 if (offMove >= 0)
6195 {
6196 if (offMove >= (KSSIZE)cbFile)
6197 {
6198 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6199 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6200 offMove = (KSSIZE)cbFile;
6201 /* For writable files, seeking beyond the end is fine, but check that we've got
6202 the type range for the request. */
6203 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
6204 {
6205 kHlpAssertMsgFailed(("%#llx\n", offMove));
6206 SetLastError(ERROR_SEEK);
6207 return INVALID_SET_FILE_POINTER;
6208 }
6209 }
6210 pHandle->offFile = (KU32)offMove;
6211 }
6212 else
6213 {
6214 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
6215 SetLastError(ERROR_NEGATIVE_SEEK);
6216 return INVALID_SET_FILE_POINTER;
6217 }
6218 if (pcbMoveHi)
6219 *pcbMoveHi = (KU64)offMove >> 32;
6220 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
6221 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6222 SetLastError(NO_ERROR);
6223 return (KU32)offMove;
6224 }
6225 }
6226 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
6227 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
6228}
6229
6230
6231/** Kernel32 - SetFilePointerEx */
6232static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
6233 DWORD dwMoveMethod)
6234{
6235 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6236 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6237 if (idxHandle < g_Sandbox.cHandles)
6238 {
6239 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6240 if (pHandle != NULL)
6241 {
6242 KI64 offMyMove = offMove.QuadPart;
6243 KU32 cbFile;
6244 switch (pHandle->enmType)
6245 {
6246 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6247 cbFile = pHandle->u.pCachedFile->cbCached;
6248 break;
6249#ifdef WITH_TEMP_MEMORY_FILES
6250 case KWHANDLETYPE_TEMP_FILE:
6251 cbFile = pHandle->u.pTempFile->cbFile;
6252 break;
6253#endif
6254 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6255 case KWHANDLETYPE_OUTPUT_BUF:
6256 default:
6257 kHlpAssertFailed();
6258 SetLastError(ERROR_INVALID_FUNCTION);
6259 return INVALID_SET_FILE_POINTER;
6260 }
6261
6262 switch (dwMoveMethod)
6263 {
6264 case FILE_BEGIN:
6265 break;
6266 case FILE_CURRENT:
6267 offMyMove += pHandle->offFile;
6268 break;
6269 case FILE_END:
6270 offMyMove += cbFile;
6271 break;
6272 default:
6273 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6274 SetLastError(ERROR_INVALID_PARAMETER);
6275 return INVALID_SET_FILE_POINTER;
6276 }
6277 if (offMyMove >= 0)
6278 {
6279 if (offMyMove >= (KSSIZE)cbFile)
6280 {
6281 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6282 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6283 offMyMove = (KSSIZE)cbFile;
6284 /* For writable files, seeking beyond the end is fine, but check that we've got
6285 the type range for the request. */
6286 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
6287 {
6288 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
6289 SetLastError(ERROR_SEEK);
6290 return INVALID_SET_FILE_POINTER;
6291 }
6292 }
6293 pHandle->offFile = (KU32)offMyMove;
6294 }
6295 else
6296 {
6297 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
6298 SetLastError(ERROR_NEGATIVE_SEEK);
6299 return INVALID_SET_FILE_POINTER;
6300 }
6301 if (poffNew)
6302 poffNew->QuadPart = offMyMove;
6303 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
6304 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6305 return TRUE;
6306 }
6307 }
6308 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
6309 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
6310}
6311
6312
6313/** Kernel32 - ReadFile */
6314static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
6315 LPOVERLAPPED pOverlapped)
6316{
6317 BOOL fRet;
6318 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6319 g_cReadFileCalls++;
6320 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6321 if (idxHandle < g_Sandbox.cHandles)
6322 {
6323 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6324 if (pHandle != NULL)
6325 {
6326 switch (pHandle->enmType)
6327 {
6328 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6329 {
6330 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
6331 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
6332 if (cbActually > cbToRead)
6333 cbActually = cbToRead;
6334
6335#ifdef WITH_HASH_MD5_CACHE
6336 if (g_Sandbox.pHashHead)
6337 {
6338 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
6339 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
6340 g_Sandbox.LastHashRead.cbRead = cbActually;
6341 g_Sandbox.LastHashRead.pvRead = pvBuffer;
6342 }
6343#endif
6344
6345 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
6346 pHandle->offFile += cbActually;
6347
6348 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6349 *pcbActuallyRead = cbActually;
6350
6351 g_cbReadFileFromReadCached += cbActually;
6352 g_cbReadFileTotal += cbActually;
6353 g_cReadFileFromReadCached++;
6354
6355 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
6356 return TRUE;
6357 }
6358
6359#ifdef WITH_TEMP_MEMORY_FILES
6360 case KWHANDLETYPE_TEMP_FILE:
6361 {
6362 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6363 KU32 cbActually;
6364 if (pHandle->offFile < pTempFile->cbFile)
6365 {
6366 cbActually = pTempFile->cbFile - pHandle->offFile;
6367 if (cbActually > cbToRead)
6368 cbActually = cbToRead;
6369
6370 /* Copy the data. */
6371 if (cbActually > 0)
6372 {
6373 KU32 cbLeft;
6374 KU32 offSeg;
6375 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
6376
6377 /* Locate the segment containing the byte at offFile. */
6378 KU32 iSeg = pTempFile->cSegs - 1;
6379 kHlpAssert(pTempFile->cSegs > 0);
6380 while (paSegs[iSeg].offData > pHandle->offFile)
6381 iSeg--;
6382
6383 /* Copy out the data. */
6384 cbLeft = cbActually;
6385 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6386 for (;;)
6387 {
6388 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6389 if (cbAvail >= cbLeft)
6390 {
6391 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
6392 break;
6393 }
6394
6395 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
6396 cbLeft -= cbAvail;
6397 offSeg = 0;
6398 iSeg++;
6399 kHlpAssert(iSeg < pTempFile->cSegs);
6400 }
6401
6402 /* Update the file offset. */
6403 pHandle->offFile += cbActually;
6404 }
6405 }
6406 /* Read does not commit file space, so return zero bytes. */
6407 else
6408 cbActually = 0;
6409
6410 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6411 *pcbActuallyRead = cbActually;
6412
6413 g_cbReadFileTotal += cbActually;
6414 g_cbReadFileFromInMemTemp += cbActually;
6415 g_cReadFileFromInMemTemp++;
6416
6417 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
6418 return TRUE;
6419 }
6420#endif /* WITH_TEMP_MEMORY_FILES */
6421
6422 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6423 case KWHANDLETYPE_OUTPUT_BUF:
6424 default:
6425 kHlpAssertFailed();
6426 SetLastError(ERROR_INVALID_FUNCTION);
6427 *pcbActuallyRead = 0;
6428 return FALSE;
6429 }
6430 }
6431 }
6432
6433 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
6434 if (fRet && pcbActuallyRead)
6435 g_cbReadFileTotal += *pcbActuallyRead;
6436 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
6437 return fRet;
6438}
6439
6440
6441/** Kernel32 - ReadFileEx */
6442static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
6443 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
6444{
6445 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6446 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6447 if (idxHandle < g_Sandbox.cHandles)
6448 {
6449 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6450 if (pHandle != NULL)
6451 {
6452 kHlpAssertFailed();
6453 }
6454 }
6455
6456 KWFS_LOG(("ReadFile(%p)\n", hFile));
6457 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
6458}
6459
6460#ifdef WITH_STD_OUT_ERR_BUFFERING
6461
6462/**
6463 * Write something to a handle, making sure everything is actually written.
6464 *
6465 * @param hHandle Where to write it to.
6466 * @param pchBuf What to write
6467 * @param cchToWrite How much to write.
6468 */
6469static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
6470{
6471 if (cchToWrite > 0)
6472 {
6473 DWORD cchWritten = 0;
6474 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
6475 {
6476 if (cchWritten == cchToWrite)
6477 { /* likely */ }
6478 else
6479 {
6480 do
6481 {
6482 pchBuf += cchWritten;
6483 cchToWrite -= cchWritten;
6484 cchWritten = 0;
6485 } while ( cchToWrite > 0
6486 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
6487 }
6488 }
6489 else
6490 kHlpAssertFailed();
6491 }
6492}
6493
6494
6495/**
6496 * Worker for WriteFile when the output isn't going to the console.
6497 *
6498 * @param pSandbox The sandbox.
6499 * @param pOutBuf The output buffer.
6500 * @param pchBuffer What to write.
6501 * @param cchToWrite How much to write.
6502 */
6503static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
6504{
6505 if (pOutBuf->u.Fully.cchBufAlloc > 0)
6506 { /* likely */ }
6507 else
6508 {
6509 /* No realloc, max size is 64KB. */
6510 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
6511 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
6512 if (!pOutBuf->u.Fully.pchBuf)
6513 {
6514 while ( !pOutBuf->u.Fully.pchBuf
6515 && pOutBuf->u.Fully.cchBufAlloc > 64)
6516 {
6517 pOutBuf->u.Fully.cchBufAlloc /= 2;
6518 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
6519 }
6520 if (!pOutBuf->u.Fully.pchBuf)
6521 {
6522 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
6523 pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
6524 }
6525 }
6526 }
6527
6528 /*
6529 * Special case: Output ends with newline and fits in the buffer.
6530 */
6531 if ( cchToWrite > 1
6532 && pchBuffer[cchToWrite - 1] == '\n'
6533 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
6534 {
6535 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
6536 pOutBuf->u.Fully.cchBuf += cchToWrite;
6537 }
6538 else
6539 {
6540 /*
6541 * Work thru the text line by line, flushing the buffer when
6542 * appropriate. The buffer is not a line buffer here, it's a
6543 * full buffer.
6544 */
6545 while (cchToWrite > 0)
6546 {
6547 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
6548 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
6549 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
6550 {
6551 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
6552 pOutBuf->u.Fully.cchBuf += cchLine;
6553 }
6554 /*
6555 * Option one: Flush the buffer and the current line.
6556 *
6557 * We choose this one when the line won't ever fit, or when we have
6558 * an incomplete line in the buffer.
6559 */
6560 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
6561 || pOutBuf->u.Fully.cchBuf == 0
6562 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
6563 {
6564 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
6565 if (pOutBuf->u.Fully.cchBuf > 0)
6566 {
6567 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
6568 pOutBuf->u.Fully.cchBuf = 0;
6569 }
6570 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
6571 }
6572 /*
6573 * Option two: Only flush the lines in the buffer.
6574 */
6575 else
6576 {
6577 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
6578 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
6579 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
6580 pOutBuf->u.Fully.cchBuf = cchLine;
6581 }
6582
6583 /* advance */
6584 pchBuffer += cchLine;
6585 cchToWrite -= cchLine;
6586 }
6587 }
6588}
6589
6590#endif /* WITH_STD_OUT_ERR_BUFFERING */
6591
6592#ifdef WITH_TEMP_MEMORY_FILES
6593static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
6594{
6595 KU32 cbMinFile = offFile + cbNeeded;
6596 if (cbMinFile >= offFile)
6597 {
6598 /* Calc how much space we've already allocated and */
6599 if (cbMinFile <= pTempFile->cbFileAllocated)
6600 return K_TRUE;
6601
6602 /* Grow the file. */
6603 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
6604 {
6605 int rc;
6606 KU32 cSegs = pTempFile->cSegs;
6607 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
6608 do
6609 {
6610 /* grow the segment array? */
6611 if ((cSegs % 16) == 0)
6612 {
6613 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
6614 if (!pvNew)
6615 return K_FALSE;
6616 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
6617 }
6618
6619 /* Use page alloc here to simplify mapping later. */
6620 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
6621 if (rc == 0)
6622 { /* likely */ }
6623 else
6624 {
6625 cbNewSeg = 64*1024;
6626 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
6627 if (rc != 0)
6628 {
6629 kHlpAssertFailed();
6630 return K_FALSE;
6631 }
6632 }
6633 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
6634 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
6635 pTempFile->cbFileAllocated += cbNewSeg;
6636 pTempFile->cSegs = ++cSegs;
6637
6638 } while (pTempFile->cbFileAllocated < cbMinFile);
6639
6640 return K_TRUE;
6641 }
6642 }
6643
6644 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
6645 return K_FALSE;
6646}
6647#endif /* WITH_TEMP_MEMORY_FILES */
6648
6649
6650#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
6651/** Kernel32 - WriteFile */
6652static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
6653 LPOVERLAPPED pOverlapped)
6654{
6655 BOOL fRet;
6656 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6657 g_cWriteFileCalls++;
6658 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_fCtrlC);
6659 if (idxHandle < g_Sandbox.cHandles)
6660 {
6661 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6662 if (pHandle != NULL)
6663 {
6664 switch (pHandle->enmType)
6665 {
6666# ifdef WITH_TEMP_MEMORY_FILES
6667 case KWHANDLETYPE_TEMP_FILE:
6668 {
6669 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6670
6671 kHlpAssert(!pOverlapped);
6672 kHlpAssert(pcbActuallyWritten);
6673
6674 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
6675 {
6676 KU32 cbLeft;
6677 KU32 offSeg;
6678
6679 /* Locate the segment containing the byte at offFile. */
6680 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
6681 KU32 iSeg = pTempFile->cSegs - 1;
6682 kHlpAssert(pTempFile->cSegs > 0);
6683 while (paSegs[iSeg].offData > pHandle->offFile)
6684 iSeg--;
6685
6686 /* Copy in the data. */
6687 cbLeft = cbToWrite;
6688 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6689 for (;;)
6690 {
6691 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6692 if (cbAvail >= cbLeft)
6693 {
6694 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
6695 break;
6696 }
6697
6698 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
6699 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
6700 cbLeft -= cbAvail;
6701 offSeg = 0;
6702 iSeg++;
6703 kHlpAssert(iSeg < pTempFile->cSegs);
6704 }
6705
6706 /* Update the file offset. */
6707 pHandle->offFile += cbToWrite;
6708 if (pHandle->offFile > pTempFile->cbFile)
6709 pTempFile->cbFile = pHandle->offFile;
6710
6711 *pcbActuallyWritten = cbToWrite;
6712
6713 g_cbWriteFileTotal += cbToWrite;
6714 g_cbWriteFileToInMemTemp += cbToWrite;
6715 g_cWriteFileToInMemTemp++;
6716
6717 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
6718 return TRUE;
6719 }
6720
6721 kHlpAssertFailed();
6722 *pcbActuallyWritten = 0;
6723 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6724 return FALSE;
6725 }
6726# endif
6727
6728 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6729 kHlpAssertFailed();
6730 SetLastError(ERROR_ACCESS_DENIED);
6731 *pcbActuallyWritten = 0;
6732 return FALSE;
6733
6734# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
6735 /*
6736 * Standard output & error.
6737 */
6738 case KWHANDLETYPE_OUTPUT_BUF:
6739 {
6740 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
6741 if (pOutBuf->fIsConsole)
6742 {
6743 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
6744 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
6745 }
6746 else
6747 {
6748# ifdef WITH_STD_OUT_ERR_BUFFERING
6749 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
6750 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
6751 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
6752# else
6753 kHlpAssertFailed();
6754# endif
6755 }
6756 if (pcbActuallyWritten)
6757 *pcbActuallyWritten = cbToWrite;
6758 g_cbWriteFileTotal += cbToWrite;
6759 return TRUE;
6760 }
6761# endif
6762
6763 default:
6764 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6765 kHlpAssertFailed();
6766 SetLastError(ERROR_INVALID_FUNCTION);
6767 *pcbActuallyWritten = 0;
6768 return FALSE;
6769 }
6770 }
6771 }
6772
6773 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
6774 if (fRet && pcbActuallyWritten)
6775 g_cbWriteFileTotal += *pcbActuallyWritten;
6776 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
6777 return fRet;
6778}
6779
6780
6781/** Kernel32 - WriteFileEx */
6782static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
6783 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
6784{
6785 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6786 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6787 if (idxHandle < g_Sandbox.cHandles)
6788 {
6789 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6790 if (pHandle != NULL)
6791 {
6792 kHlpAssertFailed();
6793 }
6794 }
6795
6796 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
6797 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
6798}
6799
6800#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
6801
6802#ifdef WITH_TEMP_MEMORY_FILES
6803
6804/** Kernel32 - SetEndOfFile; */
6805static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
6806{
6807 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6808 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6809 if (idxHandle < g_Sandbox.cHandles)
6810 {
6811 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6812 if (pHandle != NULL)
6813 {
6814 switch (pHandle->enmType)
6815 {
6816 case KWHANDLETYPE_TEMP_FILE:
6817 {
6818 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6819 if ( pHandle->offFile > pTempFile->cbFile
6820 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
6821 {
6822 kHlpAssertFailed();
6823 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6824 return FALSE;
6825 }
6826
6827 pTempFile->cbFile = pHandle->offFile;
6828 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
6829 return TRUE;
6830 }
6831
6832 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6833 kHlpAssertFailed();
6834 SetLastError(ERROR_ACCESS_DENIED);
6835 return FALSE;
6836
6837 case KWHANDLETYPE_OUTPUT_BUF:
6838 kHlpAssertFailed();
6839 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
6840 return FALSE;
6841
6842 default:
6843 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6844 kHlpAssertFailed();
6845 SetLastError(ERROR_INVALID_FUNCTION);
6846 return FALSE;
6847 }
6848 }
6849 }
6850
6851 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
6852 return SetEndOfFile(hFile);
6853}
6854
6855
6856/** Kernel32 - GetFileType */
6857static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
6858{
6859 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6860 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6861 if (idxHandle < g_Sandbox.cHandles)
6862 {
6863 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6864 if (pHandle != NULL)
6865 {
6866 switch (pHandle->enmType)
6867 {
6868 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6869 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
6870 return FILE_TYPE_DISK;
6871
6872 case KWHANDLETYPE_TEMP_FILE:
6873 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
6874 return FILE_TYPE_DISK;
6875
6876 case KWHANDLETYPE_OUTPUT_BUF:
6877 {
6878 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
6879 DWORD fRet;
6880 if (pOutBuf->fFileType != KU8_MAX)
6881 {
6882 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
6883 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
6884 }
6885 else
6886 {
6887 fRet = GetFileType(hFile);
6888 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
6889 }
6890 return fRet;
6891 }
6892
6893 }
6894 }
6895 }
6896
6897 KWFS_LOG(("GetFileType(%p)\n", hFile));
6898 return GetFileType(hFile);
6899}
6900
6901
6902/** Kernel32 - GetFileSize */
6903static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
6904{
6905 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6906 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6907 if (idxHandle < g_Sandbox.cHandles)
6908 {
6909 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6910 if (pHandle != NULL)
6911 {
6912 if (pcbHighDword)
6913 *pcbHighDword = 0;
6914 SetLastError(NO_ERROR);
6915 switch (pHandle->enmType)
6916 {
6917 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6918 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
6919 return pHandle->u.pCachedFile->cbCached;
6920
6921 case KWHANDLETYPE_TEMP_FILE:
6922 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
6923 return pHandle->u.pTempFile->cbFile;
6924
6925 case KWHANDLETYPE_OUTPUT_BUF:
6926 /* do default */
6927 break;
6928
6929 default:
6930 kHlpAssertFailed();
6931 SetLastError(ERROR_INVALID_FUNCTION);
6932 return INVALID_FILE_SIZE;
6933 }
6934 }
6935 }
6936
6937 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
6938 return GetFileSize(hFile, pcbHighDword);
6939}
6940
6941
6942/** Kernel32 - GetFileSizeEx */
6943static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
6944{
6945 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6946 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6947 if (idxHandle < g_Sandbox.cHandles)
6948 {
6949 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6950 if (pHandle != NULL)
6951 {
6952 switch (pHandle->enmType)
6953 {
6954 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6955 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
6956 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
6957 return TRUE;
6958
6959 case KWHANDLETYPE_TEMP_FILE:
6960 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
6961 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
6962 return TRUE;
6963
6964 case KWHANDLETYPE_OUTPUT_BUF:
6965 /* do default */
6966 break;
6967
6968 default:
6969 kHlpAssertFailed();
6970 SetLastError(ERROR_INVALID_FUNCTION);
6971 return INVALID_FILE_SIZE;
6972 }
6973 }
6974 }
6975
6976 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
6977 return GetFileSizeEx(hFile, pcbFile);
6978}
6979
6980
6981/** Kernel32 - CreateFileMappingW */
6982static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
6983 DWORD fProtect, DWORD dwMaximumSizeHigh,
6984 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
6985{
6986 HANDLE hMapping;
6987 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6988 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6989 if (idxHandle < g_Sandbox.cHandles)
6990 {
6991 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6992 if (pHandle != NULL)
6993 {
6994 switch (pHandle->enmType)
6995 {
6996 case KWHANDLETYPE_TEMP_FILE:
6997 {
6998 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6999 if ( ( fProtect == PAGE_READONLY
7000 || fProtect == PAGE_EXECUTE_READ)
7001 && dwMaximumSizeHigh == 0
7002 && ( dwMaximumSizeLow == 0
7003 || dwMaximumSizeLow == pTempFile->cbFile)
7004 && pwszName == NULL)
7005 {
7006 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
7007 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
7008 return hMapping;
7009 }
7010 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
7011 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7012 SetLastError(ERROR_ACCESS_DENIED);
7013 return INVALID_HANDLE_VALUE;
7014 }
7015
7016 /* moc.exe benefits from this. */
7017 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7018 {
7019 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7020 if ( ( fProtect == PAGE_READONLY
7021 || fProtect == PAGE_EXECUTE_READ)
7022 && dwMaximumSizeHigh == 0
7023 && ( dwMaximumSizeLow == 0
7024 || dwMaximumSizeLow == pCachedFile->cbCached)
7025 && pwszName == NULL)
7026 {
7027 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
7028 K_FALSE /*fIsFileHandle*/, &hMapping))
7029 { /* likely */ }
7030 else
7031 hMapping = NULL;
7032 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
7033 return hMapping;
7034 }
7035
7036 /* Do fallback (for .pch). */
7037 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
7038 ("fProtect=%#x cb=%#x'%08x name=%p\n",
7039 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7040
7041 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7042 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
7043 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7044 return hMapping;
7045 }
7046
7047 /** @todo read cached memory mapped files too for moc. */
7048 }
7049 }
7050 }
7051
7052 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7053 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
7054 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7055 return hMapping;
7056}
7057
7058
7059/** Kernel32 - MapViewOfFile */
7060static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
7061 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
7062{
7063 PVOID pvRet;
7064 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7065 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7066 if (idxHandle < g_Sandbox.cHandles)
7067 {
7068 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7069 if (pHandle != NULL)
7070 {
7071 KU32 idxMapping;
7072
7073 /*
7074 * Ensure one free entry in the mapping tracking table first,
7075 * since this is common to both temporary and cached files.
7076 */
7077 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
7078 { /* likely */ }
7079 else
7080 {
7081 void *pvNew;
7082 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
7083 if (cNew)
7084 cNew *= 2;
7085 else
7086 cNew = 32;
7087 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings));
7088 if (pvNew)
7089 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
7090 else
7091 {
7092 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
7093 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7094 return NULL;
7095 }
7096 g_Sandbox.cMemMappingsAlloc = cNew;
7097 }
7098
7099 /*
7100 * Type specific work.
7101 */
7102 switch (pHandle->enmType)
7103 {
7104 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7105 case KWHANDLETYPE_TEMP_FILE:
7106 case KWHANDLETYPE_OUTPUT_BUF:
7107 default:
7108 kHlpAssertFailed();
7109 SetLastError(ERROR_INVALID_OPERATION);
7110 return NULL;
7111
7112 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7113 {
7114 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7115 if ( dwDesiredAccess == FILE_MAP_READ
7116 && offFileHigh == 0
7117 && offFileLow == 0
7118 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
7119 {
7120 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
7121 if (pTempFile->cSegs != 1)
7122 {
7123 KU32 iSeg;
7124 KU32 cbLeft;
7125 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
7126 KU8 *pbAll = NULL;
7127 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
7128 if (rc != 0)
7129 {
7130 kHlpAssertFailed();
7131 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7132 return NULL;
7133 }
7134
7135 cbLeft = pTempFile->cbFile;
7136 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
7137 {
7138 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
7139 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
7140 cbLeft -= cbToCopy;
7141 }
7142
7143 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
7144 {
7145 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
7146 pTempFile->paSegs[iSeg].pbData = NULL;
7147 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
7148 }
7149
7150 pTempFile->cSegs = 1;
7151 pTempFile->cbFileAllocated = cbAll;
7152 pTempFile->paSegs[0].cbDataAlloc = cbAll;
7153 pTempFile->paSegs[0].pbData = pbAll;
7154 pTempFile->paSegs[0].offData = 0;
7155 }
7156
7157 pTempFile->cMappings++;
7158 kHlpAssert(pTempFile->cMappings == 1);
7159
7160 pvRet = pTempFile->paSegs[0].pbData;
7161 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
7162 break;
7163 }
7164
7165 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7166 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
7167 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7168 return NULL;
7169 }
7170
7171 /*
7172 * This is simple in comparison to the above temporary file code.
7173 */
7174 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7175 {
7176 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7177 if ( dwDesiredAccess == FILE_MAP_READ
7178 && offFileHigh == 0
7179 && offFileLow == 0
7180 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
7181 {
7182 pvRet = pCachedFile->pbCached;
7183 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
7184 break;
7185 }
7186 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7187 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
7188 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7189 return NULL;
7190 }
7191 }
7192
7193 /*
7194 * Insert into the mapping tracking table. This is common
7195 * and we should only get here with a non-NULL pvRet.
7196 *
7197 * Note! We could look for duplicates and do ref counting, but it's
7198 * easier to just append for now.
7199 */
7200 kHlpAssert(pvRet != NULL);
7201 idxMapping = g_Sandbox.cMemMappings;
7202 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
7203
7204 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
7205 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
7206 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
7207 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
7208 g_Sandbox.cMemMappings++;
7209
7210 return pvRet;
7211 }
7212 }
7213
7214 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7215 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
7216 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
7217 return pvRet;
7218}
7219
7220
7221/** Kernel32 - MapViewOfFileEx */
7222static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
7223 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
7224{
7225 PVOID pvRet;
7226 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7227 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7228 if (idxHandle < g_Sandbox.cHandles)
7229 {
7230 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7231 if (pHandle != NULL)
7232 {
7233 switch (pHandle->enmType)
7234 {
7235 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7236 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
7237 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7238 if (!pvMapAddr)
7239 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7240 kHlpAssertFailed();
7241 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7242 return NULL;
7243
7244 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7245 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
7246 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7247 if (!pvMapAddr)
7248 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7249 /* We can use fallback here as the handle is an actual section handle. */
7250 break;
7251
7252 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7253 case KWHANDLETYPE_TEMP_FILE:
7254 case KWHANDLETYPE_OUTPUT_BUF:
7255 default:
7256 kHlpAssertFailed();
7257 SetLastError(ERROR_INVALID_OPERATION);
7258 return NULL;
7259 }
7260 }
7261 }
7262
7263 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
7264 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
7265 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
7266 return pvRet;
7267
7268}
7269
7270/** Kernel32 - UnmapViewOfFile */
7271static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
7272{
7273 /*
7274 * Consult the memory mapping tracker.
7275 */
7276 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
7277 KU32 idxMapping = g_Sandbox.cMemMappings;
7278 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7279 while (idxMapping-- > 0)
7280 if (paMemMappings[idxMapping].pvMapping == pvBase)
7281 {
7282 /* Type specific stuff. */
7283 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
7284 {
7285 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
7286 paMemMappings[idxMapping].u.pTempFile->cMappings--;
7287 }
7288 else
7289 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
7290
7291 /* Deref and probably free it. */
7292 if (--paMemMappings[idxMapping].cRefs == 0)
7293 {
7294 g_Sandbox.cMemMappings--;
7295 if (idxMapping != g_Sandbox.cMemMappings)
7296 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
7297 }
7298 return TRUE;
7299 }
7300
7301 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
7302 return UnmapViewOfFile(pvBase);
7303}
7304
7305/** @todo UnmapViewOfFileEx */
7306
7307#endif /* WITH_TEMP_MEMORY_FILES */
7308
7309
7310/** Kernel32 - DuplicateHandle */
7311static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
7312 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
7313{
7314 BOOL fRet;
7315
7316 /*
7317 * We must catch our handles being duplicated.
7318 */
7319 if (hSrcProc == GetCurrentProcess())
7320 {
7321 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
7322 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7323 if (idxHandle < g_Sandbox.cHandles)
7324 {
7325 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7326 if (pHandle)
7327 {
7328 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7329 if (fRet)
7330 {
7331 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
7332 {
7333 pHandle->cRefs++;
7334 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
7335 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
7336 pHandle->enmType, pHandle->cRefs));
7337 }
7338 else
7339 {
7340 fRet = FALSE;
7341 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7342 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
7343 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
7344 }
7345 }
7346 else
7347 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
7348 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
7349 return fRet;
7350 }
7351 }
7352 }
7353
7354 /*
7355 * Not one of ours, just do what the caller asks and log it.
7356 */
7357 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7358 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
7359 fInheritHandle, dwOptions, fRet, *phNew));
7360 return fRet;
7361}
7362
7363
7364/** Kernel32 - CloseHandle */
7365static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
7366{
7367 BOOL fRet;
7368 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
7369 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_fCtrlC);
7370 if (idxHandle < g_Sandbox.cHandles)
7371 {
7372 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7373 if (pHandle)
7374 {
7375 /* Prevent the closing of the standard output and error handles. */
7376 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
7377 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
7378 {
7379 fRet = CloseHandle(hObject);
7380 if (fRet)
7381 {
7382 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7383 g_Sandbox.papHandles[idxHandle] = NULL;
7384 g_Sandbox.cActiveHandles--;
7385 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
7386 if (--pHandle->cRefs == 0)
7387 {
7388#ifdef WITH_TEMP_MEMORY_FILES
7389 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
7390 {
7391 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
7392 pHandle->u.pTempFile->cActiveHandles--;
7393 }
7394#endif
7395 kHlpFree(pHandle);
7396 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
7397 }
7398 else
7399 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
7400 }
7401 else
7402 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
7403 }
7404 else
7405 {
7406 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
7407 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
7408 fRet = TRUE;
7409 }
7410 return fRet;
7411 }
7412 }
7413
7414 fRet = CloseHandle(hObject);
7415 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
7416 return fRet;
7417}
7418
7419
7420/** Kernel32 - GetFileAttributesA. */
7421static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
7422{
7423 DWORD fRet;
7424 const char *pszExt = kHlpGetExt(pszFilename);
7425 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
7426 {
7427 KFSLOOKUPERROR enmError;
7428 PKFSOBJ pFsObj;
7429 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7430
7431 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
7432 if (pFsObj)
7433 {
7434 kHlpAssert(pFsObj->fHaveStats);
7435 fRet = pFsObj->Stats.st_attribs;
7436 kFsCacheObjRelease(g_pFsCache, pFsObj);
7437 }
7438 else
7439 {
7440 SetLastError(kwFsLookupErrorToWindowsError(enmError));
7441 fRet = INVALID_FILE_ATTRIBUTES;
7442 }
7443
7444 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
7445 return fRet;
7446 }
7447
7448 fRet = GetFileAttributesA(pszFilename);
7449 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
7450 return fRet;
7451}
7452
7453
7454/** Kernel32 - GetFileAttributesW. */
7455static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
7456{
7457 DWORD fRet;
7458 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
7459 {
7460 KFSLOOKUPERROR enmError;
7461 PKFSOBJ pFsObj;
7462 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7463
7464 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
7465 if (pFsObj)
7466 {
7467 kHlpAssert(pFsObj->fHaveStats);
7468 fRet = pFsObj->Stats.st_attribs;
7469 kFsCacheObjRelease(g_pFsCache, pFsObj);
7470 }
7471 else
7472 {
7473 SetLastError(kwFsLookupErrorToWindowsError(enmError));
7474 fRet = INVALID_FILE_ATTRIBUTES;
7475 }
7476
7477 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
7478 return fRet;
7479 }
7480
7481 fRet = GetFileAttributesW(pwszFilename);
7482 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
7483 return fRet;
7484}
7485
7486
7487/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
7488 * directory containing each include file. We cache the result to speed
7489 * things up a little. */
7490static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
7491{
7492 DWORD cwcRet;
7493 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
7494 {
7495 KFSLOOKUPERROR enmError;
7496 PKFSOBJ pObj;
7497 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7498
7499 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
7500 if (pObj)
7501 {
7502 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
7503 {
7504 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
7505 {
7506 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
7507
7508 /* Should preserve trailing slash on directory paths. */
7509 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
7510 {
7511 if ( cwcRet + 1 < cwcShortPath
7512 && pwszShortPath[cwcRet - 1] != '\\')
7513 {
7514 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
7515 if ( cwcIn > 0
7516 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
7517 {
7518 pwszShortPath[cwcRet++] = '\\';
7519 pwszShortPath[cwcRet] = '\0';
7520 }
7521 }
7522 }
7523
7524 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
7525 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
7526 kFsCacheObjRelease(g_pFsCache, pObj);
7527 return cwcRet;
7528 }
7529
7530 /* fall back for complicated cases. */
7531 }
7532 kFsCacheObjRelease(g_pFsCache, pObj);
7533 }
7534 }
7535 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
7536 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
7537 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
7538 return cwcRet;
7539}
7540
7541
7542#ifdef WITH_TEMP_MEMORY_FILES
7543/** Kernel32 - DeleteFileW
7544 * Skip deleting the in-memory files. */
7545static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
7546{
7547 BOOL fRc;
7548 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7549 && kwFsIsClTempFileW(pwszFilename))
7550 {
7551 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7552 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
7553 fRc = TRUE;
7554 }
7555 else
7556 {
7557 fRc = DeleteFileW(pwszFilename);
7558 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
7559 }
7560 return fRc;
7561}
7562#endif /* WITH_TEMP_MEMORY_FILES */
7563
7564
7565
7566#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
7567
7568/*
7569 *
7570 * Console output buffering.
7571 * Console output buffering.
7572 * Console output buffering.
7573 *
7574 */
7575
7576
7577/**
7578 * Write a wide char string to the console.
7579 *
7580 * @param pSandbox The sandbox which output buffer to flush.
7581 */
7582static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
7583{
7584 if (cwcToWrite > 0)
7585 {
7586 DWORD cwcWritten = 0;
7587 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
7588 {
7589 if (cwcWritten == cwcToWrite)
7590 { /* likely */ }
7591 else
7592 {
7593 DWORD off = 0;
7594 do
7595 {
7596 off += cwcWritten;
7597 cwcWritten = 0;
7598 } while ( off < cwcToWrite
7599 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
7600 kHlpAssert(off == cwcWritten);
7601 }
7602 }
7603 else
7604 kHlpAssertFailed();
7605 pSandbox->Combined.cFlushes++;
7606 }
7607}
7608
7609
7610/**
7611 * Flushes the combined console output buffer.
7612 *
7613 * @param pSandbox The sandbox which output buffer to flush.
7614 */
7615static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
7616{
7617 if (pSandbox->Combined.cwcBuf > 0)
7618 {
7619 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
7620 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
7621 pSandbox->Combined.cwcBuf = 0;
7622 }
7623}
7624
7625
7626/**
7627 * For handling combined buffer overflow cases line by line.
7628 *
7629 * @param pSandbox The sandbox.
7630 * @param pwcBuf What to add to the combined buffer. Usually a
7631 * line, unless we're really low on buffer space.
7632 * @param cwcBuf The length of what to add.
7633 * @param fBrokenLine Whether this is a broken line.
7634 */
7635static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
7636{
7637 if (fBrokenLine)
7638 kwSandboxConsoleFlushCombined(pSandbox);
7639 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
7640 {
7641 kwSandboxConsoleFlushCombined(pSandbox);
7642 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
7643 }
7644 else
7645 {
7646 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
7647 pSandbox->Combined.cwcBuf += cwcBuf;
7648 }
7649}
7650
7651
7652/**
7653 * Called to final flush a line buffer via the combined buffer (if applicable).
7654 *
7655 * @param pSandbox The sandbox.
7656 * @param pLineBuf The line buffer.
7657 * @param pszName The line buffer name (for logging)
7658 */
7659static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
7660{
7661 if (pLineBuf->fIsConsole)
7662 {
7663 if (pLineBuf->u.Con.cwcBuf > 0)
7664 {
7665 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
7666
7667 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
7668 {
7669 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
7670 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
7671 }
7672 else
7673 {
7674 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
7675 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
7676 }
7677 pLineBuf->u.Con.cwcBuf = 0;
7678 }
7679 }
7680#ifdef WITH_STD_OUT_ERR_BUFFERING
7681 else if (pLineBuf->u.Fully.cchBuf > 0)
7682 {
7683 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
7684
7685 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
7686 pLineBuf->u.Fully.cchBuf = 0;
7687 }
7688#endif
7689}
7690
7691
7692/**
7693 * Called at the end of sandboxed execution to flush both stream buffers.
7694 *
7695 * @param pSandbox The sandbox.
7696 */
7697static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
7698{
7699 /*
7700 * First do the cl.exe source file supression trick, if applicable.
7701 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
7702 * handle.
7703 */
7704 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7705 && pSandbox->Combined.cFlushes == 0)
7706 {
7707 if ( pSandbox->StdOut.fIsConsole
7708 || pSandbox->StdErr.fIsConsole)
7709 {
7710 if ( pSandbox->Combined.cwcBuf >= 3
7711 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
7712 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
7713 {
7714 KI32 off = pSandbox->Combined.cwcBuf - 1;
7715 if (pSandbox->Combined.wszBuf[off] == '\n')
7716 {
7717 KBOOL fOk = K_TRUE;
7718 while (off-- > 0)
7719 {
7720 wchar_t const wc = pSandbox->Combined.wszBuf[off];
7721 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
7722 { /* likely */ }
7723 else
7724 {
7725 fOk = K_FALSE;
7726 break;
7727 }
7728 }
7729 if (fOk)
7730 {
7731 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
7732 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
7733 pSandbox->Combined.cwcBuf = 0;
7734 return;
7735 }
7736 }
7737 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
7738 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
7739 }
7740 }
7741#ifdef WITH_STD_OUT_ERR_BUFFERING
7742 /*
7743 * Otherwise, it goes to standard output (redirected).
7744 */
7745 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
7746 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
7747 {
7748 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
7749 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
7750 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
7751
7752 if (pchBuf[off] == '\n')
7753 {
7754 KBOOL fOk = K_TRUE;
7755 if (pchBuf[off - 1] == '\r')
7756 off--;
7757 while (off-- > 0)
7758 {
7759 char const ch = pchBuf[off];
7760 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
7761 { /* likely */ }
7762 else
7763 {
7764 fOk = K_FALSE;
7765 break;
7766 }
7767 }
7768 if (fOk)
7769 {
7770 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
7771 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
7772 pSandbox->StdOut.u.Fully.cchBuf = 0;
7773 return;
7774 }
7775 }
7776 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
7777 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
7778 }
7779#endif
7780 }
7781
7782 /*
7783 * Flush the two line buffer, the the combined buffer.
7784 */
7785 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
7786 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
7787 kwSandboxConsoleFlushCombined(pSandbox);
7788}
7789
7790
7791/**
7792 * Writes a string to the given output stream.
7793 *
7794 * @param pSandbox The sandbox.
7795 * @param pLineBuf The line buffer for the output stream.
7796 * @param pwcBuffer The buffer to write.
7797 * @param cwcToWrite The number of wchar_t's in the buffer.
7798 */
7799static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
7800{
7801 kHlpAssert(pLineBuf->fIsConsole);
7802 if (cwcToWrite > 0)
7803 {
7804 /*
7805 * First, find the start of the last incomplete line so we can figure
7806 * out how much line buffering we need to do.
7807 */
7808 KU32 cchLastIncompleteLine;
7809 KU32 offLastIncompleteLine = cwcToWrite;
7810 while ( offLastIncompleteLine > 0
7811 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
7812 offLastIncompleteLine--;
7813 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
7814
7815 /* Was there anything to line buffer? */
7816 if (offLastIncompleteLine < cwcToWrite)
7817 {
7818 /* Need to grow the line buffer? */
7819 KU32 cwcNeeded = offLastIncompleteLine == 0
7820 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
7821 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
7822 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
7823 {
7824 void *pvNew;
7825 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
7826 while (cwcNew < cwcNeeded)
7827 cwcNew *= 2;
7828 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
7829 if (pvNew)
7830 {
7831 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
7832 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
7833 }
7834 else
7835 {
7836 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
7837 if (pvNew)
7838 {
7839 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
7840 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
7841 }
7842 else
7843 {
7844 /* This isn't perfect, but it will have to do for now. */
7845 if (pLineBuf->u.Con.cwcBuf > 0)
7846 {
7847 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
7848 K_TRUE /*fBrokenLine*/);
7849 pLineBuf->u.Con.cwcBuf = 0;
7850 }
7851 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
7852 return;
7853 }
7854 }
7855 }
7856
7857 /*
7858 * Handle the case where we only add to the line buffer.
7859 */
7860 if (offLastIncompleteLine == 0)
7861 {
7862 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
7863 pLineBuf->u.Con.cwcBuf += cwcToWrite;
7864 return;
7865 }
7866 }
7867
7868 /*
7869 * If there is sufficient combined buffer to handle this request, this is rather simple.
7870 */
7871 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
7872 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
7873 {
7874 if (pLineBuf->u.Con.cwcBuf > 0)
7875 {
7876 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7877 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
7878 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
7879 pLineBuf->u.Con.cwcBuf = 0;
7880 }
7881
7882 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7883 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
7884 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
7885 }
7886 else
7887 {
7888 /*
7889 * Do line-by-line processing of the input, flusing the combined buffer
7890 * when it becomes necessary. We may have to write lines
7891 */
7892 KU32 off = 0;
7893 KU32 offNextLine = 0;
7894
7895 /* If there are buffered chars, we handle the first line outside the
7896 main loop. We must try our best outputting it as a complete line. */
7897 if (pLineBuf->u.Con.cwcBuf > 0)
7898 {
7899 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
7900 offNextLine++;
7901 offNextLine++;
7902 kHlpAssert(offNextLine <= offLastIncompleteLine);
7903
7904 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
7905 {
7906 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7907 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
7908 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
7909 pLineBuf->u.Con.cwcBuf = 0;
7910
7911 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
7912 pSandbox->Combined.cwcBuf += offNextLine;
7913 }
7914 else
7915 {
7916 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
7917 if (cwcLeft > 0)
7918 {
7919 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
7920 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
7921 pLineBuf->u.Con.cwcBuf += cwcCopy;
7922 off += cwcCopy;
7923 }
7924 if (pLineBuf->u.Con.cwcBuf > 0)
7925 {
7926 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
7927 K_TRUE /*fBrokenLine*/);
7928 pLineBuf->u.Con.cwcBuf = 0;
7929 }
7930 if (off < offNextLine)
7931 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
7932 }
7933 off = offNextLine;
7934 }
7935
7936 /* Deal with the remaining lines */
7937 while (off < offLastIncompleteLine)
7938 {
7939 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
7940 offNextLine++;
7941 offNextLine++;
7942 kHlpAssert(offNextLine <= offLastIncompleteLine);
7943 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
7944 off = offNextLine;
7945 }
7946 }
7947
7948 /*
7949 * Buffer any remaining incomplete line chars.
7950 */
7951 if (cchLastIncompleteLine)
7952 {
7953 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
7954 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
7955 }
7956 }
7957}
7958
7959
7960/**
7961 * Worker for WriteConsoleA and WriteFile.
7962 *
7963 * @param pSandbox The sandbox.
7964 * @param pLineBuf The line buffer.
7965 * @param pchBuffer What to write.
7966 * @param cchToWrite How much to write.
7967 */
7968static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
7969{
7970 /*
7971 * Convert it to wide char and use the 'W' to do the work.
7972 */
7973 int cwcRet;
7974 KU32 cwcBuf = cchToWrite * 2 + 1;
7975 wchar_t *pwcBufFree = NULL;
7976 wchar_t *pwcBuf;
7977 kHlpAssert(pLineBuf->fIsConsole);
7978
7979 if (cwcBuf <= 4096)
7980 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
7981 else
7982 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
7983
7984 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
7985 if (cwcRet > 0)
7986 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
7987 else
7988 {
7989 DWORD cchWritten;
7990 kHlpAssertFailed();
7991
7992 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
7993 if (pLineBuf->u.Con.cwcBuf > 0)
7994 {
7995 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
7996 pLineBuf->u.Con.cwcBuf = 0;
7997 }
7998 kwSandboxConsoleFlushCombined(pSandbox);
7999
8000 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
8001 {
8002 if (cchWritten >= cchToWrite)
8003 { /* likely */ }
8004 else
8005 {
8006 KU32 off = 0;
8007 do
8008 {
8009 off += cchWritten;
8010 cchWritten = 0;
8011 } while ( off < cchToWrite
8012 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
8013 }
8014 }
8015 }
8016
8017 if (pwcBufFree)
8018 kHlpFree(pwcBufFree);
8019}
8020
8021
8022/** Kernel32 - WriteConsoleA */
8023BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
8024 PVOID pvReserved)
8025{
8026 BOOL fRc;
8027 PKWOUTPUTSTREAMBUF pLineBuf;
8028 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8029
8030 if (hConOutput == g_Sandbox.StdErr.hOutput)
8031 pLineBuf = &g_Sandbox.StdErr;
8032 else
8033 pLineBuf = &g_Sandbox.StdOut;
8034 if (pLineBuf->fIsConsole)
8035 {
8036 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
8037
8038 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
8039 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
8040 if (pcbWritten)
8041 *pcbWritten = cbToWrite;
8042 fRc = TRUE;
8043 }
8044 else
8045 {
8046 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
8047 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
8048 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
8049 }
8050 return fRc;
8051}
8052
8053
8054/** Kernel32 - WriteConsoleW */
8055BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
8056 PVOID pvReserved)
8057{
8058 BOOL fRc;
8059 PKWOUTPUTSTREAMBUF pLineBuf;
8060 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8061
8062 if (hConOutput == g_Sandbox.StdErr.hOutput)
8063 pLineBuf = &g_Sandbox.StdErr;
8064 else if (hConOutput == g_Sandbox.StdOut.hOutput)
8065 pLineBuf = &g_Sandbox.StdOut;
8066 else
8067 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
8068 if (pLineBuf->fIsConsole)
8069 {
8070 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
8071
8072 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
8073 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
8074 if (pcwcWritten)
8075 *pcwcWritten = cwcToWrite;
8076 fRc = TRUE;
8077 }
8078 else
8079 {
8080 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
8081 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
8082 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
8083 }
8084 return fRc;
8085}
8086
8087#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8088
8089
8090
8091/*
8092 *
8093 * Virtual memory leak prevension.
8094 * Virtual memory leak prevension.
8095 * Virtual memory leak prevension.
8096 *
8097 */
8098
8099#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8100
8101/** For debug logging. */
8102# ifndef NDEBUG
8103static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
8104{
8105 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
8106 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
8107 kHlpAssert(cbMemInfo == sizeof(MemInfo));
8108 if (cbMemInfo != 0)
8109 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
8110 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
8111 MemInfo.BaseAddress,
8112 MemInfo.AllocationBase,
8113 MemInfo.RegionSize,
8114 MemInfo.State,
8115 MemInfo.Protect,
8116 MemInfo.Type));
8117}
8118# else
8119# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
8120# endif
8121
8122/**
8123 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
8124 *
8125 * @param idxFixed The fixed allocation index to "free".
8126 */
8127static void kwSandboxResetFixedAllocation(KU32 idxFixed)
8128{
8129 BOOL fRc;
8130 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
8131 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
8132 kHlpAssert(fRc); K_NOREF(fRc);
8133 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
8134 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
8135}
8136
8137#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
8138
8139
8140/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
8141 * location (~78MB in 32-bit 2010 compiler). */
8142static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
8143{
8144 PVOID pvMem;
8145 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8146 {
8147 KU32 idxPreAllocated = KU32_MAX;
8148
8149#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8150 /*
8151 * Look for a pre-reserved CL.exe heap allocation.
8152 */
8153 pvMem = NULL;
8154 if ( pvAddr != 0
8155 && (fAllocType & MEM_RESERVE))
8156 {
8157 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8158 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
8159 while (idxFixed-- > 0)
8160 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
8161 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
8162 {
8163 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
8164 {
8165 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
8166 {
8167 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
8168 pvMem = pvAddr;
8169 idxPreAllocated = idxFixed;
8170 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
8171 pvAddr, cb, fAllocType, fProt, pvMem));
8172 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
8173 SetLastError(NO_ERROR);
8174 break;
8175 }
8176 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
8177 }
8178 else
8179 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
8180 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
8181 }
8182 }
8183 if (!pvMem)
8184#endif
8185 {
8186 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8187 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8188 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8189 if (pvAddr && pvAddr != pvMem)
8190 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
8191 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
8192 }
8193
8194 if (pvMem)
8195 {
8196 /*
8197 * Track it.
8198 */
8199 PKWVIRTALLOC pTracker;
8200 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8201
8202 pTracker = g_Sandbox.pVirtualAllocHead;
8203 while ( pTracker
8204 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
8205 pTracker = pTracker->pNext;
8206 if (!pTracker)
8207 {
8208 DWORD dwErr = GetLastError();
8209 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
8210 if (pTracker)
8211 {
8212 pTracker->pvAlloc = pvMem;
8213 pTracker->cbAlloc = cb;
8214 pTracker->idxPreAllocated = idxPreAllocated;
8215 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8216 g_Sandbox.pVirtualAllocHead = pTracker;
8217 }
8218 SetLastError(dwErr);
8219 }
8220 }
8221 }
8222 else
8223 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8224 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8225 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8226 return pvMem;
8227}
8228
8229
8230/** Kernel32 - VirtualFree. */
8231static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
8232{
8233 BOOL fRc;
8234 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8235 {
8236 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8237 if (dwFreeType & MEM_RELEASE)
8238 {
8239 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
8240 if (pTracker)
8241 {
8242 if (pTracker->pvAlloc == pvAddr)
8243 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
8244 else
8245 {
8246 PKWVIRTALLOC pPrev;
8247 do
8248 {
8249 pPrev = pTracker;
8250 pTracker = pTracker->pNext;
8251 } while (pTracker && pTracker->pvAlloc != pvAddr);
8252 if (pTracker)
8253 pPrev->pNext = pTracker->pNext;
8254 }
8255 if (pTracker)
8256 {
8257#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8258 if (pTracker->idxPreAllocated != KU32_MAX)
8259 {
8260 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
8261 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
8262 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
8263 kHlpFree(pTracker);
8264 return TRUE;
8265 }
8266#endif
8267
8268 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8269 if (fRc)
8270 kHlpFree(pTracker);
8271 else
8272 {
8273 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8274 g_Sandbox.pVirtualAllocHead = pTracker;
8275 }
8276 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8277 return fRc;
8278 }
8279
8280 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
8281 }
8282 }
8283 }
8284
8285#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8286 /*
8287 * Protect our fixed allocations (this isn't just paranoia, btw.).
8288 */
8289 if (dwFreeType & MEM_RELEASE)
8290 {
8291 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8292 while (idxFixed-- > 0)
8293 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
8294 {
8295 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
8296 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
8297 return TRUE;
8298 }
8299 }
8300#endif
8301
8302 /*
8303 * Not tracker or not actually free the virtual range.
8304 */
8305 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8306 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8307 return fRc;
8308}
8309
8310
8311/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
8312HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
8313{
8314 HANDLE hHeap;
8315 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8316
8317 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
8318 if (hHeap != NULL)
8319 {
8320 DWORD dwErr = GetLastError();
8321 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
8322 if (pTracker)
8323 {
8324 pTracker->hHeap = hHeap;
8325 pTracker->pNext = g_Sandbox.pHeapHead;
8326 g_Sandbox.pHeapHead = pTracker;
8327 }
8328
8329 SetLastError(dwErr);
8330 }
8331 return hHeap;
8332
8333}
8334
8335
8336/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
8337BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
8338{
8339 BOOL fRc = HeapDestroy(hHeap);
8340 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
8341 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8342 if (fRc)
8343 {
8344 PKWHEAP pTracker = g_Sandbox.pHeapHead;
8345 if (pTracker)
8346 {
8347 if (pTracker->hHeap == hHeap)
8348 g_Sandbox.pHeapHead = pTracker->pNext;
8349 else
8350 {
8351 PKWHEAP pPrev;
8352 do
8353 {
8354 pPrev = pTracker;
8355 pTracker = pTracker->pNext;
8356 } while (pTracker && pTracker->hHeap == hHeap);
8357 if (pTracker)
8358 pPrev->pNext = pTracker->pNext;
8359 }
8360 if (pTracker)
8361 kHlpFree(pTracker);
8362 else
8363 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
8364 }
8365 }
8366
8367 return fRc;
8368}
8369
8370
8371
8372/*
8373 *
8374 * Thread/Fiber local storage leak prevention.
8375 * Thread/Fiber local storage leak prevention.
8376 * Thread/Fiber local storage leak prevention.
8377 *
8378 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
8379 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
8380 * we're leaking these indexes, but more importantely we crash during
8381 * worker exit since the callback is triggered multiple times.
8382 */
8383
8384
8385/** Kernel32 - FlsAlloc */
8386DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
8387{
8388 DWORD idxFls = FlsAlloc(pfnCallback);
8389 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
8390 if (idxFls != FLS_OUT_OF_INDEXES)
8391 {
8392 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
8393 if (pTracker)
8394 {
8395 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8396 pTracker->idx = idxFls;
8397 pTracker->pNext = g_Sandbox.pFlsAllocHead;
8398 g_Sandbox.pFlsAllocHead = pTracker;
8399 }
8400 }
8401
8402 return idxFls;
8403}
8404
8405/** Kernel32 - FlsFree */
8406BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
8407{
8408 BOOL fRc = FlsFree(idxFls);
8409 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
8410 if (fRc)
8411 {
8412 PKWLOCALSTORAGE pTracker;
8413 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8414
8415 pTracker = g_Sandbox.pFlsAllocHead;
8416 if (pTracker)
8417 {
8418 if (pTracker->idx == idxFls)
8419 g_Sandbox.pFlsAllocHead = pTracker->pNext;
8420 else
8421 {
8422 PKWLOCALSTORAGE pPrev;
8423 do
8424 {
8425 pPrev = pTracker;
8426 pTracker = pTracker->pNext;
8427 } while (pTracker && pTracker->idx != idxFls);
8428 if (pTracker)
8429 pPrev->pNext = pTracker->pNext;
8430 }
8431 if (pTracker)
8432 {
8433 pTracker->idx = FLS_OUT_OF_INDEXES;
8434 pTracker->pNext = NULL;
8435 kHlpFree(pTracker);
8436 }
8437 }
8438 }
8439 return fRc;
8440}
8441
8442
8443/** Kernel32 - TlsAlloc */
8444DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
8445{
8446 DWORD idxTls = TlsAlloc();
8447 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
8448 if (idxTls != TLS_OUT_OF_INDEXES)
8449 {
8450 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
8451 if (pTracker)
8452 {
8453 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8454 pTracker->idx = idxTls;
8455 pTracker->pNext = g_Sandbox.pTlsAllocHead;
8456 g_Sandbox.pTlsAllocHead = pTracker;
8457 }
8458 }
8459
8460 return idxTls;
8461}
8462
8463/** Kernel32 - TlsFree */
8464BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
8465{
8466 BOOL fRc = TlsFree(idxTls);
8467 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
8468 if (fRc)
8469 {
8470 PKWLOCALSTORAGE pTracker;
8471 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8472
8473 pTracker = g_Sandbox.pTlsAllocHead;
8474 if (pTracker)
8475 {
8476 if (pTracker->idx == idxTls)
8477 g_Sandbox.pTlsAllocHead = pTracker->pNext;
8478 else
8479 {
8480 PKWLOCALSTORAGE pPrev;
8481 do
8482 {
8483 pPrev = pTracker;
8484 pTracker = pTracker->pNext;
8485 } while (pTracker && pTracker->idx != idxTls);
8486 if (pTracker)
8487 pPrev->pNext = pTracker->pNext;
8488 }
8489 if (pTracker)
8490 {
8491 pTracker->idx = TLS_OUT_OF_INDEXES;
8492 pTracker->pNext = NULL;
8493 kHlpFree(pTracker);
8494 }
8495 }
8496 }
8497 return fRc;
8498}
8499
8500
8501
8502/*
8503 *
8504 * Header file hashing.
8505 * Header file hashing.
8506 * Header file hashing.
8507 *
8508 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
8509 * indicated that ~12% of the time was spent doing MD5 caluclation when
8510 * rebuiling openssl. The hashing it done right after reading the source
8511 * via ReadFile, same buffers and sizes.
8512 */
8513
8514#ifdef WITH_HASH_MD5_CACHE
8515
8516/** AdvApi32 - CryptCreateHash */
8517static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
8518 HCRYPTHASH *phHash)
8519{
8520 BOOL fRc;
8521
8522 /*
8523 * Only do this for cl.exe when it request normal MD5.
8524 */
8525 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8526 {
8527 if (idAlg == CALG_MD5)
8528 {
8529 if (hKey == 0)
8530 {
8531 if (dwFlags == 0)
8532 {
8533 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
8534 if (pHash)
8535 {
8536 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8537 pHash->uMagic = KWHASHMD5_MAGIC;
8538 pHash->cbHashed = 0;
8539 pHash->fGoneBad = K_FALSE;
8540 pHash->fFallbackMode = K_FALSE;
8541 pHash->fFinal = K_FALSE;
8542
8543 /* link it. */
8544 pHash->pNext = g_Sandbox.pHashHead;
8545 g_Sandbox.pHashHead = pHash;
8546
8547 *phHash = (KUPTR)pHash;
8548 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
8549 hProv, *phHash, TRUE));
8550 return TRUE;
8551 }
8552
8553 kwErrPrintf("CryptCreateHash: out of memory!\n");
8554 }
8555 else
8556 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
8557 }
8558 else
8559 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
8560 }
8561 else
8562 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
8563 }
8564
8565 /*
8566 * Fallback.
8567 */
8568 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
8569 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
8570 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
8571 return fRc;
8572}
8573
8574
8575/** AdvApi32 - CryptHashData */
8576static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
8577{
8578 BOOL fRc;
8579 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
8580 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8581 while (pHash && (KUPTR)pHash != hHash)
8582 pHash = pHash->pNext;
8583 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
8584 hHash, pHash, pbData, cbData, dwFlags));
8585 if (pHash)
8586 {
8587 /*
8588 * Validate the state.
8589 */
8590 if ( pHash->uMagic == KWHASHMD5_MAGIC
8591 && !pHash->fFinal)
8592 {
8593 if (!pHash->fFallbackMode)
8594 {
8595 /*
8596 * Does this match the previous ReadFile call to a cached file?
8597 * If it doesn't, try falling back.
8598 */
8599 if ( g_Sandbox.LastHashRead.cbRead == cbData
8600 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
8601 {
8602 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
8603 if ( pCachedFile
8604 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
8605 {
8606
8607 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
8608 {
8609 if ( pHash->pCachedFile == NULL
8610 && pHash->cbHashed == 0)
8611 pHash->pCachedFile = pCachedFile;
8612 if (pHash->pCachedFile == pCachedFile)
8613 {
8614 pHash->cbHashed += cbData;
8615 g_Sandbox.LastHashRead.pCachedFile = NULL;
8616 g_Sandbox.LastHashRead.pvRead = NULL;
8617 g_Sandbox.LastHashRead.cbRead = 0;
8618 g_Sandbox.LastHashRead.offRead = 0;
8619 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
8620 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
8621 return TRUE;
8622 }
8623
8624 /* Note! it's possible to fall back here too, if necessary. */
8625 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
8626 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
8627 }
8628 else
8629 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
8630 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
8631 }
8632 else if (!pCachedFile)
8633 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
8634 else
8635 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
8636 }
8637 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
8638 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
8639 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
8640 if (pHash->cbHashed == 0)
8641 pHash->fFallbackMode = K_TRUE;
8642 if (pHash->fFallbackMode)
8643 {
8644 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
8645 pHash->fFallbackMode = K_TRUE;
8646 MD5Init(&pHash->Md5Ctx);
8647 MD5Update(&pHash->Md5Ctx, pbData, cbData);
8648 pHash->cbHashed = cbData;
8649 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
8650 hHash, pbData, cbData, dwFlags));
8651 return TRUE;
8652 }
8653 pHash->fGoneBad = K_TRUE;
8654 SetLastError(ERROR_INVALID_PARAMETER);
8655 fRc = FALSE;
8656 }
8657 else
8658 {
8659 /* fallback. */
8660 MD5Update(&pHash->Md5Ctx, pbData, cbData);
8661 pHash->cbHashed += cbData;
8662 fRc = TRUE;
8663 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
8664 hHash, pbData, cbData, dwFlags));
8665 }
8666 }
8667 /*
8668 * Bad handle state.
8669 */
8670 else
8671 {
8672 if (pHash->uMagic != KWHASHMD5_MAGIC)
8673 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
8674 else
8675 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
8676 SetLastError(NTE_BAD_HASH);
8677 fRc = FALSE;
8678 }
8679 }
8680 else
8681 {
8682
8683 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
8684 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
8685 }
8686 return fRc;
8687}
8688
8689
8690/** AdvApi32 - CryptGetHashParam */
8691static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
8692 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
8693{
8694 BOOL fRc;
8695 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
8696 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8697 while (pHash && (KUPTR)pHash != hHash)
8698 pHash = pHash->pNext;
8699 if (pHash)
8700 {
8701 if (pHash->uMagic == KWHASHMD5_MAGIC)
8702 {
8703 if (dwFlags == 0)
8704 {
8705 DWORD cbRet;
8706 void *pvRet;
8707 union
8708 {
8709 DWORD dw;
8710 } uBuf;
8711
8712 switch (dwParam)
8713 {
8714 case HP_HASHVAL:
8715 {
8716 /* Check the hash progress. */
8717 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
8718 if (pCachedFile)
8719 {
8720 if ( pCachedFile->cbCached == pHash->cbHashed
8721 && !pHash->fGoneBad)
8722 {
8723 if (pCachedFile->fValidMd5)
8724 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
8725 else
8726 {
8727 MD5Init(&pHash->Md5Ctx);
8728 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
8729 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
8730 pCachedFile->fValidMd5 = K_TRUE;
8731 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
8732 }
8733 pvRet = pCachedFile->abMd5Digest;
8734 }
8735 else
8736 {
8737 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
8738 from what I can tell, so just deal with it. */
8739 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
8740 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
8741 pHash, pCachedFile, pCachedFile->szPath));
8742 pHash->fFallbackMode = K_TRUE;
8743 pHash->pCachedFile = NULL;
8744 MD5Init(&pHash->Md5Ctx);
8745 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
8746 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
8747 pvRet = pHash->abDigest;
8748 }
8749 pHash->fFinal = K_TRUE;
8750 cbRet = 16;
8751 break;
8752 }
8753 else if (pHash->fFallbackMode)
8754 {
8755 if (!pHash->fFinal)
8756 {
8757 pHash->fFinal = K_TRUE;
8758 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
8759 }
8760 pvRet = pHash->abDigest;
8761 cbRet = 16;
8762 break;
8763 }
8764 else
8765 {
8766 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
8767 SetLastError(ERROR_INVALID_SERVER_STATE);
8768 }
8769 return FALSE;
8770 }
8771
8772 case HP_HASHSIZE:
8773 uBuf.dw = 16;
8774 pvRet = &uBuf;
8775 cbRet = sizeof(DWORD);
8776 break;
8777
8778 case HP_ALGID:
8779 uBuf.dw = CALG_MD5;
8780 pvRet = &uBuf;
8781 cbRet = sizeof(DWORD);
8782 break;
8783
8784 default:
8785 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
8786 SetLastError(NTE_BAD_TYPE);
8787 return FALSE;
8788 }
8789
8790 /*
8791 * Copy out cbRet from pvRet.
8792 */
8793 if (pbData)
8794 {
8795 if (*pcbData >= cbRet)
8796 {
8797 *pcbData = cbRet;
8798 kHlpMemCopy(pbData, pvRet, cbRet);
8799 if (cbRet == 4)
8800 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
8801 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
8802 else if (cbRet == 16)
8803 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x [cached]\n",
8804 dwParam, pHash, pHash->pCachedFile, cbRet,
8805 pbData[0], pbData[1], pbData[2], pbData[3],
8806 pbData[4], pbData[5], pbData[6], pbData[7],
8807 pbData[8], pbData[9], pbData[10], pbData[11],
8808 pbData[12], pbData[13], pbData[14], pbData[15]));
8809 else
8810 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
8811 dwParam, pHash, pHash->pCachedFile, cbRet));
8812 return TRUE;
8813 }
8814
8815 kHlpMemCopy(pbData, pvRet, *pcbData);
8816 }
8817 SetLastError(ERROR_MORE_DATA);
8818 *pcbData = cbRet;
8819 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
8820 }
8821 else
8822 {
8823 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
8824 SetLastError(NTE_BAD_FLAGS);
8825 }
8826 }
8827 else
8828 {
8829 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
8830 SetLastError(NTE_BAD_HASH);
8831 }
8832 fRc = FALSE;
8833 }
8834 /*
8835 * Regular handle.
8836 */
8837 else
8838 {
8839 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
8840 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
8841 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
8842 }
8843
8844 return fRc;
8845}
8846
8847
8848/** AdvApi32 - CryptDestroyHash */
8849static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
8850{
8851 BOOL fRc;
8852 PKWHASHMD5 pPrev = NULL;
8853 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
8854 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8855 while (pHash && (KUPTR)pHash != hHash)
8856 {
8857 pPrev = pHash;
8858 pHash = pHash->pNext;
8859 }
8860 if (pHash)
8861 {
8862 if (pHash->uMagic == KWHASHMD5_MAGIC)
8863 {
8864 pHash->uMagic = 0;
8865 if (!pPrev)
8866 g_Sandbox.pHashHead = pHash->pNext;
8867 else
8868 pPrev->pNext = pHash->pNext;
8869 kHlpFree(pHash);
8870 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
8871 fRc = TRUE;
8872 }
8873 else
8874 {
8875 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
8876 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
8877 SetLastError(ERROR_INVALID_HANDLE);
8878 fRc = FALSE;
8879 }
8880 }
8881 /*
8882 * Regular handle.
8883 */
8884 else
8885 {
8886 fRc = CryptDestroyHash(hHash);
8887 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
8888 }
8889 return fRc;
8890}
8891
8892#endif /* WITH_HASH_MD5_CACHE */
8893
8894
8895/*
8896 *
8897 * Reuse crypt context.
8898 * Reuse crypt context.
8899 * Reuse crypt context.
8900 *
8901 *
8902 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
8903 *
8904 */
8905
8906#ifdef WITH_CRYPT_CTX_REUSE
8907
8908/** AdvApi32 - CryptAcquireContextW. */
8909static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
8910 DWORD dwProvType, DWORD dwFlags)
8911{
8912 BOOL fRet;
8913
8914 /*
8915 * Lookup reusable context based on the input.
8916 */
8917 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
8918 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
8919 KU32 iCtx = g_Sandbox.cCryptCtxs;
8920 while (iCtx-- > 0)
8921 {
8922 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
8923 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
8924 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
8925 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
8926 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
8927 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
8928 {
8929 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
8930 {
8931 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
8932 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
8933 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8934 return TRUE;
8935 }
8936 }
8937 }
8938
8939 /*
8940 * Create it and enter it into the reused array if possible.
8941 */
8942 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
8943 if (fRet)
8944 {
8945 iCtx = g_Sandbox.cCryptCtxs;
8946 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
8947 {
8948 /* Try duplicate the input strings. */
8949 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
8950 (cwcContainer + 1) * sizeof(wchar_t));
8951 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
8952 {
8953 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
8954 (cwcProvider + 1) * sizeof(wchar_t));
8955 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
8956 {
8957 /* Add a couple of references just to be on the safe side and all that. */
8958 HCRYPTPROV hProv = *phProv;
8959 if (CryptContextAddRef(hProv, NULL, 0))
8960 {
8961 if (CryptContextAddRef(hProv, NULL, 0))
8962 {
8963 /* Okay, finish the entry and return success */
8964 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
8965 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
8966 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
8967 g_Sandbox.cCryptCtxs = iCtx + 1;
8968
8969 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
8970 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8971 return TRUE;
8972 }
8973 CryptReleaseContext(hProv, 0);
8974 }
8975 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
8976
8977 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
8978 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
8979 }
8980 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
8981 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
8982 }
8983 }
8984 else
8985 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
8986 }
8987
8988 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
8989 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8990 return fRet;
8991}
8992
8993
8994/** AdvApi32 - CryptReleaseContext */
8995static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
8996{
8997 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
8998 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
8999 return fRet;
9000}
9001
9002
9003/** AdvApi32 - CryptContextAddRef */
9004static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
9005{
9006 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
9007 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
9008 return fRet;
9009}
9010
9011#endif /* WITH_CRYPT_CTX_REUSE */
9012
9013/*
9014 *
9015 * Structured exception handling.
9016 * Structured exception handling.
9017 * Structured exception handling.
9018 *
9019 */
9020#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9021
9022# define EH_NONCONTINUABLE KU32_C(0x00000001)
9023# define EH_UNWINDING KU32_C(0x00000002)
9024# define EH_EXIT_UNWIND KU32_C(0x00000004)
9025# define EH_STACK_INVALID KU32_C(0x00000008)
9026# define EH_NESTED_CALL KU32_C(0x00000010)
9027
9028typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
9029 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
9030typedef struct _EXCEPTION_REGISTRATION_RECORD
9031{
9032 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
9033 PFNXCPTHANDLER pfnXcptHandler;
9034};
9035
9036
9037/**
9038 * Calls @a pfnHandler.
9039 */
9040static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
9041 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
9042 PFNXCPTHANDLER pfnHandler)
9043{
9044# if 1
9045 /* This is a more robust version that isn't subject to calling
9046 convension cleanup disputes and such. */
9047 KU32 uSavedEdi;
9048 KU32 uSavedEsi;
9049 KU32 uSavedEbx;
9050 KU32 rcHandler;
9051
9052 __asm
9053 {
9054 mov [uSavedEdi], edi
9055 mov [uSavedEsi], esi
9056 mov [uSavedEbx], ebx
9057 mov esi, esp
9058 mov edi, esp
9059 mov edi, [pXcptRec]
9060 mov edx, [pRegRec]
9061 mov eax, [pXcptCtx]
9062 mov ebx, [ppRegRec]
9063 mov ecx, [pfnHandler]
9064 sub esp, 16
9065 and esp, 0fffffff0h
9066 mov [esp ], edi
9067 mov [esp + 4], edx
9068 mov [esp + 8], eax
9069 mov [esp + 12], ebx
9070 mov edi, esi
9071 call ecx
9072 mov esp, esi
9073 cmp esp, edi
9074 je stack_ok
9075 int 3
9076 stack_ok:
9077 mov edi, [uSavedEdi]
9078 mov esi, [uSavedEsi]
9079 mov ebx, [uSavedEbx]
9080 mov [rcHandler], eax
9081 }
9082 return rcHandler;
9083# else
9084 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
9085# endif
9086}
9087
9088
9089/**
9090 * Vectored exception handler that emulates x86 chained exception handler.
9091 *
9092 * This is necessary because the RtlIsValidHandler check fails for self loaded
9093 * code and prevents cl.exe from working. (On AMD64 we can register function
9094 * tables, but on X86 cooking your own handling seems to be the only viabke
9095 * alternative.)
9096 *
9097 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
9098 * @param pXcptPtrs The exception details.
9099 */
9100static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
9101{
9102 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9103 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
9104 if (g_Sandbox.fRunning)
9105 {
9106 HANDLE const hCurProc = GetCurrentProcess();
9107 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
9108 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
9109 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9110 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
9111 {
9112 /* Read the exception record in a safe manner. */
9113 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9114 DWORD cbActuallyRead = 0;
9115 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9116 && cbActuallyRead == sizeof(RegRec))
9117 {
9118 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9119 KU32 rcHandler;
9120 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9121 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9122 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9123 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9124 if (rcHandler == ExceptionContinueExecution)
9125 {
9126 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
9127 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
9128 return EXCEPTION_CONTINUE_EXECUTION;
9129 }
9130
9131 if (rcHandler == ExceptionContinueSearch)
9132 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9133 else if (rcHandler == ExceptionNestedException)
9134 kHlpAssertMsgFailed(("Nested exceptions.\n"));
9135 else
9136 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9137 }
9138 else
9139 {
9140 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9141 break;
9142 }
9143
9144 /*
9145 * Next.
9146 */
9147 pRegRec = RegRec.pPrevRegRec;
9148 }
9149 }
9150 return EXCEPTION_CONTINUE_SEARCH;
9151}
9152
9153
9154/** NtDll,Kernel32 - RtlUnwind */
9155static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
9156 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
9157{
9158 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9159 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
9160 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
9161 if (g_Sandbox.fRunning)
9162 {
9163 HANDLE const hCurProc = GetCurrentProcess();
9164 PCONTEXT pXcptCtx = NULL;
9165 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9166
9167 /*
9168 * Update / create an exception record.
9169 */
9170 if (pXcptRec)
9171 pXcptRec->ExceptionFlags |= EH_UNWINDING;
9172 else
9173 {
9174 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
9175 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
9176 pXcptRec->ExceptionCode = STATUS_UNWIND;
9177 pXcptRec->ExceptionFlags = EH_UNWINDING;
9178 }
9179 if (!pStopXcptRec)
9180 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
9181
9182 /*
9183 * Walk the chain till we find pStopXctpRec.
9184 */
9185 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
9186 && pRegRec != NULL
9187 && pRegRec != pStopXcptRec)
9188 {
9189 /* Read the exception record in a safe manner. */
9190 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9191 DWORD cbActuallyRead = 0;
9192 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9193 && cbActuallyRead == sizeof(RegRec))
9194 {
9195 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9196 KU32 rcHandler;
9197 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9198 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9199 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9200 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9201
9202 if (rcHandler == ExceptionContinueSearch)
9203 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9204 else if (rcHandler == ExceptionCollidedUnwind)
9205 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
9206 else
9207 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9208 }
9209 else
9210 {
9211 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9212 break;
9213 }
9214
9215 /*
9216 * Pop next.
9217 */
9218 pTib->ExceptionList = RegRec.pPrevRegRec;
9219 pRegRec = RegRec.pPrevRegRec;
9220 }
9221 return;
9222 }
9223
9224 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
9225}
9226
9227#endif /* WINDOWS + X86 */
9228
9229
9230/*
9231 *
9232 * Misc function only intercepted while debugging.
9233 * Misc function only intercepted while debugging.
9234 * Misc function only intercepted while debugging.
9235 *
9236 */
9237
9238#ifndef NDEBUG
9239
9240/** CRT - memcpy */
9241static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
9242{
9243 KU8 const *pbSrc = (KU8 const *)pvSrc;
9244 KU8 *pbDst = (KU8 *)pvDst;
9245 KSIZE cbLeft = cb;
9246 while (cbLeft-- > 0)
9247 *pbDst++ = *pbSrc++;
9248 return pvDst;
9249}
9250
9251
9252/** CRT - memset */
9253static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
9254{
9255 KU8 *pbDst = (KU8 *)pvDst;
9256 KSIZE cbLeft = cb;
9257 while (cbLeft-- > 0)
9258 *pbDst++ = bFiller;
9259 return pvDst;
9260}
9261
9262#endif /* NDEBUG */
9263
9264
9265
9266/**
9267 * Functions that needs replacing for sandboxed execution.
9268 */
9269KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
9270{
9271 /*
9272 * Kernel32.dll and friends.
9273 */
9274 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
9275 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9276
9277 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
9278 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
9279 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
9280 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
9281 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
9282 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
9283 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
9284 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
9285 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
9286 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
9287 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9288
9289 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
9290 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
9291 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
9292 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
9293
9294 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
9295
9296 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
9297 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
9298 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
9299 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
9300 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
9301 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
9302 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
9303 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
9304 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
9305 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
9306 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
9307
9308 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
9309 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
9310 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
9311 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9312#ifdef WITH_TEMP_MEMORY_FILES
9313 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
9314 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9315 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9316 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
9317 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
9318 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9319 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9320 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9321 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9322 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9323#endif
9324 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9325 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9326 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9327 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
9328 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9329 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9330 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9331#ifdef WITH_TEMP_MEMORY_FILES
9332 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9333#endif
9334
9335 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
9336 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
9337
9338 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
9339 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
9340
9341 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
9342 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
9343
9344 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
9345 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
9346 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
9347 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
9348
9349 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
9350
9351#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9352 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
9353#endif
9354
9355#ifdef WITH_HASH_MD5_CACHE
9356 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
9357 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
9358 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
9359 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
9360#endif
9361
9362#ifdef WITH_CRYPT_CTX_REUSE
9363 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
9364 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
9365 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
9366#endif
9367
9368 /*
9369 * MS Visual C++ CRTs.
9370 */
9371 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
9372 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
9373 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
9374 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
9375 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
9376 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
9377
9378 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
9379 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
9380 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
9381
9382 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
9383 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
9384 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
9385
9386 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
9387 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
9388 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
9389 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
9390 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
9391 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
9392 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
9393 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
9394 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
9395 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
9396 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
9397 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
9398 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
9399 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
9400 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
9401 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
9402 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
9403 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
9404 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
9405 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
9406
9407 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
9408 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
9409 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
9410 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
9411 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
9412 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
9413 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
9414 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
9415 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
9416 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
9417 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
9418 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
9419 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
9420 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
9421
9422#ifndef NDEBUG
9423 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
9424 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
9425#endif
9426};
9427/** Number of entries in g_aReplacements. */
9428KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
9429
9430
9431/**
9432 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
9433 * execution.
9434 */
9435KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
9436{
9437 /*
9438 * Kernel32.dll and friends.
9439 */
9440 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
9441 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9442
9443#if 0
9444 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
9445#endif
9446
9447 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
9448 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
9449 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
9450 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9451#ifdef WITH_TEMP_MEMORY_FILES
9452 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
9453 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9454 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9455 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
9456 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
9457 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9458 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9459 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9460 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9461 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9462#endif
9463 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9464 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9465 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9466 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
9467 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9468 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9469 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9470#ifdef WITH_TEMP_MEMORY_FILES
9471 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9472#endif
9473 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
9474 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
9475
9476 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
9477 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
9478
9479#ifdef WITH_HASH_MD5_CACHE
9480 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
9481 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
9482 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
9483 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
9484#endif
9485
9486 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9487
9488#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9489 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
9490#endif
9491
9492 /*
9493 * MS Visual C++ CRTs.
9494 */
9495 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
9496 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
9497 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
9498 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
9499 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
9500 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
9501
9502#if 0 /* used by mspdbXXX.dll */
9503 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
9504 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
9505#endif
9506};
9507/** Number of entries in g_aSandboxNativeReplacements. */
9508KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
9509
9510
9511/**
9512 * Functions that needs replacing when queried by GetProcAddress.
9513 */
9514KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
9515{
9516 /*
9517 * Kernel32.dll and friends.
9518 */
9519 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
9520 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
9521 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
9522 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
9523};
9524/** Number of entries in g_aSandboxGetProcReplacements. */
9525KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
9526
9527
9528/**
9529 * Control handler.
9530 *
9531 * @returns TRUE if handled, FALSE if not.
9532 * @param dwCtrlType The signal.
9533 */
9534static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
9535{
9536 switch (dwCtrlType)
9537 {
9538 case CTRL_C_EVENT:
9539 fprintf(stderr, "kWorker: Ctrl-C\n");
9540 g_fCtrlC = K_TRUE;
9541 exit(9);
9542 break;
9543
9544 case CTRL_BREAK_EVENT:
9545 fprintf(stderr, "kWorker: Ctrl-Break\n");
9546 g_fCtrlC = K_TRUE;
9547 exit(10);
9548 break;
9549
9550 case CTRL_CLOSE_EVENT:
9551 fprintf(stderr, "kWorker: console closed\n");
9552 g_fCtrlC = K_TRUE;
9553 exit(11);
9554 break;
9555
9556 case CTRL_LOGOFF_EVENT:
9557 fprintf(stderr, "kWorker: logoff event\n");
9558 g_fCtrlC = K_TRUE;
9559 exit(11);
9560 break;
9561
9562 case CTRL_SHUTDOWN_EVENT:
9563 fprintf(stderr, "kWorker: shutdown event\n");
9564 g_fCtrlC = K_TRUE;
9565 exit(11);
9566 break;
9567
9568 default:
9569 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
9570 break;
9571 }
9572 return TRUE;
9573}
9574
9575
9576/**
9577 * Used by kwSandboxExec to reset the state of the module tree.
9578 *
9579 * This is done recursively.
9580 *
9581 * @param pMod The root of the tree to consider.
9582 */
9583static void kwSandboxResetModuleState(PKWMODULE pMod)
9584{
9585 if ( !pMod->fNative
9586 && pMod->u.Manual.enmState != KWMODSTATE_NEEDS_BITS)
9587 {
9588 KSIZE iImp;
9589 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
9590 iImp = pMod->u.Manual.cImpMods;
9591 while (iImp-- > 0)
9592 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
9593 }
9594}
9595
9596static PPEB kwSandboxGetProcessEnvironmentBlock(void)
9597{
9598#if K_ARCH == K_ARCH_X86_32
9599 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
9600#elif K_ARCH == K_ARCH_AMD64
9601 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
9602#else
9603# error "Port me!"
9604#endif
9605}
9606
9607
9608/**
9609 * Enters the given handle into the handle table.
9610 *
9611 * @returns K_TRUE on success, K_FALSE on failure.
9612 * @param pSandbox The sandbox.
9613 * @param pHandle The handle.
9614 * @param hHandle The handle value to enter it under (for the
9615 * duplicate handle API).
9616 */
9617static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
9618{
9619 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
9620 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
9621
9622 /*
9623 * Grow handle table.
9624 */
9625 if (idxHandle >= pSandbox->cHandles)
9626 {
9627 void *pvNew;
9628 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
9629 while (cHandles <= idxHandle)
9630 cHandles *= 2;
9631 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
9632 if (!pvNew)
9633 {
9634 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
9635 return K_FALSE;
9636 }
9637 pSandbox->papHandles = (PKWHANDLE *)pvNew;
9638 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
9639 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
9640 pSandbox->cHandles = cHandles;
9641 }
9642
9643 /*
9644 * Check that the entry is unused then insert it.
9645 */
9646 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
9647 pSandbox->papHandles[idxHandle] = pHandle;
9648 pSandbox->cActiveHandles++;
9649 return K_TRUE;
9650}
9651
9652
9653/**
9654 * Creates a correctly quoted ANSI command line string from the given argv.
9655 *
9656 * @returns Pointer to the command line.
9657 * @param cArgs Number of arguments.
9658 * @param papszArgs The argument vector.
9659 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
9660 * @param pcbCmdLine Where to return the command line length,
9661 * including one terminator.
9662 */
9663static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
9664{
9665 KU32 i;
9666 KSIZE cbCmdLine;
9667 char *pszCmdLine;
9668
9669 /* Make a copy of the argument vector that we'll be quoting. */
9670 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
9671 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
9672
9673 /* Quote the arguments that need it. */
9674 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
9675
9676 /* figure out cmd line length. */
9677 cbCmdLine = 0;
9678 for (i = 0; i < cArgs; i++)
9679 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
9680 *pcbCmdLine = cbCmdLine;
9681
9682 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
9683 if (pszCmdLine)
9684 {
9685 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
9686 if (papszQuotedArgs[0] != papszArgs[0])
9687 free(papszQuotedArgs[0]);
9688
9689 for (i = 1; i < cArgs; i++)
9690 {
9691 *psz++ = ' ';
9692 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
9693 if (papszQuotedArgs[i] != papszArgs[i])
9694 free(papszQuotedArgs[i]);
9695 }
9696 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
9697
9698 *psz++ = '\0';
9699 *psz++ = '\0';
9700 }
9701
9702 return pszCmdLine;
9703}
9704
9705
9706
9707static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
9708 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
9709 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
9710{
9711 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
9712 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
9713 wchar_t *pwcPool;
9714 KSIZE cbStrings;
9715 KSIZE cwc;
9716 KSIZE cbCmdLine;
9717 KU32 i;
9718
9719 /* Simple stuff. */
9720 pSandbox->rcExitCode = 256;
9721 pSandbox->pTool = pTool;
9722 pSandbox->idMainThread = GetCurrentThreadId();
9723 pSandbox->pgmptr = (char *)pTool->pszPath;
9724 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
9725#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
9726 if (pSandbox->StdOut.fIsConsole)
9727 pSandbox->StdOut.u.Con.cwcBuf = 0;
9728 else
9729 pSandbox->StdOut.u.Fully.cchBuf = 0;
9730 if (pSandbox->StdErr.fIsConsole)
9731 pSandbox->StdErr.u.Con.cwcBuf = 0;
9732 else
9733 pSandbox->StdErr.u.Fully.cchBuf = 0;
9734 pSandbox->Combined.cwcBuf = 0;
9735 pSandbox->Combined.cFlushes = 0;
9736#endif
9737 pSandbox->fNoPchCaching = fNoPchCaching;
9738 pSandbox->cArgs = cArgs;
9739 pSandbox->papszArgs = (char **)papszArgs;
9740 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
9741 if (!pSandbox->pszCmdLine)
9742 return KERR_NO_MEMORY;
9743
9744 /*
9745 * Convert command line and argv to UTF-16.
9746 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
9747 */
9748 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
9749 if (!pSandbox->papwszArgs)
9750 return KERR_NO_MEMORY;
9751 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
9752 for (i = 0; i < cArgs; i++)
9753 {
9754 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
9755 pSandbox->papwszArgs[i] = pwcPool;
9756 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
9757 pwcPool++;
9758 }
9759 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
9760 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
9761
9762 /*
9763 * Convert the commandline string to UTF-16, same pessimistic approach as above.
9764 */
9765 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
9766 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
9767 if (!pSandbox->pwszCmdLine)
9768 return KERR_NO_MEMORY;
9769 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
9770
9771 pSandbox->SavedCommandLine = pProcParams->CommandLine;
9772 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
9773 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
9774
9775 /*
9776 * Setup the environment.
9777 */
9778 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
9779 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
9780 {
9781 KU32 iDst = 0;
9782 for (i = 0; i < cEnvVars; i++)
9783 {
9784 const char *pszVar = papszEnvVars[i];
9785 KSIZE cchVar = kHlpStrLen(pszVar);
9786 const char *pszEqual;
9787 if ( cchVar > 0
9788 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
9789 {
9790 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
9791 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
9792 if (pszCopy && pwszCopy)
9793 {
9794 pSandbox->papszEnvVars[iDst] = pszCopy;
9795 pSandbox->environ[iDst] = pszCopy;
9796 pSandbox->papwszEnvVars[iDst] = pwszCopy;
9797 pSandbox->wenviron[iDst] = pwszCopy;
9798
9799 /* When we see the path, we must tell the system or native exec and module loading won't work . */
9800 if ( (pszEqual - pszVar) == 4
9801 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
9802 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
9803 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
9804 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
9805 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
9806 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
9807
9808 iDst++;
9809 }
9810 else
9811 {
9812 kHlpFree(pszCopy);
9813 kHlpFree(pwszCopy);
9814 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
9815 }
9816 }
9817 else
9818 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
9819 }
9820 pSandbox->papszEnvVars[iDst] = NULL;
9821 pSandbox->environ[iDst] = NULL;
9822 pSandbox->papwszEnvVars[iDst] = NULL;
9823 pSandbox->wenviron[iDst] = NULL;
9824 }
9825 else
9826 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
9827
9828 /*
9829 * Invalidate the volatile parts of cache (kBuild output directory,
9830 * temporary directory, whatever).
9831 */
9832 kFsCacheInvalidateCustomBoth(g_pFsCache);
9833
9834#ifdef WITH_HISTORY
9835 /*
9836 * Record command line in debug history.
9837 */
9838 kHlpFree(g_apszHistory[g_iHistoryNext]);
9839 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
9840 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
9841#endif
9842
9843 return 0;
9844}
9845
9846
9847/**
9848 * Does sandbox cleanup between jobs.
9849 *
9850 * We postpone whatever isn't externally visible (i.e. files) and doesn't
9851 * influence the result, so that kmk can get on with things ASAP.
9852 *
9853 * @param pSandbox The sandbox.
9854 */
9855static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
9856{
9857 PROCESS_MEMORY_COUNTERS MemInfo;
9858 PKWVIRTALLOC pTracker;
9859 PKWHEAP pHeap;
9860 PKWLOCALSTORAGE pLocalStorage;
9861#ifdef WITH_HASH_MD5_CACHE
9862 PKWHASHMD5 pHash;
9863#endif
9864#ifdef WITH_TEMP_MEMORY_FILES
9865 PKWFSTEMPFILE pTempFile;
9866#endif
9867 PKWEXITCALLACK pExitCallback;
9868
9869 /*
9870 * First stuff that may cause code to run.
9871 */
9872
9873 /* Do exit callback first. */
9874 pExitCallback = g_Sandbox.pExitCallbackHead;
9875 g_Sandbox.pExitCallbackHead = NULL;
9876 while (pExitCallback)
9877 {
9878 PKWEXITCALLACK pNext = pExitCallback->pNext;
9879 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
9880 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
9881 __try
9882 {
9883 pExitCallback->pfnCallback();
9884 }
9885 __except (EXCEPTION_EXECUTE_HANDLER)
9886 {
9887 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
9888 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
9889 kHlpAssertFailed();
9890 }
9891 kHlpFree(pExitCallback);
9892 pExitCallback = pNext;
9893 }
9894
9895 /* Free left behind FlsAlloc leaks. */
9896 pLocalStorage = g_Sandbox.pFlsAllocHead;
9897 g_Sandbox.pFlsAllocHead = NULL;
9898 while (pLocalStorage)
9899 {
9900 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
9901 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
9902 FlsFree(pLocalStorage->idx);
9903 kHlpFree(pLocalStorage);
9904 pLocalStorage = pNext;
9905 }
9906
9907 /* Free left behind TlsAlloc leaks. */
9908 pLocalStorage = g_Sandbox.pTlsAllocHead;
9909 g_Sandbox.pTlsAllocHead = NULL;
9910 while (pLocalStorage)
9911 {
9912 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
9913 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
9914 TlsFree(pLocalStorage->idx);
9915 kHlpFree(pLocalStorage);
9916 pLocalStorage = pNext;
9917 }
9918
9919
9920 /*
9921 * Then free resources associated with the sandbox run.
9922 */
9923
9924 /* Open handles, except fixed handles (stdout and stderr). */
9925 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
9926 {
9927 KU32 idxHandle = pSandbox->cHandles;
9928 while (idxHandle-- > 0)
9929 if (pSandbox->papHandles[idxHandle] == NULL)
9930 { /* likely */ }
9931 else
9932 {
9933 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
9934 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
9935 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
9936 {
9937 pSandbox->papHandles[idxHandle] = NULL;
9938 pSandbox->cLeakedHandles++;
9939
9940 switch (pHandle->enmType)
9941 {
9942 case KWHANDLETYPE_FSOBJ_READ_CACHE:
9943 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
9944 idxHandle, pHandle->hHandle, pHandle->cRefs));
9945 break;
9946 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
9947 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
9948 idxHandle, pHandle->hHandle, pHandle->cRefs));
9949 break;
9950 case KWHANDLETYPE_OUTPUT_BUF:
9951 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
9952 idxHandle, pHandle->hHandle, pHandle->cRefs));
9953 break;
9954 case KWHANDLETYPE_TEMP_FILE:
9955 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
9956 idxHandle, pHandle->hHandle, pHandle->cRefs));
9957 pHandle->u.pTempFile->cActiveHandles--;
9958 break;
9959 case KWHANDLETYPE_TEMP_FILE_MAPPING:
9960 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
9961 idxHandle, pHandle->hHandle, pHandle->cRefs));
9962 pHandle->u.pTempFile->cActiveHandles--;
9963 break;
9964 default:
9965 kHlpAssertFailed();
9966 }
9967 if (--pHandle->cRefs == 0)
9968 kHlpFree(pHandle);
9969 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
9970 break;
9971 }
9972 }
9973 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
9974 }
9975
9976 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
9977 g_Sandbox.cMemMappings = 0;
9978
9979#ifdef WITH_TEMP_MEMORY_FILES
9980 /* The temporary files aren't externally visible, they're all in memory. */
9981 pTempFile = pSandbox->pTempFileHead;
9982 pSandbox->pTempFileHead = NULL;
9983 while (pTempFile)
9984 {
9985 PKWFSTEMPFILE pNext = pTempFile->pNext;
9986 KU32 iSeg = pTempFile->cSegs;
9987 while (iSeg-- > 0)
9988 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
9989 kHlpFree(pTempFile->paSegs);
9990 pTempFile->pNext = NULL;
9991 kHlpFree(pTempFile);
9992
9993 pTempFile = pNext;
9994 }
9995#endif
9996
9997 /* Free left behind HeapCreate leaks. */
9998 pHeap = g_Sandbox.pHeapHead;
9999 g_Sandbox.pHeapHead = NULL;
10000 while (pHeap != NULL)
10001 {
10002 PKWHEAP pNext = pHeap->pNext;
10003 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
10004 HeapDestroy(pHeap->hHeap);
10005 pHeap = pNext;
10006 }
10007
10008 /* Free left behind VirtualAlloc leaks. */
10009 pTracker = g_Sandbox.pVirtualAllocHead;
10010 g_Sandbox.pVirtualAllocHead = NULL;
10011 while (pTracker)
10012 {
10013 PKWVIRTALLOC pNext = pTracker->pNext;
10014 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
10015
10016#ifdef WITH_FIXED_VIRTUAL_ALLOCS
10017 if (pTracker->idxPreAllocated != KU32_MAX)
10018 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
10019 else
10020#endif
10021 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
10022 kHlpFree(pTracker);
10023 pTracker = pNext;
10024 }
10025
10026 /* Free the environment. */
10027 if (pSandbox->papszEnvVars)
10028 {
10029 KU32 i;
10030 for (i = 0; pSandbox->papszEnvVars[i]; i++)
10031 kHlpFree(pSandbox->papszEnvVars[i]);
10032 pSandbox->environ[0] = NULL;
10033 pSandbox->papszEnvVars[0] = NULL;
10034
10035 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
10036 kHlpFree(pSandbox->papwszEnvVars[i]);
10037 pSandbox->wenviron[0] = NULL;
10038 pSandbox->papwszEnvVars[0] = NULL;
10039 }
10040
10041#ifdef WITH_HASH_MD5_CACHE
10042 /*
10043 * Hash handles.
10044 */
10045 pHash = pSandbox->pHashHead;
10046 pSandbox->pHashHead = NULL;
10047 while (pHash)
10048 {
10049 PKWHASHMD5 pNext = pHash->pNext;
10050 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
10051 kHlpFree(pHash);
10052 pHash = pNext;
10053 }
10054#endif
10055
10056 /*
10057 * Check the memory usage. If it's getting high, trigger a respawn
10058 * after the next job.
10059 */
10060 MemInfo.WorkingSetSize = 0;
10061 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
10062 {
10063 /* The first time thru, we figure out approximately when to restart
10064 based on installed RAM and CPU threads. */
10065 static KU64 s_cbMaxWorkingSet = 0;
10066 if (s_cbMaxWorkingSet != 0)
10067 { /* likely */ }
10068 else
10069 {
10070 SYSTEM_INFO SysInfo;
10071 MEMORYSTATUSEX GlobalMemInfo;
10072 const char *pszValue;
10073
10074 /* Calculate a reasonable estimate. */
10075 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
10076 GetNativeSystemInfo(&SysInfo);
10077
10078 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
10079 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
10080 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
10081#if K_ARCH_BITS >= 64
10082 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
10083#else
10084 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
10085#endif
10086 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
10087 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10088
10089 /* User limit. */
10090 pszValue = getenv("KWORKER_MEMORY_LIMIT");
10091 if (pszValue != NULL)
10092 {
10093 char *pszNext;
10094 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
10095 if (*pszNext == '\0' || *pszNext == 'M')
10096 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
10097 else if (*pszNext == 'K')
10098 s_cbMaxWorkingSet = ulValue * (KU64)1024;
10099 else if (*pszNext == 'G')
10100 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
10101 else
10102 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
10103 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10104 }
10105
10106 /* Clamp it a little. */
10107 if (s_cbMaxWorkingSet < 168*1024*1024)
10108 s_cbMaxWorkingSet = 168*1024*1024;
10109#if K_ARCH_BITS < 64
10110 else
10111 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
10112 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
10113 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
10114 : 1536*1024*1024 /* got 4GB VA */);
10115#endif
10116 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
10117 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
10118 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10119 }
10120
10121 /* Finally the check. */
10122 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
10123 {
10124 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
10125 g_fRestart = K_TRUE;
10126 }
10127 }
10128
10129 /*
10130 * The CRT has a max of 8192 handles, so we better restart after a while if
10131 * someone is leaking handles or we risk running out of descriptors.
10132 *
10133 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
10134 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
10135 */
10136 if (pSandbox->cLeakedHandles > 6000)
10137 {
10138 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
10139 g_fRestart = K_TRUE;
10140 }
10141}
10142
10143
10144/**
10145 * Does essential cleanups and restoring, anything externally visible.
10146 *
10147 * All cleanups that aren't externally visible are postponed till after we've
10148 * informed kmk of the result, so it can be done in the dead time between jobs.
10149 *
10150 * @param pSandbox The sandbox.
10151 */
10152static void kwSandboxCleanup(PKWSANDBOX pSandbox)
10153{
10154 /*
10155 * Restore the parent command line string.
10156 */
10157 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10158 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10159 pProcParams->CommandLine = pSandbox->SavedCommandLine;
10160 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
10161 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
10162}
10163
10164
10165static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10166 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10167{
10168 int rcExit = 42;
10169 int rc;
10170
10171 /*
10172 * Initialize the sandbox environment.
10173 */
10174 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
10175 if (rc == 0)
10176 {
10177 /*
10178 * Do module initialization.
10179 */
10180 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
10181 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
10182 if (rc == 0)
10183 {
10184 /*
10185 * Call the main function.
10186 */
10187#if K_ARCH == K_ARCH_AMD64
10188 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
10189#elif K_ARCH == K_ARCH_X86_32
10190 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
10191#else
10192# error "Port me!"
10193#endif
10194
10195 /* Save the NT TIB first (should do that here, not in some other function). */
10196 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10197 pSandbox->TibMainThread = *pTib;
10198
10199 /* Make the call in a guarded fashion. */
10200#if K_ARCH == K_ARCH_AMD64
10201 /* AMD64 */
10202 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
10203 __try
10204 {
10205 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10206 if (setjmp(pSandbox->JmpBuf) == 0)
10207 {
10208 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10209 pSandbox->fRunning = K_TRUE;
10210 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
10211 pSandbox->fRunning = K_FALSE;
10212 }
10213 else
10214 rcExit = pSandbox->rcExitCode;
10215 }
10216#elif K_ARCH == K_ARCH_X86_32
10217 /* x86 (see _tmainCRTStartup) */
10218 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
10219 __try
10220 {
10221 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10222 if (setjmp(pSandbox->JmpBuf) == 0)
10223 {
10224 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10225 pSandbox->fRunning = K_TRUE;
10226 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
10227 pSandbox->fRunning = K_FALSE;
10228 }
10229 else
10230 rcExit = pSandbox->rcExitCode;
10231 }
10232#endif
10233 __except (EXCEPTION_EXECUTE_HANDLER)
10234 {
10235 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
10236#ifdef WITH_HISTORY
10237 {
10238 KU32 cPrinted = 0;
10239 while (cPrinted++ < 5)
10240 {
10241 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
10242 if (g_apszHistory[idx])
10243 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
10244 }
10245 }
10246#endif
10247 rcExit = 512;
10248 }
10249 pSandbox->fRunning = K_FALSE;
10250
10251 /* Now, restore the NT TIB. */
10252 *pTib = pSandbox->TibMainThread;
10253 }
10254 else
10255 rcExit = 42 + 4;
10256
10257 /*
10258 * Flush and clean up the essential bits only, postpone whatever we
10259 * can till after we've replied to kmk.
10260 */
10261#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10262 kwSandboxConsoleFlushAll(&g_Sandbox);
10263#endif
10264 kwSandboxCleanup(&g_Sandbox);
10265 }
10266 else
10267 rcExit = 42 + 3;
10268
10269 return rcExit;
10270}
10271
10272
10273/**
10274 * Does the post command part of a job (optional).
10275 *
10276 * @returns The exit code of the job.
10277 * @param cPostCmdArgs Number of post command arguments (includes cmd).
10278 * @param papszPostCmdArgs The post command and its argument.
10279 */
10280static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
10281{
10282 const char *pszCmd = papszPostCmdArgs[0];
10283
10284 /* Allow the kmk builtin prefix. */
10285 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
10286 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
10287 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
10288
10289 /* Command switch. */
10290 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
10291 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL);
10292
10293 return kwErrPrintfRc(42 + 5 , "Unknown post command: '%s'\n", pszCmd);
10294}
10295
10296
10297/**
10298 * Part 2 of the "JOB" command handler.
10299 *
10300 * @returns The exit code of the job.
10301 * @param pszExecutable The executable to execute.
10302 * @param pszCwd The current working directory of the job.
10303 * @param cArgs The number of arguments.
10304 * @param papszArgs The argument vector.
10305 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10306 * @param cEnvVars The number of environment variables.
10307 * @param papszEnvVars The environment vector.
10308 * @param fNoPchCaching Whether to disable precompiled header file
10309 * caching. Avoid trouble when creating them.
10310 * @param cPostCmdArgs Number of post command arguments (includes cmd).
10311 * @param papszPostCmdArgs The post command and its argument.
10312 */
10313static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
10314 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10315 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching,
10316 KU32 cPostCmdArgs, const char **papszPostCmdArgs)
10317{
10318 int rcExit;
10319 PKWTOOL pTool;
10320
10321 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
10322 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
10323#ifdef KW_LOG_ENABLED
10324 {
10325 KU32 i;
10326 for (i = 0; i < cArgs; i++)
10327 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
10328 for (i = 0; i < cPostCmdArgs; i++)
10329 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
10330 }
10331#endif
10332 g_cJobs++;
10333
10334 /*
10335 * Lookup the tool.
10336 */
10337 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
10338 if (pTool)
10339 {
10340 /*
10341 * Change the directory if we're going to execute the job inside
10342 * this process. Then invoke the tool type specific handler.
10343 */
10344 switch (pTool->enmType)
10345 {
10346 case KWTOOLTYPE_SANDBOXED:
10347 case KWTOOLTYPE_WATCOM:
10348 {
10349 /* Change dir. */
10350 KFSLOOKUPERROR enmError;
10351 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
10352 if ( pNewCurDir == g_pCurDirObj
10353 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
10354 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
10355 else if (SetCurrentDirectoryA(pszCwd))
10356 {
10357 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
10358 g_pCurDirObj = pNewCurDir;
10359 }
10360 else
10361 {
10362 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
10363 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
10364 rcExit = 42 + 1;
10365 break;
10366 }
10367
10368 /* Call specific handler. */
10369 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
10370 {
10371 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
10372 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
10373 cEnvVars, papszEnvVars, fNoPchCaching);
10374 }
10375 else
10376 {
10377 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
10378 rcExit = 42 + 2;
10379 }
10380 break;
10381 }
10382
10383 case KWTOOLTYPE_EXEC:
10384 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
10385 rcExit = 42 + 2;
10386 break;
10387
10388 default:
10389 kHlpAssertFailed();
10390 kwErrPrintf("Internal tool type corruption!!\n");
10391 rcExit = 42 + 2;
10392 g_fRestart = K_TRUE;
10393 break;
10394 }
10395
10396 /*
10397 * Do the post command, if present.
10398 */
10399 if (cPostCmdArgs && rcExit == 0)
10400 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
10401 }
10402 else
10403 rcExit = 42 + 1;
10404 return rcExit;
10405}
10406
10407
10408/**
10409 * Handles a "JOB" command.
10410 *
10411 * @returns The exit code of the job.
10412 * @param pszMsg Points to the "JOB" command part of the message.
10413 * @param cbMsg Number of message bytes at @a pszMsg. There are
10414 * 4 more zero bytes after the message body to
10415 * simplify parsing.
10416 */
10417static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
10418{
10419 int rcExit = 42;
10420
10421 /*
10422 * Unpack the message.
10423 */
10424 const char *pszExecutable;
10425 KSIZE cbTmp;
10426
10427 pszMsg += sizeof("JOB");
10428 cbMsg -= sizeof("JOB");
10429
10430 /* Executable name. */
10431 pszExecutable = pszMsg;
10432 cbTmp = kHlpStrLen(pszMsg) + 1;
10433 pszMsg += cbTmp;
10434 if ( cbTmp < cbMsg
10435 && cbTmp > 2)
10436 {
10437 const char *pszCwd;
10438 cbMsg -= cbTmp;
10439
10440 /* Current working directory. */
10441 pszCwd = pszMsg;
10442 cbTmp = kHlpStrLen(pszMsg) + 1;
10443 pszMsg += cbTmp;
10444 if ( cbTmp + sizeof(KU32) < cbMsg
10445 && cbTmp >= 2)
10446 {
10447 KU32 cArgs;
10448 cbMsg -= cbTmp;
10449
10450 /* Argument count. */
10451 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
10452 pszMsg += sizeof(cArgs);
10453 cbMsg -= sizeof(cArgs);
10454
10455 if (cArgs > 0 && cArgs < 4096)
10456 {
10457 /* The argument vector. */
10458 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
10459 if (papszArgs)
10460 {
10461 KU32 i;
10462 for (i = 0; i < cArgs; i++)
10463 {
10464 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
10465 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
10466 pszMsg += cbTmp;
10467 if (cbTmp < cbMsg)
10468 cbMsg -= cbTmp;
10469 else
10470 {
10471 cbMsg = 0;
10472 break;
10473 }
10474
10475 }
10476 papszArgs[cArgs] = 0;
10477
10478 /* Environment variable count. */
10479 if (cbMsg > sizeof(KU32))
10480 {
10481 KU32 cEnvVars;
10482 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
10483 pszMsg += sizeof(cEnvVars);
10484 cbMsg -= sizeof(cEnvVars);
10485
10486 if (cEnvVars >= 0 && cEnvVars < 4096)
10487 {
10488 /* The argument vector. */
10489 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
10490 if (papszEnvVars)
10491 {
10492 for (i = 0; i < cEnvVars; i++)
10493 {
10494 papszEnvVars[i] = pszMsg;
10495 cbTmp = kHlpStrLen(pszMsg) + 1;
10496 pszMsg += cbTmp;
10497 if (cbTmp < cbMsg)
10498 cbMsg -= cbTmp;
10499 else
10500 {
10501 cbMsg = 0;
10502 break;
10503 }
10504 }
10505 papszEnvVars[cEnvVars] = 0;
10506
10507 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
10508 if (cbMsg >= sizeof(KU8) * 2)
10509 {
10510 KBOOL fWatcomBrainDamange = *pszMsg++;
10511 KBOOL fNoPchCaching = *pszMsg++;
10512 cbMsg -= 2;
10513
10514 /* Post command argument count (can be zero). */
10515 if (cbMsg >= sizeof(KU32))
10516 {
10517 KU32 cPostCmdArgs;
10518 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
10519 pszMsg += sizeof(cPostCmdArgs);
10520 cbMsg -= sizeof(cPostCmdArgs);
10521
10522 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
10523 {
10524 char const *apszPostCmdArgs[32+1];
10525 for (i = 0; i < cPostCmdArgs; i++)
10526 {
10527 apszPostCmdArgs[i] = pszMsg;
10528 cbTmp = kHlpStrLen(pszMsg) + 1;
10529 pszMsg += cbTmp;
10530 if ( cbTmp < cbMsg
10531 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
10532 cbMsg -= cbTmp;
10533 else
10534 {
10535 cbMsg = KSIZE_MAX;
10536 break;
10537 }
10538 }
10539 if (cbMsg == 0)
10540 {
10541 apszPostCmdArgs[cPostCmdArgs] = NULL;
10542
10543 /*
10544 * The next step.
10545 */
10546 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
10547 cArgs, papszArgs, fWatcomBrainDamange,
10548 cEnvVars, papszEnvVars, fNoPchCaching,
10549 cPostCmdArgs, apszPostCmdArgs);
10550 }
10551 else if (cbMsg == KSIZE_MAX)
10552 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
10553 else
10554 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
10555 }
10556 else
10557 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
10558 }
10559 else
10560 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
10561 }
10562 else
10563 kwErrPrintf("Detected bogus message unpacking environment variables!\n");
10564 kHlpFree((void *)papszEnvVars);
10565 }
10566 else
10567 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
10568 }
10569 else
10570 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
10571 }
10572 else
10573 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
10574 kHlpFree((void *)papszArgs);
10575 }
10576 else
10577 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
10578 }
10579 else
10580 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
10581 }
10582 else
10583 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
10584 }
10585 else
10586 kwErrPrintf("Detected bogus message unpacking executable path!\n");
10587 return rcExit;
10588}
10589
10590
10591/**
10592 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
10593 *
10594 * @retval 0 on success.
10595 * @retval -1 on error (fully bitched).
10596 *
10597 * @param hPipe The pipe handle.
10598 * @param pvBuf The buffer to write out out.
10599 * @param cbToWrite The number of bytes to write.
10600 */
10601static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
10602{
10603 KU8 const *pbBuf = (KU8 const *)pvBuf;
10604 KU32 cbLeft = cbToWrite;
10605 for (;;)
10606 {
10607 DWORD cbActuallyWritten = 0;
10608 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
10609 {
10610 cbLeft -= cbActuallyWritten;
10611 if (!cbLeft)
10612 return 0;
10613 pbBuf += cbActuallyWritten;
10614 }
10615 else
10616 {
10617 DWORD dwErr = GetLastError();
10618 if (cbLeft == cbToWrite)
10619 kwErrPrintf("WriteFile failed: %u\n", dwErr);
10620 else
10621 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
10622 return -1;
10623 }
10624 }
10625}
10626
10627
10628/**
10629 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
10630 *
10631 * @retval 0 on success.
10632 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
10633 * @retval -1 on error (fully bitched).
10634 * @param hPipe The pipe handle.
10635 * @param pvBuf The buffer to read into.
10636 * @param cbToRead The number of bytes to read.
10637 * @param fShutdownOkay Whether connection shutdown while reading the
10638 * first byte is okay or not.
10639 */
10640static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
10641{
10642 KU8 *pbBuf = (KU8 *)pvBuf;
10643 KU32 cbLeft = cbToRead;
10644 for (;;)
10645 {
10646 DWORD cbActuallyRead = 0;
10647 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
10648 {
10649 cbLeft -= cbActuallyRead;
10650 if (!cbLeft)
10651 return 0;
10652 pbBuf += cbActuallyRead;
10653 }
10654 else
10655 {
10656 DWORD dwErr = GetLastError();
10657 if (cbLeft == cbToRead)
10658 {
10659 if ( fMayShutdown
10660 && dwErr == ERROR_BROKEN_PIPE)
10661 return 1;
10662 kwErrPrintf("ReadFile failed: %u\n", dwErr);
10663 }
10664 else
10665 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
10666 return -1;
10667 }
10668 }
10669}
10670
10671
10672/**
10673 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
10674 *
10675 * @returns pszBuf
10676 * @param pszBuf The buffer (sufficiently large).
10677 * @param uValue The value.
10678 */
10679static const char *kwFmtU64(char *pszBuf, KU64 uValue)
10680{
10681 char szTmp[64];
10682 char *psz = &szTmp[63];
10683 int cch = 4;
10684
10685 *psz-- = '\0';
10686 do
10687 {
10688 if (--cch == 0)
10689 {
10690 *psz-- = ' ';
10691 cch = 3;
10692 }
10693 *psz-- = (uValue % 10) + '0';
10694 uValue /= 10;
10695 } while (uValue != 0);
10696
10697 return strcpy(pszBuf, psz + 1);
10698}
10699
10700
10701/**
10702 * Prints statistics.
10703 */
10704static void kwPrintStats(void)
10705{
10706 PROCESS_MEMORY_COUNTERS_EX MemInfo;
10707 MEMORYSTATUSEX MemStatus;
10708 IO_COUNTERS IoCounters;
10709 DWORD cHandles;
10710 KSIZE cMisses;
10711 char szBuf[16*1024];
10712 int off = 0;
10713 char szPrf[24];
10714 char sz1[64];
10715 char sz2[64];
10716 char sz3[64];
10717 char sz4[64];
10718 extern size_t maybe_con_fwrite(void const *pvBuf, size_t cbUnit, size_t cUnits, FILE *pFile);
10719
10720 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
10721
10722 szBuf[off++] = '\n';
10723
10724 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
10725 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
10726 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
10727 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
10728 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
10729
10730 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
10731 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
10732
10733 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
10734 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
10735 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
10736
10737 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
10738 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
10739 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
10740
10741 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
10742 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
10743 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
10744 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
10745 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
10746 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
10747 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
10748
10749 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
10750 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
10751 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
10752 kwFmtU64(sz1, g_pFsCache->cObjects),
10753 kwFmtU64(sz2, g_pFsCache->cbObjects),
10754 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
10755 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
10756 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
10757 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
10758 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
10759 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
10760#ifdef KFSCACHE_CFG_UTF16
10761 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
10762 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
10763 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
10764 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
10765 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
10766#endif
10767 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
10768 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
10769 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
10770 kwFmtU64(sz3, g_pFsCache->cChildHashed),
10771 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
10772
10773 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
10774 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
10775 kwFmtU64(sz1, g_pFsCache->cLookups),
10776 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
10777 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
10778 kwFmtU64(sz3, g_pFsCache->cWalkHits),
10779 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
10780 kwFmtU64(sz4, cMisses),
10781 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
10782
10783 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
10784 kwFmtU64(sz1, g_pFsCache->cChildSearches),
10785 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
10786 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
10787 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
10788 kwFmtU64(sz1, g_pFsCache->cNameChanges),
10789 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
10790 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
10791
10792
10793 /*
10794 * Process & Memory details.
10795 */
10796 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
10797 cHandles = 0;
10798 MemInfo.cb = sizeof(MemInfo);
10799 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
10800 memset(&MemInfo, 0, sizeof(MemInfo));
10801 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
10802 kwFmtU64(sz1, cHandles),
10803 kwFmtU64(sz2, MemInfo.PageFaultCount),
10804 kwFmtU64(sz3, MemInfo.PagefileUsage),
10805 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
10806 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
10807 kwFmtU64(sz1, MemInfo.WorkingSetSize),
10808 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
10809 kwFmtU64(sz3, MemInfo.PrivateUsage));
10810 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
10811 szPrf,
10812 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
10813 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
10814 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
10815 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
10816
10817 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
10818 memset(&IoCounters, 0, sizeof(IoCounters));
10819 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
10820 kwFmtU64(sz1, IoCounters.ReadTransferCount),
10821 kwFmtU64(sz2, IoCounters.ReadOperationCount));
10822 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
10823 kwFmtU64(sz1, IoCounters.WriteTransferCount),
10824 kwFmtU64(sz2, IoCounters.WriteOperationCount));
10825 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
10826 kwFmtU64(sz1, IoCounters.OtherTransferCount),
10827 kwFmtU64(sz2, IoCounters.OtherOperationCount));
10828
10829 MemStatus.dwLength = sizeof(MemStatus);
10830 if (!GlobalMemoryStatusEx(&MemStatus))
10831 memset(&MemStatus, 0, sizeof(MemStatus));
10832 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
10833 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
10834 MemStatus.ullAvailVirtual);
10835 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
10836
10837 maybe_con_fwrite(szBuf, off, 1, stdout);
10838 fflush(stdout);
10839}
10840
10841
10842/**
10843 * Handles what comes after --test.
10844 *
10845 * @returns Exit code.
10846 * @param argc Number of arguments after --test.
10847 * @param argv Arguments after --test.
10848 */
10849static int kwTestRun(int argc, char **argv)
10850{
10851 int i;
10852 int j;
10853 int rcExit;
10854 int cRepeats;
10855 char szCwd[MAX_PATH];
10856 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
10857 KU32 cEnvVars;
10858 KBOOL fWatcomBrainDamange = K_FALSE;
10859 KBOOL fNoPchCaching = K_FALSE;
10860
10861 /*
10862 * Parse arguments.
10863 */
10864 /* Repeat count. */
10865 i = 0;
10866 if (i >= argc)
10867 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
10868 if (strcmp(argv[i], "--") != 0)
10869 {
10870 cRepeats = atoi(argv[i]);
10871 if (cRepeats <= 0)
10872 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
10873 i++;
10874
10875 /* Optional directory change. */
10876 if ( i < argc
10877 && ( strcmp(argv[i], "--chdir") == 0
10878 || strcmp(argv[i], "-C") == 0 ) )
10879 {
10880 i++;
10881 if (i >= argc)
10882 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
10883 pszCwd = argv[i++];
10884 }
10885
10886 /* Optional watcom flag directory change. */
10887 if ( i < argc
10888 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
10889 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
10890 {
10891 fWatcomBrainDamange = K_TRUE;
10892 i++;
10893 }
10894
10895 /* Optional watcom flag directory change. */
10896 if ( i < argc
10897 && strcmp(argv[i], "--no-pch-caching") == 0)
10898 {
10899 fNoPchCaching = K_TRUE;
10900 i++;
10901 }
10902
10903 /* Trigger breakpoint */
10904 if ( i < argc
10905 && strcmp(argv[i], "--breakpoint") == 0)
10906 {
10907 __debugbreak();
10908 i++;
10909 }
10910
10911 /* Check for '--'. */
10912 if (i >= argc)
10913 return kwErrPrintfRc(2, "Missing '--'\n");
10914 if (strcmp(argv[i], "--") != 0)
10915 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
10916 i++;
10917 }
10918 else
10919 {
10920 cRepeats = 1;
10921 i++;
10922 }
10923 if (i >= argc)
10924 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
10925
10926 /*
10927 * Do the job.
10928 */
10929 cEnvVars = 0;
10930 while (environ[cEnvVars] != NULL)
10931 cEnvVars++;
10932
10933 for (j = 0; j < cRepeats; j++)
10934 {
10935 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
10936 argc - i, &argv[i], fWatcomBrainDamange,
10937 cEnvVars, environ, fNoPchCaching,
10938 0, NULL);
10939 KW_LOG(("rcExit=%d\n", rcExit));
10940 kwSandboxCleanupLate(&g_Sandbox);
10941 }
10942
10943 if (getenv("KWORKER_STATS") != NULL)
10944 kwPrintStats();
10945
10946# ifdef WITH_LOG_FILE
10947 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
10948 CloseHandle(g_hLogFile);
10949# endif
10950 return rcExit;
10951}
10952
10953
10954int main(int argc, char **argv)
10955{
10956 KSIZE cbMsgBuf = 0;
10957 KU8 *pbMsgBuf = NULL;
10958 int i;
10959 HANDLE hPipe = INVALID_HANDLE_VALUE;
10960 const char *pszTmp;
10961 KFSLOOKUPERROR enmIgnored;
10962#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10963 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
10964 kwSandboxVecXcptEmulateChained);
10965#endif
10966#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10967 HANDLE hCurProc = GetCurrentProcess();
10968 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10969 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10970 DWORD dwType;
10971#endif
10972
10973
10974#ifdef WITH_FIXED_VIRTUAL_ALLOCS
10975 /*
10976 * Reserve memory for cl.exe
10977 */
10978 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
10979 {
10980 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
10981 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
10982 g_aFixedVirtualAllocs[i].cbFixed,
10983 MEM_RESERVE, PAGE_READWRITE);
10984 if ( !g_aFixedVirtualAllocs[i].pvReserved
10985 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
10986 {
10987 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
10988 GetLastError());
10989 if (g_aFixedVirtualAllocs[i].pvReserved)
10990 {
10991 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
10992 g_aFixedVirtualAllocs[i].pvReserved = NULL;
10993 }
10994 }
10995 }
10996#endif
10997
10998 /*
10999 * Register our Control-C and Control-Break handlers.
11000 */
11001 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
11002 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
11003
11004 /*
11005 * Create the cache and mark the temporary directory as using the custom revision.
11006 */
11007 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
11008 if (!g_pFsCache)
11009 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
11010
11011 pszTmp = getenv("TEMP");
11012 if (pszTmp && *pszTmp != '\0')
11013 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
11014 pszTmp = getenv("TMP");
11015 if (pszTmp && *pszTmp != '\0')
11016 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
11017 pszTmp = getenv("TMPDIR");
11018 if (pszTmp && *pszTmp != '\0')
11019 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
11020
11021 /*
11022 * Make g_abDefLdBuf executable.
11023 */
11024 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
11025 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
11026 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
11027
11028#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11029 /*
11030 * Get and duplicate the console handles.
11031 */
11032 /* Standard output. */
11033 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
11034 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
11035 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
11036 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
11037 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
11038 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
11039 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
11040 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
11041 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
11042 g_Sandbox.HandleStdOut.cRefs = 0x10001;
11043 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
11044 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
11045 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
11046 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
11047 {
11048 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
11049 g_Sandbox.cFixedHandles++;
11050 else
11051 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
11052 }
11053 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
11054 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
11055
11056 /* Standard error. */
11057 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
11058 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
11059 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
11060 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
11061 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
11062 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
11063 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
11064 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
11065 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
11066 g_Sandbox.HandleStdErr.cRefs = 0x10001;
11067 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
11068 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
11069 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
11070 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
11071 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
11072 {
11073 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
11074 g_Sandbox.cFixedHandles++;
11075 else
11076 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
11077 }
11078 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
11079 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
11080
11081 /* Combined console buffer. */
11082 if (g_Sandbox.StdErr.fIsConsole)
11083 {
11084 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
11085 g_Sandbox.Combined.uCodepage = GetConsoleCP();
11086 }
11087 else if (g_Sandbox.StdOut.fIsConsole)
11088 {
11089 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
11090 g_Sandbox.Combined.uCodepage = GetConsoleCP();
11091 }
11092 else
11093 {
11094 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
11095 g_Sandbox.Combined.uCodepage = CP_ACP;
11096 }
11097 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
11098#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
11099
11100
11101 /*
11102 * Parse arguments.
11103 */
11104 for (i = 1; i < argc; i++)
11105 {
11106 if (strcmp(argv[i], "--pipe") == 0)
11107 {
11108 i++;
11109 if (i < argc)
11110 {
11111 char *pszEnd = NULL;
11112 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
11113 if ( *argv[i]
11114 && pszEnd != NULL
11115 && *pszEnd == '\0'
11116 && u64Value != 0
11117 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
11118 && (uintptr_t)u64Value == u64Value)
11119 hPipe = (HANDLE)(uintptr_t)u64Value;
11120 else
11121 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
11122 }
11123 else
11124 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
11125 }
11126 else if (strcmp(argv[i], "--volatile") == 0)
11127 {
11128 i++;
11129 if (i < argc)
11130 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
11131 else
11132 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
11133 }
11134 else if (strcmp(argv[i], "--test") == 0)
11135 return kwTestRun(argc - i - 1, &argv[i + 1]);
11136 else if (strcmp(argv[i], "--priority") == 0)
11137 {
11138 i++;
11139 if (i < argc)
11140 {
11141 char *pszEnd = NULL;
11142 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
11143 if ( *argv[i]
11144 && pszEnd != NULL
11145 && *pszEnd == '\0'
11146 && uValue >= 1
11147 && uValue <= 5)
11148 {
11149 DWORD dwClass, dwPriority;
11150 switch (uValue)
11151 {
11152 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
11153 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
11154 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
11155 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
11156 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
11157 }
11158 SetPriorityClass(GetCurrentProcess(), dwClass);
11159 if (dwPriority != 0xffffffff)
11160 SetThreadPriority(GetCurrentThread(), dwPriority);
11161 }
11162 else
11163 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
11164 }
11165 else
11166 return kwErrPrintfRc(2, "--priority takes an argument!\n");
11167
11168 }
11169 else if ( strcmp(argv[i], "--help") == 0
11170 || strcmp(argv[i], "-h") == 0
11171 || strcmp(argv[i], "-?") == 0)
11172 {
11173 printf("usage: kWorker [--volatile dir] [--priority <1-5>] --pipe <pipe-handle>\n"
11174 "usage: kWorker <--help|-h>\n"
11175 "usage: kWorker <--version|-V>\n"
11176 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
11177 "\n"
11178 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
11179 return 0;
11180 }
11181 else if ( strcmp(argv[i], "--version") == 0
11182 || strcmp(argv[i], "-V") == 0)
11183 return kbuild_version(argv[0]);
11184 else
11185 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
11186 }
11187
11188 if (hPipe == INVALID_HANDLE_VALUE)
11189 return kwErrPrintfRc(2, "Missing --pipe <pipe-handle> argument!\n");
11190
11191 /*
11192 * Serve the pipe.
11193 */
11194 for (;;)
11195 {
11196 KU32 cbMsg = 0;
11197 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
11198 if (rc == 0)
11199 {
11200 /* Make sure the message length is within sane bounds. */
11201 if ( cbMsg > 4
11202 && cbMsg <= 256*1024*1024)
11203 {
11204 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
11205 if (cbMsg + 4 <= cbMsgBuf)
11206 { /* likely */ }
11207 else
11208 {
11209 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
11210 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
11211 if (!pbMsgBuf)
11212 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
11213 }
11214
11215 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
11216 *(KU32 *)pbMsgBuf = cbMsg;
11217 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
11218 if (rc == 0)
11219 {
11220 const char *psz;
11221
11222 pbMsgBuf[cbMsg] = '\0';
11223 pbMsgBuf[cbMsg + 1] = '\0';
11224 pbMsgBuf[cbMsg + 2] = '\0';
11225 pbMsgBuf[cbMsg + 3] = '\0';
11226
11227 /* The first string after the header is the command. */
11228 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
11229 if (strcmp(psz, "JOB") == 0)
11230 {
11231 struct
11232 {
11233 KI32 rcExitCode;
11234 KU8 bExiting;
11235 KU8 abZero[3];
11236 } Reply;
11237 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
11238 Reply.bExiting = g_fRestart;
11239 Reply.abZero[0] = 0;
11240 Reply.abZero[1] = 0;
11241 Reply.abZero[2] = 0;
11242 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
11243 if ( rc == 0
11244 && !g_fRestart)
11245 {
11246 kwSandboxCleanupLate(&g_Sandbox);
11247 continue;
11248 }
11249 }
11250 else
11251 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
11252 }
11253 }
11254 else
11255 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
11256 }
11257
11258 /*
11259 * If we're exitting because we're restarting, we need to delay till
11260 * kmk/kSubmit has read the result. Windows documentation says it
11261 * immediately discards pipe buffers once the pipe is broken by the
11262 * server (us). So, We flush the buffer and queues a 1 byte read
11263 * waiting for kSubmit to close the pipe when it receives the
11264 * bExiting = K_TRUE result.
11265 */
11266 if (g_fRestart)
11267 {
11268 KU8 b;
11269 FlushFileBuffers(hPipe);
11270 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
11271 }
11272
11273 CloseHandle(hPipe);
11274#ifdef WITH_LOG_FILE
11275 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
11276 CloseHandle(g_hLogFile);
11277#endif
11278 if (getenv("KWORKER_STATS") != NULL)
11279 kwPrintStats();
11280 return rc > 0 ? 0 : 1;
11281 }
11282}
11283
11284
11285/** @page pg_kWorker kSubmit / kWorker
11286 *
11287 * @section sec_kWorker_Motivation Motivation / Inspiration
11288 *
11289 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
11290 * builds on machines "infested" by Anti Virus protection and disk encryption
11291 * software. Build times jumping from 35-40 min to 77-82 min after the machine
11292 * got "infected".
11293 *
11294 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
11295 * mainly a bunch of tiny assembly and C files being compiler a million times.
11296 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
11297 * own toolchain from within the same process, saving a lot of process creation
11298 * and teardown overhead.
11299 *
11300 *
11301 * @section sec_kWorker_kSubmit About kSubmit
11302 *
11303 * When wanting to execute a job in a kWorker instance, it must be submitted
11304 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
11305 * built into kmk and does not exist as an external program. The reason for
11306 * this is that it keep track of the kWorker instances.
11307 *
11308 * The kSubmit command has the --32-bit and --64-bit options for selecting
11309 * between 32-bit and 64-bit worker instance. We generally assume the user of
11310 * the command knows which bit count the executable has, so kSubmit is spared
11311 * the extra work of finding out.
11312 *
11313 * The kSubmit command shares a environment and current directory manipulation
11314 * with the kRedirect command, but not the file redirection. So long no file
11315 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
11316 * hand for tools like OpenWatcom, NASM and YASM which all require environment
11317 * and/or current directory changes to work.
11318 *
11319 * Unlike the kRedirect command, the kSubmit command can also specify an
11320 * internall post command to be executed after the main command succeeds.
11321 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
11322 * information from Microsoft COFF object files and Watcom OMF object files and
11323 * is scheduled to replace kDepIDB.
11324 *
11325 *
11326 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
11327 *
11328 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
11329 * A job request is written by kSubmit and kWorker read, unpacks it and executes
11330 * it. When the job is completed, kWorker writes a short reply with the exit
11331 * code and an internal status indicating whether it is going to restart.
11332 *
11333 * The kWorker intance will reply to kSubmit before completing all the internal
11334 * cleanup work, so as not to delay the next job execution unnecessarily. This
11335 * includes checking its own memory consumption and checking whether it needs
11336 * restarting. So, a decision to restart unfortunately have to wait till after
11337 * the next job has completed. This is a little bit unfortunate if the next job
11338 * requires a lot of memory and kWorker has already leaked/used a lot.
11339 *
11340 *
11341 * @section sec_kWorker_How_Works How kWorker Works
11342 *
11343 * kWorker will load the executable specified by kSubmit into memory and call
11344 * it's entrypoint in a lightly sandbox'ed environment.
11345 *
11346 *
11347 * @subsection ssec_kWorker_Loaing Image loading
11348 *
11349 * kWorker will manually load all the executable images into memory, fix them
11350 * up, and make a copy of the virgin image so it can be restored using memcpy
11351 * the next time it is used.
11352 *
11353 * Imported functions are monitored and replacements used for a few of them.
11354 * These replacements are serve the following purposes:
11355 * - Provide a different command line.
11356 * - Provide a different environment.
11357 * - Intercept process termination.
11358 * - Intercept thread creation (only linker is allowed to create threads).
11359 * - Intercept file reading for caching (header files, ++) as file system
11360 * access is made even slower by anti-virus software.
11361 * - Intercept crypto hash APIs to cache MD5 digests of header files
11362 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
11363 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
11364 * in memory as writing files grows expensive with encryption and
11365 * anti-virus software active.
11366 * - Intercept some file system queries to use the kFsCache instead of
11367 * going to the kernel and slowly worm thru the AV filter driver.
11368 * - Intercept standard output/error and console writes to aggressivly
11369 * buffer the output. The MS CRT does not buffer either when it goes to
11370 * the console, resulting in terrible performance and mixing up output
11371 * with other compile jobs.
11372 * This also allows us to filter out the annoying source file announcements
11373 * by cl.exe.
11374 * - Intercept VirtualAlloc and VirtualFree to prevent
11375 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
11376 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
11377 * the callbacks run after each job.
11378 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
11379 * executables and tools using custom heaps (like the microsoft linker).
11380 * [exectuable images only]
11381 * - Intercept atexit and _onexit registration to be able run them after
11382 * each job instead of crashing as kWorker exits. This also helps avoid
11383 * some leaks. [executable image only]
11384 *
11385 * DLLs falls into two categories, system DLLs which we always load using the
11386 * native loader, and tool DLLs which can be handled like the executable or
11387 * optionally using the native loader. We maintain a hardcoded white listing of
11388 * tool DLLs we trust to load using the native loader.
11389 *
11390 * Imports of natively loaded DLLs are processed too, but we only replace a
11391 * subset of the functions compared to natively loaded excutable and DLL images.
11392 *
11393 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
11394 * This is to speed up job execution.
11395 *
11396 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
11397 * for each job run, but so far this hasn't been necessary.
11398 *
11399 *
11400 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
11401 *
11402 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
11403 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
11404 * intermediate representation between the first (c1/c1xx.dll) and second pass
11405 * (c2.dll).
11406 *
11407 * kWorker helps the compiler as best as it can. Given a little knowledge about
11408 * stable and volatile file system areas, it can do a lot of caching that a
11409 * normal compiler driver cannot easily do when given a single file.
11410 *
11411 *
11412 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
11413 *
11414 * The preprocessor part will open and process header files exactly as they are
11415 * encountered in the source files. If string.h is included by the main source
11416 * and five other header files, it will be searched for (include path), opened,
11417 * read, MD5-summed, and pre-processed six times. The last five times is just a
11418 * waste of time because of the guards or \#pragma once. A smart compiler would
11419 * make a little extra effort and realize this.
11420 *
11421 * kWorker will cache help the preprocessor by remembering places where the
11422 * header was not found with help of kFsCache, and cache the file in memory when
11423 * found. The first part is taken care of by intercepting GetFileAttributesW,
11424 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
11425 * cached, the file is kept open and the CreateFileW call returns a duplicate of
11426 * that handle. An internal handle table is used by ReadFile and CloseFile to
11427 * keep track of intercepted handles (also used for temporary file, temporary
11428 * file mappings, console buffering, and standard out/err buffering).
11429 *
11430 * PS. The header search optimization also comes in handy when cl.exe goes on
11431 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
11432 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
11433 * optionally compile the three pass DLLs as executables during development
11434 * and problem analysis.
11435 *
11436 *
11437 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
11438 *
11439 * The issues of the temporary files is pretty severe on the Dell machine used
11440 * for benchmarking with full AV and encryption. The synthetic benchmark
11441 * improved by 30% when kWorker implemented measures to keep them entirely in
11442 * memory.
11443 *
11444 * kWorker implement these by recognizing the filename pattern in CreateFileW
11445 * and creating/opening the given file as needed. The handle returned is a
11446 * duplicate of the current process, thus giving us a good chance of catching
11447 * API calls we're not intercepting.
11448 *
11449 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
11450 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
11451 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
11452 * UnmapViewOfFile.
11453 *
11454 *
11455 * @section sec_kWorker_Numbers Some measurements.
11456 *
11457 * - r2881 building src/VBox/Runtime:
11458 * - without: 2m01.016388s = 120.016388 s
11459 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
11460 * - r2884 building vbox/debug (r110512):
11461 * - without: 11m14.446609s = 674.446609 s
11462 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
11463 * - r2896 building vbox/debug (r110577):
11464 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
11465 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
11466 * MS Defender as AV):
11467 * - without: 10m24.990389s = 624.990389s
11468 * - with: 08m04.738184s = 484.738184s
11469 * - delta: 624.99s - 484.74s = 140.25s
11470 * - saved: 140.25/624.99 = 22% faster
11471 *
11472 *
11473 * @subsection subsec_kWorker_Early_Numbers Early Experiments
11474 *
11475 * These are some early experiments doing 1024 compilations of
11476 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
11477 * main function:
11478 *
11479 * Skylake (W10/amd64, only stdandard MS defender):
11480 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
11481 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
11482 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
11483 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
11484 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
11485 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
11486 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
11487 *
11488 * Dell (W7/amd64, infected by mcafee):
11489 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
11490 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
11491 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
11492 *
11493 * The command line:
11494 * @code{.cpp}
11495 "\"E:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/bin/amd64/cl.exe\" -c -c -TP -nologo -Zi -Zi -Zl -GR- -EHsc -GF -Zc:wchar_t- -Oy- -MT -W4 -Wall -wd4065 -wd4996 -wd4127 -wd4706 -wd4201 -wd4214 -wd4510 -wd4512 -wd4610 -wd4514 -wd4820 -wd4365 -wd4987 -wd4710 -wd4061 -wd4986 -wd4191 -wd4574 -wd4917 -wd4711 -wd4611 -wd4571 -wd4324 -wd4505 -wd4263 -wd4264 -wd4738 -wd4242 -wd4244 -WX -RTCsu -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/include -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/atlmfc/include -IE:/vbox/svn/trunk/tools/win.x86/sdk/v7.1/Include -IE:/vbox/svn/trunk/include -IE:/vbox/svn/trunk/out/win.amd64/debug -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/include -IE:/vbox/svn/trunk/tools/win.x86/vcc/v10sp1/atlmfc/include -DVBOX -DVBOX_WITH_64_BITS_GUESTS -DVBOX_WITH_REM -DVBOX_WITH_RAW_MODE -DDEBUG -DDEBUG_bird -DDEBUG_USERNAME=bird -DRT_OS_WINDOWS -D__WIN__ -DRT_ARCH_AMD64 -D__AMD64__ -D__WIN64__ -DVBOX_WITH_DEBUGGER -DRT_LOCK_STRICT -DRT_LOCK_STRICT_ORDER -DIN_RING3 -DLOG_DISABLED -DIN_BLD_PROG -D_CRT_SECURE_NO_DEPRECATE -FdE:/vbox/svn/trunk/out/win.amd64/debug/obj/VBoxBs2Linker/VBoxBs2Linker-obj.pdb -FD -FoE:/vbox/svn/trunk/out/win.amd64/debug/obj/VBoxBs2Linker/VBoxBs2Linker.obj E:\\vbox\\svn\\trunk\\src\\VBox\\ValidationKit\\bootsectors\\VBoxBs2Linker.cpp"
11496 * @endcode
11497 */
11498
Note: See TracBrowser for help on using the repository browser.

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