VirtualBox

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

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

kWorker: Intercept TlsAlloc and TlsFree too, same reason as for the FlsAlloc/Free.

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