VirtualBox

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

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

kWorker: Better handling of fixed allocations in cl.exe, avoiding that fatal /Ym error with precompiled headers.

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

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