VirtualBox

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

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

kWorker: Fixed busted read-only caching, checking out using memory mapped files for it. Made the CreateFileW interceptor not callCreateFileA for read-only cached files. Deal with
?\ prefixed paths (long paths) and other moc.exe related fixes.

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