VirtualBox

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

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

kWorker: Fixed failure 43 (42+1) problem when running built tools from the kBuild output dirs. Implmented base image TLS support and added a _beginthreadex hack for VCC120. The TLS stuff needs more work, probably.

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

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