VirtualBox

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

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

kWorker: More stats and cache stats for a few more file types.

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

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