VirtualBox

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

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

kmk,kWorker: More aggressive handling of --nice (and --priority) on windows.

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