VirtualBox

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

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

kmk,kWorker: Some fixes wrt output capture and ctrl-c.

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

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