VirtualBox

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

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

kWorker: Fixed combined console buffer overflow problem when there's a lot of errors/output.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 388.9 KB
Line 
1/* $Id: kWorker.c 2962 2016-09-22 23:30:03Z 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
7210 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
7211 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
7212 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
7213 {
7214 void *pvNew;
7215 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
7216 while (cwcNew < cwcNeeded)
7217 cwcNew *= 2;
7218 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
7219 if (pvNew)
7220 {
7221 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
7222 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
7223 }
7224 else
7225 {
7226 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
7227 if (pvNew)
7228 {
7229 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
7230 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
7231 }
7232 else
7233 {
7234 /* This isn't perfect, but it will have to do for now. */
7235 if (pLineBuf->u.Con.cwcBuf > 0)
7236 {
7237 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
7238 K_TRUE /*fBrokenLine*/);
7239 pLineBuf->u.Con.cwcBuf = 0;
7240 }
7241 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
7242 return;
7243 }
7244 }
7245 }
7246
7247 /*
7248 * Handle the case where we only add to the line buffer.
7249 */
7250 if (offLastIncompleteLine == 0)
7251 {
7252 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
7253 pLineBuf->u.Con.cwcBuf += cwcToWrite;
7254 return;
7255 }
7256 }
7257
7258 /*
7259 * If there is sufficient combined buffer to handle this request, this is rather simple.
7260 */
7261 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
7262 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
7263 {
7264 if (pLineBuf->u.Con.cwcBuf > 0)
7265 {
7266 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7267 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
7268 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
7269 pLineBuf->u.Con.cwcBuf = 0;
7270 }
7271
7272 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7273 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
7274 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
7275 }
7276 else
7277 {
7278 /*
7279 * Do line-by-line processing of the input, flusing the combined buffer
7280 * when it becomes necessary. We may have to write lines
7281 */
7282 KU32 off = 0;
7283 KU32 offNextLine = 0;
7284
7285 /* If there are buffered chars, we handle the first line outside the
7286 main loop. We must try our best outputting it as a complete line. */
7287 if (pLineBuf->u.Con.cwcBuf > 0)
7288 {
7289 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
7290 offNextLine++;
7291 offNextLine++;
7292 kHlpAssert(offNextLine <= offLastIncompleteLine);
7293
7294 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
7295 {
7296 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
7297 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
7298 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
7299 pLineBuf->u.Con.cwcBuf = 0;
7300
7301 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
7302 pSandbox->Combined.cwcBuf += offNextLine;
7303 }
7304 else
7305 {
7306 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
7307 if (cwcLeft > 0)
7308 {
7309 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
7310 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
7311 pLineBuf->u.Con.cwcBuf += cwcCopy;
7312 off += cwcCopy;
7313 }
7314 if (pLineBuf->u.Con.cwcBuf > 0)
7315 {
7316 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
7317 K_TRUE /*fBrokenLine*/);
7318 pLineBuf->u.Con.cwcBuf = 0;
7319 }
7320 if (off < offNextLine)
7321 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
7322 }
7323 off = offNextLine;
7324 }
7325
7326 /* Deal with the remaining lines */
7327 while (off < offLastIncompleteLine)
7328 {
7329 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
7330 offNextLine++;
7331 offNextLine++;
7332 kHlpAssert(offNextLine <= offLastIncompleteLine);
7333 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
7334 off = offNextLine;
7335 }
7336 }
7337
7338 /*
7339 * Buffer any remaining incomplete line chars.
7340 */
7341 if (cchLastIncompleteLine)
7342 {
7343 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
7344 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
7345 }
7346 }
7347}
7348
7349
7350/**
7351 * Worker for WriteConsoleA and WriteFile.
7352 *
7353 * @param pSandbox The sandbox.
7354 * @param pLineBuf The line buffer.
7355 * @param pchBuffer What to write.
7356 * @param cchToWrite How much to write.
7357 */
7358static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
7359{
7360 /*
7361 * Convert it to wide char and use the 'W' to do the work.
7362 */
7363 int cwcRet;
7364 KU32 cwcBuf = cchToWrite * 2 + 1;
7365 wchar_t *pwcBufFree = NULL;
7366 wchar_t *pwcBuf;
7367 kHlpAssert(pLineBuf->fIsConsole);
7368
7369 if (cwcBuf <= 4096)
7370 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
7371 else
7372 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
7373
7374 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
7375 if (cwcRet > 0)
7376 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
7377 else
7378 {
7379 DWORD cchWritten;
7380 kHlpAssertFailed();
7381
7382 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
7383 if (pLineBuf->u.Con.cwcBuf > 0)
7384 {
7385 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
7386 pLineBuf->u.Con.cwcBuf = 0;
7387 }
7388 kwSandboxConsoleFlushCombined(pSandbox);
7389
7390 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
7391 {
7392 if (cchWritten >= cchToWrite)
7393 { /* likely */ }
7394 else
7395 {
7396 KU32 off = 0;
7397 do
7398 {
7399 off += cchWritten;
7400 cchWritten = 0;
7401 } while ( off < cchToWrite
7402 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
7403 }
7404 }
7405 }
7406
7407 if (pwcBufFree)
7408 kHlpFree(pwcBufFree);
7409}
7410
7411
7412/** Kernel32 - WriteConsoleA */
7413BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
7414 PVOID pvReserved)
7415{
7416 BOOL fRc;
7417 PKWOUTPUTSTREAMBUF pLineBuf;
7418 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7419
7420 if (hConOutput == g_Sandbox.StdErr.hOutput)
7421 pLineBuf = &g_Sandbox.StdErr;
7422 else
7423 pLineBuf = &g_Sandbox.StdOut;
7424 if (pLineBuf->fIsConsole)
7425 {
7426 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
7427
7428 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
7429 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
7430 if (pcbWritten)
7431 *pcbWritten = cbToWrite;
7432 fRc = TRUE;
7433 }
7434 else
7435 {
7436 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
7437 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
7438 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
7439 }
7440 return fRc;
7441}
7442
7443
7444/** Kernel32 - WriteConsoleW */
7445BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
7446 PVOID pvReserved)
7447{
7448 BOOL fRc;
7449 PKWOUTPUTSTREAMBUF pLineBuf;
7450 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7451
7452 if (hConOutput == g_Sandbox.StdErr.hOutput)
7453 pLineBuf = &g_Sandbox.StdErr;
7454 else if (hConOutput == g_Sandbox.StdOut.hOutput)
7455 pLineBuf = &g_Sandbox.StdOut;
7456 else
7457 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
7458 if (pLineBuf->fIsConsole)
7459 {
7460 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
7461
7462 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
7463 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
7464 if (pcwcWritten)
7465 *pcwcWritten = cwcToWrite;
7466 fRc = TRUE;
7467 }
7468 else
7469 {
7470 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
7471 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
7472 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
7473 }
7474 return fRc;
7475}
7476
7477#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
7478
7479
7480
7481/*
7482 *
7483 * Virtual memory leak prevension.
7484 * Virtual memory leak prevension.
7485 * Virtual memory leak prevension.
7486 *
7487 */
7488
7489#ifdef WITH_FIXED_VIRTUAL_ALLOCS
7490
7491/** For debug logging. */
7492# ifndef NDEBUG
7493static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
7494{
7495 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
7496 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
7497 kHlpAssert(cbMemInfo == sizeof(MemInfo));
7498 if (cbMemInfo != 0)
7499 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
7500 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
7501 MemInfo.BaseAddress,
7502 MemInfo.AllocationBase,
7503 MemInfo.RegionSize,
7504 MemInfo.State,
7505 MemInfo.Protect,
7506 MemInfo.Type));
7507}
7508# else
7509# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
7510# endif
7511
7512/**
7513 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
7514 *
7515 * @param idxFixed The fixed allocation index to "free".
7516 */
7517static void kwSandboxResetFixedAllocation(KU32 idxFixed)
7518{
7519 BOOL fRc;
7520 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
7521 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
7522 kHlpAssert(fRc); K_NOREF(fRc);
7523 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
7524 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
7525}
7526
7527#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
7528
7529
7530/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
7531 * location (~78MB in 32-bit 2010 compiler). */
7532static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
7533{
7534 PVOID pvMem;
7535 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
7536 {
7537 KU32 idxPreAllocated = KU32_MAX;
7538
7539#ifdef WITH_FIXED_VIRTUAL_ALLOCS
7540 /*
7541 * Look for a pre-reserved CL.exe heap allocation.
7542 */
7543 pvMem = NULL;
7544 if ( pvAddr != 0
7545 && (fAllocType & MEM_RESERVE))
7546 {
7547 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
7548 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
7549 while (idxFixed-- > 0)
7550 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
7551 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
7552 {
7553 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
7554 {
7555 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
7556 {
7557 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
7558 pvMem = pvAddr;
7559 idxPreAllocated = idxFixed;
7560 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
7561 pvAddr, cb, fAllocType, fProt, pvMem));
7562 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
7563 SetLastError(NO_ERROR);
7564 break;
7565 }
7566 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
7567 }
7568 else
7569 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
7570 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
7571 }
7572 }
7573 if (!pvMem)
7574#endif
7575 {
7576 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
7577 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
7578 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
7579 if (pvAddr && pvAddr != pvMem)
7580 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
7581 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
7582 }
7583
7584 if (pvMem)
7585 {
7586 /*
7587 * Track it.
7588 */
7589 PKWVIRTALLOC pTracker;
7590 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7591
7592 pTracker = g_Sandbox.pVirtualAllocHead;
7593 while ( pTracker
7594 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
7595 pTracker = pTracker->pNext;
7596 if (!pTracker)
7597 {
7598 DWORD dwErr = GetLastError();
7599 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
7600 if (pTracker)
7601 {
7602 pTracker->pvAlloc = pvMem;
7603 pTracker->cbAlloc = cb;
7604 pTracker->idxPreAllocated = idxPreAllocated;
7605 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
7606 g_Sandbox.pVirtualAllocHead = pTracker;
7607 }
7608 SetLastError(dwErr);
7609 }
7610 }
7611 }
7612 else
7613 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
7614 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
7615 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
7616 return pvMem;
7617}
7618
7619
7620/** Kernel32 - VirtualFree. */
7621static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
7622{
7623 BOOL fRc;
7624 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
7625 {
7626 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7627 if (dwFreeType & MEM_RELEASE)
7628 {
7629 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
7630 if (pTracker)
7631 {
7632 if (pTracker->pvAlloc == pvAddr)
7633 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
7634 else
7635 {
7636 PKWVIRTALLOC pPrev;
7637 do
7638 {
7639 pPrev = pTracker;
7640 pTracker = pTracker->pNext;
7641 } while (pTracker && pTracker->pvAlloc != pvAddr);
7642 if (pTracker)
7643 pPrev->pNext = pTracker->pNext;
7644 }
7645 if (pTracker)
7646 {
7647#ifdef WITH_FIXED_VIRTUAL_ALLOCS
7648 if (pTracker->idxPreAllocated != KU32_MAX)
7649 {
7650 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
7651 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
7652 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
7653 kHlpFree(pTracker);
7654 return TRUE;
7655 }
7656#endif
7657
7658 fRc = VirtualFree(pvAddr, cb, dwFreeType);
7659 if (fRc)
7660 kHlpFree(pTracker);
7661 else
7662 {
7663 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
7664 g_Sandbox.pVirtualAllocHead = pTracker;
7665 }
7666 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
7667 return fRc;
7668 }
7669
7670 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
7671 }
7672 }
7673 }
7674
7675#ifdef WITH_FIXED_VIRTUAL_ALLOCS
7676 /*
7677 * Protect our fixed allocations (this isn't just paranoia, btw.).
7678 */
7679 if (dwFreeType & MEM_RELEASE)
7680 {
7681 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
7682 while (idxFixed-- > 0)
7683 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
7684 {
7685 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
7686 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
7687 return TRUE;
7688 }
7689 }
7690#endif
7691
7692 /*
7693 * Not tracker or not actually free the virtual range.
7694 */
7695 fRc = VirtualFree(pvAddr, cb, dwFreeType);
7696 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
7697 return fRc;
7698}
7699
7700
7701/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
7702HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
7703{
7704 HANDLE hHeap;
7705 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7706
7707 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
7708 if (hHeap != NULL)
7709 {
7710 DWORD dwErr = GetLastError();
7711 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
7712 if (pTracker)
7713 {
7714 pTracker->hHeap = hHeap;
7715 pTracker->pNext = g_Sandbox.pHeapHead;
7716 g_Sandbox.pHeapHead = pTracker;
7717 }
7718
7719 SetLastError(dwErr);
7720 }
7721 return hHeap;
7722
7723}
7724
7725
7726/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
7727BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
7728{
7729 BOOL fRc = HeapDestroy(hHeap);
7730 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
7731 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7732 if (fRc)
7733 {
7734 PKWHEAP pTracker = g_Sandbox.pHeapHead;
7735 if (pTracker)
7736 {
7737 if (pTracker->hHeap == hHeap)
7738 g_Sandbox.pHeapHead = pTracker->pNext;
7739 else
7740 {
7741 PKWHEAP pPrev;
7742 do
7743 {
7744 pPrev = pTracker;
7745 pTracker = pTracker->pNext;
7746 } while (pTracker && pTracker->hHeap == hHeap);
7747 if (pTracker)
7748 pPrev->pNext = pTracker->pNext;
7749 }
7750 if (pTracker)
7751 kHlpFree(pTracker);
7752 else
7753 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
7754 }
7755 }
7756
7757 return fRc;
7758}
7759
7760
7761
7762/*
7763 *
7764 * Thread/Fiber local storage leak prevention.
7765 * Thread/Fiber local storage leak prevention.
7766 * Thread/Fiber local storage leak prevention.
7767 *
7768 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
7769 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
7770 * we're leaking these indexes, but more importantely we crash during
7771 * worker exit since the callback is triggered multiple times.
7772 */
7773
7774
7775/** Kernel32 - FlsAlloc */
7776DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
7777{
7778 DWORD idxFls = FlsAlloc(pfnCallback);
7779 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
7780 if (idxFls != FLS_OUT_OF_INDEXES)
7781 {
7782 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
7783 if (pTracker)
7784 {
7785 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7786 pTracker->idx = idxFls;
7787 pTracker->pNext = g_Sandbox.pFlsAllocHead;
7788 g_Sandbox.pFlsAllocHead = pTracker;
7789 }
7790 }
7791
7792 return idxFls;
7793}
7794
7795/** Kernel32 - FlsFree */
7796BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
7797{
7798 BOOL fRc = FlsFree(idxFls);
7799 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
7800 if (fRc)
7801 {
7802 PKWLOCALSTORAGE pTracker;
7803 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7804
7805 pTracker = g_Sandbox.pFlsAllocHead;
7806 if (pTracker)
7807 {
7808 if (pTracker->idx == idxFls)
7809 g_Sandbox.pFlsAllocHead = pTracker->pNext;
7810 else
7811 {
7812 PKWLOCALSTORAGE pPrev;
7813 do
7814 {
7815 pPrev = pTracker;
7816 pTracker = pTracker->pNext;
7817 } while (pTracker && pTracker->idx != idxFls);
7818 if (pTracker)
7819 pPrev->pNext = pTracker->pNext;
7820 }
7821 if (pTracker)
7822 {
7823 pTracker->idx = FLS_OUT_OF_INDEXES;
7824 pTracker->pNext = NULL;
7825 kHlpFree(pTracker);
7826 }
7827 }
7828 }
7829 return fRc;
7830}
7831
7832
7833/** Kernel32 - TlsAlloc */
7834DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
7835{
7836 DWORD idxTls = TlsAlloc();
7837 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
7838 if (idxTls != TLS_OUT_OF_INDEXES)
7839 {
7840 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
7841 if (pTracker)
7842 {
7843 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7844 pTracker->idx = idxTls;
7845 pTracker->pNext = g_Sandbox.pTlsAllocHead;
7846 g_Sandbox.pTlsAllocHead = pTracker;
7847 }
7848 }
7849
7850 return idxTls;
7851}
7852
7853/** Kernel32 - TlsFree */
7854BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
7855{
7856 BOOL fRc = TlsFree(idxTls);
7857 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
7858 if (fRc)
7859 {
7860 PKWLOCALSTORAGE pTracker;
7861 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7862
7863 pTracker = g_Sandbox.pTlsAllocHead;
7864 if (pTracker)
7865 {
7866 if (pTracker->idx == idxTls)
7867 g_Sandbox.pTlsAllocHead = pTracker->pNext;
7868 else
7869 {
7870 PKWLOCALSTORAGE pPrev;
7871 do
7872 {
7873 pPrev = pTracker;
7874 pTracker = pTracker->pNext;
7875 } while (pTracker && pTracker->idx != idxTls);
7876 if (pTracker)
7877 pPrev->pNext = pTracker->pNext;
7878 }
7879 if (pTracker)
7880 {
7881 pTracker->idx = TLS_OUT_OF_INDEXES;
7882 pTracker->pNext = NULL;
7883 kHlpFree(pTracker);
7884 }
7885 }
7886 }
7887 return fRc;
7888}
7889
7890
7891
7892/*
7893 *
7894 * Header file hashing.
7895 * Header file hashing.
7896 * Header file hashing.
7897 *
7898 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
7899 * indicated that ~12% of the time was spent doing MD5 caluclation when
7900 * rebuiling openssl. The hashing it done right after reading the source
7901 * via ReadFile, same buffers and sizes.
7902 */
7903
7904#ifdef WITH_HASH_MD5_CACHE
7905
7906/** AdvApi32 - CryptCreateHash */
7907static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
7908 HCRYPTHASH *phHash)
7909{
7910 BOOL fRc;
7911
7912 /*
7913 * Only do this for cl.exe when it request normal MD5.
7914 */
7915 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
7916 {
7917 if (idAlg == CALG_MD5)
7918 {
7919 if (hKey == 0)
7920 {
7921 if (dwFlags == 0)
7922 {
7923 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
7924 if (pHash)
7925 {
7926 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7927 pHash->uMagic = KWHASHMD5_MAGIC;
7928 pHash->cbHashed = 0;
7929 pHash->fGoneBad = K_FALSE;
7930 pHash->fFallbackMode = K_FALSE;
7931 pHash->fFinal = K_FALSE;
7932
7933 /* link it. */
7934 pHash->pNext = g_Sandbox.pHashHead;
7935 g_Sandbox.pHashHead = pHash;
7936
7937 *phHash = (KUPTR)pHash;
7938 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
7939 hProv, *phHash, TRUE));
7940 return TRUE;
7941 }
7942
7943 kwErrPrintf("CryptCreateHash: out of memory!\n");
7944 }
7945 else
7946 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
7947 }
7948 else
7949 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
7950 }
7951 else
7952 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
7953 }
7954
7955 /*
7956 * Fallback.
7957 */
7958 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
7959 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
7960 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
7961 return fRc;
7962}
7963
7964
7965/** AdvApi32 - CryptHashData */
7966static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
7967{
7968 BOOL fRc;
7969 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
7970 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7971 while (pHash && (KUPTR)pHash != hHash)
7972 pHash = pHash->pNext;
7973 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
7974 hHash, pHash, pbData, cbData, dwFlags));
7975 if (pHash)
7976 {
7977 /*
7978 * Validate the state.
7979 */
7980 if ( pHash->uMagic == KWHASHMD5_MAGIC
7981 && !pHash->fFinal)
7982 {
7983 if (!pHash->fFallbackMode)
7984 {
7985 /*
7986 * Does this match the previous ReadFile call to a cached file?
7987 * If it doesn't, try falling back.
7988 */
7989 if ( g_Sandbox.LastHashRead.cbRead == cbData
7990 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
7991 {
7992 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
7993 if ( pCachedFile
7994 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
7995 {
7996
7997 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
7998 {
7999 if ( pHash->pCachedFile == NULL
8000 && pHash->cbHashed == 0)
8001 pHash->pCachedFile = pCachedFile;
8002 if (pHash->pCachedFile == pCachedFile)
8003 {
8004 pHash->cbHashed += cbData;
8005 g_Sandbox.LastHashRead.pCachedFile = NULL;
8006 g_Sandbox.LastHashRead.pvRead = NULL;
8007 g_Sandbox.LastHashRead.cbRead = 0;
8008 g_Sandbox.LastHashRead.offRead = 0;
8009 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
8010 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
8011 return TRUE;
8012 }
8013
8014 /* Note! it's possible to fall back here too, if necessary. */
8015 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
8016 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
8017 }
8018 else
8019 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
8020 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
8021 }
8022 else if (!pCachedFile)
8023 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
8024 else
8025 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
8026 }
8027 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
8028 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
8029 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
8030 if (pHash->cbHashed == 0)
8031 pHash->fFallbackMode = K_TRUE;
8032 if (pHash->fFallbackMode)
8033 {
8034 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
8035 pHash->fFallbackMode = K_TRUE;
8036 MD5Init(&pHash->Md5Ctx);
8037 MD5Update(&pHash->Md5Ctx, pbData, cbData);
8038 pHash->cbHashed = cbData;
8039 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
8040 hHash, pbData, cbData, dwFlags));
8041 return TRUE;
8042 }
8043 pHash->fGoneBad = K_TRUE;
8044 SetLastError(ERROR_INVALID_PARAMETER);
8045 fRc = FALSE;
8046 }
8047 else
8048 {
8049 /* fallback. */
8050 MD5Update(&pHash->Md5Ctx, pbData, cbData);
8051 pHash->cbHashed += cbData;
8052 fRc = TRUE;
8053 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
8054 hHash, pbData, cbData, dwFlags));
8055 }
8056 }
8057 /*
8058 * Bad handle state.
8059 */
8060 else
8061 {
8062 if (pHash->uMagic != KWHASHMD5_MAGIC)
8063 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
8064 else
8065 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
8066 SetLastError(NTE_BAD_HASH);
8067 fRc = FALSE;
8068 }
8069 }
8070 else
8071 {
8072
8073 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
8074 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
8075 }
8076 return fRc;
8077}
8078
8079
8080/** AdvApi32 - CryptGetHashParam */
8081static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
8082 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
8083{
8084 BOOL fRc;
8085 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
8086 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8087 while (pHash && (KUPTR)pHash != hHash)
8088 pHash = pHash->pNext;
8089 if (pHash)
8090 {
8091 if (pHash->uMagic == KWHASHMD5_MAGIC)
8092 {
8093 if (dwFlags == 0)
8094 {
8095 DWORD cbRet;
8096 void *pvRet;
8097 union
8098 {
8099 DWORD dw;
8100 } uBuf;
8101
8102 switch (dwParam)
8103 {
8104 case HP_HASHVAL:
8105 {
8106 /* Check the hash progress. */
8107 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
8108 if (pCachedFile)
8109 {
8110 if ( pCachedFile->cbCached == pHash->cbHashed
8111 && !pHash->fGoneBad)
8112 {
8113 if (pCachedFile->fValidMd5)
8114 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
8115 else
8116 {
8117 MD5Init(&pHash->Md5Ctx);
8118 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
8119 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
8120 pCachedFile->fValidMd5 = K_TRUE;
8121 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
8122 }
8123 pvRet = pCachedFile->abMd5Digest;
8124 }
8125 else
8126 {
8127 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
8128 from what I can tell, so just deal with it. */
8129 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
8130 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
8131 pHash, pCachedFile, pCachedFile->szPath));
8132 pHash->fFallbackMode = K_TRUE;
8133 pHash->pCachedFile = NULL;
8134 MD5Init(&pHash->Md5Ctx);
8135 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
8136 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
8137 pvRet = pHash->abDigest;
8138 }
8139 pHash->fFinal = K_TRUE;
8140 cbRet = 16;
8141 break;
8142 }
8143 else if (pHash->fFallbackMode)
8144 {
8145 if (!pHash->fFinal)
8146 {
8147 pHash->fFinal = K_TRUE;
8148 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
8149 }
8150 pvRet = pHash->abDigest;
8151 cbRet = 16;
8152 break;
8153 }
8154 else
8155 {
8156 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
8157 SetLastError(ERROR_INVALID_SERVER_STATE);
8158 }
8159 return FALSE;
8160 }
8161
8162 case HP_HASHSIZE:
8163 uBuf.dw = 16;
8164 pvRet = &uBuf;
8165 cbRet = sizeof(DWORD);
8166 break;
8167
8168 case HP_ALGID:
8169 uBuf.dw = CALG_MD5;
8170 pvRet = &uBuf;
8171 cbRet = sizeof(DWORD);
8172 break;
8173
8174 default:
8175 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
8176 SetLastError(NTE_BAD_TYPE);
8177 return FALSE;
8178 }
8179
8180 /*
8181 * Copy out cbRet from pvRet.
8182 */
8183 if (pbData)
8184 {
8185 if (*pcbData >= cbRet)
8186 {
8187 *pcbData = cbRet;
8188 kHlpMemCopy(pbData, pvRet, cbRet);
8189 if (cbRet == 4)
8190 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
8191 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
8192 else if (cbRet == 16)
8193 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",
8194 dwParam, pHash, pHash->pCachedFile, cbRet,
8195 pbData[0], pbData[1], pbData[2], pbData[3],
8196 pbData[4], pbData[5], pbData[6], pbData[7],
8197 pbData[8], pbData[9], pbData[10], pbData[11],
8198 pbData[12], pbData[13], pbData[14], pbData[15]));
8199 else
8200 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
8201 dwParam, pHash, pHash->pCachedFile, cbRet));
8202 return TRUE;
8203 }
8204
8205 kHlpMemCopy(pbData, pvRet, *pcbData);
8206 }
8207 SetLastError(ERROR_MORE_DATA);
8208 *pcbData = cbRet;
8209 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
8210 }
8211 else
8212 {
8213 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
8214 SetLastError(NTE_BAD_FLAGS);
8215 }
8216 }
8217 else
8218 {
8219 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
8220 SetLastError(NTE_BAD_HASH);
8221 }
8222 fRc = FALSE;
8223 }
8224 /*
8225 * Regular handle.
8226 */
8227 else
8228 {
8229 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
8230 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
8231 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
8232 }
8233
8234 return fRc;
8235}
8236
8237
8238/** AdvApi32 - CryptDestroyHash */
8239static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
8240{
8241 BOOL fRc;
8242 PKWHASHMD5 pPrev = NULL;
8243 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
8244 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8245 while (pHash && (KUPTR)pHash != hHash)
8246 {
8247 pPrev = pHash;
8248 pHash = pHash->pNext;
8249 }
8250 if (pHash)
8251 {
8252 if (pHash->uMagic == KWHASHMD5_MAGIC)
8253 {
8254 pHash->uMagic = 0;
8255 if (!pPrev)
8256 g_Sandbox.pHashHead = pHash->pNext;
8257 else
8258 pPrev->pNext = pHash->pNext;
8259 kHlpFree(pHash);
8260 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
8261 fRc = TRUE;
8262 }
8263 else
8264 {
8265 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
8266 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
8267 SetLastError(ERROR_INVALID_HANDLE);
8268 fRc = FALSE;
8269 }
8270 }
8271 /*
8272 * Regular handle.
8273 */
8274 else
8275 {
8276 fRc = CryptDestroyHash(hHash);
8277 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
8278 }
8279 return fRc;
8280}
8281
8282#endif /* WITH_HASH_MD5_CACHE */
8283
8284
8285/*
8286 *
8287 * Reuse crypt context.
8288 * Reuse crypt context.
8289 * Reuse crypt context.
8290 *
8291 *
8292 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
8293 *
8294 */
8295
8296#ifdef WITH_CRYPT_CTX_REUSE
8297
8298/** AdvApi32 - CryptAcquireContextW. */
8299static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
8300 DWORD dwProvType, DWORD dwFlags)
8301{
8302 BOOL fRet;
8303
8304 /*
8305 * Lookup reusable context based on the input.
8306 */
8307 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
8308 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
8309 KU32 iCtx = g_Sandbox.cCryptCtxs;
8310 while (iCtx-- > 0)
8311 {
8312 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
8313 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
8314 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
8315 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
8316 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
8317 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
8318 {
8319 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
8320 {
8321 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
8322 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
8323 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8324 return TRUE;
8325 }
8326 }
8327 }
8328
8329 /*
8330 * Create it and enter it into the reused array if possible.
8331 */
8332 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
8333 if (fRet)
8334 {
8335 iCtx = g_Sandbox.cCryptCtxs;
8336 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
8337 {
8338 /* Try duplicate the input strings. */
8339 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
8340 (cwcContainer + 1) * sizeof(wchar_t));
8341 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
8342 {
8343 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
8344 (cwcProvider + 1) * sizeof(wchar_t));
8345 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
8346 {
8347 /* Add a couple of references just to be on the safe side and all that. */
8348 HCRYPTPROV hProv = *phProv;
8349 if (CryptContextAddRef(hProv, NULL, 0))
8350 {
8351 if (CryptContextAddRef(hProv, NULL, 0))
8352 {
8353 /* Okay, finish the entry and return success */
8354 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
8355 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
8356 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
8357 g_Sandbox.cCryptCtxs = iCtx + 1;
8358
8359 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
8360 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8361 return TRUE;
8362 }
8363 CryptReleaseContext(hProv, 0);
8364 }
8365 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
8366
8367 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
8368 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
8369 }
8370 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
8371 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
8372 }
8373 }
8374 else
8375 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
8376 }
8377
8378 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
8379 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
8380 return fRet;
8381}
8382
8383
8384/** AdvApi32 - CryptReleaseContext */
8385static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
8386{
8387 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
8388 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
8389 return fRet;
8390}
8391
8392
8393/** AdvApi32 - CryptContextAddRef */
8394static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
8395{
8396 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
8397 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
8398 return fRet;
8399}
8400
8401#endif /* WITH_CRYPT_CTX_REUSE */
8402
8403/*
8404 *
8405 * Structured exception handling.
8406 * Structured exception handling.
8407 * Structured exception handling.
8408 *
8409 */
8410#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
8411
8412# define EH_NONCONTINUABLE KU32_C(0x00000001)
8413# define EH_UNWINDING KU32_C(0x00000002)
8414# define EH_EXIT_UNWIND KU32_C(0x00000004)
8415# define EH_STACK_INVALID KU32_C(0x00000008)
8416# define EH_NESTED_CALL KU32_C(0x00000010)
8417
8418typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
8419 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
8420typedef struct _EXCEPTION_REGISTRATION_RECORD
8421{
8422 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
8423 PFNXCPTHANDLER pfnXcptHandler;
8424};
8425
8426
8427/**
8428 * Calls @a pfnHandler.
8429 */
8430static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
8431 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
8432 PFNXCPTHANDLER pfnHandler)
8433{
8434# if 1
8435 /* This is a more robust version that isn't subject to calling
8436 convension cleanup disputes and such. */
8437 KU32 uSavedEdi;
8438 KU32 uSavedEsi;
8439 KU32 uSavedEbx;
8440 KU32 rcHandler;
8441
8442 __asm
8443 {
8444 mov [uSavedEdi], edi
8445 mov [uSavedEsi], esi
8446 mov [uSavedEbx], ebx
8447 mov esi, esp
8448 mov edi, esp
8449 mov edi, [pXcptRec]
8450 mov edx, [pRegRec]
8451 mov eax, [pXcptCtx]
8452 mov ebx, [ppRegRec]
8453 mov ecx, [pfnHandler]
8454 sub esp, 16
8455 and esp, 0fffffff0h
8456 mov [esp ], edi
8457 mov [esp + 4], edx
8458 mov [esp + 8], eax
8459 mov [esp + 12], ebx
8460 mov edi, esi
8461 call ecx
8462 mov esp, esi
8463 cmp esp, edi
8464 je stack_ok
8465 int 3
8466 stack_ok:
8467 mov edi, [uSavedEdi]
8468 mov esi, [uSavedEsi]
8469 mov ebx, [uSavedEbx]
8470 mov [rcHandler], eax
8471 }
8472 return rcHandler;
8473# else
8474 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
8475# endif
8476}
8477
8478
8479/**
8480 * Vectored exception handler that emulates x86 chained exception handler.
8481 *
8482 * This is necessary because the RtlIsValidHandler check fails for self loaded
8483 * code and prevents cl.exe from working. (On AMD64 we can register function
8484 * tables, but on X86 cooking your own handling seems to be the only viabke
8485 * alternative.)
8486 *
8487 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
8488 * @param pXcptPtrs The exception details.
8489 */
8490static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
8491{
8492 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
8493 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
8494 if (g_Sandbox.fRunning)
8495 {
8496 HANDLE const hCurProc = GetCurrentProcess();
8497 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
8498 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
8499 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
8500 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
8501 {
8502 /* Read the exception record in a safe manner. */
8503 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
8504 DWORD cbActuallyRead = 0;
8505 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
8506 && cbActuallyRead == sizeof(RegRec))
8507 {
8508 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
8509 KU32 rcHandler;
8510 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
8511 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
8512 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
8513 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
8514 if (rcHandler == ExceptionContinueExecution)
8515 {
8516 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
8517 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
8518 return EXCEPTION_CONTINUE_EXECUTION;
8519 }
8520
8521 if (rcHandler == ExceptionContinueSearch)
8522 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
8523 else if (rcHandler == ExceptionNestedException)
8524 kHlpAssertMsgFailed(("Nested exceptions.\n"));
8525 else
8526 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
8527 }
8528 else
8529 {
8530 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
8531 break;
8532 }
8533
8534 /*
8535 * Next.
8536 */
8537 pRegRec = RegRec.pPrevRegRec;
8538 }
8539 }
8540 return EXCEPTION_CONTINUE_SEARCH;
8541}
8542
8543
8544/** NtDll,Kernel32 - RtlUnwind */
8545static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
8546 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
8547{
8548 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
8549 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
8550 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
8551 if (g_Sandbox.fRunning)
8552 {
8553 HANDLE const hCurProc = GetCurrentProcess();
8554 PCONTEXT pXcptCtx = NULL;
8555 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
8556
8557 /*
8558 * Update / create an exception record.
8559 */
8560 if (pXcptRec)
8561 pXcptRec->ExceptionFlags |= EH_UNWINDING;
8562 else
8563 {
8564 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
8565 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
8566 pXcptRec->ExceptionCode = STATUS_UNWIND;
8567 pXcptRec->ExceptionFlags = EH_UNWINDING;
8568 }
8569 if (!pStopXcptRec)
8570 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
8571
8572 /*
8573 * Walk the chain till we find pStopXctpRec.
8574 */
8575 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
8576 && pRegRec != NULL
8577 && pRegRec != pStopXcptRec)
8578 {
8579 /* Read the exception record in a safe manner. */
8580 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
8581 DWORD cbActuallyRead = 0;
8582 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
8583 && cbActuallyRead == sizeof(RegRec))
8584 {
8585 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
8586 KU32 rcHandler;
8587 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
8588 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
8589 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
8590 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
8591
8592 if (rcHandler == ExceptionContinueSearch)
8593 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
8594 else if (rcHandler == ExceptionCollidedUnwind)
8595 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
8596 else
8597 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
8598 }
8599 else
8600 {
8601 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
8602 break;
8603 }
8604
8605 /*
8606 * Pop next.
8607 */
8608 pTib->ExceptionList = RegRec.pPrevRegRec;
8609 pRegRec = RegRec.pPrevRegRec;
8610 }
8611 return;
8612 }
8613
8614 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
8615}
8616
8617#endif /* WINDOWS + X86 */
8618
8619
8620/*
8621 *
8622 * Misc function only intercepted while debugging.
8623 * Misc function only intercepted while debugging.
8624 * Misc function only intercepted while debugging.
8625 *
8626 */
8627
8628#ifndef NDEBUG
8629
8630/** CRT - memcpy */
8631static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
8632{
8633 KU8 const *pbSrc = (KU8 const *)pvSrc;
8634 KU8 *pbDst = (KU8 *)pvDst;
8635 KSIZE cbLeft = cb;
8636 while (cbLeft-- > 0)
8637 *pbDst++ = *pbSrc++;
8638 return pvDst;
8639}
8640
8641#endif /* NDEBUG */
8642
8643
8644
8645/**
8646 * Functions that needs replacing for sandboxed execution.
8647 */
8648KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
8649{
8650 /*
8651 * Kernel32.dll and friends.
8652 */
8653 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
8654 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
8655
8656 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
8657 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
8658 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
8659 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
8660 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
8661 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
8662 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
8663 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
8664 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
8665 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
8666 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
8667
8668 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
8669 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
8670 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
8671 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
8672
8673 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
8674
8675 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
8676 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
8677 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
8678 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
8679 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
8680 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
8681 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
8682 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
8683 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
8684 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
8685 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
8686
8687 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
8688 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
8689 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
8690 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
8691#ifdef WITH_TEMP_MEMORY_FILES
8692 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
8693 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
8694 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
8695 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
8696 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
8697 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
8698 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
8699 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
8700 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
8701 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
8702#endif
8703 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
8704 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
8705 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
8706 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
8707 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
8708 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
8709 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
8710#ifdef WITH_TEMP_MEMORY_FILES
8711 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
8712#endif
8713
8714 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
8715 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
8716
8717 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
8718 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
8719
8720 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
8721 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
8722
8723 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
8724 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
8725 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
8726 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
8727
8728 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
8729
8730#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
8731 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
8732#endif
8733
8734#ifdef WITH_HASH_MD5_CACHE
8735 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
8736 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
8737 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
8738 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
8739#endif
8740
8741#ifdef WITH_CRYPT_CTX_REUSE
8742 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
8743 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
8744 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
8745#endif
8746
8747 /*
8748 * MS Visual C++ CRTs.
8749 */
8750 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
8751 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
8752 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
8753 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
8754 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
8755 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
8756
8757 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
8758 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
8759 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
8760
8761 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
8762 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
8763
8764 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
8765 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
8766 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
8767 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
8768 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
8769 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
8770 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
8771 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
8772 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
8773 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
8774 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
8775 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
8776 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
8777 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
8778 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
8779 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
8780 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
8781 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
8782 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
8783 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
8784
8785 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
8786 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
8787 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
8788 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
8789 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
8790 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
8791 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
8792 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
8793 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
8794 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
8795 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
8796 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
8797 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
8798 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
8799
8800#ifndef NDEBUG
8801 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
8802#endif
8803};
8804/** Number of entries in g_aReplacements. */
8805KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
8806
8807
8808/**
8809 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
8810 * execution.
8811 */
8812KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
8813{
8814 /*
8815 * Kernel32.dll and friends.
8816 */
8817 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
8818 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
8819
8820#if 0
8821 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
8822#endif
8823
8824 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
8825 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
8826 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
8827 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
8828#ifdef WITH_TEMP_MEMORY_FILES
8829 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
8830 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
8831 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
8832 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
8833 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
8834 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
8835 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
8836 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
8837 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
8838 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
8839#endif
8840 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
8841 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
8842 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
8843 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
8844 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
8845 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
8846 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
8847#ifdef WITH_TEMP_MEMORY_FILES
8848 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
8849#endif
8850 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
8851
8852 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
8853 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
8854
8855#ifdef WITH_HASH_MD5_CACHE
8856 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
8857 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
8858 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
8859 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
8860#endif
8861
8862 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
8863
8864#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
8865 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
8866#endif
8867
8868 /*
8869 * MS Visual C++ CRTs.
8870 */
8871 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
8872 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
8873 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
8874 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
8875 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
8876 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
8877
8878#if 0 /* used by mspdbXXX.dll */
8879 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
8880 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
8881#endif
8882};
8883/** Number of entries in g_aSandboxNativeReplacements. */
8884KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
8885
8886
8887/**
8888 * Functions that needs replacing when queried by GetProcAddress.
8889 */
8890KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
8891{
8892 /*
8893 * Kernel32.dll and friends.
8894 */
8895 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
8896 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
8897 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
8898 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
8899};
8900/** Number of entries in g_aSandboxGetProcReplacements. */
8901KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
8902
8903
8904/**
8905 * Control handler.
8906 *
8907 * @returns TRUE if handled, FALSE if not.
8908 * @param dwCtrlType The signal.
8909 */
8910static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
8911{
8912 switch (dwCtrlType)
8913 {
8914 case CTRL_C_EVENT:
8915 fprintf(stderr, "kWorker: Ctrl-C\n");
8916 g_fCtrlC = K_TRUE;
8917 exit(9);
8918 break;
8919
8920 case CTRL_BREAK_EVENT:
8921 fprintf(stderr, "kWorker: Ctrl-Break\n");
8922 g_fCtrlC = K_TRUE;
8923 exit(10);
8924 break;
8925
8926 case CTRL_CLOSE_EVENT:
8927 fprintf(stderr, "kWorker: console closed\n");
8928 g_fCtrlC = K_TRUE;
8929 exit(11);
8930 break;
8931
8932 case CTRL_LOGOFF_EVENT:
8933 fprintf(stderr, "kWorker: logoff event\n");
8934 g_fCtrlC = K_TRUE;
8935 exit(11);
8936 break;
8937
8938 case CTRL_SHUTDOWN_EVENT:
8939 fprintf(stderr, "kWorker: shutdown event\n");
8940 g_fCtrlC = K_TRUE;
8941 exit(11);
8942 break;
8943
8944 default:
8945 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
8946 break;
8947 }
8948 return TRUE;
8949}
8950
8951
8952/**
8953 * Used by kwSandboxExec to reset the state of the module tree.
8954 *
8955 * This is done recursively.
8956 *
8957 * @param pMod The root of the tree to consider.
8958 */
8959static void kwSandboxResetModuleState(PKWMODULE pMod)
8960{
8961 if ( !pMod->fNative
8962 && pMod->u.Manual.enmState != KWMODSTATE_NEEDS_BITS)
8963 {
8964 KSIZE iImp;
8965 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
8966 iImp = pMod->u.Manual.cImpMods;
8967 while (iImp-- > 0)
8968 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
8969 }
8970}
8971
8972static PPEB kwSandboxGetProcessEnvironmentBlock(void)
8973{
8974#if K_ARCH == K_ARCH_X86_32
8975 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
8976#elif K_ARCH == K_ARCH_AMD64
8977 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
8978#else
8979# error "Port me!"
8980#endif
8981}
8982
8983
8984/**
8985 * Enters the given handle into the handle table.
8986 *
8987 * @returns K_TRUE on success, K_FALSE on failure.
8988 * @param pSandbox The sandbox.
8989 * @param pHandle The handle.
8990 * @param hHandle The handle value to enter it under (for the
8991 * duplicate handle API).
8992 */
8993static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
8994{
8995 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
8996 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
8997
8998 /*
8999 * Grow handle table.
9000 */
9001 if (idxHandle >= pSandbox->cHandles)
9002 {
9003 void *pvNew;
9004 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
9005 while (cHandles <= idxHandle)
9006 cHandles *= 2;
9007 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
9008 if (!pvNew)
9009 {
9010 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
9011 return K_FALSE;
9012 }
9013 pSandbox->papHandles = (PKWHANDLE *)pvNew;
9014 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
9015 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
9016 pSandbox->cHandles = cHandles;
9017 }
9018
9019 /*
9020 * Check that the entry is unused then insert it.
9021 */
9022 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
9023 pSandbox->papHandles[idxHandle] = pHandle;
9024 pSandbox->cActiveHandles++;
9025 return K_TRUE;
9026}
9027
9028
9029/**
9030 * Creates a correctly quoted ANSI command line string from the given argv.
9031 *
9032 * @returns Pointer to the command line.
9033 * @param cArgs Number of arguments.
9034 * @param papszArgs The argument vector.
9035 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
9036 * @param pcbCmdLine Where to return the command line length,
9037 * including one terminator.
9038 */
9039static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
9040{
9041 KU32 i;
9042 KSIZE cbCmdLine;
9043 char *pszCmdLine;
9044
9045 /* Make a copy of the argument vector that we'll be quoting. */
9046 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
9047 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
9048
9049 /* Quote the arguments that need it. */
9050 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
9051
9052 /* figure out cmd line length. */
9053 cbCmdLine = 0;
9054 for (i = 0; i < cArgs; i++)
9055 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
9056 *pcbCmdLine = cbCmdLine;
9057
9058 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
9059 if (pszCmdLine)
9060 {
9061 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
9062 if (papszQuotedArgs[0] != papszArgs[0])
9063 free(papszQuotedArgs[0]);
9064
9065 for (i = 1; i < cArgs; i++)
9066 {
9067 *psz++ = ' ';
9068 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
9069 if (papszQuotedArgs[i] != papszArgs[i])
9070 free(papszQuotedArgs[i]);
9071 }
9072 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
9073
9074 *psz++ = '\0';
9075 *psz++ = '\0';
9076 }
9077
9078 return pszCmdLine;
9079}
9080
9081
9082
9083static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
9084 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
9085 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
9086{
9087 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
9088 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
9089 wchar_t *pwcPool;
9090 KSIZE cbStrings;
9091 KSIZE cwc;
9092 KSIZE cbCmdLine;
9093 KU32 i;
9094
9095 /* Simple stuff. */
9096 pSandbox->rcExitCode = 256;
9097 pSandbox->pTool = pTool;
9098 pSandbox->idMainThread = GetCurrentThreadId();
9099 pSandbox->pgmptr = (char *)pTool->pszPath;
9100 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
9101#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
9102 if (pSandbox->StdOut.fIsConsole)
9103 pSandbox->StdOut.u.Con.cwcBuf = 0;
9104 else
9105 pSandbox->StdOut.u.Fully.cchBuf = 0;
9106 if (pSandbox->StdErr.fIsConsole)
9107 pSandbox->StdErr.u.Con.cwcBuf = 0;
9108 else
9109 pSandbox->StdErr.u.Fully.cchBuf = 0;
9110 pSandbox->Combined.cwcBuf = 0;
9111 pSandbox->Combined.cFlushes = 0;
9112#endif
9113 pSandbox->fNoPchCaching = fNoPchCaching;
9114 pSandbox->cArgs = cArgs;
9115 pSandbox->papszArgs = (char **)papszArgs;
9116 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
9117 if (!pSandbox->pszCmdLine)
9118 return KERR_NO_MEMORY;
9119
9120 /*
9121 * Convert command line and argv to UTF-16.
9122 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
9123 */
9124 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
9125 if (!pSandbox->papwszArgs)
9126 return KERR_NO_MEMORY;
9127 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
9128 for (i = 0; i < cArgs; i++)
9129 {
9130 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
9131 pSandbox->papwszArgs[i] = pwcPool;
9132 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
9133 pwcPool++;
9134 }
9135 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
9136 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
9137
9138 /*
9139 * Convert the commandline string to UTF-16, same pessimistic approach as above.
9140 */
9141 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
9142 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
9143 if (!pSandbox->pwszCmdLine)
9144 return KERR_NO_MEMORY;
9145 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
9146
9147 pSandbox->SavedCommandLine = pProcParams->CommandLine;
9148 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
9149 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
9150
9151 /*
9152 * Setup the environment.
9153 */
9154 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
9155 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
9156 {
9157 KU32 iDst = 0;
9158 for (i = 0; i < cEnvVars; i++)
9159 {
9160 const char *pszVar = papszEnvVars[i];
9161 KSIZE cchVar = kHlpStrLen(pszVar);
9162 if ( cchVar > 0
9163 && kHlpMemChr(pszVar, '=', cchVar) != NULL)
9164 {
9165 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
9166 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
9167 if (pszCopy && pwszCopy)
9168 {
9169 pSandbox->papszEnvVars[iDst] = pszCopy;
9170 pSandbox->environ[iDst] = pszCopy;
9171 pSandbox->papwszEnvVars[iDst] = pwszCopy;
9172 pSandbox->wenviron[iDst] = pwszCopy;
9173 iDst++;
9174 }
9175 else
9176 {
9177 kHlpFree(pszCopy);
9178 kHlpFree(pwszCopy);
9179 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
9180 }
9181 }
9182 else
9183 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
9184 }
9185 pSandbox->papszEnvVars[iDst] = NULL;
9186 pSandbox->environ[iDst] = NULL;
9187 pSandbox->papwszEnvVars[iDst] = NULL;
9188 pSandbox->wenviron[iDst] = NULL;
9189 }
9190 else
9191 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
9192
9193
9194 /*
9195 * Invalidate the volatile parts of cache (kBuild output directory,
9196 * temporary directory, whatever).
9197 */
9198 kFsCacheInvalidateCustomBoth(g_pFsCache);
9199
9200#ifdef WITH_HISTORY
9201 /*
9202 * Record command line in debug history.
9203 */
9204 kHlpFree(g_apszHistory[g_iHistoryNext]);
9205 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
9206 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
9207#endif
9208
9209 return 0;
9210}
9211
9212
9213/**
9214 * Does sandbox cleanup between jobs.
9215 *
9216 * We postpone whatever isn't externally visible (i.e. files) and doesn't
9217 * influence the result, so that kmk can get on with things ASAP.
9218 *
9219 * @param pSandbox The sandbox.
9220 */
9221static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
9222{
9223 PROCESS_MEMORY_COUNTERS MemInfo;
9224 PKWVIRTALLOC pTracker;
9225 PKWHEAP pHeap;
9226 PKWLOCALSTORAGE pLocalStorage;
9227#ifdef WITH_HASH_MD5_CACHE
9228 PKWHASHMD5 pHash;
9229#endif
9230#ifdef WITH_TEMP_MEMORY_FILES
9231 PKWFSTEMPFILE pTempFile;
9232#endif
9233 PKWEXITCALLACK pExitCallback;
9234
9235 /*
9236 * First stuff that may cause code to run.
9237 */
9238
9239 /* Do exit callback first. */
9240 pExitCallback = g_Sandbox.pExitCallbackHead;
9241 g_Sandbox.pExitCallbackHead = NULL;
9242 while (pExitCallback)
9243 {
9244 PKWEXITCALLACK pNext = pExitCallback->pNext;
9245 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
9246 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
9247 __try
9248 {
9249 pExitCallback->pfnCallback();
9250 }
9251 __except (EXCEPTION_EXECUTE_HANDLER)
9252 {
9253 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
9254 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
9255 kHlpAssertFailed();
9256 }
9257 kHlpFree(pExitCallback);
9258 pExitCallback = pNext;
9259 }
9260
9261 /* Free left behind FlsAlloc leaks. */
9262 pLocalStorage = g_Sandbox.pFlsAllocHead;
9263 g_Sandbox.pFlsAllocHead = NULL;
9264 while (pLocalStorage)
9265 {
9266 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
9267 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
9268 FlsFree(pLocalStorage->idx);
9269 kHlpFree(pLocalStorage);
9270 pLocalStorage = pNext;
9271 }
9272
9273 /* Free left behind TlsAlloc leaks. */
9274 pLocalStorage = g_Sandbox.pTlsAllocHead;
9275 g_Sandbox.pTlsAllocHead = NULL;
9276 while (pLocalStorage)
9277 {
9278 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
9279 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
9280 TlsFree(pLocalStorage->idx);
9281 kHlpFree(pLocalStorage);
9282 pLocalStorage = pNext;
9283 }
9284
9285
9286 /*
9287 * Then free resources associated with the sandbox run.
9288 */
9289
9290 /* Open handles, except fixed handles (stdout and stderr). */
9291 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
9292 {
9293 KU32 idxHandle = pSandbox->cHandles;
9294 while (idxHandle-- > 0)
9295 if (pSandbox->papHandles[idxHandle] == NULL)
9296 { /* likely */ }
9297 else
9298 {
9299 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
9300 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
9301 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
9302 {
9303 pSandbox->papHandles[idxHandle] = NULL;
9304 pSandbox->cLeakedHandles++;
9305
9306 switch (pHandle->enmType)
9307 {
9308 case KWHANDLETYPE_FSOBJ_READ_CACHE:
9309 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
9310 idxHandle, pHandle->hHandle, pHandle->cRefs));
9311 break;
9312 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
9313 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
9314 idxHandle, pHandle->hHandle, pHandle->cRefs));
9315 break;
9316 case KWHANDLETYPE_OUTPUT_BUF:
9317 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
9318 idxHandle, pHandle->hHandle, pHandle->cRefs));
9319 break;
9320 case KWHANDLETYPE_TEMP_FILE:
9321 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
9322 idxHandle, pHandle->hHandle, pHandle->cRefs));
9323 pHandle->u.pTempFile->cActiveHandles--;
9324 break;
9325 case KWHANDLETYPE_TEMP_FILE_MAPPING:
9326 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
9327 idxHandle, pHandle->hHandle, pHandle->cRefs));
9328 pHandle->u.pTempFile->cActiveHandles--;
9329 break;
9330 default:
9331 kHlpAssertFailed();
9332 }
9333 if (--pHandle->cRefs == 0)
9334 kHlpFree(pHandle);
9335 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
9336 break;
9337 }
9338 }
9339 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
9340 }
9341
9342 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
9343 g_Sandbox.cMemMappings = 0;
9344
9345#ifdef WITH_TEMP_MEMORY_FILES
9346 /* The temporary files aren't externally visible, they're all in memory. */
9347 pTempFile = pSandbox->pTempFileHead;
9348 pSandbox->pTempFileHead = NULL;
9349 while (pTempFile)
9350 {
9351 PKWFSTEMPFILE pNext = pTempFile->pNext;
9352 KU32 iSeg = pTempFile->cSegs;
9353 while (iSeg-- > 0)
9354 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
9355 kHlpFree(pTempFile->paSegs);
9356 pTempFile->pNext = NULL;
9357 kHlpFree(pTempFile);
9358
9359 pTempFile = pNext;
9360 }
9361#endif
9362
9363 /* Free left behind HeapCreate leaks. */
9364 pHeap = g_Sandbox.pHeapHead;
9365 g_Sandbox.pHeapHead = NULL;
9366 while (pHeap != NULL)
9367 {
9368 PKWHEAP pNext = pHeap->pNext;
9369 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
9370 HeapDestroy(pHeap->hHeap);
9371 pHeap = pNext;
9372 }
9373
9374 /* Free left behind VirtualAlloc leaks. */
9375 pTracker = g_Sandbox.pVirtualAllocHead;
9376 g_Sandbox.pVirtualAllocHead = NULL;
9377 while (pTracker)
9378 {
9379 PKWVIRTALLOC pNext = pTracker->pNext;
9380 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
9381
9382#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9383 if (pTracker->idxPreAllocated != KU32_MAX)
9384 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9385 else
9386#endif
9387 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
9388 kHlpFree(pTracker);
9389 pTracker = pNext;
9390 }
9391
9392 /* Free the environment. */
9393 if (pSandbox->papszEnvVars)
9394 {
9395 KU32 i;
9396 for (i = 0; pSandbox->papszEnvVars[i]; i++)
9397 kHlpFree(pSandbox->papszEnvVars[i]);
9398 pSandbox->environ[0] = NULL;
9399 pSandbox->papszEnvVars[0] = NULL;
9400
9401 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
9402 kHlpFree(pSandbox->papwszEnvVars[i]);
9403 pSandbox->wenviron[0] = NULL;
9404 pSandbox->papwszEnvVars[0] = NULL;
9405 }
9406
9407#ifdef WITH_HASH_MD5_CACHE
9408 /*
9409 * Hash handles.
9410 */
9411 pHash = pSandbox->pHashHead;
9412 pSandbox->pHashHead = NULL;
9413 while (pHash)
9414 {
9415 PKWHASHMD5 pNext = pHash->pNext;
9416 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
9417 kHlpFree(pHash);
9418 pHash = pNext;
9419 }
9420#endif
9421
9422 /*
9423 * Check the memory usage. If it's getting high, trigger a respawn
9424 * after the next job.
9425 */
9426 MemInfo.WorkingSetSize = 0;
9427 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
9428 {
9429 /* The first time thru, we figure out approximately when to restart
9430 based on installed RAM and CPU threads. */
9431 static KU64 s_cbMaxWorkingSet = 0;
9432 if (s_cbMaxWorkingSet != 0)
9433 { /* likely */ }
9434 else
9435 {
9436 SYSTEM_INFO SysInfo;
9437 MEMORYSTATUSEX GlobalMemInfo;
9438 const char *pszValue;
9439
9440 /* Calculate a reasonable estimate. */
9441 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
9442 GetNativeSystemInfo(&SysInfo);
9443
9444 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
9445 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
9446 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
9447#if K_ARCH_BITS >= 64
9448 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
9449#else
9450 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
9451#endif
9452 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
9453 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
9454
9455 /* User limit. */
9456 pszValue = getenv("KWORKER_MEMORY_LIMIT");
9457 if (pszValue != NULL)
9458 {
9459 char *pszNext;
9460 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
9461 if (*pszNext == '\0' || *pszNext == 'M')
9462 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
9463 else if (*pszNext == 'K')
9464 s_cbMaxWorkingSet = ulValue * (KU64)1024;
9465 else if (*pszNext == 'G')
9466 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
9467 else
9468 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
9469 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
9470 }
9471
9472 /* Clamp it a little. */
9473 if (s_cbMaxWorkingSet < 168*1024*1024)
9474 s_cbMaxWorkingSet = 168*1024*1024;
9475#if K_ARCH_BITS < 64
9476 else
9477 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
9478 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
9479 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
9480 : 1536*1024*1024 /* got 4GB VA */);
9481#endif
9482 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
9483 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
9484 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
9485 }
9486
9487 /* Finally the check. */
9488 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
9489 {
9490 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
9491 g_fRestart = K_TRUE;
9492 }
9493 }
9494
9495 /*
9496 * The CRT has a max of 8192 handles, so we better restart after a while if
9497 * someone is leaking handles or we risk running out of descriptors.
9498 *
9499 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
9500 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
9501 */
9502 if (pSandbox->cLeakedHandles > 6000)
9503 {
9504 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
9505 g_fRestart = K_TRUE;
9506 }
9507}
9508
9509
9510/**
9511 * Does essential cleanups and restoring, anything externally visible.
9512 *
9513 * All cleanups that aren't externally visible are postponed till after we've
9514 * informed kmk of the result, so it can be done in the dead time between jobs.
9515 *
9516 * @param pSandbox The sandbox.
9517 */
9518static void kwSandboxCleanup(PKWSANDBOX pSandbox)
9519{
9520 /*
9521 * Restore the parent command line string.
9522 */
9523 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
9524 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
9525 pProcParams->CommandLine = pSandbox->SavedCommandLine;
9526 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
9527 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
9528}
9529
9530
9531static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
9532 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
9533{
9534 int rcExit = 42;
9535 int rc;
9536
9537 /*
9538 * Initialize the sandbox environment.
9539 */
9540 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
9541 if (rc == 0)
9542 {
9543 /*
9544 * Do module initialization.
9545 */
9546 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
9547 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
9548 if (rc == 0)
9549 {
9550 /*
9551 * Call the main function.
9552 */
9553#if K_ARCH == K_ARCH_AMD64
9554 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
9555#elif K_ARCH == K_ARCH_X86_32
9556 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
9557#else
9558# error "Port me!"
9559#endif
9560
9561 /* Save the NT TIB first (should do that here, not in some other function). */
9562 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9563 pSandbox->TibMainThread = *pTib;
9564
9565 /* Make the call in a guarded fashion. */
9566#if K_ARCH == K_ARCH_AMD64
9567 /* AMD64 */
9568 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
9569 __try
9570 {
9571 pSandbox->pOutXcptListHead = pTib->ExceptionList;
9572 if (setjmp(pSandbox->JmpBuf) == 0)
9573 {
9574 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
9575 pSandbox->fRunning = K_TRUE;
9576 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
9577 pSandbox->fRunning = K_FALSE;
9578 }
9579 else
9580 rcExit = pSandbox->rcExitCode;
9581 }
9582#elif K_ARCH == K_ARCH_X86_32
9583 /* x86 (see _tmainCRTStartup) */
9584 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
9585 __try
9586 {
9587 pSandbox->pOutXcptListHead = pTib->ExceptionList;
9588 if (setjmp(pSandbox->JmpBuf) == 0)
9589 {
9590 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
9591 pSandbox->fRunning = K_TRUE;
9592 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
9593 pSandbox->fRunning = K_FALSE;
9594 }
9595 else
9596 rcExit = pSandbox->rcExitCode;
9597 }
9598#endif
9599 __except (EXCEPTION_EXECUTE_HANDLER)
9600 {
9601 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
9602#ifdef WITH_HISTORY
9603 {
9604 KU32 cPrinted = 0;
9605 while (cPrinted++ < 5)
9606 {
9607 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
9608 if (g_apszHistory[idx])
9609 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
9610 }
9611 }
9612#endif
9613 rcExit = 512;
9614 }
9615 pSandbox->fRunning = K_FALSE;
9616
9617 /* Now, restore the NT TIB. */
9618 *pTib = pSandbox->TibMainThread;
9619 }
9620 else
9621 rcExit = 42 + 4;
9622
9623 /*
9624 * Flush and clean up the essential bits only, postpone whatever we
9625 * can till after we've replied to kmk.
9626 */
9627#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
9628 kwSandboxConsoleFlushAll(&g_Sandbox);
9629#endif
9630 kwSandboxCleanup(&g_Sandbox);
9631 }
9632 else
9633 rcExit = 42 + 3;
9634
9635 return rcExit;
9636}
9637
9638
9639/**
9640 * Does the post command part of a job (optional).
9641 *
9642 * @returns The exit code of the job.
9643 * @param cPostCmdArgs Number of post command arguments (includes cmd).
9644 * @param papszPostCmdArgs The post command and its argument.
9645 */
9646static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
9647{
9648 const char *pszCmd = papszPostCmdArgs[0];
9649
9650 /* Allow the kmk builtin prefix. */
9651 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
9652 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
9653 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
9654
9655 /* Command switch. */
9656 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
9657 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL);
9658
9659 return kwErrPrintfRc(42 + 5 , "Unknown post command: '%s'\n", pszCmd);
9660}
9661
9662
9663/**
9664 * Part 2 of the "JOB" command handler.
9665 *
9666 * @returns The exit code of the job.
9667 * @param pszExecutable The executable to execute.
9668 * @param pszCwd The current working directory of the job.
9669 * @param cArgs The number of arguments.
9670 * @param papszArgs The argument vector.
9671 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
9672 * @param cEnvVars The number of environment variables.
9673 * @param papszEnvVars The environment vector.
9674 * @param fNoPchCaching Whether to disable precompiled header file
9675 * caching. Avoid trouble when creating them.
9676 * @param cPostCmdArgs Number of post command arguments (includes cmd).
9677 * @param papszPostCmdArgs The post command and its argument.
9678 */
9679static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
9680 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
9681 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching,
9682 KU32 cPostCmdArgs, const char **papszPostCmdArgs)
9683{
9684 int rcExit;
9685 PKWTOOL pTool;
9686
9687 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
9688 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
9689#ifdef KW_LOG_ENABLED
9690 {
9691 KU32 i;
9692 for (i = 0; i < cArgs; i++)
9693 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
9694 for (i = 0; i < cPostCmdArgs; i++)
9695 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
9696 }
9697#endif
9698
9699 /*
9700 * Lookup the tool.
9701 */
9702 pTool = kwToolLookup(pszExecutable);
9703 if (pTool)
9704 {
9705 /*
9706 * Change the directory if we're going to execute the job inside
9707 * this process. Then invoke the tool type specific handler.
9708 */
9709 switch (pTool->enmType)
9710 {
9711 case KWTOOLTYPE_SANDBOXED:
9712 case KWTOOLTYPE_WATCOM:
9713 {
9714 /* Change dir. */
9715 KFSLOOKUPERROR enmError;
9716 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
9717 if ( pNewCurDir == g_pCurDirObj
9718 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
9719 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
9720 else if (SetCurrentDirectoryA(pszCwd))
9721 {
9722 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
9723 g_pCurDirObj = pNewCurDir;
9724 }
9725 else
9726 {
9727 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
9728 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
9729 rcExit = 42 + 1;
9730 break;
9731 }
9732
9733 /* Call specific handler. */
9734 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
9735 {
9736 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
9737 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
9738 cEnvVars, papszEnvVars, fNoPchCaching);
9739 }
9740 else
9741 {
9742 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
9743 rcExit = 42 + 2;
9744 }
9745 break;
9746 }
9747
9748 case KWTOOLTYPE_EXEC:
9749 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
9750 rcExit = 42 + 2;
9751 break;
9752
9753 default:
9754 kHlpAssertFailed();
9755 kwErrPrintf("Internal tool type corruption!!\n");
9756 rcExit = 42 + 2;
9757 g_fRestart = K_TRUE;
9758 break;
9759 }
9760
9761 /*
9762 * Do the post command, if present.
9763 */
9764 if (cPostCmdArgs && rcExit == 0)
9765 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
9766 }
9767 else
9768 rcExit = 42 + 1;
9769 return rcExit;
9770}
9771
9772
9773/**
9774 * Handles a "JOB" command.
9775 *
9776 * @returns The exit code of the job.
9777 * @param pszMsg Points to the "JOB" command part of the message.
9778 * @param cbMsg Number of message bytes at @a pszMsg. There are
9779 * 4 more zero bytes after the message body to
9780 * simplify parsing.
9781 */
9782static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
9783{
9784 int rcExit = 42;
9785
9786 /*
9787 * Unpack the message.
9788 */
9789 const char *pszExecutable;
9790 KSIZE cbTmp;
9791
9792 pszMsg += sizeof("JOB");
9793 cbMsg -= sizeof("JOB");
9794
9795 /* Executable name. */
9796 pszExecutable = pszMsg;
9797 cbTmp = kHlpStrLen(pszMsg) + 1;
9798 pszMsg += cbTmp;
9799 if ( cbTmp < cbMsg
9800 && cbTmp > 2)
9801 {
9802 const char *pszCwd;
9803 cbMsg -= cbTmp;
9804
9805 /* Current working directory. */
9806 pszCwd = pszMsg;
9807 cbTmp = kHlpStrLen(pszMsg) + 1;
9808 pszMsg += cbTmp;
9809 if ( cbTmp + sizeof(KU32) < cbMsg
9810 && cbTmp >= 2)
9811 {
9812 KU32 cArgs;
9813 cbMsg -= cbTmp;
9814
9815 /* Argument count. */
9816 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
9817 pszMsg += sizeof(cArgs);
9818 cbMsg -= sizeof(cArgs);
9819
9820 if (cArgs > 0 && cArgs < 4096)
9821 {
9822 /* The argument vector. */
9823 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
9824 if (papszArgs)
9825 {
9826 KU32 i;
9827 for (i = 0; i < cArgs; i++)
9828 {
9829 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
9830 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
9831 pszMsg += cbTmp;
9832 if (cbTmp < cbMsg)
9833 cbMsg -= cbTmp;
9834 else
9835 {
9836 cbMsg = 0;
9837 break;
9838 }
9839
9840 }
9841 papszArgs[cArgs] = 0;
9842
9843 /* Environment variable count. */
9844 if (cbMsg > sizeof(KU32))
9845 {
9846 KU32 cEnvVars;
9847 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
9848 pszMsg += sizeof(cEnvVars);
9849 cbMsg -= sizeof(cEnvVars);
9850
9851 if (cEnvVars >= 0 && cEnvVars < 4096)
9852 {
9853 /* The argument vector. */
9854 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
9855 if (papszEnvVars)
9856 {
9857 for (i = 0; i < cEnvVars; i++)
9858 {
9859 papszEnvVars[i] = pszMsg;
9860 cbTmp = kHlpStrLen(pszMsg) + 1;
9861 pszMsg += cbTmp;
9862 if (cbTmp < cbMsg)
9863 cbMsg -= cbTmp;
9864 else
9865 {
9866 cbMsg = 0;
9867 break;
9868 }
9869 }
9870 papszEnvVars[cEnvVars] = 0;
9871
9872 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
9873 if (cbMsg >= sizeof(KU8) * 2)
9874 {
9875 KBOOL fWatcomBrainDamange = *pszMsg++;
9876 KBOOL fNoPchCaching = *pszMsg++;
9877 cbMsg -= 2;
9878
9879 /* Post command argument count (can be zero). */
9880 if (cbMsg >= sizeof(KU32))
9881 {
9882 KU32 cPostCmdArgs;
9883 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
9884 pszMsg += sizeof(cPostCmdArgs);
9885 cbMsg -= sizeof(cPostCmdArgs);
9886
9887 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
9888 {
9889 char const *apszPostCmdArgs[32+1];
9890 for (i = 0; i < cPostCmdArgs; i++)
9891 {
9892 apszPostCmdArgs[i] = pszMsg;
9893 cbTmp = kHlpStrLen(pszMsg) + 1;
9894 pszMsg += cbTmp;
9895 if ( cbTmp < cbMsg
9896 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
9897 cbMsg -= cbTmp;
9898 else
9899 {
9900 cbMsg = KSIZE_MAX;
9901 break;
9902 }
9903 }
9904 if (cbMsg == 0)
9905 {
9906 apszPostCmdArgs[cPostCmdArgs] = NULL;
9907
9908 /*
9909 * The next step.
9910 */
9911 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
9912 cArgs, papszArgs, fWatcomBrainDamange,
9913 cEnvVars, papszEnvVars, fNoPchCaching,
9914 cPostCmdArgs, apszPostCmdArgs);
9915 }
9916 else if (cbMsg == KSIZE_MAX)
9917 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
9918 else
9919 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
9920 }
9921 else
9922 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
9923 }
9924 else
9925 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
9926 }
9927 else
9928 kwErrPrintf("Detected bogus message unpacking environment variables!\n");
9929 kHlpFree((void *)papszEnvVars);
9930 }
9931 else
9932 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
9933 }
9934 else
9935 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
9936 }
9937 else
9938 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
9939 kHlpFree((void *)papszArgs);
9940 }
9941 else
9942 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
9943 }
9944 else
9945 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
9946 }
9947 else
9948 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
9949 }
9950 else
9951 kwErrPrintf("Detected bogus message unpacking executable path!\n");
9952 return rcExit;
9953}
9954
9955
9956/**
9957 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
9958 *
9959 * @retval 0 on success.
9960 * @retval -1 on error (fully bitched).
9961 *
9962 * @param hPipe The pipe handle.
9963 * @param pvBuf The buffer to write out out.
9964 * @param cbToWrite The number of bytes to write.
9965 */
9966static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
9967{
9968 KU8 const *pbBuf = (KU8 const *)pvBuf;
9969 KU32 cbLeft = cbToWrite;
9970 for (;;)
9971 {
9972 DWORD cbActuallyWritten = 0;
9973 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
9974 {
9975 cbLeft -= cbActuallyWritten;
9976 if (!cbLeft)
9977 return 0;
9978 pbBuf += cbActuallyWritten;
9979 }
9980 else
9981 {
9982 DWORD dwErr = GetLastError();
9983 if (cbLeft == cbToWrite)
9984 kwErrPrintf("WriteFile failed: %u\n", dwErr);
9985 else
9986 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
9987 return -1;
9988 }
9989 }
9990}
9991
9992
9993/**
9994 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
9995 *
9996 * @retval 0 on success.
9997 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
9998 * @retval -1 on error (fully bitched).
9999 * @param hPipe The pipe handle.
10000 * @param pvBuf The buffer to read into.
10001 * @param cbToRead The number of bytes to read.
10002 * @param fShutdownOkay Whether connection shutdown while reading the
10003 * first byte is okay or not.
10004 */
10005static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
10006{
10007 KU8 *pbBuf = (KU8 *)pvBuf;
10008 KU32 cbLeft = cbToRead;
10009 for (;;)
10010 {
10011 DWORD cbActuallyRead = 0;
10012 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
10013 {
10014 cbLeft -= cbActuallyRead;
10015 if (!cbLeft)
10016 return 0;
10017 pbBuf += cbActuallyRead;
10018 }
10019 else
10020 {
10021 DWORD dwErr = GetLastError();
10022 if (cbLeft == cbToRead)
10023 {
10024 if ( fMayShutdown
10025 && dwErr == ERROR_BROKEN_PIPE)
10026 return 1;
10027 kwErrPrintf("ReadFile failed: %u\n", dwErr);
10028 }
10029 else
10030 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
10031 return -1;
10032 }
10033 }
10034}
10035
10036
10037/**
10038 * Handles what comes after --test.
10039 *
10040 * @returns Exit code.
10041 * @param argc Number of arguments after --test.
10042 * @param argv Arguments after --test.
10043 */
10044static int kwTestRun(int argc, char **argv)
10045{
10046 int i;
10047 int j;
10048 int rcExit;
10049 int cRepeats;
10050 char szCwd[MAX_PATH];
10051 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
10052 KU32 cEnvVars;
10053 KBOOL fWatcomBrainDamange = K_FALSE;
10054 KBOOL fNoPchCaching = K_FALSE;
10055
10056 /*
10057 * Parse arguments.
10058 */
10059 /* Repeat count. */
10060 i = 0;
10061 if (i >= argc)
10062 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
10063 if (strcmp(argv[i], "--") != 0)
10064 {
10065 cRepeats = atoi(argv[i]);
10066 if (cRepeats <= 0)
10067 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
10068 i++;
10069
10070 /* Optional directory change. */
10071 if ( i < argc
10072 && ( strcmp(argv[i], "--chdir") == 0
10073 || strcmp(argv[i], "-C") == 0 ) )
10074 {
10075 i++;
10076 if (i >= argc)
10077 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
10078 pszCwd = argv[i++];
10079 }
10080
10081 /* Optional watcom flag directory change. */
10082 if ( i < argc
10083 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
10084 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
10085 {
10086 fWatcomBrainDamange = K_TRUE;
10087 i++;
10088 }
10089
10090 /* Optional watcom flag directory change. */
10091 if ( i < argc
10092 && strcmp(argv[i], "--no-pch-caching") == 0)
10093 {
10094 fNoPchCaching = K_TRUE;
10095 i++;
10096 }
10097
10098 /* Trigger breakpoint */
10099 if ( i < argc
10100 && strcmp(argv[i], "--breakpoint") == 0)
10101 {
10102 __debugbreak();
10103 i++;
10104 }
10105
10106 /* Check for '--'. */
10107 if (i >= argc)
10108 return kwErrPrintfRc(2, "Missing '--'\n");
10109 if (strcmp(argv[i], "--") != 0)
10110 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
10111 i++;
10112 }
10113 else
10114 {
10115 cRepeats = 1;
10116 i++;
10117 }
10118 if (i >= argc)
10119 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
10120
10121 /*
10122 * Do the job.
10123 */
10124 cEnvVars = 0;
10125 while (environ[cEnvVars] != NULL)
10126 cEnvVars++;
10127
10128 for (j = 0; j < cRepeats; j++)
10129 {
10130 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
10131 argc - i, &argv[i], fWatcomBrainDamange,
10132 cEnvVars, environ, fNoPchCaching,
10133 0, NULL);
10134 KW_LOG(("rcExit=%d\n", rcExit));
10135 kwSandboxCleanupLate(&g_Sandbox);
10136 }
10137
10138# ifdef WITH_LOG_FILE
10139 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
10140 CloseHandle(g_hLogFile);
10141# endif
10142 return rcExit;
10143}
10144
10145
10146int main(int argc, char **argv)
10147{
10148 KSIZE cbMsgBuf = 0;
10149 KU8 *pbMsgBuf = NULL;
10150 int i;
10151 HANDLE hPipe = INVALID_HANDLE_VALUE;
10152 const char *pszTmp;
10153 KFSLOOKUPERROR enmIgnored;
10154#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10155 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
10156 kwSandboxVecXcptEmulateChained);
10157#endif
10158#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10159 HANDLE hCurProc = GetCurrentProcess();
10160 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10161 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10162 DWORD dwType;
10163#endif
10164
10165
10166#ifdef WITH_FIXED_VIRTUAL_ALLOCS
10167 /*
10168 * Reserve memory for cl.exe
10169 */
10170 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
10171 {
10172 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
10173 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
10174 g_aFixedVirtualAllocs[i].cbFixed,
10175 MEM_RESERVE, PAGE_READWRITE);
10176 if ( !g_aFixedVirtualAllocs[i].pvReserved
10177 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
10178 {
10179 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
10180 GetLastError());
10181 if (g_aFixedVirtualAllocs[i].pvReserved)
10182 {
10183 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
10184 g_aFixedVirtualAllocs[i].pvReserved = NULL;
10185 }
10186 }
10187 }
10188#endif
10189
10190 /*
10191 * Register our Control-C and Control-Break handlers.
10192 */
10193 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
10194 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
10195
10196 /*
10197 * Create the cache and mark the temporary directory as using the custom revision.
10198 */
10199 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
10200 if (!g_pFsCache)
10201 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
10202
10203 pszTmp = getenv("TEMP");
10204 if (pszTmp && *pszTmp != '\0')
10205 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
10206 pszTmp = getenv("TMP");
10207 if (pszTmp && *pszTmp != '\0')
10208 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
10209 pszTmp = getenv("TMPDIR");
10210 if (pszTmp && *pszTmp != '\0')
10211 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
10212
10213 /*
10214 * Make g_abDefLdBuf executable.
10215 */
10216 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
10217 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
10218 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
10219
10220#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10221 /*
10222 * Get and duplicate the console handles.
10223 */
10224 /* Standard output. */
10225 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
10226 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
10227 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
10228 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
10229 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
10230 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
10231 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
10232 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
10233 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
10234 g_Sandbox.HandleStdOut.cRefs = 0x10001;
10235 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
10236 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
10237 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
10238 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
10239 {
10240 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
10241 g_Sandbox.cFixedHandles++;
10242 else
10243 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
10244 }
10245 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
10246 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
10247
10248 /* Standard error. */
10249 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
10250 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
10251 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
10252 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
10253 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
10254 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
10255 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
10256 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
10257 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
10258 g_Sandbox.HandleStdErr.cRefs = 0x10001;
10259 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
10260 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
10261 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
10262 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
10263 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
10264 {
10265 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
10266 g_Sandbox.cFixedHandles++;
10267 else
10268 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
10269 }
10270 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
10271 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
10272
10273 /* Combined console buffer. */
10274 if (g_Sandbox.StdErr.fIsConsole)
10275 {
10276 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
10277 g_Sandbox.Combined.uCodepage = GetConsoleCP();
10278 }
10279 else if (g_Sandbox.StdOut.fIsConsole)
10280 {
10281 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
10282 g_Sandbox.Combined.uCodepage = GetConsoleCP();
10283 }
10284 else
10285 {
10286 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
10287 g_Sandbox.Combined.uCodepage = CP_ACP;
10288 }
10289 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
10290#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
10291
10292
10293 /*
10294 * Parse arguments.
10295 */
10296 for (i = 1; i < argc; i++)
10297 {
10298 if (strcmp(argv[i], "--pipe") == 0)
10299 {
10300 i++;
10301 if (i < argc)
10302 {
10303 char *pszEnd = NULL;
10304 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
10305 if ( *argv[i]
10306 && pszEnd != NULL
10307 && *pszEnd == '\0'
10308 && u64Value != 0
10309 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
10310 && (uintptr_t)u64Value == u64Value)
10311 hPipe = (HANDLE)(uintptr_t)u64Value;
10312 else
10313 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
10314 }
10315 else
10316 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
10317 }
10318 else if (strcmp(argv[i], "--volatile") == 0)
10319 {
10320 i++;
10321 if (i < argc)
10322 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
10323 else
10324 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
10325 }
10326 else if (strcmp(argv[i], "--test") == 0)
10327 return kwTestRun(argc - i - 1, &argv[i + 1]);
10328 else if ( strcmp(argv[i], "--help") == 0
10329 || strcmp(argv[i], "-h") == 0
10330 || strcmp(argv[i], "-?") == 0)
10331 {
10332 printf("usage: kWorker [--volatile dir] --pipe <pipe-handle>\n"
10333 "usage: kWorker <--help|-h>\n"
10334 "usage: kWorker <--version|-V>\n"
10335 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
10336 "\n"
10337 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
10338 return 0;
10339 }
10340 else if ( strcmp(argv[i], "--version") == 0
10341 || strcmp(argv[i], "-V") == 0)
10342 return kbuild_version(argv[0]);
10343 else
10344 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
10345 }
10346
10347 if (hPipe == INVALID_HANDLE_VALUE)
10348 return kwErrPrintfRc(2, "Missing --pipe <pipe-handle> argument!\n");
10349
10350 /*
10351 * Serve the pipe.
10352 */
10353 for (;;)
10354 {
10355 KU32 cbMsg = 0;
10356 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
10357 if (rc == 0)
10358 {
10359 /* Make sure the message length is within sane bounds. */
10360 if ( cbMsg > 4
10361 && cbMsg <= 256*1024*1024)
10362 {
10363 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
10364 if (cbMsg + 4 <= cbMsgBuf)
10365 { /* likely */ }
10366 else
10367 {
10368 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
10369 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
10370 if (!pbMsgBuf)
10371 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
10372 }
10373
10374 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
10375 *(KU32 *)pbMsgBuf = cbMsg;
10376 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
10377 if (rc == 0)
10378 {
10379 const char *psz;
10380
10381 pbMsgBuf[cbMsg] = '\0';
10382 pbMsgBuf[cbMsg + 1] = '\0';
10383 pbMsgBuf[cbMsg + 2] = '\0';
10384 pbMsgBuf[cbMsg + 3] = '\0';
10385
10386 /* The first string after the header is the command. */
10387 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
10388 if (strcmp(psz, "JOB") == 0)
10389 {
10390 struct
10391 {
10392 KI32 rcExitCode;
10393 KU8 bExiting;
10394 KU8 abZero[3];
10395 } Reply;
10396 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
10397 Reply.bExiting = g_fRestart;
10398 Reply.abZero[0] = 0;
10399 Reply.abZero[1] = 0;
10400 Reply.abZero[2] = 0;
10401 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
10402 if ( rc == 0
10403 && !g_fRestart)
10404 {
10405 kwSandboxCleanupLate(&g_Sandbox);
10406 continue;
10407 }
10408 }
10409 else
10410 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
10411 }
10412 }
10413 else
10414 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
10415 }
10416
10417 /*
10418 * If we're exitting because we're restarting, we need to delay till
10419 * kmk/kSubmit has read the result. Windows documentation says it
10420 * immediately discards pipe buffers once the pipe is broken by the
10421 * server (us). So, We flush the buffer and queues a 1 byte read
10422 * waiting for kSubmit to close the pipe when it receives the
10423 * bExiting = K_TRUE result.
10424 */
10425 if (g_fRestart)
10426 {
10427 KU8 b;
10428 FlushFileBuffers(hPipe);
10429 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
10430 }
10431
10432 CloseHandle(hPipe);
10433#ifdef WITH_LOG_FILE
10434 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
10435 CloseHandle(g_hLogFile);
10436#endif
10437 return rc > 0 ? 0 : 1;
10438 }
10439}
10440
10441
10442/** @page pg_kWorker kSubmit / kWorker
10443 *
10444 * @section sec_kWorker_Motivation Motivation / Inspiration
10445 *
10446 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
10447 * builds on machines "infested" by Anti Virus protection and disk encryption
10448 * software. Build times jumping from 35-40 min to 77-82 min after the machine
10449 * got "infected".
10450 *
10451 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
10452 * mainly a bunch of tiny assembly and C files being compiler a million times.
10453 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
10454 * own toolchain from within the same process, saving a lot of process creation
10455 * and teardown overhead.
10456 *
10457 *
10458 * @section sec_kWorker_kSubmit About kSubmit
10459 *
10460 * When wanting to execute a job in a kWorker instance, it must be submitted
10461 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
10462 * built into kmk and does not exist as an external program. The reason for
10463 * this is that it keep track of the kWorker instances.
10464 *
10465 * The kSubmit command has the --32-bit and --64-bit options for selecting
10466 * between 32-bit and 64-bit worker instance. We generally assume the user of
10467 * the command knows which bit count the executable has, so kSubmit is spared
10468 * the extra work of finding out.
10469 *
10470 * The kSubmit command shares a environment and current directory manipulation
10471 * with the kRedirect command, but not the file redirection. So long no file
10472 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
10473 * hand for tools like OpenWatcom, NASM and YASM which all require environment
10474 * and/or current directory changes to work.
10475 *
10476 * Unlike the kRedirect command, the kSubmit command can also specify an
10477 * internall post command to be executed after the main command succeeds.
10478 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
10479 * information from Microsoft COFF object files and Watcom OMF object files and
10480 * is scheduled to replace kDepIDB.
10481 *
10482 *
10483 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
10484 *
10485 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
10486 * A job request is written by kSubmit and kWorker read, unpacks it and executes
10487 * it. When the job is completed, kWorker writes a short reply with the exit
10488 * code and an internal status indicating whether it is going to restart.
10489 *
10490 * The kWorker intance will reply to kSubmit before completing all the internal
10491 * cleanup work, so as not to delay the next job execution unnecessarily. This
10492 * includes checking its own memory consumption and checking whether it needs
10493 * restarting. So, a decision to restart unfortunately have to wait till after
10494 * the next job has completed. This is a little bit unfortunate if the next job
10495 * requires a lot of memory and kWorker has already leaked/used a lot.
10496 *
10497 *
10498 * @section sec_kWorker_How_Works How kWorker Works
10499 *
10500 * kWorker will load the executable specified by kSubmit into memory and call
10501 * it's entrypoint in a lightly sandbox'ed environment.
10502 *
10503 *
10504 * @subsection ssec_kWorker_Loaing Image loading
10505 *
10506 * kWorker will manually load all the executable images into memory, fix them
10507 * up, and make a copy of the virgin image so it can be restored using memcpy
10508 * the next time it is used.
10509 *
10510 * Imported functions are monitored and replacements used for a few of them.
10511 * These replacements are serve the following purposes:
10512 * - Provide a different command line.
10513 * - Provide a different environment.
10514 * - Intercept process termination.
10515 * - Intercept thread creation (only linker is allowed to create threads).
10516 * - Intercept file reading for caching (header files, ++) as file system
10517 * access is made even slower by anti-virus software.
10518 * - Intercept crypto hash APIs to cache MD5 digests of header files
10519 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
10520 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
10521 * in memory as writing files grows expensive with encryption and
10522 * anti-virus software active.
10523 * - Intercept some file system queries to use the kFsCache instead of
10524 * going to the kernel and slowly worm thru the AV filter driver.
10525 * - Intercept standard output/error and console writes to aggressivly
10526 * buffer the output. The MS CRT does not buffer either when it goes to
10527 * the console, resulting in terrible performance and mixing up output
10528 * with other compile jobs.
10529 * This also allows us to filter out the annoying source file announcements
10530 * by cl.exe.
10531 * - Intercept VirtualAlloc and VirtualFree to prevent
10532 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
10533 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
10534 * the callbacks run after each job.
10535 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
10536 * executables and tools using custom heaps (like the microsoft linker).
10537 * [exectuable images only]
10538 * - Intercept atexit and _onexit registration to be able run them after
10539 * each job instead of crashing as kWorker exits. This also helps avoid
10540 * some leaks. [executable image only]
10541 *
10542 * DLLs falls into two categories, system DLLs which we always load using the
10543 * native loader, and tool DLLs which can be handled like the executable or
10544 * optionally using the native loader. We maintain a hardcoded white listing of
10545 * tool DLLs we trust to load using the native loader.
10546 *
10547 * Imports of natively loaded DLLs are processed too, but we only replace a
10548 * subset of the functions compared to natively loaded excutable and DLL images.
10549 *
10550 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
10551 * This is to speed up job execution.
10552 *
10553 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
10554 * for each job run, but so far this hasn't been necessary.
10555 *
10556 *
10557 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
10558 *
10559 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
10560 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
10561 * intermediate representation between the first (c1/c1xx.dll) and second pass
10562 * (c2.dll).
10563 *
10564 * kWorker helps the compiler as best as it can. Given a little knowledge about
10565 * stable and volatile file system areas, it can do a lot of caching that a
10566 * normal compiler driver cannot easily do when given a single file.
10567 *
10568 *
10569 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
10570 *
10571 * The preprocessor part will open and process header files exactly as they are
10572 * encountered in the source files. If string.h is included by the main source
10573 * and five other header files, it will be searched for (include path), opened,
10574 * read, MD5-summed, and pre-processed six times. The last five times is just a
10575 * waste of time because of the guards or \#pragma once. A smart compiler would
10576 * make a little extra effort and realize this.
10577 *
10578 * kWorker will cache help the preprocessor by remembering places where the
10579 * header was not found with help of kFsCache, and cache the file in memory when
10580 * found. The first part is taken care of by intercepting GetFileAttributesW,
10581 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
10582 * cached, the file is kept open and the CreateFileW call returns a duplicate of
10583 * that handle. An internal handle table is used by ReadFile and CloseFile to
10584 * keep track of intercepted handles (also used for temporary file, temporary
10585 * file mappings, console buffering, and standard out/err buffering).
10586 *
10587 * PS. The header search optimization also comes in handy when cl.exe goes on
10588 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
10589 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
10590 * optionally compile the three pass DLLs as executables during development
10591 * and problem analysis.
10592 *
10593 *
10594 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
10595 *
10596 * The issues of the temporary files is pretty severe on the Dell machine used
10597 * for benchmarking with full AV and encryption. The synthetic benchmark
10598 * improved by 30% when kWorker implemented measures to keep them entirely in
10599 * memory.
10600 *
10601 * kWorker implement these by recognizing the filename pattern in CreateFileW
10602 * and creating/opening the given file as needed. The handle returned is a
10603 * duplicate of the current process, thus giving us a good chance of catching
10604 * API calls we're not intercepting.
10605 *
10606 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
10607 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
10608 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
10609 * UnmapViewOfFile.
10610 *
10611 *
10612 * @section sec_kWorker_Numbers Some measurements.
10613 *
10614 * - r2881 building src/VBox/Runtime:
10615 * - without: 2m01.016388s = 120.016388 s
10616 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
10617 * - r2884 building vbox/debug (r110512):
10618 * - without: 11m14.446609s = 674.446609 s
10619 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
10620 * - r2896 building vbox/debug (r110577):
10621 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
10622 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
10623 * MS Defender as AV):
10624 * - without: 10m24.990389s = 624.990389s
10625 * - with: 08m04.738184s = 484.738184s
10626 * - delta: 624.99s - 484.74s = 140.25s
10627 * - saved: 140.25/624.99 = 22% faster
10628 *
10629 *
10630 * @subsection subsec_kWorker_Early_Numbers Early Experiments
10631 *
10632 * These are some early experiments doing 1024 compilations of
10633 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
10634 * main function:
10635 *
10636 * Skylake (W10/amd64, only stdandard MS defender):
10637 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
10638 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
10639 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
10640 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
10641 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
10642 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
10643 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
10644 *
10645 * Dell (W7/amd64, infected by mcafee):
10646 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
10647 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
10648 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
10649 *
10650 * The command line:
10651 * @code{.cpp}
10652 "\"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"
10653 * @endcode
10654 */
10655
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