VirtualBox

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

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

kWorker: A few more words.

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