VirtualBox

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

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

kSubmit/kWorker: Added --no-pch-caching parameter to work around trouble with precompiled header file creation. Enabled pch caching in the 64-bit version of kWorker, not virtual memory problems there.

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