VirtualBox

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

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

kWorker: Reuse crypt context between cl runs.

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

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