VirtualBox

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

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

kWorker: TlsAlloc and FlsAlloc must only be intercepted for EXE images, otherwise we'll end up freeing in-use indexes when doing the late sandbox cleanup. Also seems we need to roll our own RtlUnwind for x86 for the same reason as we need to do our exception dispatching (our modules are not in the module table / inverse function table / whatever, and the handlers are considered invalid). RtlUnwind is used for error handling in cl.exe.

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