VirtualBox

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

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

kWorker/kDep: save a few header stat calls while optimizing dependencies.

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