VirtualBox

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

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

kWorker.c: Don't call kwSandboxGrowEnv unless it's necessary. Duh.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 332.4 KB
Line 
1/* $Id: kWorker.c 2932 2016-09-18 16:20:11Z 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 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
3127 if (pvNew)
3128 {
3129 pSandbox->environ = (char **)pvNew;
3130 pSandbox->environ[cOld] = NULL;
3131
3132 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
3133 if (pvNew)
3134 {
3135 pSandbox->papszEnvVars = (char **)pvNew;
3136 pSandbox->papszEnvVars[cOld] = NULL;
3137
3138 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
3139 if (pvNew)
3140 {
3141 pSandbox->wenviron = (wchar_t **)pvNew;
3142 pSandbox->wenviron[cOld] = NULL;
3143
3144 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
3145 if (pvNew)
3146 {
3147 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
3148 pSandbox->papwszEnvVars[cOld] = NULL;
3149
3150 pSandbox->cEnvVarsAllocated = cNew;
3151 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
3152 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
3153 return 0;
3154 }
3155 }
3156 }
3157 }
3158 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
3159 return KERR_NO_MEMORY;
3160}
3161
3162
3163/**
3164 * Sets an environment variable, ANSI style.
3165 *
3166 * @returns 0 on success, non-zero on failure.
3167 * @param pSandbox The sandbox.
3168 * @param pchVar The variable name.
3169 * @param cchVar The length of the name.
3170 * @param pszValue The value.
3171 */
3172static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
3173{
3174 /* Allocate and construct the new strings. */
3175 KSIZE cchTmp = kHlpStrLen(pszValue);
3176 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
3177 if (pszNew)
3178 {
3179 wchar_t *pwszNew;
3180 kHlpMemCopy(pszNew, pchVar, cchVar);
3181 pszNew[cchVar] = '=';
3182 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
3183 cchTmp += cchVar + 1;
3184 pszNew[cchTmp] = '\0';
3185
3186 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
3187 if (pwszNew)
3188 {
3189 /* Look it up. */
3190 KSIZE iVar = 0;
3191 char *pszEnv;
3192 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
3193 {
3194 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
3195 && pszEnv[cchVar] == '=')
3196 {
3197 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
3198 " iVar=%d: %p='%s' and %p='%ls'\n",
3199 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
3200 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
3201 iVar, pszNew, pszNew, pwszNew, pwszNew));
3202
3203 kHlpFree(pSandbox->papszEnvVars[iVar]);
3204 pSandbox->papszEnvVars[iVar] = pszNew;
3205 pSandbox->environ[iVar] = pszNew;
3206
3207 kHlpFree(pSandbox->papwszEnvVars[iVar]);
3208 pSandbox->papwszEnvVars[iVar] = pwszNew;
3209 pSandbox->wenviron[iVar] = pwszNew;
3210 return 0;
3211 }
3212 iVar++;
3213 }
3214
3215 /* Not found, do we need to grow the table first? */
3216 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
3217 kwSandboxGrowEnv(pSandbox, iVar + 2);
3218 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
3219 {
3220 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
3221
3222 pSandbox->papszEnvVars[iVar + 1] = NULL;
3223 pSandbox->papszEnvVars[iVar] = pszNew;
3224 pSandbox->environ[iVar + 1] = NULL;
3225 pSandbox->environ[iVar] = pszNew;
3226
3227 pSandbox->papwszEnvVars[iVar + 1] = NULL;
3228 pSandbox->papwszEnvVars[iVar] = pwszNew;
3229 pSandbox->wenviron[iVar + 1] = NULL;
3230 pSandbox->wenviron[iVar] = pwszNew;
3231 return 0;
3232 }
3233
3234 kHlpFree(pwszNew);
3235 }
3236 kHlpFree(pszNew);
3237 }
3238 KW_LOG(("Out of memory!\n"));
3239 return 0;
3240}
3241
3242
3243/**
3244 * Sets an environment variable, UTF-16 style.
3245 *
3246 * @returns 0 on success, non-zero on failure.
3247 * @param pSandbox The sandbox.
3248 * @param pwcVar The variable name.
3249 * @param cwcVar The length of the name.
3250 * @param pwszValue The value.
3251 */
3252static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
3253{
3254 /* Allocate and construct the new strings. */
3255 KSIZE cwcTmp = kwUtf16Len(pwszValue);
3256 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
3257 if (pwszNew)
3258 {
3259 char *pszNew;
3260 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
3261 pwszNew[cwcVar] = '=';
3262 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
3263 cwcTmp += cwcVar + 1;
3264 pwszNew[cwcVar] = '\0';
3265
3266 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
3267 if (pszNew)
3268 {
3269 /* Look it up. */
3270 KSIZE iVar = 0;
3271 wchar_t *pwszEnv;
3272 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
3273 {
3274 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
3275 && pwszEnv[cwcVar] == '=')
3276 {
3277 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
3278 " iVar=%d: %p='%s' and %p='%ls'\n",
3279 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
3280 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
3281 iVar, pszNew, pszNew, pwszNew, pwszNew));
3282
3283 kHlpFree(pSandbox->papszEnvVars[iVar]);
3284 pSandbox->papszEnvVars[iVar] = pszNew;
3285 pSandbox->environ[iVar] = pszNew;
3286
3287 kHlpFree(pSandbox->papwszEnvVars[iVar]);
3288 pSandbox->papwszEnvVars[iVar] = pwszNew;
3289 pSandbox->wenviron[iVar] = pwszNew;
3290 return 0;
3291 }
3292 iVar++;
3293 }
3294
3295 /* Not found, do we need to grow the table first? */
3296 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
3297 kwSandboxGrowEnv(pSandbox, iVar + 2);
3298 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
3299 {
3300 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
3301
3302 pSandbox->papszEnvVars[iVar + 1] = NULL;
3303 pSandbox->papszEnvVars[iVar] = pszNew;
3304 pSandbox->environ[iVar + 1] = NULL;
3305 pSandbox->environ[iVar] = pszNew;
3306
3307 pSandbox->papwszEnvVars[iVar + 1] = NULL;
3308 pSandbox->papwszEnvVars[iVar] = pwszNew;
3309 pSandbox->wenviron[iVar + 1] = NULL;
3310 pSandbox->wenviron[iVar] = pwszNew;
3311 return 0;
3312 }
3313
3314 kHlpFree(pwszNew);
3315 }
3316 kHlpFree(pszNew);
3317 }
3318 KW_LOG(("Out of memory!\n"));
3319 return 0;
3320}
3321
3322
3323/** ANSI unsetenv worker. */
3324static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
3325{
3326 KSIZE iVar = 0;
3327 char *pszEnv;
3328 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
3329 {
3330 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
3331 && pszEnv[cchVar] == '=')
3332 {
3333 KSIZE cVars = iVar;
3334 while (pSandbox->papszEnvVars[cVars])
3335 cVars++;
3336 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
3337 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
3338
3339 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
3340 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
3341 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
3342
3343 kHlpFree(pSandbox->papszEnvVars[iVar]);
3344 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
3345 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
3346 pSandbox->papszEnvVars[cVars] = NULL;
3347 pSandbox->environ[cVars] = NULL;
3348
3349 kHlpFree(pSandbox->papwszEnvVars[iVar]);
3350 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
3351 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
3352 pSandbox->papwszEnvVars[cVars] = NULL;
3353 pSandbox->wenviron[cVars] = NULL;
3354 return 0;
3355 }
3356 iVar++;
3357 }
3358 return KERR_ENVVAR_NOT_FOUND;
3359}
3360
3361
3362/** UTF-16 unsetenv worker. */
3363static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
3364{
3365 KSIZE iVar = 0;
3366 wchar_t *pwszEnv;
3367 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
3368 {
3369 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
3370 && pwszEnv[cwcVar] == '=')
3371 {
3372 KSIZE cVars = iVar;
3373 while (pSandbox->papwszEnvVars[cVars])
3374 cVars++;
3375 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
3376 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
3377
3378 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
3379 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
3380 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
3381
3382 kHlpFree(pSandbox->papszEnvVars[iVar]);
3383 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
3384 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
3385 pSandbox->papszEnvVars[cVars] = NULL;
3386 pSandbox->environ[cVars] = NULL;
3387
3388 kHlpFree(pSandbox->papwszEnvVars[iVar]);
3389 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
3390 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
3391 pSandbox->papwszEnvVars[cVars] = NULL;
3392 pSandbox->wenviron[cVars] = NULL;
3393 return 0;
3394 }
3395 iVar++;
3396 }
3397 return KERR_ENVVAR_NOT_FOUND;
3398}
3399
3400
3401
3402/** ANSI getenv worker. */
3403static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
3404{
3405 KSIZE iVar = 0;
3406 char *pszEnv;
3407 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
3408 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
3409 && pszEnv[cchVar] == '=')
3410 return &pszEnv[cchVar + 1];
3411 return NULL;
3412}
3413
3414
3415/** UTF-16 getenv worker. */
3416static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
3417{
3418 KSIZE iVar = 0;
3419 wchar_t *pwszEnv;
3420 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
3421 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
3422 && pwszEnv[cwcVar] == '=')
3423 return &pwszEnv[cwcVar + 1];
3424 return NULL;
3425}
3426
3427
3428/** Kernel32 - GetEnvironmentVariableA() */
3429static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
3430{
3431 char *pszFoundValue;
3432 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3433
3434 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
3435 if (pszFoundValue)
3436 {
3437 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
3438 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
3439 return cchRet;
3440 }
3441 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
3442 SetLastError(ERROR_ENVVAR_NOT_FOUND);
3443 return 0;
3444}
3445
3446
3447/** Kernel32 - GetEnvironmentVariableW() */
3448static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
3449{
3450 wchar_t *pwszFoundValue;
3451 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3452
3453 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
3454 if (pwszFoundValue)
3455 {
3456 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
3457 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
3458 return cchRet;
3459 }
3460 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
3461 SetLastError(ERROR_ENVVAR_NOT_FOUND);
3462 return 0;
3463}
3464
3465
3466/** Kernel32 - SetEnvironmentVariableA() */
3467static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
3468{
3469 int rc;
3470 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3471
3472 if (pszValue)
3473 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
3474 else
3475 {
3476 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
3477 rc = 0; //??
3478 }
3479 if (rc == 0)
3480 {
3481 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
3482 return TRUE;
3483 }
3484 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3485 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
3486 return FALSE;
3487}
3488
3489
3490/** Kernel32 - SetEnvironmentVariableW() */
3491static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
3492{
3493 int rc;
3494 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3495
3496 if (pwszValue)
3497 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
3498 else
3499 {
3500 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
3501 rc = 0; //??
3502 }
3503 if (rc == 0)
3504 {
3505 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
3506 return TRUE;
3507 }
3508 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3509 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
3510 return FALSE;
3511}
3512
3513
3514/** Kernel32 - ExpandEnvironmentStringsA() */
3515static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
3516{
3517 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3518 KWFS_TODO();
3519 return 0;
3520}
3521
3522
3523/** Kernel32 - ExpandEnvironmentStringsW() */
3524static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
3525{
3526 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3527 KWFS_TODO();
3528 return 0;
3529}
3530
3531
3532/** CRT - _putenv(). */
3533static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
3534{
3535 int rc;
3536 char const *pszEqual;
3537 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3538
3539 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
3540 if (pszEqual)
3541 {
3542 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
3543 if (rc == 0)
3544 { }
3545 else
3546 rc = -1;
3547 }
3548 else
3549 {
3550 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
3551 rc = 0;
3552 }
3553 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
3554 return rc;
3555}
3556
3557
3558/** CRT - _wputenv(). */
3559static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
3560{
3561 int rc;
3562 wchar_t const *pwszEqual;
3563 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3564
3565 pwszEqual = wcschr(pwszVarEqualValue, '=');
3566 if (pwszEqual)
3567 {
3568 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
3569 if (rc == 0)
3570 { }
3571 else
3572 rc = -1;
3573 }
3574 else
3575 {
3576 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
3577 rc = 0;
3578 }
3579 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
3580 return rc;
3581}
3582
3583
3584/** CRT - _putenv_s(). */
3585static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
3586{
3587 char const *pszEqual;
3588 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3589
3590 pszEqual = kHlpStrChr(pszVar, '=');
3591 if (pszEqual == NULL)
3592 {
3593 if (pszValue)
3594 {
3595 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
3596 if (rc == 0)
3597 {
3598 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
3599 return 0;
3600 }
3601 }
3602 else
3603 {
3604 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
3605 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
3606 return 0;
3607 }
3608 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
3609 return ENOMEM;
3610 }
3611 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
3612 return EINVAL;
3613}
3614
3615
3616/** CRT - _wputenv_s(). */
3617static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
3618{
3619 wchar_t const *pwszEqual;
3620 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3621
3622 pwszEqual = wcschr(pwszVar, '=');
3623 if (pwszEqual == NULL)
3624 {
3625 if (pwszValue)
3626 {
3627 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
3628 if (rc == 0)
3629 {
3630 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
3631 return 0;
3632 }
3633 }
3634 else
3635 {
3636 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
3637 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
3638 return 0;
3639 }
3640 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
3641 return ENOMEM;
3642 }
3643 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
3644 return EINVAL;
3645}
3646
3647
3648/** CRT - get pointer to the __initenv variable (initial environment). */
3649static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
3650{
3651 KW_LOG(("__p___initenv\n"));
3652 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3653 KWFS_TODO();
3654 return &g_Sandbox.initenv;
3655}
3656
3657
3658/** CRT - get pointer to the __winitenv variable (initial environment). */
3659static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
3660{
3661 KW_LOG(("__p___winitenv\n"));
3662 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3663 KWFS_TODO();
3664 return &g_Sandbox.winitenv;
3665}
3666
3667
3668/** CRT - get pointer to the _environ variable (current environment). */
3669static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
3670{
3671 KW_LOG(("__p__environ\n"));
3672 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3673 return &g_Sandbox.environ;
3674}
3675
3676
3677/** CRT - get pointer to the _wenviron variable (current environment). */
3678static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
3679{
3680 KW_LOG(("__p__wenviron\n"));
3681 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3682 return &g_Sandbox.wenviron;
3683}
3684
3685
3686/** CRT - get the _environ variable (current environment).
3687 * @remarks Not documented or prototyped? */
3688static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
3689{
3690 KWFS_TODO(); /** @todo check the callers expectations! */
3691 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3692 *ppapszEnviron = g_Sandbox.environ;
3693 return 0;
3694}
3695
3696
3697/** CRT - get the _wenviron variable (current environment).
3698 * @remarks Not documented or prototyped? */
3699static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
3700{
3701 KWFS_TODO(); /** @todo check the callers expectations! */
3702 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3703 *ppapwszEnviron = g_Sandbox.wenviron;
3704 return 0;
3705}
3706
3707
3708
3709/*
3710 *
3711 * Loader related APIs
3712 * Loader related APIs
3713 * Loader related APIs
3714 *
3715 */
3716
3717/**
3718 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
3719 */
3720static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
3721{
3722 /* Load it first. */
3723 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
3724 if (hmod)
3725 {
3726 pDynLoad->hmod = hmod;
3727 pDynLoad->pMod = NULL; /* indicates special */
3728
3729 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
3730 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
3731 KW_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
3732 }
3733 else
3734 kHlpFree(pDynLoad);
3735 return hmod;
3736}
3737
3738
3739/**
3740 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
3741 */
3742static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
3743{
3744 HMODULE hmod;
3745 PKWMODULE pMod;
3746 KU32 uHashPath;
3747 KSIZE idxHash;
3748 char szNormPath[256];
3749 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
3750
3751 /*
3752 * Lower case it.
3753 */
3754 if (cbFilename <= sizeof(szNormPath))
3755 {
3756 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
3757 _strlwr(szNormPath);
3758 }
3759 else
3760 {
3761 SetLastError(ERROR_FILENAME_EXCED_RANGE);
3762 return NULL;
3763 }
3764
3765 /*
3766 * Check if it has already been loaded so we don't create an unnecessary
3767 * loader module for it.
3768 */
3769 uHashPath = kwStrHash(szNormPath);
3770 idxHash = uHashPath % K_ELEMENTS(g_apModules);
3771 pMod = g_apModules[idxHash];
3772 if (pMod)
3773 {
3774 do
3775 {
3776 if ( pMod->uHashPath == uHashPath
3777 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3778 {
3779 pDynLoad->pMod = kwLdrModuleRetain(pMod);
3780 pDynLoad->hmod = pMod->hOurMod;
3781
3782 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
3783 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
3784 KW_LOG(("LoadLibraryExA(%s,,) -> %p [already loaded]\n", pDynLoad->szRequest, pDynLoad->hmod));
3785 return pDynLoad->hmod;
3786 }
3787 pMod = pMod->pNext;
3788 } while (pMod);
3789 }
3790
3791
3792 /*
3793 * Try load it and make a kLdr module for it.
3794 */
3795 hmod = LoadLibraryExA(szNormPath, NULL /*hFile*/, fFlags);
3796 if (hmod)
3797 {
3798 PKLDRMOD pLdrMod;
3799 int rc = kLdrModOpenNativeByHandle((KUPTR)hmod, &pLdrMod);
3800 if (rc == 0)
3801 {
3802 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cbFilename, uHashPath,
3803 K_FALSE /*fDoReplacements*/);
3804 if (pMod)
3805 {
3806 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
3807
3808 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cbFilename + cbFilename * sizeof(wchar_t));
3809 if (pDynLoad)
3810 {
3811 pDynLoad->pMod = pMod;
3812 pDynLoad->hmod = hmod;
3813
3814 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
3815 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
3816 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
3817 return hmod;
3818 }
3819
3820 KWFS_TODO();
3821 }
3822 else
3823 KWFS_TODO();
3824 }
3825 else
3826 KWFS_TODO();
3827 }
3828 kHlpFree(pDynLoad);
3829 return hmod;
3830}
3831
3832
3833/** Kernel32 - LoadLibraryExA() */
3834static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
3835{
3836 KSIZE cchFilename = kHlpStrLen(pszFilename);
3837 PKWDYNLOAD pDynLoad;
3838 PKWMODULE pMod;
3839 int rc;
3840 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3841
3842 /*
3843 * Deal with a couple of extremely unlikely special cases right away.
3844 */
3845 if ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
3846 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
3847 { /* likely */ }
3848 else
3849 {
3850 KWFS_TODO();
3851 return LoadLibraryExA(pszFilename, hFile, fFlags);
3852 }
3853
3854 /*
3855 * Check if we've already got a dynload entry for this one.
3856 */
3857 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
3858 if ( pDynLoad->cchRequest == cchFilename
3859 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
3860 {
3861 if (pDynLoad->pMod)
3862 rc = kwLdrModuleInitTree(pDynLoad->pMod);
3863 else
3864 rc = 0;
3865 if (rc == 0)
3866 {
3867 KW_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
3868 return pDynLoad->hmod;
3869 }
3870 SetLastError(ERROR_DLL_INIT_FAILED);
3871 return NULL;
3872 }
3873
3874 /*
3875 * Allocate a dynload entry for the request.
3876 */
3877 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
3878 if (pDynLoad)
3879 {
3880 pDynLoad->cchRequest = cchFilename;
3881 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
3882 }
3883 else
3884 {
3885 KW_LOG(("LoadLibraryExA: Out of memory!\n"));
3886 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3887 return NULL;
3888 }
3889
3890 /*
3891 * Deal with resource / data DLLs.
3892 */
3893 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
3894 | LOAD_LIBRARY_AS_DATAFILE
3895 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
3896 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
3897
3898 /*
3899 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
3900 */
3901 if ( strnicmp(pszFilename, TUPLE("api-ms-")) == 0
3902 && kHlpIsFilenameOnly(pszFilename))
3903 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
3904
3905 /*
3906 * Normal library loading.
3907 * We start by being very lazy and reusing the code for resolving imports.
3908 */
3909 if (!kHlpIsFilenameOnly(pszFilename))
3910 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe);
3911 else
3912 {
3913 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, &pMod);
3914 if (rc != 0)
3915 pMod = NULL;
3916 }
3917 if (pMod)
3918 {
3919 /* Enter it into the tool module table and dynamic link request cache. */
3920 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
3921
3922 pDynLoad->pMod = pMod;
3923 pDynLoad->hmod = pMod->hOurMod;
3924
3925 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
3926 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
3927
3928 /*
3929 * Make sure it's initialized (need to link it first since DllMain may
3930 * use loader APIs).
3931 */
3932 rc = kwLdrModuleInitTree(pMod);
3933 if (rc == 0)
3934 {
3935 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
3936 return pDynLoad->hmod;
3937 }
3938
3939 SetLastError(ERROR_DLL_INIT_FAILED);
3940 }
3941 else
3942 {
3943 KWFS_TODO();
3944 kHlpFree(pDynLoad);
3945 SetLastError(ERROR_MOD_NOT_FOUND);
3946 }
3947 return NULL;
3948}
3949
3950
3951/** Kernel32 - LoadLibraryExW() */
3952static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
3953{
3954 char szTmp[4096];
3955 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
3956 if (cchTmp < sizeof(szTmp))
3957 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
3958
3959 KWFS_TODO();
3960 SetLastError(ERROR_FILENAME_EXCED_RANGE);
3961 return NULL;
3962}
3963
3964/** Kernel32 - LoadLibraryA() */
3965static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
3966{
3967 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
3968}
3969
3970
3971/** Kernel32 - LoadLibraryW() */
3972static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
3973{
3974 char szTmp[4096];
3975 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
3976 if (cchTmp < sizeof(szTmp))
3977 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
3978 KWFS_TODO();
3979 SetLastError(ERROR_FILENAME_EXCED_RANGE);
3980 return NULL;
3981}
3982
3983
3984/** Kernel32 - FreeLibrary() */
3985static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
3986{
3987 /* Ignored, we like to keep everything loaded. */
3988 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3989 return TRUE;
3990}
3991
3992
3993/** Kernel32 - GetModuleHandleA() */
3994static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
3995{
3996 KSIZE i;
3997 KSIZE cchModule;
3998 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3999
4000 /*
4001 * The executable.
4002 */
4003 if (pszModule == NULL)
4004 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
4005
4006 /*
4007 * Cache of system modules we've seen queried.
4008 */
4009 cchModule = kHlpStrLen(pszModule);
4010 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
4011 if ( g_aGetModuleHandleCache[i].cchName == cchModule
4012 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
4013 {
4014 if (g_aGetModuleHandleCache[i].hmod != NULL)
4015 return g_aGetModuleHandleCache[i].hmod;
4016 return g_aGetModuleHandleCache[i].hmod = GetModuleHandleA(pszModule);
4017 }
4018
4019 KWFS_TODO();
4020 return NULL;
4021}
4022
4023
4024/** Kernel32 - GetModuleHandleW() */
4025static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
4026{
4027 KSIZE i;
4028 KSIZE cwcModule;
4029 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4030
4031 /*
4032 * The executable.
4033 */
4034 if (pwszModule == NULL)
4035 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
4036
4037 /*
4038 * Cache of system modules we've seen queried.
4039 */
4040 cwcModule = kwUtf16Len(pwszModule);
4041 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
4042 if ( g_aGetModuleHandleCache[i].cwcName == cwcModule
4043 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
4044 {
4045 if (g_aGetModuleHandleCache[i].hmod != NULL)
4046 return g_aGetModuleHandleCache[i].hmod;
4047 return g_aGetModuleHandleCache[i].hmod = GetModuleHandleW(pwszModule);
4048 }
4049
4050 KWFS_TODO();
4051 return NULL;
4052}
4053
4054
4055/** Used to debug dynamically resolved procedures. */
4056static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
4057{
4058#ifdef _MSC_VER
4059 __debugbreak();
4060#else
4061 KWFS_TODO();
4062#endif
4063 return -1;
4064}
4065
4066
4067/** Kernel32 - GetProcAddress() */
4068static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
4069{
4070 KSIZE i;
4071 PKWMODULE pMod;
4072 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4073
4074 /*
4075 * Try locate the module.
4076 */
4077 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
4078 if (pMod)
4079 {
4080 KLDRADDR uValue;
4081 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
4082 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
4083 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pvLoad,
4084 KU32_MAX /*iSymbol*/,
4085 pszProc,
4086 kHlpStrLen(pszProc),
4087 NULL /*pszVersion*/,
4088 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
4089 &uValue,
4090 NULL /*pfKind*/);
4091 if (rc == 0)
4092 {
4093 //static int s_cDbgGets = 0;
4094 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
4095 KU32 i = g_cSandboxGetProcReplacements;
4096 while (i-- > 0)
4097 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
4098 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
4099 {
4100 if ( !g_aSandboxGetProcReplacements[i].pszModule
4101 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
4102 {
4103 if ( pMod->fExe
4104 || !g_aSandboxGetProcReplacements[i].fOnlyExe)
4105 {
4106 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
4107 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
4108 }
4109 kwLdrModuleRelease(pMod);
4110 return (FARPROC)(KUPTR)uValue;
4111 }
4112 }
4113
4114 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
4115 kwLdrModuleRelease(pMod);
4116 //s_cDbgGets++;
4117 //if (s_cGets >= 3)
4118 // return (FARPROC)kwSandbox_BreakIntoDebugger;
4119 return (FARPROC)(KUPTR)uValue;
4120 }
4121
4122 KWFS_TODO();
4123 SetLastError(ERROR_PROC_NOT_FOUND);
4124 kwLdrModuleRelease(pMod);
4125 return NULL;
4126 }
4127
4128 /*
4129 * Hmm... could be a cached module-by-name.
4130 */
4131 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
4132 if (g_aGetModuleHandleCache[i].hmod == hmod)
4133 return GetProcAddress(hmod, pszProc);
4134
4135 KWFS_TODO();
4136 return GetProcAddress(hmod, pszProc);
4137}
4138
4139
4140/** Kernel32 - GetModuleFileNameA() */
4141static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
4142{
4143 PKWMODULE pMod;
4144 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4145
4146 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
4147 if (pMod != NULL)
4148 {
4149 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
4150 kwLdrModuleRelease(pMod);
4151 return cbRet;
4152 }
4153 KWFS_TODO();
4154 return 0;
4155}
4156
4157
4158/** Kernel32 - GetModuleFileNameW() */
4159static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
4160{
4161 PKWMODULE pMod;
4162 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4163
4164 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
4165 if (pMod)
4166 {
4167 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
4168 kwLdrModuleRelease(pMod);
4169 return cwcRet;
4170 }
4171
4172 KWFS_TODO();
4173 return 0;
4174}
4175
4176
4177/** NtDll - RtlPcToFileHeader
4178 * This is necessary for msvcr100.dll!CxxThrowException. */
4179static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
4180{
4181 PVOID pvRet;
4182
4183 /*
4184 * Do a binary lookup of the module table for the current tool.
4185 * This will give us a
4186 */
4187 if (g_Sandbox.fRunning)
4188 {
4189 KUPTR const uPC = (KUPTR)pvPC;
4190 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
4191 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
4192 KU32 i;
4193 if (iEnd)
4194 {
4195 KU32 iStart = 0;
4196 i = iEnd / 2;
4197 for (;;)
4198 {
4199 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
4200 if (uPC < uHModThis)
4201 {
4202 iEnd = i;
4203 if (iStart < i)
4204 { }
4205 else
4206 break;
4207 }
4208 else if (uPC != uHModThis)
4209 {
4210 iStart = ++i;
4211 if (i < iEnd)
4212 { }
4213 else
4214 break;
4215 }
4216 else
4217 {
4218 /* This isn't supposed to happen. */
4219 break;
4220 }
4221
4222 i = iStart + (iEnd - iStart) / 2;
4223 }
4224
4225 /* For reasons of simplicity (= copy & paste), we end up with the
4226 module after the one we're interested in here. */
4227 i--;
4228 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
4229 && papMods[i]->pLdrMod)
4230 {
4231 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
4232 if (uRvaPC < papMods[i]->cbImage)
4233 {
4234 *ppvImageBase = papMods[i]->hOurMod;
4235 pvRet = papMods[i]->hOurMod;
4236 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
4237 return pvRet;
4238 }
4239 }
4240 }
4241 else
4242 i = 0;
4243 }
4244
4245 /*
4246 * Call the regular API.
4247 */
4248 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
4249 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
4250 return pvRet;
4251}
4252
4253
4254/*
4255 *
4256 * File access APIs (for speeding them up).
4257 * File access APIs (for speeding them up).
4258 * File access APIs (for speeding them up).
4259 *
4260 */
4261
4262
4263/**
4264 * Converts a lookup error to a windows error code.
4265 *
4266 * @returns The windows error code.
4267 * @param enmError The lookup error.
4268 */
4269static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
4270{
4271 switch (enmError)
4272 {
4273 case KFSLOOKUPERROR_NOT_FOUND:
4274 case KFSLOOKUPERROR_NOT_DIR:
4275 return ERROR_FILE_NOT_FOUND;
4276
4277 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
4278 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
4279 return ERROR_PATH_NOT_FOUND;
4280
4281 case KFSLOOKUPERROR_PATH_TOO_LONG:
4282 return ERROR_FILENAME_EXCED_RANGE;
4283
4284 case KFSLOOKUPERROR_OUT_OF_MEMORY:
4285 return ERROR_NOT_ENOUGH_MEMORY;
4286
4287 default:
4288 return ERROR_PATH_NOT_FOUND;
4289 }
4290}
4291
4292#ifdef WITH_TEMP_MEMORY_FILES
4293
4294/**
4295 * Checks for a cl.exe temporary file.
4296 *
4297 * There are quite a bunch of these. They seems to be passing data between the
4298 * first and second compiler pass. Since they're on disk, they get subjected to
4299 * AV software screening and normal file consistency rules. So, not necessarily
4300 * a very efficient way of handling reasonably small amounts of data.
4301 *
4302 * We make the files live in virtual memory by intercepting their opening,
4303 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
4304 *
4305 * @returns K_TRUE / K_FALSE
4306 * @param pwszFilename The file name being accessed.
4307 */
4308static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
4309{
4310 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
4311 if (pwszName)
4312 {
4313 /* The name starts with _CL_... */
4314 if ( pwszName[0] == '_'
4315 && pwszName[1] == 'C'
4316 && pwszName[2] == 'L'
4317 && pwszName[3] == '_' )
4318 {
4319 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
4320 this check by just checking that it's alpha numerical ascii from here on. */
4321 wchar_t wc;
4322 pwszName += 4;
4323 while ((wc = *pwszName++) != '\0')
4324 {
4325 if (wc < 127 && iswalnum(wc))
4326 { /* likely */ }
4327 else
4328 return K_FALSE;
4329 }
4330 return K_TRUE;
4331 }
4332 }
4333 return K_FALSE;
4334}
4335
4336
4337/**
4338 * Creates a handle to a temporary file.
4339 *
4340 * @returns The handle on success.
4341 * INVALID_HANDLE_VALUE and SetLastError on failure.
4342 * @param pTempFile The temporary file.
4343 * @param dwDesiredAccess The desired access to the handle.
4344 * @param fMapping Whether this is a mapping (K_TRUE) or file
4345 * (K_FALSE) handle type.
4346 */
4347static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
4348{
4349 /*
4350 * Create a handle to the temporary file.
4351 */
4352 HANDLE hFile = INVALID_HANDLE_VALUE;
4353 HANDLE hProcSelf = GetCurrentProcess();
4354 if (DuplicateHandle(hProcSelf, hProcSelf,
4355 hProcSelf, &hFile,
4356 SYNCHRONIZE, FALSE,
4357 0 /*dwOptions*/))
4358 {
4359 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
4360 if (pHandle)
4361 {
4362 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
4363 pHandle->cRefs = 1;
4364 pHandle->offFile = 0;
4365 pHandle->hHandle = hFile;
4366 pHandle->dwDesiredAccess = dwDesiredAccess;
4367 pHandle->u.pTempFile = pTempFile;
4368 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
4369 {
4370 pTempFile->cActiveHandles++;
4371 kHlpAssert(pTempFile->cActiveHandles >= 1);
4372 kHlpAssert(pTempFile->cActiveHandles <= 2);
4373 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
4374 return hFile;
4375 }
4376
4377 kHlpFree(pHandle);
4378 }
4379 else
4380 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
4381 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4382 }
4383 else
4384 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
4385 return INVALID_HANDLE_VALUE;
4386}
4387
4388
4389static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
4390{
4391 HANDLE hFile;
4392 DWORD dwErr;
4393
4394 /*
4395 * Check if we've got an existing temp file.
4396 * ASSUME exact same path for now.
4397 */
4398 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
4399 PKWFSTEMPFILE pTempFile;
4400 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
4401 {
4402 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
4403 if ( pTempFile->cwcPath == cwcFilename
4404 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
4405 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
4406 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
4407 break;
4408 }
4409
4410 /*
4411 * Create a new temporary file instance if not found.
4412 */
4413 if (pTempFile == NULL)
4414 {
4415 KSIZE cbFilename;
4416
4417 switch (dwCreationDisposition)
4418 {
4419 case CREATE_ALWAYS:
4420 case OPEN_ALWAYS:
4421 dwErr = NO_ERROR;
4422 break;
4423
4424 case CREATE_NEW:
4425 kHlpAssertFailed();
4426 SetLastError(ERROR_ALREADY_EXISTS);
4427 return INVALID_HANDLE_VALUE;
4428
4429 case OPEN_EXISTING:
4430 case TRUNCATE_EXISTING:
4431 kHlpAssertFailed();
4432 SetLastError(ERROR_FILE_NOT_FOUND);
4433 return INVALID_HANDLE_VALUE;
4434
4435 default:
4436 kHlpAssertFailed();
4437 SetLastError(ERROR_INVALID_PARAMETER);
4438 return INVALID_HANDLE_VALUE;
4439 }
4440
4441 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
4442 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
4443 if (pTempFile)
4444 {
4445 pTempFile->cwcPath = (KU16)cwcFilename;
4446 pTempFile->cbFile = 0;
4447 pTempFile->cbFileAllocated = 0;
4448 pTempFile->cActiveHandles = 0;
4449 pTempFile->cMappings = 0;
4450 pTempFile->cSegs = 0;
4451 pTempFile->paSegs = NULL;
4452 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
4453
4454 pTempFile->pNext = g_Sandbox.pTempFileHead;
4455 g_Sandbox.pTempFileHead = pTempFile;
4456 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
4457 }
4458 else
4459 {
4460 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
4461 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4462 return INVALID_HANDLE_VALUE;
4463 }
4464 }
4465 else
4466 {
4467 switch (dwCreationDisposition)
4468 {
4469 case OPEN_EXISTING:
4470 dwErr = NO_ERROR;
4471 break;
4472 case OPEN_ALWAYS:
4473 dwErr = ERROR_ALREADY_EXISTS ;
4474 break;
4475
4476 case TRUNCATE_EXISTING:
4477 case CREATE_ALWAYS:
4478 kHlpAssertFailed();
4479 pTempFile->cbFile = 0;
4480 dwErr = ERROR_ALREADY_EXISTS;
4481 break;
4482
4483 case CREATE_NEW:
4484 kHlpAssertFailed();
4485 SetLastError(ERROR_FILE_EXISTS);
4486 return INVALID_HANDLE_VALUE;
4487
4488 default:
4489 kHlpAssertFailed();
4490 SetLastError(ERROR_INVALID_PARAMETER);
4491 return INVALID_HANDLE_VALUE;
4492 }
4493 }
4494
4495 /*
4496 * Create a handle to the temporary file.
4497 */
4498 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
4499 if (hFile != INVALID_HANDLE_VALUE)
4500 SetLastError(dwErr);
4501 return hFile;
4502}
4503
4504#endif /* WITH_TEMP_MEMORY_FILES */
4505
4506
4507/**
4508 * Checks if the file extension indicates that the file/dir is something we
4509 * ought to cache.
4510 *
4511 * @returns K_TRUE if cachable, K_FALSE if not.
4512 * @param pszExt The kHlpGetExt result.
4513 * @param fAttrQuery Set if it's for an attribute query, clear if for
4514 * file creation.
4515 */
4516static KBOOL kwFsIsCachableExtensionA(const char *pszExt, KBOOL fAttrQuery)
4517{
4518 char const chFirst = *pszExt;
4519
4520 /* C++ header without an extension or a directory. */
4521 if (chFirst == '\0')
4522 {
4523 /** @todo exclude temporary files... */
4524 return K_TRUE;
4525 }
4526
4527 /* C Header: .h */
4528 if (chFirst == 'h' || chFirst == 'H')
4529 {
4530 char chThird;
4531 char const chSecond = pszExt[1];
4532 if (chSecond == '\0')
4533 return K_TRUE;
4534 chThird = pszExt[2];
4535
4536 /* C++ Header: .hpp, .hxx */
4537 if ( (chSecond == 'p' || chSecond == 'P')
4538 && (chThird == 'p' || chThird == 'P')
4539 && pszExt[3] == '\0')
4540 return K_TRUE;
4541 if ( (chSecond == 'x' || chSecond == 'X')
4542 && (chThird == 'x' || chThird == 'X')
4543 && pszExt[3] == '\0')
4544 return K_TRUE;
4545 }
4546 /* Misc starting with i. */
4547 else if (chFirst == 'i' || chFirst == 'I')
4548 {
4549 char const chSecond = pszExt[1];
4550 if (chSecond != '\0')
4551 {
4552 if (chSecond == 'n' || chSecond == 'N')
4553 {
4554 char const chThird = pszExt[2];
4555
4556 /* C++ inline header: .inl */
4557 if ( (chThird == 'l' || chThird == 'L')
4558 && pszExt[3] == '\0')
4559 return K_TRUE;
4560
4561 /* Assembly include file: .inc */
4562 if ( (chThird == 'c' || chThird == 'C')
4563 && pszExt[3] == '\0')
4564 return K_TRUE;
4565 }
4566 }
4567 }
4568 /* Assembly header: .mac */
4569 else if (chFirst == 'm' || chFirst == 'M')
4570 {
4571 char const chSecond = pszExt[1];
4572 if (chSecond == 'a' || chSecond == 'A')
4573 {
4574 char const chThird = pszExt[2];
4575 if ( (chThird == 'c' || chThird == 'C')
4576 && pszExt[3] == '\0')
4577 return K_TRUE;
4578 }
4579 }
4580 else if (fAttrQuery)
4581 {
4582 /* Dynamic link library: .dll */
4583 if (chFirst == 'd' || chFirst == 'D')
4584 {
4585 char const chSecond = pszExt[1];
4586 if (chSecond == 'l' || chSecond == 'L')
4587 {
4588 char const chThird = pszExt[2];
4589 if (chThird == 'l' || chThird == 'L')
4590 return K_TRUE;
4591 }
4592 }
4593 /* Executable file: .exe */
4594 else if (chFirst == 'e' || chFirst == 'E')
4595 {
4596 char const chSecond = pszExt[1];
4597 if (chSecond == 'x' || chSecond == 'X')
4598 {
4599 char const chThird = pszExt[2];
4600 if (chThird == 'e' || chThird == 'e')
4601 return K_TRUE;
4602 }
4603 }
4604 }
4605
4606 return K_FALSE;
4607}
4608
4609
4610/**
4611 * Checks if the extension of the given UTF-16 path indicates that the file/dir
4612 * should be cached.
4613 *
4614 * @returns K_TRUE if cachable, K_FALSE if not.
4615 * @param pwszPath The UTF-16 path to examine.
4616 * @param fAttrQuery Set if it's for an attribute query, clear if for
4617 * file creation.
4618 */
4619static KBOOL kwFsIsCachablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
4620{
4621 /*
4622 * Extract the extension, check that it's in the applicable range, roughly
4623 * convert it to ASCII/ANSI, and feed it to kwFsIsCachableExtensionA for
4624 * the actual check. This avoids a lot of code duplication.
4625 */
4626 wchar_t wc;
4627 char szExt[4];
4628 KSIZE cwcExt;
4629 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
4630 switch (cwcExt)
4631 {
4632 case 3: if ((wchar_t)(szExt[2] = (char)(wc = pwszExt[2])) == wc) { /*likely*/ } else break;
4633 case 2: if ((wchar_t)(szExt[1] = (char)(wc = pwszExt[1])) == wc) { /*likely*/ } else break;
4634 case 1: if ((wchar_t)(szExt[0] = (char)(wc = pwszExt[0])) == wc) { /*likely*/ } else break;
4635 case 0:
4636 szExt[cwcExt] = '\0';
4637 return kwFsIsCachableExtensionA(szExt, fAttrQuery);
4638 }
4639 return K_FALSE;
4640}
4641
4642
4643
4644/**
4645 * Creates a new
4646 *
4647 * @returns
4648 * @param pFsObj .
4649 * @param pwszFilename .
4650 */
4651static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
4652{
4653 HANDLE hFile;
4654 MY_IO_STATUS_BLOCK Ios;
4655 MY_OBJECT_ATTRIBUTES ObjAttr;
4656 MY_UNICODE_STRING UniStr;
4657 MY_NTSTATUS rcNt;
4658 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4659
4660 /*
4661 * Open the file relative to the parent directory.
4662 */
4663 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
4664 kHlpAssert(pFsObj->pParent);
4665 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
4666
4667 Ios.Information = -1;
4668 Ios.u.Status = -1;
4669
4670 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
4671 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
4672 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
4673
4674 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
4675
4676 rcNt = g_pfnNtCreateFile(&hFile,
4677 GENERIC_READ | SYNCHRONIZE,
4678 &ObjAttr,
4679 &Ios,
4680 NULL, /*cbFileInitialAlloc */
4681 FILE_ATTRIBUTE_NORMAL,
4682 FILE_SHARE_READ,
4683 FILE_OPEN,
4684 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
4685 NULL, /*pEaBuffer*/
4686 0); /*cbEaBuffer*/
4687 if (MY_NT_SUCCESS(rcNt))
4688 {
4689 /*
4690 * Read the whole file into memory.
4691 */
4692 LARGE_INTEGER cbFile;
4693 if (GetFileSizeEx(hFile, &cbFile))
4694 {
4695 if ( cbFile.QuadPart >= 0
4696 && cbFile.QuadPart < 16*1024*1024)
4697 {
4698 KU32 cbCache = (KU32)cbFile.QuadPart;
4699 KU8 *pbCache = (KU8 *)kHlpAlloc(cbCache);
4700 if (pbCache)
4701 {
4702 DWORD cbActually = 0;
4703 if ( ReadFile(hFile, pbCache, cbCache, &cbActually, NULL)
4704 && cbActually == cbCache)
4705 {
4706 LARGE_INTEGER offZero;
4707 offZero.QuadPart = 0;
4708 if (SetFilePointerEx(hFile, offZero, NULL /*poffNew*/, FILE_BEGIN))
4709 {
4710 /*
4711 * Create the cached file object.
4712 */
4713 PKFSWCACHEDFILE pCachedFile;
4714 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
4715 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
4716 sizeof(*pCachedFile) + cbPath);
4717 if (pCachedFile)
4718 {
4719 pCachedFile->hCached = hFile;
4720 pCachedFile->cbCached = cbCache;
4721 pCachedFile->pbCached = pbCache;
4722 pCachedFile->pFsObj = pFsObj;
4723 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
4724 kFsCacheObjRetain(pFsObj);
4725 return pCachedFile;
4726 }
4727
4728 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
4729 }
4730 else
4731 KWFS_LOG(("Failed to seek to start of cached file! err=%u\n", GetLastError()));
4732 }
4733 else
4734 KWFS_LOG(("Failed to read %#x bytes into cache! err=%u cbActually=%#x\n",
4735 cbCache, GetLastError(), cbActually));
4736 kHlpFree(pbCache);
4737 }
4738 else
4739 KWFS_LOG(("Failed to allocate %#x bytes for cache!\n", cbCache));
4740 }
4741 else
4742 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
4743 }
4744 else
4745 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
4746 g_pfnNtClose(hFile);
4747 }
4748 else
4749 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
4750 return NULL;
4751}
4752
4753
4754/**
4755 * Kernel32 - Common code for CreateFileW and CreateFileA.
4756 */
4757static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
4758{
4759 *phFile = INVALID_HANDLE_VALUE;
4760 kHlpAssert(pFsObj->fHaveStats);
4761
4762 /*
4763 * At the moment we only handle existing files.
4764 */
4765 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
4766 {
4767 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
4768 if ( pCachedFile != NULL
4769 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
4770 {
4771 HANDLE hProcSelf = GetCurrentProcess();
4772 if (DuplicateHandle(hProcSelf, pCachedFile->hCached,
4773 hProcSelf, phFile,
4774 dwDesiredAccess, fInheritHandle,
4775 0 /*dwOptions*/))
4776 {
4777 /*
4778 * Create handle table entry for the duplicate handle.
4779 */
4780 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
4781 if (pHandle)
4782 {
4783 pHandle->enmType = KWHANDLETYPE_FSOBJ_READ_CACHE;
4784 pHandle->cRefs = 1;
4785 pHandle->offFile = 0;
4786 pHandle->hHandle = *phFile;
4787 pHandle->dwDesiredAccess = dwDesiredAccess;
4788 pHandle->u.pCachedFile = pCachedFile;
4789 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
4790 return K_TRUE;
4791
4792 kHlpFree(pHandle);
4793 }
4794 else
4795 KWFS_LOG(("Out of memory for handle!\n"));
4796
4797 CloseHandle(*phFile);
4798 *phFile = INVALID_HANDLE_VALUE;
4799 }
4800 else
4801 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
4802 }
4803 }
4804 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
4805
4806 /* Do fallback, please. */
4807 return K_FALSE;
4808}
4809
4810
4811/** Kernel32 - CreateFileA */
4812static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
4813 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
4814 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
4815{
4816 HANDLE hFile;
4817 if (dwCreationDisposition == FILE_OPEN_IF)
4818 {
4819 if ( dwDesiredAccess == GENERIC_READ
4820 || dwDesiredAccess == FILE_GENERIC_READ)
4821 {
4822 if (dwShareMode & FILE_SHARE_READ)
4823 {
4824 if ( !pSecAttrs
4825 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
4826 && pSecAttrs->lpSecurityDescriptor == NULL ) )
4827 {
4828 const char *pszExt = kHlpGetExt(pszFilename);
4829 if (kwFsIsCachableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
4830 {
4831 KFSLOOKUPERROR enmError;
4832 PKFSOBJ pFsObj;
4833 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4834
4835 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
4836 if (pFsObj)
4837 {
4838 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
4839 &hFile);
4840 kFsCacheObjRelease(g_pFsCache, pFsObj);
4841 if (fRc)
4842 {
4843 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
4844 return hFile;
4845 }
4846 }
4847 /* These are for nasm and yasm header searching. Cache will already
4848 have checked the directories for the file, no need to call
4849 CreateFile to do it again. */
4850 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
4851 {
4852 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
4853 return INVALID_HANDLE_VALUE;
4854 }
4855 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
4856 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
4857 {
4858 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
4859 return INVALID_HANDLE_VALUE;
4860 }
4861
4862 /* fallback */
4863 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
4864 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
4865 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
4866 return hFile;
4867 }
4868 }
4869 }
4870 }
4871 }
4872
4873 /*
4874 * Okay, normal.
4875 */
4876 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
4877 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
4878 if (hFile != INVALID_HANDLE_VALUE)
4879 {
4880 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
4881 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
4882 }
4883 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
4884 return hFile;
4885}
4886
4887
4888/** Kernel32 - CreateFileW */
4889static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
4890 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
4891 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
4892{
4893 HANDLE hFile;
4894
4895#ifdef WITH_TEMP_MEMORY_FILES
4896 /* First check for temporary files (cl.exe only). */
4897 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4898 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
4899 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
4900 && kwFsIsClTempFileW(pwszFilename))
4901 {
4902 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition);
4903 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
4904 return hFile;
4905 }
4906#endif
4907
4908 /* Then check for include files and similar. */
4909 if (dwCreationDisposition == FILE_OPEN_IF)
4910 {
4911 if ( dwDesiredAccess == GENERIC_READ
4912 || dwDesiredAccess == FILE_GENERIC_READ)
4913 {
4914 if (dwShareMode & FILE_SHARE_READ)
4915 {
4916 if ( !pSecAttrs
4917 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
4918 && pSecAttrs->lpSecurityDescriptor == NULL ) )
4919 {
4920 if (kwFsIsCachablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
4921 {
4922 /** @todo rewrite in pure UTF-16. */
4923 char szTmp[2048];
4924 KSIZE cch = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
4925 if (cch < sizeof(szTmp))
4926 return kwSandbox_Kernel32_CreateFileA(szTmp, dwDesiredAccess, dwShareMode, pSecAttrs,
4927 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
4928 }
4929 }
4930 else
4931 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
4932 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
4933 }
4934 else
4935 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
4936 }
4937 else
4938 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
4939 }
4940 else
4941 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
4942
4943 /*
4944 * Okay, normal.
4945 */
4946 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
4947 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
4948 if (hFile != INVALID_HANDLE_VALUE)
4949 {
4950 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
4951 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
4952 }
4953 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
4954 return hFile;
4955}
4956
4957
4958/** Kernel32 - SetFilePointer */
4959static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
4960{
4961 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
4962 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4963 if (idxHandle < g_Sandbox.cHandles)
4964 {
4965 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
4966 if (pHandle != NULL)
4967 {
4968 KU32 cbFile;
4969 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
4970 switch (pHandle->enmType)
4971 {
4972 case KWHANDLETYPE_FSOBJ_READ_CACHE:
4973 cbFile = pHandle->u.pCachedFile->cbCached;
4974 break;
4975#ifdef WITH_TEMP_MEMORY_FILES
4976 case KWHANDLETYPE_TEMP_FILE:
4977 cbFile = pHandle->u.pTempFile->cbFile;
4978 break;
4979#endif
4980 case KWHANDLETYPE_TEMP_FILE_MAPPING:
4981 case KWHANDLETYPE_OUTPUT_BUF:
4982 default:
4983 kHlpAssertFailed();
4984 SetLastError(ERROR_INVALID_FUNCTION);
4985 return INVALID_SET_FILE_POINTER;
4986 }
4987
4988 switch (dwMoveMethod)
4989 {
4990 case FILE_BEGIN:
4991 break;
4992 case FILE_CURRENT:
4993 offMove += pHandle->offFile;
4994 break;
4995 case FILE_END:
4996 offMove += cbFile;
4997 break;
4998 default:
4999 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
5000 SetLastError(ERROR_INVALID_PARAMETER);
5001 return INVALID_SET_FILE_POINTER;
5002 }
5003 if (offMove >= 0)
5004 {
5005 if (offMove >= (KSSIZE)cbFile)
5006 {
5007 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
5008 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
5009 offMove = (KSSIZE)cbFile;
5010 /* For writable files, seeking beyond the end is fine, but check that we've got
5011 the type range for the request. */
5012 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
5013 {
5014 kHlpAssertMsgFailed(("%#llx\n", offMove));
5015 SetLastError(ERROR_SEEK);
5016 return INVALID_SET_FILE_POINTER;
5017 }
5018 }
5019 pHandle->offFile = (KU32)offMove;
5020 }
5021 else
5022 {
5023 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
5024 SetLastError(ERROR_NEGATIVE_SEEK);
5025 return INVALID_SET_FILE_POINTER;
5026 }
5027 if (pcbMoveHi)
5028 *pcbMoveHi = (KU64)offMove >> 32;
5029 KWFS_LOG(("SetFilePointer(%p) -> %#llx [cached]\n", hFile, offMove));
5030 SetLastError(NO_ERROR);
5031 return (KU32)offMove;
5032 }
5033 }
5034 KWFS_LOG(("SetFilePointer(%p)\n", hFile));
5035 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
5036}
5037
5038
5039/** Kernel32 - SetFilePointerEx */
5040static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
5041 DWORD dwMoveMethod)
5042{
5043 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5044 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5045 if (idxHandle < g_Sandbox.cHandles)
5046 {
5047 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5048 if (pHandle != NULL)
5049 {
5050 KI64 offMyMove = offMove.QuadPart;
5051 KU32 cbFile;
5052 switch (pHandle->enmType)
5053 {
5054 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5055 cbFile = pHandle->u.pCachedFile->cbCached;
5056 break;
5057#ifdef WITH_TEMP_MEMORY_FILES
5058 case KWHANDLETYPE_TEMP_FILE:
5059 cbFile = pHandle->u.pTempFile->cbFile;
5060 break;
5061#endif
5062 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5063 case KWHANDLETYPE_OUTPUT_BUF:
5064 default:
5065 kHlpAssertFailed();
5066 SetLastError(ERROR_INVALID_FUNCTION);
5067 return INVALID_SET_FILE_POINTER;
5068 }
5069
5070 switch (dwMoveMethod)
5071 {
5072 case FILE_BEGIN:
5073 break;
5074 case FILE_CURRENT:
5075 offMyMove += pHandle->offFile;
5076 break;
5077 case FILE_END:
5078 offMyMove += cbFile;
5079 break;
5080 default:
5081 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
5082 SetLastError(ERROR_INVALID_PARAMETER);
5083 return INVALID_SET_FILE_POINTER;
5084 }
5085 if (offMyMove >= 0)
5086 {
5087 if (offMyMove >= (KSSIZE)cbFile)
5088 {
5089 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
5090 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
5091 offMyMove = (KSSIZE)cbFile;
5092 /* For writable files, seeking beyond the end is fine, but check that we've got
5093 the type range for the request. */
5094 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
5095 {
5096 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
5097 SetLastError(ERROR_SEEK);
5098 return INVALID_SET_FILE_POINTER;
5099 }
5100 }
5101 pHandle->offFile = (KU32)offMyMove;
5102 }
5103 else
5104 {
5105 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
5106 SetLastError(ERROR_NEGATIVE_SEEK);
5107 return INVALID_SET_FILE_POINTER;
5108 }
5109 if (poffNew)
5110 poffNew->QuadPart = offMyMove;
5111 KWFS_LOG(("SetFilePointerEx(%p) -> TRUE, %#llx [cached]\n", hFile, offMyMove));
5112 return TRUE;
5113 }
5114 }
5115 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
5116 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
5117}
5118
5119
5120/** Kernel32 - ReadFile */
5121static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
5122 LPOVERLAPPED pOverlapped)
5123{
5124 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5125 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5126 if (idxHandle < g_Sandbox.cHandles)
5127 {
5128 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5129 if (pHandle != NULL)
5130 {
5131 switch (pHandle->enmType)
5132 {
5133 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5134 {
5135 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
5136 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
5137 if (cbActually > cbToRead)
5138 cbActually = cbToRead;
5139 else if (cbActually < cbToRead) // debug debug debug
5140 kHlpMemSet((KU8 *)pvBuffer + cbActually, '\0', cbToRead - cbActually); // debug debug debug
5141
5142#ifdef WITH_HASH_MD5_CACHE
5143 if (g_Sandbox.pHashHead)
5144 {
5145 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
5146 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
5147 g_Sandbox.LastHashRead.cbRead = cbActually;
5148 g_Sandbox.LastHashRead.pvRead = pvBuffer;
5149 }
5150#endif
5151
5152 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
5153 pHandle->offFile += cbActually;
5154
5155 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
5156 *pcbActuallyRead = cbActually;
5157
5158 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
5159 return TRUE;
5160 }
5161
5162#ifdef WITH_TEMP_MEMORY_FILES
5163 case KWHANDLETYPE_TEMP_FILE:
5164 {
5165 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5166 KU32 cbActually;
5167 if (pHandle->offFile < pTempFile->cbFile)
5168 {
5169 cbActually = pTempFile->cbFile - pHandle->offFile;
5170 if (cbActually > cbToRead)
5171 cbActually = cbToRead;
5172
5173 /* Copy the data. */
5174 if (cbActually > 0)
5175 {
5176 KU32 cbLeft;
5177 KU32 offSeg;
5178 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
5179
5180 /* Locate the segment containing the byte at offFile. */
5181 KU32 iSeg = pTempFile->cSegs - 1;
5182 kHlpAssert(pTempFile->cSegs > 0);
5183 while (paSegs[iSeg].offData > pHandle->offFile)
5184 iSeg--;
5185
5186 /* Copy out the data. */
5187 cbLeft = cbActually;
5188 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
5189 for (;;)
5190 {
5191 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
5192 if (cbAvail >= cbLeft)
5193 {
5194 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
5195 break;
5196 }
5197
5198 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
5199 cbLeft -= cbAvail;
5200 offSeg = 0;
5201 iSeg++;
5202 kHlpAssert(iSeg < pTempFile->cSegs);
5203 }
5204
5205 /* Update the file offset. */
5206 pHandle->offFile += cbActually;
5207 }
5208 }
5209 /* Read does not commit file space, so return zero bytes. */
5210 else
5211 cbActually = 0;
5212
5213 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
5214 *pcbActuallyRead = cbActually;
5215
5216 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
5217 return TRUE;
5218 }
5219#endif /* WITH_TEMP_MEMORY_FILES */
5220
5221 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5222 case KWHANDLETYPE_OUTPUT_BUF:
5223 default:
5224 kHlpAssertFailed();
5225 SetLastError(ERROR_INVALID_FUNCTION);
5226 *pcbActuallyRead = 0;
5227 return FALSE;
5228 }
5229 }
5230 }
5231
5232 KWFS_LOG(("ReadFile(%p)\n", hFile));
5233 return ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
5234}
5235
5236
5237/** Kernel32 - ReadFileEx */
5238static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
5239 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
5240{
5241 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5242 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5243 if (idxHandle < g_Sandbox.cHandles)
5244 {
5245 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5246 if (pHandle != NULL)
5247 {
5248 kHlpAssertFailed();
5249 }
5250 }
5251
5252 KWFS_LOG(("ReadFile(%p)\n", hFile));
5253 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
5254}
5255
5256#ifdef WITH_STD_OUT_ERR_BUFFERING
5257
5258/**
5259 * Write something to a handle, making sure everything is actually written.
5260 *
5261 * @param hHandle Where to write it to.
5262 * @param pchBuf What to write
5263 * @param cchToWrite How much to write.
5264 */
5265static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
5266{
5267 if (cchToWrite > 0)
5268 {
5269 DWORD cchWritten = 0;
5270 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
5271 {
5272 if (cchWritten == cchToWrite)
5273 { /* likely */ }
5274 else
5275 {
5276 do
5277 {
5278 pchBuf += cchWritten;
5279 cchToWrite -= cchWritten;
5280 cchWritten = 0;
5281 } while ( cchToWrite > 0
5282 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
5283 }
5284 }
5285 else
5286 kHlpAssertFailed();
5287 }
5288}
5289
5290
5291/**
5292 * Worker for WriteFile when the output isn't going to the console.
5293 *
5294 * @param pSandbox The sandbox.
5295 * @param pOutBuf The output buffer.
5296 * @param pchBuffer What to write.
5297 * @param cchToWrite How much to write.
5298 */
5299static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
5300{
5301 if (pOutBuf->u.Fully.cchBufAlloc > 0)
5302 { /* likely */ }
5303 else
5304 {
5305 /* No realloc, max size is 64KB. */
5306 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
5307 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
5308 if (!pOutBuf->u.Fully.pchBuf)
5309 {
5310 while ( !pOutBuf->u.Fully.pchBuf
5311 && pOutBuf->u.Fully.cchBufAlloc > 64)
5312 {
5313 pOutBuf->u.Fully.cchBufAlloc /= 2;
5314 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
5315 }
5316 if (!pOutBuf->u.Fully.pchBuf)
5317 {
5318 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
5319 pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
5320 }
5321 }
5322 }
5323
5324 /*
5325 * Special case: Output ends with newline and fits in the buffer.
5326 */
5327 if ( cchToWrite > 1
5328 && pchBuffer[cchToWrite - 1] == '\n'
5329 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
5330 {
5331 memcpy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
5332 pOutBuf->u.Fully.cchBuf += cchToWrite;
5333 }
5334 else
5335 {
5336 /*
5337 * Work thru the text line by line, flushing the buffer when
5338 * appropriate. The buffer is not a line buffer here, it's a
5339 * full buffer.
5340 */
5341 while (cchToWrite > 0)
5342 {
5343 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
5344 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
5345 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
5346 {
5347 memcpy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
5348 pOutBuf->u.Fully.cchBuf += cchLine;
5349 }
5350 /*
5351 * Option one: Flush the buffer and the current line.
5352 *
5353 * We choose this one when the line won't ever fit, or when we have
5354 * an incomplete line in the buffer.
5355 */
5356 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
5357 || pOutBuf->u.Fully.cchBuf == 0
5358 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
5359 {
5360 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
5361 if (pOutBuf->u.Fully.cchBuf > 0)
5362 {
5363 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
5364 pOutBuf->u.Fully.cchBuf = 0;
5365 }
5366 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
5367 }
5368 /*
5369 * Option two: Only flush the lines in the buffer.
5370 */
5371 else
5372 {
5373 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
5374 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
5375 memcpy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
5376 pOutBuf->u.Fully.cchBuf = cchLine;
5377 }
5378
5379 /* advance */
5380 pchBuffer += cchLine;
5381 cchToWrite -= cchLine;
5382 }
5383 }
5384}
5385
5386#endif /* WITH_STD_OUT_ERR_BUFFERING */
5387
5388#ifdef WITH_TEMP_MEMORY_FILES
5389static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
5390{
5391 KU32 cbMinFile = offFile + cbNeeded;
5392 if (cbMinFile >= offFile)
5393 {
5394 /* Calc how much space we've already allocated and */
5395 if (cbMinFile <= pTempFile->cbFileAllocated)
5396 return K_TRUE;
5397
5398 /* Grow the file. */
5399 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
5400 {
5401 int rc;
5402 KU32 cSegs = pTempFile->cSegs;
5403 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
5404 do
5405 {
5406 /* grow the segment array? */
5407 if ((cSegs % 16) == 0)
5408 {
5409 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
5410 if (!pvNew)
5411 return K_FALSE;
5412 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
5413 }
5414
5415 /* Use page alloc here to simplify mapping later. */
5416 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
5417 if (rc == 0)
5418 { /* likely */ }
5419 else
5420 {
5421 cbNewSeg = 64*1024;
5422 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
5423 if (rc != 0)
5424 {
5425 kHlpAssertFailed();
5426 return K_FALSE;
5427 }
5428 }
5429 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
5430 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
5431 pTempFile->cbFileAllocated += cbNewSeg;
5432 pTempFile->cSegs = ++cSegs;
5433
5434 } while (pTempFile->cbFileAllocated < cbMinFile);
5435
5436 return K_TRUE;
5437 }
5438 }
5439
5440 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
5441 return K_FALSE;
5442}
5443#endif /* WITH_TEMP_MEMORY_FILES */
5444
5445
5446#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
5447/** Kernel32 - WriteFile */
5448static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
5449 LPOVERLAPPED pOverlapped)
5450{
5451 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5452 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5453 if (idxHandle < g_Sandbox.cHandles)
5454 {
5455 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5456 if (pHandle != NULL)
5457 {
5458 switch (pHandle->enmType)
5459 {
5460# ifdef WITH_TEMP_MEMORY_FILES
5461 case KWHANDLETYPE_TEMP_FILE:
5462 {
5463 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5464
5465 kHlpAssert(!pOverlapped);
5466 kHlpAssert(pcbActuallyWritten);
5467
5468 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
5469 {
5470 KU32 cbLeft;
5471 KU32 offSeg;
5472
5473 /* Locate the segment containing the byte at offFile. */
5474 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
5475 KU32 iSeg = pTempFile->cSegs - 1;
5476 kHlpAssert(pTempFile->cSegs > 0);
5477 while (paSegs[iSeg].offData > pHandle->offFile)
5478 iSeg--;
5479
5480 /* Copy in the data. */
5481 cbLeft = cbToWrite;
5482 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
5483 for (;;)
5484 {
5485 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
5486 if (cbAvail >= cbLeft)
5487 {
5488 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
5489 break;
5490 }
5491
5492 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
5493 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
5494 cbLeft -= cbAvail;
5495 offSeg = 0;
5496 iSeg++;
5497 kHlpAssert(iSeg < pTempFile->cSegs);
5498 }
5499
5500 /* Update the file offset. */
5501 pHandle->offFile += cbToWrite;
5502 if (pHandle->offFile > pTempFile->cbFile)
5503 pTempFile->cbFile = pHandle->offFile;
5504
5505 *pcbActuallyWritten = cbToWrite;
5506 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
5507 return TRUE;
5508 }
5509
5510 kHlpAssertFailed();
5511 *pcbActuallyWritten = 0;
5512 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5513 return FALSE;
5514 }
5515# endif
5516
5517 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5518 kHlpAssertFailed();
5519 SetLastError(ERROR_ACCESS_DENIED);
5520 *pcbActuallyWritten = 0;
5521 return FALSE;
5522
5523# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
5524 /*
5525 * Standard output & error.
5526 */
5527 case KWHANDLETYPE_OUTPUT_BUF:
5528 {
5529 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
5530 if (pOutBuf->fIsConsole)
5531 {
5532 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
5533 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
5534 }
5535 else
5536 {
5537# ifdef WITH_STD_OUT_ERR_BUFFERING
5538 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
5539 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
5540 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
5541# else
5542 kHlpAssertFailed();
5543# endif
5544 }
5545 if (pcbActuallyWritten)
5546 *pcbActuallyWritten = cbToWrite;
5547 return TRUE;
5548 }
5549# endif
5550
5551 default:
5552 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5553 kHlpAssertFailed();
5554 SetLastError(ERROR_INVALID_FUNCTION);
5555 *pcbActuallyWritten = 0;
5556 return FALSE;
5557 }
5558 }
5559 }
5560
5561 KWFS_LOG(("WriteFile(%p,,%#x)\n", hFile, cbToWrite));
5562 return WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
5563}
5564
5565
5566/** Kernel32 - WriteFileEx */
5567static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
5568 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
5569{
5570 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5571 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5572 if (idxHandle < g_Sandbox.cHandles)
5573 {
5574 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5575 if (pHandle != NULL)
5576 {
5577 kHlpAssertFailed();
5578 }
5579 }
5580
5581 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
5582 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
5583}
5584
5585#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
5586
5587#ifdef WITH_TEMP_MEMORY_FILES
5588
5589/** Kernel32 - SetEndOfFile; */
5590static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
5591{
5592 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5593 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5594 if (idxHandle < g_Sandbox.cHandles)
5595 {
5596 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5597 if (pHandle != NULL)
5598 {
5599 switch (pHandle->enmType)
5600 {
5601 case KWHANDLETYPE_TEMP_FILE:
5602 {
5603 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5604 if ( pHandle->offFile > pTempFile->cbFile
5605 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
5606 {
5607 kHlpAssertFailed();
5608 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5609 return FALSE;
5610 }
5611
5612 pTempFile->cbFile = pHandle->offFile;
5613 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
5614 return TRUE;
5615 }
5616
5617 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5618 kHlpAssertFailed();
5619 SetLastError(ERROR_ACCESS_DENIED);
5620 return FALSE;
5621
5622 case KWHANDLETYPE_OUTPUT_BUF:
5623 kHlpAssertFailed();
5624 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
5625 return FALSE;
5626
5627 default:
5628 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5629 kHlpAssertFailed();
5630 SetLastError(ERROR_INVALID_FUNCTION);
5631 return FALSE;
5632 }
5633 }
5634 }
5635
5636 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
5637 return SetEndOfFile(hFile);
5638}
5639
5640
5641/** Kernel32 - GetFileType */
5642static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
5643{
5644 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5645 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5646 if (idxHandle < g_Sandbox.cHandles)
5647 {
5648 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5649 if (pHandle != NULL)
5650 {
5651 switch (pHandle->enmType)
5652 {
5653 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5654 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
5655 return FILE_TYPE_DISK;
5656
5657 case KWHANDLETYPE_TEMP_FILE:
5658 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
5659 return FILE_TYPE_DISK;
5660
5661 case KWHANDLETYPE_OUTPUT_BUF:
5662 {
5663 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
5664 DWORD fRet;
5665 if (pOutBuf->fFileType != KU8_MAX)
5666 {
5667 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
5668 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
5669 }
5670 else
5671 {
5672 fRet = GetFileType(hFile);
5673 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
5674 }
5675 return fRet;
5676 }
5677
5678 }
5679 }
5680 }
5681
5682 KWFS_LOG(("GetFileType(%p)\n", hFile));
5683 return GetFileType(hFile);
5684}
5685
5686
5687/** Kernel32 - GetFileSize */
5688static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
5689{
5690 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5691 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5692 if (idxHandle < g_Sandbox.cHandles)
5693 {
5694 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5695 if (pHandle != NULL)
5696 {
5697 if (pcbHighDword)
5698 *pcbHighDword = 0;
5699 SetLastError(NO_ERROR);
5700 switch (pHandle->enmType)
5701 {
5702 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5703 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
5704 return pHandle->u.pCachedFile->cbCached;
5705
5706 case KWHANDLETYPE_TEMP_FILE:
5707 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
5708 return pHandle->u.pTempFile->cbFile;
5709
5710 case KWHANDLETYPE_OUTPUT_BUF:
5711 /* do default */
5712 break;
5713
5714 default:
5715 kHlpAssertFailed();
5716 SetLastError(ERROR_INVALID_FUNCTION);
5717 return INVALID_FILE_SIZE;
5718 }
5719 }
5720 }
5721
5722 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
5723 return GetFileSize(hFile, pcbHighDword);
5724}
5725
5726
5727/** Kernel32 - GetFileSizeEx */
5728static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
5729{
5730 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5731 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5732 if (idxHandle < g_Sandbox.cHandles)
5733 {
5734 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5735 if (pHandle != NULL)
5736 {
5737 switch (pHandle->enmType)
5738 {
5739 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5740 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
5741 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
5742 return TRUE;
5743
5744 case KWHANDLETYPE_TEMP_FILE:
5745 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
5746 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
5747 return TRUE;
5748
5749 case KWHANDLETYPE_OUTPUT_BUF:
5750 /* do default */
5751 break;
5752
5753 default:
5754 kHlpAssertFailed();
5755 SetLastError(ERROR_INVALID_FUNCTION);
5756 return INVALID_FILE_SIZE;
5757 }
5758 }
5759 }
5760
5761 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
5762 return GetFileSizeEx(hFile, pcbFile);
5763}
5764
5765
5766/** Kernel32 - CreateFileMapping */
5767static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
5768 DWORD fProtect, DWORD dwMaximumSizeHigh,
5769 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
5770{
5771 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5772 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5773 if (idxHandle < g_Sandbox.cHandles)
5774 {
5775 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5776 if (pHandle != NULL)
5777 {
5778 switch (pHandle->enmType)
5779 {
5780 case KWHANDLETYPE_TEMP_FILE:
5781 {
5782 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5783 if ( ( fProtect == PAGE_READONLY
5784 || fProtect == PAGE_EXECUTE_READ)
5785 && dwMaximumSizeHigh == 0
5786 && ( dwMaximumSizeLow == 0
5787 || dwMaximumSizeLow == pTempFile->cbFile)
5788 && pwszName == NULL)
5789 {
5790 HANDLE hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
5791 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
5792 return hMapping;
5793 }
5794 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
5795 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
5796 SetLastError(ERROR_ACCESS_DENIED);
5797 return INVALID_HANDLE_VALUE;
5798 }
5799 }
5800 }
5801 }
5802
5803 KWFS_LOG(("CreateFileMappingW(%p)\n", hFile));
5804 return CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
5805}
5806
5807/** Kernel32 - MapViewOfFile */
5808static HANDLE WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
5809 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
5810{
5811 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
5812 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5813 if (idxHandle < g_Sandbox.cHandles)
5814 {
5815 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5816 if (pHandle != NULL)
5817 {
5818 switch (pHandle->enmType)
5819 {
5820 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5821 case KWHANDLETYPE_TEMP_FILE:
5822 case KWHANDLETYPE_OUTPUT_BUF:
5823 kHlpAssertFailed();
5824 SetLastError(ERROR_INVALID_OPERATION);
5825 return NULL;
5826
5827 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5828 {
5829 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5830 if ( dwDesiredAccess == FILE_MAP_READ
5831 && offFileHigh == 0
5832 && offFileLow == 0
5833 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
5834 {
5835 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
5836 if (pTempFile->cSegs != 1)
5837 {
5838 KU32 iSeg;
5839 KU32 cbLeft;
5840 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
5841 KU8 *pbAll = NULL;
5842 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
5843 if (rc != 0)
5844 {
5845 kHlpAssertFailed();
5846 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5847 return NULL;
5848 }
5849
5850 cbLeft = pTempFile->cbFile;
5851 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
5852 {
5853 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
5854 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
5855 cbLeft -= cbToCopy;
5856 }
5857
5858 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
5859 {
5860 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
5861 pTempFile->paSegs[iSeg].pbData = NULL;
5862 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
5863 }
5864
5865 pTempFile->cSegs = 1;
5866 pTempFile->cbFileAllocated = cbAll;
5867 pTempFile->paSegs[0].cbDataAlloc = cbAll;
5868 pTempFile->paSegs[0].pbData = pbAll;
5869 pTempFile->paSegs[0].offData = 0;
5870 }
5871
5872 pTempFile->cMappings++;
5873 kHlpAssert(pTempFile->cMappings == 1);
5874
5875 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pTempFile->paSegs[0].pbData));
5876 return pTempFile->paSegs[0].pbData;
5877 }
5878
5879 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
5880 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
5881 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5882 return NULL;
5883 }
5884 }
5885 }
5886 }
5887
5888 KWFS_LOG(("MapViewOfFile(%p)\n", hSection));
5889 return MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
5890}
5891/** @todo MapViewOfFileEx */
5892
5893
5894/** Kernel32 - UnmapViewOfFile */
5895static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
5896{
5897 /* Is this one of our temporary mappings? */
5898 PKWFSTEMPFILE pCur = g_Sandbox.pTempFileHead;
5899 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5900 while (pCur)
5901 {
5902 if ( pCur->cMappings > 0
5903 && pCur->paSegs[0].pbData == (KU8 *)pvBase)
5904 {
5905 pCur->cMappings--;
5906 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
5907 return TRUE;
5908 }
5909 pCur = pCur->pNext;
5910 }
5911
5912 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
5913 return UnmapViewOfFile(pvBase);
5914}
5915
5916/** @todo UnmapViewOfFileEx */
5917
5918#endif /* WITH_TEMP_MEMORY_FILES */
5919
5920
5921/** Kernel32 - DuplicateHandle */
5922static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
5923 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
5924{
5925 BOOL fRet;
5926
5927 /*
5928 * We must catch our handles being duplicated.
5929 */
5930 if (hSrcProc == GetCurrentProcess())
5931 {
5932 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
5933 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5934 if (idxHandle < g_Sandbox.cHandles)
5935 {
5936 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5937 if (pHandle)
5938 {
5939 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
5940 if (fRet)
5941 {
5942 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
5943 {
5944 pHandle->cRefs++;
5945 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
5946 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
5947 pHandle->enmType, pHandle->cRefs));
5948 }
5949 else
5950 {
5951 fRet = FALSE;
5952 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5953 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
5954 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
5955 }
5956 }
5957 else
5958 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
5959 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
5960 return fRet;
5961 }
5962 }
5963 }
5964
5965 /*
5966 * Not one of ours, just do what the caller asks and log it.
5967 */
5968 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
5969 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
5970 fInheritHandle, dwOptions, fRet, *phNew));
5971 return fRet;
5972}
5973
5974
5975/** Kernel32 - CloseHandle */
5976static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
5977{
5978 BOOL fRet;
5979 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
5980 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5981 if (idxHandle < g_Sandbox.cHandles)
5982 {
5983 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5984 if (pHandle)
5985 {
5986 /* Prevent the closing of the standard output and error handles. */
5987 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
5988 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
5989 {
5990 fRet = CloseHandle(hObject);
5991 if (fRet)
5992 {
5993 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5994 g_Sandbox.papHandles[idxHandle] = NULL;
5995 g_Sandbox.cActiveHandles--;
5996 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
5997 if (--pHandle->cRefs == 0)
5998 {
5999#ifdef WITH_TEMP_MEMORY_FILES
6000 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
6001 {
6002 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
6003 pHandle->u.pTempFile->cActiveHandles--;
6004 }
6005#endif
6006 kHlpFree(pHandle);
6007 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
6008 }
6009 else
6010 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
6011 }
6012 else
6013 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
6014 }
6015 else
6016 {
6017 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
6018 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
6019 fRet = TRUE;
6020 }
6021 return fRet;
6022 }
6023 }
6024
6025 fRet = CloseHandle(hObject);
6026 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
6027 return fRet;
6028}
6029
6030
6031/** Kernel32 - GetFileAttributesA. */
6032static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
6033{
6034 DWORD fRet;
6035 const char *pszExt = kHlpGetExt(pszFilename);
6036 if (kwFsIsCachableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
6037 {
6038 KFSLOOKUPERROR enmError;
6039 PKFSOBJ pFsObj;
6040 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6041
6042 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
6043 if (pFsObj)
6044 {
6045 kHlpAssert(pFsObj->fHaveStats);
6046 fRet = pFsObj->Stats.st_attribs;
6047 kFsCacheObjRelease(g_pFsCache, pFsObj);
6048 }
6049 else
6050 {
6051 SetLastError(kwFsLookupErrorToWindowsError(enmError));
6052 fRet = INVALID_FILE_ATTRIBUTES;
6053 }
6054
6055 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
6056 return fRet;
6057 }
6058
6059 fRet = GetFileAttributesA(pszFilename);
6060 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
6061 return fRet;
6062}
6063
6064
6065/** Kernel32 - GetFileAttributesW. */
6066static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
6067{
6068 DWORD fRet;
6069 if (kwFsIsCachablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
6070 {
6071 KFSLOOKUPERROR enmError;
6072 PKFSOBJ pFsObj;
6073 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6074
6075 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6076 if (pFsObj)
6077 {
6078 kHlpAssert(pFsObj->fHaveStats);
6079 fRet = pFsObj->Stats.st_attribs;
6080 kFsCacheObjRelease(g_pFsCache, pFsObj);
6081 }
6082 else
6083 {
6084 SetLastError(kwFsLookupErrorToWindowsError(enmError));
6085 fRet = INVALID_FILE_ATTRIBUTES;
6086 }
6087
6088 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
6089 return fRet;
6090 }
6091
6092 fRet = GetFileAttributesW(pwszFilename);
6093 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
6094 return fRet;
6095}
6096
6097
6098/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
6099 * directory containing each include file. We cache the result to speed
6100 * things up a little. */
6101static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
6102{
6103 DWORD cwcRet;
6104 if (kwFsIsCachablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
6105 {
6106 KFSLOOKUPERROR enmError;
6107 PKFSOBJ pObj;
6108 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6109
6110 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
6111 if (pObj)
6112 {
6113 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
6114 {
6115 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
6116 {
6117 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
6118
6119 /* Should preserve trailing slash on directory paths. */
6120 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
6121 {
6122 if ( cwcRet + 1 < cwcShortPath
6123 && pwszShortPath[cwcRet - 1] != '\\')
6124 {
6125 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
6126 if ( cwcIn > 0
6127 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
6128 {
6129 pwszShortPath[cwcRet++] = '\\';
6130 pwszShortPath[cwcRet] = '\0';
6131 }
6132 }
6133 }
6134
6135 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
6136 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
6137 kFsCacheObjRelease(g_pFsCache, pObj);
6138 return cwcRet;
6139 }
6140
6141 /* fall back for complicated cases. */
6142 }
6143 kFsCacheObjRelease(g_pFsCache, pObj);
6144 }
6145 }
6146 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
6147 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
6148 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
6149 return cwcRet;
6150}
6151
6152
6153#ifdef WITH_TEMP_MEMORY_FILES
6154/** Kernel32 - DeleteFileW
6155 * Skip deleting the in-memory files. */
6156static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
6157{
6158 BOOL fRc;
6159 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6160 && kwFsIsClTempFileW(pwszFilename))
6161 {
6162 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6163 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
6164 fRc = TRUE;
6165 }
6166 else
6167 {
6168 fRc = DeleteFileW(pwszFilename);
6169 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
6170 }
6171 return fRc;
6172}
6173#endif /* WITH_TEMP_MEMORY_FILES */
6174
6175
6176
6177#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
6178
6179/*
6180 *
6181 * Console output buffering.
6182 * Console output buffering.
6183 * Console output buffering.
6184 *
6185 */
6186
6187
6188/**
6189 * Write a wide char string to the console.
6190 *
6191 * @param pSandbox The sandbox which output buffer to flush.
6192 */
6193static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
6194{
6195 if (cwcToWrite > 0)
6196 {
6197 DWORD cwcWritten = 0;
6198 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
6199 {
6200 if (cwcWritten == cwcToWrite)
6201 { /* likely */ }
6202 else
6203 {
6204 DWORD off = 0;
6205 do
6206 {
6207 off += cwcWritten;
6208 cwcWritten = 0;
6209 } while ( off < cwcToWrite
6210 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
6211 kHlpAssert(off == cwcWritten);
6212 }
6213 }
6214 else
6215 kHlpAssertFailed();
6216 pSandbox->Combined.cFlushes++;
6217 }
6218}
6219
6220
6221/**
6222 * Flushes the combined console output buffer.
6223 *
6224 * @param pSandbox The sandbox which output buffer to flush.
6225 */
6226static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
6227{
6228 if (pSandbox->Combined.cwcBuf > 0)
6229 {
6230 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
6231 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
6232 pSandbox->Combined.cwcBuf = 0;
6233 }
6234}
6235
6236
6237/**
6238 * For handling combined buffer overflow cases line by line.
6239 *
6240 * @param pSandbox The sandbox.
6241 * @param pwcBuf What to add to the combined buffer. Usually a
6242 * line, unless we're really low on buffer space.
6243 * @param cwcBuf The length of what to add.
6244 * @param fBrokenLine Whether this is a broken line.
6245 */
6246static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
6247{
6248 if (fBrokenLine)
6249 kwSandboxConsoleFlushCombined(pSandbox);
6250 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
6251 {
6252 kwSandboxConsoleFlushCombined(pSandbox);
6253 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
6254 }
6255 else
6256 {
6257 memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
6258 pSandbox->Combined.cwcBuf += cwcBuf;
6259 }
6260}
6261
6262
6263/**
6264 * Called to final flush a line buffer via the combined buffer (if applicable).
6265 *
6266 * @param pSandbox The sandbox.
6267 * @param pLineBuf The line buffer.
6268 * @param pszName The line buffer name (for logging)
6269 */
6270static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
6271{
6272 if (pLineBuf->fIsConsole)
6273 {
6274 if (pLineBuf->u.Con.cwcBuf > 0)
6275 {
6276 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
6277
6278 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
6279 {
6280 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
6281 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
6282 }
6283 else
6284 {
6285 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
6286 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
6287 }
6288 pLineBuf->u.Con.cwcBuf = 0;
6289 }
6290 }
6291#ifdef WITH_STD_OUT_ERR_BUFFERING
6292 else if (pLineBuf->u.Fully.cchBuf > 0)
6293 {
6294 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
6295
6296 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
6297 pLineBuf->u.Fully.cchBuf = 0;
6298 }
6299#endif
6300}
6301
6302
6303/**
6304 * Called at the end of sandboxed execution to flush both stream buffers.
6305 *
6306 * @param pSandbox The sandbox.
6307 */
6308static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
6309{
6310 /*
6311 * First do the cl.exe source file supression trick, if applicable.
6312 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
6313 * handle.
6314 */
6315 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6316 && pSandbox->Combined.cFlushes == 0)
6317 {
6318 if ( pSandbox->StdOut.fIsConsole
6319 || pSandbox->StdErr.fIsConsole)
6320 {
6321 if ( pSandbox->Combined.cwcBuf >= 3
6322 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
6323 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
6324 {
6325 KI32 off = pSandbox->Combined.cwcBuf - 1;
6326 if (pSandbox->Combined.wszBuf[off] == '\n')
6327 {
6328 KBOOL fOk = K_TRUE;
6329 while (off-- > 0)
6330 {
6331 wchar_t const wc = pSandbox->Combined.wszBuf[off];
6332 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
6333 { /* likely */ }
6334 else
6335 {
6336 fOk = K_FALSE;
6337 break;
6338 }
6339 }
6340 if (fOk)
6341 {
6342 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
6343 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
6344 pSandbox->Combined.cwcBuf = 0;
6345 return;
6346 }
6347 }
6348 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
6349 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
6350 }
6351 }
6352#ifdef WITH_STD_OUT_ERR_BUFFERING
6353 /*
6354 * Otherwise, it goes to standard output (redirected).
6355 */
6356 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
6357 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
6358 {
6359 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
6360 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
6361 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
6362
6363 if (pchBuf[off] == '\n')
6364 {
6365 KBOOL fOk = K_TRUE;
6366 if (pchBuf[off - 1] == '\r')
6367 off--;
6368 while (off-- > 0)
6369 {
6370 char const ch = pchBuf[off];
6371 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
6372 { /* likely */ }
6373 else
6374 {
6375 fOk = K_FALSE;
6376 break;
6377 }
6378 }
6379 if (fOk)
6380 {
6381 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
6382 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
6383 pSandbox->StdOut.u.Fully.cchBuf = 0;
6384 return;
6385 }
6386 }
6387 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
6388 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
6389 }
6390#endif
6391 }
6392
6393 /*
6394 * Flush the two line buffer, the the combined buffer.
6395 */
6396 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
6397 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
6398 kwSandboxConsoleFlushCombined(pSandbox);
6399}
6400
6401
6402/**
6403 * Writes a string to the given output stream.
6404 *
6405 * @param pSandbox The sandbox.
6406 * @param pLineBuf The line buffer for the output stream.
6407 * @param pwcBuffer The buffer to write.
6408 * @param cwcToWrite The number of wchar_t's in the buffer.
6409 */
6410static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
6411{
6412 kHlpAssert(pLineBuf->fIsConsole);
6413 if (cwcToWrite > 0)
6414 {
6415 /*
6416 * First, find the start of the last incomplete line so we can figure
6417 * out how much line buffering we need to do.
6418 */
6419 KU32 cchLastIncompleteLine;
6420 KU32 offLastIncompleteLine = cwcToWrite;
6421 while ( offLastIncompleteLine > 0
6422 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
6423 offLastIncompleteLine--;
6424 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
6425
6426 /* Was there anything to line buffer? */
6427 if (offLastIncompleteLine < cwcToWrite)
6428 {
6429 /* Need to grow the line buffer? */
6430 KU32 cwcNeeded = offLastIncompleteLine != 0 ? offLastIncompleteLine : cchLastIncompleteLine + pLineBuf->u.Con.cwcBuf;
6431 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
6432 {
6433 void *pvNew;
6434 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
6435 while (cwcNew < cwcNeeded)
6436 cwcNew *= 2;
6437 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
6438 if (pvNew)
6439 {
6440 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
6441 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
6442 }
6443 else
6444 {
6445 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
6446 if (pvNew)
6447 {
6448 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
6449 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
6450 }
6451 else
6452 {
6453 /* This isn't perfect, but it will have to do for now. */
6454 if (pLineBuf->u.Con.cwcBuf > 0)
6455 {
6456 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
6457 K_TRUE /*fBrokenLine*/);
6458 pLineBuf->u.Con.cwcBuf = 0;
6459 }
6460 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
6461 return;
6462 }
6463 }
6464 }
6465
6466 /*
6467 * Handle the case where we only add to the line buffer.
6468 */
6469 if (offLastIncompleteLine == 0)
6470 {
6471 memcpy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
6472 pLineBuf->u.Con.cwcBuf += cwcToWrite;
6473 return;
6474 }
6475 }
6476
6477 /*
6478 * If there is sufficient combined buffer to handle this request, this are rather simple.
6479 */
6480 if (pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
6481 {
6482 if (pLineBuf->u.Con.cwcBuf > 0)
6483 {
6484 memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
6485 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
6486 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
6487 pLineBuf->u.Con.cwcBuf = 0;
6488 }
6489
6490 memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
6491 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
6492 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
6493 }
6494 else
6495 {
6496 /*
6497 * Do line-by-line processing of the input, flusing the combined buffer
6498 * when it becomes necessary. We may have to write lines
6499 */
6500 KU32 off = 0;
6501 KU32 offNextLine = 0;
6502
6503 /* If there is buffered chars, we handle the first line outside the
6504 main loop. We must try our best outputting it as a complete line. */
6505 if (pLineBuf->u.Con.cwcBuf > 0)
6506 {
6507 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
6508 offNextLine++;
6509 offNextLine++;
6510 kHlpAssert(offNextLine <= offLastIncompleteLine);
6511
6512 if (pLineBuf->u.Con.cwcBuf + offNextLine + pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf))
6513 {
6514 memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
6515 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
6516 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
6517 pLineBuf->u.Con.cwcBuf = 0;
6518
6519 memcpy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
6520 pSandbox->Combined.cwcBuf += offNextLine;
6521 }
6522 else
6523 {
6524 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
6525 if (cwcLeft > 0)
6526 {
6527 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
6528 memcpy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
6529 pLineBuf->u.Con.cwcBuf += cwcCopy;
6530 off += cwcCopy;
6531 }
6532 if (pLineBuf->u.Con.cwcBuf > 0)
6533 {
6534 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
6535 K_TRUE /*fBrokenLine*/);
6536 pLineBuf->u.Con.cwcBuf = 0;
6537 }
6538 if (off < offNextLine)
6539 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
6540 }
6541 off = offNextLine;
6542 }
6543
6544 /* Deal with the remaining lines */
6545 while (off < offLastIncompleteLine)
6546 {
6547 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
6548 offNextLine++;
6549 offNextLine++;
6550 kHlpAssert(offNextLine <= offLastIncompleteLine);
6551 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
6552 off = offNextLine;
6553 }
6554 }
6555
6556 /*
6557 * Buffer any remaining incomplete line chars.
6558 */
6559 if (offLastIncompleteLine < cwcToWrite)
6560 {
6561 memcpy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
6562 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
6563 }
6564 }
6565}
6566
6567
6568/**
6569 * Worker for WriteConsoleA and WriteFile.
6570 *
6571 * @param pSandbox The sandbox.
6572 * @param pLineBuf The line buffer.
6573 * @param pchBuffer What to write.
6574 * @param cchToWrite How much to write.
6575 */
6576static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
6577{
6578 /*
6579 * Convert it to wide char and use the 'W' to do the work.
6580 */
6581 int cwcRet;
6582 KU32 cwcBuf = cchToWrite * 2 + 1;
6583 wchar_t *pwcBufFree = NULL;
6584 wchar_t *pwcBuf;
6585 kHlpAssert(pLineBuf->fIsConsole);
6586
6587 if (cwcBuf <= 4096)
6588 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
6589 else
6590 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
6591
6592 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
6593 if (cwcRet > 0)
6594 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
6595 else
6596 {
6597 DWORD cchWritten;
6598 kHlpAssertFailed();
6599
6600 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
6601 if (pLineBuf->u.Con.cwcBuf > 0)
6602 {
6603 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
6604 pLineBuf->u.Con.cwcBuf = 0;
6605 }
6606 kwSandboxConsoleFlushCombined(pSandbox);
6607
6608 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
6609 {
6610 if (cchWritten >= cchToWrite)
6611 { /* likely */ }
6612 else
6613 {
6614 KU32 off = 0;
6615 do
6616 {
6617 off += cchWritten;
6618 cchWritten = 0;
6619 } while ( off < cchToWrite
6620 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
6621 }
6622 }
6623 }
6624
6625 if (pwcBufFree)
6626 kHlpFree(pwcBufFree);
6627}
6628
6629
6630/** Kernel32 - WriteConsoleA */
6631BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
6632 PVOID pvReserved)
6633{
6634 BOOL fRc;
6635 PKWOUTPUTSTREAMBUF pLineBuf;
6636 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6637
6638 if (hConOutput == g_Sandbox.StdErr.hOutput)
6639 pLineBuf = &g_Sandbox.StdErr;
6640 else
6641 pLineBuf = &g_Sandbox.StdOut;
6642 if (pLineBuf->fIsConsole)
6643 {
6644 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
6645
6646 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
6647 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
6648 if (pcbWritten)
6649 *pcbWritten = cbToWrite;
6650 fRc = TRUE;
6651 }
6652 else
6653 {
6654 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
6655 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
6656 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
6657 }
6658 return fRc;
6659}
6660
6661
6662/** Kernel32 - WriteConsoleW */
6663BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
6664 PVOID pvReserved)
6665{
6666 BOOL fRc;
6667 PKWOUTPUTSTREAMBUF pLineBuf;
6668 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6669
6670 if (hConOutput == g_Sandbox.StdErr.hOutput)
6671 pLineBuf = &g_Sandbox.StdErr;
6672 else if (hConOutput == g_Sandbox.StdOut.hOutput)
6673 pLineBuf = &g_Sandbox.StdOut;
6674 else
6675 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
6676 if (pLineBuf->fIsConsole)
6677 {
6678 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
6679
6680 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
6681 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
6682 if (pcwcWritten)
6683 *pcwcWritten = cwcToWrite;
6684 fRc = TRUE;
6685 }
6686 else
6687 {
6688 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
6689 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
6690 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
6691 }
6692 return fRc;
6693}
6694
6695#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
6696
6697
6698
6699/*
6700 *
6701 * Virtual memory leak prevension.
6702 * Virtual memory leak prevension.
6703 * Virtual memory leak prevension.
6704 *
6705 */
6706
6707/** Kernel32 - VirtualAlloc - for c1[xx].dll 78GB leaks. */
6708static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
6709{
6710 PVOID pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
6711 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
6712 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
6713 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6714 && pvMem)
6715 {
6716 PKWVIRTALLOC pTracker;
6717 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6718
6719 pTracker = g_Sandbox.pVirtualAllocHead;
6720 while ( pTracker
6721 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
6722 pTracker = pTracker->pNext;
6723 if (!pTracker)
6724 {
6725 DWORD dwErr = GetLastError();
6726 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
6727 if (pTracker)
6728 {
6729 pTracker->pvAlloc = pvMem;
6730 pTracker->cbAlloc = cb;
6731 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
6732 g_Sandbox.pVirtualAllocHead = pTracker;
6733 }
6734 SetLastError(dwErr);
6735 }
6736 }
6737 return pvMem;
6738}
6739
6740
6741/** Kernel32 - VirtualFree. */
6742static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
6743{
6744 BOOL fRc = VirtualFree(pvAddr, cb, dwFreeType);
6745 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
6746 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
6747 {
6748 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6749 if (dwFreeType & MEM_RELEASE)
6750 {
6751 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
6752 if (pTracker)
6753 {
6754 if (pTracker->pvAlloc == pvAddr)
6755 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
6756 else
6757 {
6758 PKWVIRTALLOC pPrev;
6759 do
6760 {
6761 pPrev = pTracker;
6762 pTracker = pTracker->pNext;
6763 } while (pTracker && pTracker->pvAlloc != pvAddr);
6764 if (pTracker)
6765 pPrev->pNext = pTracker->pNext;
6766 }
6767 if (pTracker)
6768 kHlpFree(pTracker);
6769 else
6770 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
6771 }
6772 }
6773 }
6774 return fRc;
6775}
6776
6777
6778/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
6779HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
6780{
6781 HANDLE hHeap;
6782 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6783
6784 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
6785 if (hHeap != NULL)
6786 {
6787 DWORD dwErr = GetLastError();
6788 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
6789 if (pTracker)
6790 {
6791 pTracker->hHeap = hHeap;
6792 pTracker->pNext = g_Sandbox.pHeapHead;
6793 g_Sandbox.pHeapHead = pTracker;
6794 }
6795
6796 SetLastError(dwErr);
6797 }
6798 return hHeap;
6799
6800}
6801
6802
6803/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
6804BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
6805{
6806 BOOL fRc = HeapDestroy(hHeap);
6807 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
6808 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6809 if (fRc)
6810 {
6811 PKWHEAP pTracker = g_Sandbox.pHeapHead;
6812 if (pTracker)
6813 {
6814 if (pTracker->hHeap == hHeap)
6815 g_Sandbox.pHeapHead = pTracker->pNext;
6816 else
6817 {
6818 PKWHEAP pPrev;
6819 do
6820 {
6821 pPrev = pTracker;
6822 pTracker = pTracker->pNext;
6823 } while (pTracker && pTracker->hHeap == hHeap);
6824 if (pTracker)
6825 pPrev->pNext = pTracker->pNext;
6826 }
6827 if (pTracker)
6828 kHlpFree(pTracker);
6829 else
6830 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
6831 }
6832 }
6833
6834 return fRc;
6835}
6836
6837
6838
6839/*
6840 *
6841 * Thread/Fiber local storage leak prevention.
6842 * Thread/Fiber local storage leak prevention.
6843 * Thread/Fiber local storage leak prevention.
6844 *
6845 * Note! The FlsAlloc/Free causes problems for statically linked VS2010
6846 * code like VBoxBs3ObjConverter.exe. One thing is that we're
6847 * leaking these indexes, but more importantely we crash during
6848 * worker exit since the callback is triggered multiple times.
6849 */
6850
6851
6852/** Kernel32 - FlsAlloc */
6853DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
6854{
6855 DWORD idxFls = FlsAlloc(pfnCallback);
6856 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
6857 if (idxFls != FLS_OUT_OF_INDEXES)
6858 {
6859 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
6860 if (pTracker)
6861 {
6862 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6863 pTracker->idx = idxFls;
6864 pTracker->pNext = g_Sandbox.pFlsAllocHead;
6865 g_Sandbox.pFlsAllocHead = pTracker;
6866 }
6867 }
6868
6869 return idxFls;
6870}
6871
6872/** Kernel32 - FlsFree */
6873BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
6874{
6875 BOOL fRc = FlsFree(idxFls);
6876 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
6877 if (fRc)
6878 {
6879 PKWLOCALSTORAGE pTracker;
6880 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6881
6882 pTracker = g_Sandbox.pFlsAllocHead;
6883 if (pTracker)
6884 {
6885 if (pTracker->idx == idxFls)
6886 g_Sandbox.pFlsAllocHead = pTracker->pNext;
6887 else
6888 {
6889 PKWLOCALSTORAGE pPrev;
6890 do
6891 {
6892 pPrev = pTracker;
6893 pTracker = pTracker->pNext;
6894 } while (pTracker && pTracker->idx != idxFls);
6895 if (pTracker)
6896 pPrev->pNext = pTracker->pNext;
6897 }
6898 if (pTracker)
6899 {
6900 pTracker->idx = FLS_OUT_OF_INDEXES;
6901 pTracker->pNext = NULL;
6902 kHlpFree(pTracker);
6903 }
6904 }
6905 }
6906 return fRc;
6907}
6908
6909
6910
6911/*
6912 *
6913 * Header file hashing.
6914 * Header file hashing.
6915 * Header file hashing.
6916 *
6917 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
6918 * indicated that ~12% of the time was spent doing MD5 caluclation when
6919 * rebuiling openssl. The hashing it done right after reading the source
6920 * via ReadFile, same buffers and sizes.
6921 */
6922
6923#ifdef WITH_HASH_MD5_CACHE
6924
6925/** Advapi32 - CryptCreateHash */
6926static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
6927 HCRYPTHASH *phHash)
6928{
6929 BOOL fRc;
6930
6931 /*
6932 * Only do this for cl.exe when it request normal MD5.
6933 */
6934 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
6935 {
6936 if (idAlg == CALG_MD5)
6937 {
6938 if (hKey == 0)
6939 {
6940 if (dwFlags == 0)
6941 {
6942 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
6943 if (pHash)
6944 {
6945 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6946 pHash->uMagic = KWHASHMD5_MAGIC;
6947 pHash->cbHashed = 0;
6948 pHash->fGoneBad = K_FALSE;
6949 pHash->fFallbackMode = K_FALSE;
6950 pHash->fFinal = K_FALSE;
6951
6952 /* link it. */
6953 pHash->pNext = g_Sandbox.pHashHead;
6954 g_Sandbox.pHashHead = pHash;
6955
6956 *phHash = (KUPTR)pHash;
6957 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
6958 hProv, *phHash, TRUE));
6959 return TRUE;
6960 }
6961
6962 kwErrPrintf("CryptCreateHash: out of memory!\n");
6963 }
6964 else
6965 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
6966 }
6967 else
6968 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
6969 }
6970 else
6971 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
6972 }
6973
6974 /*
6975 * Fallback.
6976 */
6977 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
6978 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
6979 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
6980 return fRc;
6981}
6982
6983
6984/** Advapi32 - CryptHashData */
6985static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
6986{
6987 BOOL fRc;
6988 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
6989 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6990 while (pHash && (KUPTR)pHash != hHash)
6991 pHash = pHash->pNext;
6992 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
6993 hHash, pHash, pbData, cbData, dwFlags));
6994 if (pHash)
6995 {
6996 /*
6997 * Validate the state.
6998 */
6999 if ( pHash->uMagic == KWHASHMD5_MAGIC
7000 && !pHash->fFinal)
7001 {
7002 if (!pHash->fFallbackMode)
7003 {
7004 /*
7005 * Does this match the previous ReadFile call to a cached file?
7006 * If it doesn't, try falling back.
7007 */
7008 if ( g_Sandbox.LastHashRead.cbRead == cbData
7009 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
7010 {
7011 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
7012 if ( pCachedFile
7013 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
7014 {
7015
7016 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
7017 {
7018 if ( pHash->pCachedFile == NULL
7019 && pHash->cbHashed == 0)
7020 pHash->pCachedFile = pCachedFile;
7021 if (pHash->pCachedFile == pCachedFile)
7022 {
7023 pHash->cbHashed += cbData;
7024 g_Sandbox.LastHashRead.pCachedFile = NULL;
7025 g_Sandbox.LastHashRead.pvRead = NULL;
7026 g_Sandbox.LastHashRead.cbRead = 0;
7027 g_Sandbox.LastHashRead.offRead = 0;
7028 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
7029 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
7030 return TRUE;
7031 }
7032
7033 /* Note! it's possible to fall back here too, if necessary. */
7034 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
7035 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
7036 }
7037 else
7038 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
7039 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
7040 }
7041 else if (!pCachedFile)
7042 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
7043 else
7044 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
7045 }
7046 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
7047 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
7048 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
7049 if (pHash->cbHashed == 0)
7050 pHash->fFallbackMode = K_TRUE;
7051 if (pHash->fFallbackMode)
7052 {
7053 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
7054 pHash->fFallbackMode = K_TRUE;
7055 MD5Init(&pHash->Md5Ctx);
7056 MD5Update(&pHash->Md5Ctx, pbData, cbData);
7057 pHash->cbHashed = cbData;
7058 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
7059 hHash, pbData, cbData, dwFlags));
7060 return TRUE;
7061 }
7062 pHash->fGoneBad = K_TRUE;
7063 SetLastError(ERROR_INVALID_PARAMETER);
7064 fRc = FALSE;
7065 }
7066 else
7067 {
7068 /* fallback. */
7069 MD5Update(&pHash->Md5Ctx, pbData, cbData);
7070 pHash->cbHashed += cbData;
7071 fRc = TRUE;
7072 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
7073 hHash, pbData, cbData, dwFlags));
7074 }
7075 }
7076 /*
7077 * Bad handle state.
7078 */
7079 else
7080 {
7081 if (pHash->uMagic != KWHASHMD5_MAGIC)
7082 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
7083 else
7084 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
7085 SetLastError(NTE_BAD_HASH);
7086 fRc = FALSE;
7087 }
7088 }
7089 else
7090 {
7091
7092 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
7093 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
7094 }
7095 return fRc;
7096}
7097
7098
7099/** Advapi32 - CryptGetHashParam */
7100static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
7101 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
7102{
7103 BOOL fRc;
7104 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
7105 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7106 while (pHash && (KUPTR)pHash != hHash)
7107 pHash = pHash->pNext;
7108 if (pHash)
7109 {
7110 if (pHash->uMagic == KWHASHMD5_MAGIC)
7111 {
7112 if (dwFlags == 0)
7113 {
7114 DWORD cbRet;
7115 void *pvRet;
7116 union
7117 {
7118 DWORD dw;
7119 } uBuf;
7120
7121 switch (dwParam)
7122 {
7123 case HP_HASHVAL:
7124 {
7125 /* Check the hash progress. */
7126 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
7127 if (pCachedFile)
7128 {
7129 if ( pCachedFile->cbCached == pHash->cbHashed
7130 && !pHash->fGoneBad)
7131 {
7132 if (pCachedFile->fValidMd5)
7133 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
7134 else
7135 {
7136 MD5Init(&pHash->Md5Ctx);
7137 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
7138 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
7139 pCachedFile->fValidMd5 = K_TRUE;
7140 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
7141 }
7142 pvRet = pCachedFile->abMd5Digest;
7143 }
7144 else
7145 {
7146 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
7147 from what I can tell, so just deal with it. */
7148 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
7149 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
7150 pHash, pCachedFile, pCachedFile->szPath));
7151 pHash->fFallbackMode = K_TRUE;
7152 pHash->pCachedFile = NULL;
7153 MD5Init(&pHash->Md5Ctx);
7154 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
7155 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
7156 pvRet = pHash->abDigest;
7157 }
7158 pHash->fFinal = K_TRUE;
7159 cbRet = 16;
7160 break;
7161 }
7162 else if (pHash->fFallbackMode)
7163 {
7164 if (!pHash->fFinal)
7165 {
7166 pHash->fFinal = K_TRUE;
7167 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
7168 }
7169 pvRet = pHash->abDigest;
7170 cbRet = 16;
7171 break;
7172 }
7173 else
7174 {
7175 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
7176 SetLastError(ERROR_INVALID_SERVER_STATE);
7177 }
7178 return FALSE;
7179 }
7180
7181 case HP_HASHSIZE:
7182 uBuf.dw = 16;
7183 pvRet = &uBuf;
7184 cbRet = sizeof(DWORD);
7185 break;
7186
7187 case HP_ALGID:
7188 uBuf.dw = CALG_MD5;
7189 pvRet = &uBuf;
7190 cbRet = sizeof(DWORD);
7191 break;
7192
7193 default:
7194 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
7195 SetLastError(NTE_BAD_TYPE);
7196 return FALSE;
7197 }
7198
7199 /*
7200 * Copy out cbRet from pvRet.
7201 */
7202 if (pbData)
7203 {
7204 if (*pcbData >= cbRet)
7205 {
7206 *pcbData = cbRet;
7207 kHlpMemCopy(pbData, pvRet, cbRet);
7208 if (cbRet == 4)
7209 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
7210 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
7211 else if (cbRet == 16)
7212 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",
7213 dwParam, pHash, pHash->pCachedFile, cbRet,
7214 pbData[0], pbData[1], pbData[2], pbData[3],
7215 pbData[4], pbData[5], pbData[6], pbData[7],
7216 pbData[8], pbData[9], pbData[10], pbData[11],
7217 pbData[12], pbData[13], pbData[14], pbData[15]));
7218 else
7219 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
7220 dwParam, pHash, pHash->pCachedFile, cbRet));
7221 return TRUE;
7222 }
7223
7224 kHlpMemCopy(pbData, pvRet, *pcbData);
7225 }
7226 SetLastError(ERROR_MORE_DATA);
7227 *pcbData = cbRet;
7228 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
7229 }
7230 else
7231 {
7232 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
7233 SetLastError(NTE_BAD_FLAGS);
7234 }
7235 }
7236 else
7237 {
7238 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
7239 SetLastError(NTE_BAD_HASH);
7240 }
7241 fRc = FALSE;
7242 }
7243 /*
7244 * Regular handle.
7245 */
7246 else
7247 {
7248 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
7249 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
7250 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
7251 }
7252
7253 return fRc;
7254}
7255
7256
7257/** Advapi32 - CryptDestroyHash */
7258static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
7259{
7260 BOOL fRc;
7261 PKWHASHMD5 pPrev = NULL;
7262 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
7263 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7264 while (pHash && (KUPTR)pHash != hHash)
7265 {
7266 pPrev = pHash;
7267 pHash = pHash->pNext;
7268 }
7269 if (pHash)
7270 {
7271 if (pHash->uMagic == KWHASHMD5_MAGIC)
7272 {
7273 pHash->uMagic = 0;
7274 if (!pPrev)
7275 g_Sandbox.pHashHead = pHash->pNext;
7276 else
7277 pPrev->pNext = pHash->pNext;
7278 kHlpFree(pHash);
7279 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
7280 fRc = TRUE;
7281 }
7282 else
7283 {
7284 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
7285 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
7286 SetLastError(ERROR_INVALID_HANDLE);
7287 fRc = FALSE;
7288 }
7289 }
7290 /*
7291 * Regular handle.
7292 */
7293 else
7294 {
7295 fRc = CryptDestroyHash(hHash);
7296 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
7297 }
7298 return fRc;
7299}
7300
7301#endif /* WITH_HASH_MD5_CACHE */
7302
7303
7304/*
7305 *
7306 * Misc function only intercepted while debugging.
7307 * Misc function only intercepted while debugging.
7308 * Misc function only intercepted while debugging.
7309 *
7310 */
7311
7312#ifndef NDEBUG
7313
7314/** CRT - memcpy */
7315static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
7316{
7317 KU8 const *pbSrc = (KU8 const *)pvSrc;
7318 KU8 *pbDst = (KU8 *)pvDst;
7319 KSIZE cbLeft = cb;
7320 while (cbLeft-- > 0)
7321 *pbDst++ = *pbSrc++;
7322 return pvDst;
7323}
7324
7325#endif /* NDEBUG */
7326
7327
7328
7329/**
7330 * Functions that needs replacing for sandboxed execution.
7331 */
7332KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
7333{
7334 /*
7335 * Kernel32.dll and friends.
7336 */
7337 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
7338 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
7339
7340 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
7341 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
7342 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
7343 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
7344 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
7345 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
7346 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
7347 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
7348 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
7349 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
7350 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
7351
7352 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
7353 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
7354 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
7355 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
7356
7357 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
7358
7359 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
7360 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
7361 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
7362 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
7363 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
7364 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
7365 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
7366 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
7367 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
7368 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
7369 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
7370
7371 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
7372 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
7373 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
7374 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
7375#ifdef WITH_TEMP_MEMORY_FILES
7376 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
7377 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
7378 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
7379 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
7380 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
7381 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
7382 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
7383 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
7384 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
7385#endif
7386 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
7387 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
7388 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
7389 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
7390 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
7391 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
7392 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
7393#ifdef WITH_TEMP_MEMORY_FILES
7394 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
7395#endif
7396
7397 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
7398 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
7399
7400 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
7401 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
7402
7403 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
7404 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
7405
7406 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc },
7407 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree },
7408
7409 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
7410
7411#ifdef WITH_HASH_MD5_CACHE
7412 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
7413 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
7414 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
7415 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
7416#endif
7417
7418 /*
7419 * MS Visual C++ CRTs.
7420 */
7421 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
7422 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
7423 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
7424 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
7425 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
7426 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
7427
7428 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
7429 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
7430 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
7431
7432 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
7433 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
7434
7435 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
7436 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
7437 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
7438 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
7439 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
7440 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
7441 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
7442 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
7443 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
7444 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
7445 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
7446 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
7447 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
7448 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
7449 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
7450 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
7451 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
7452 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
7453 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
7454 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
7455
7456 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
7457 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
7458 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
7459 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
7460 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
7461 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
7462 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
7463 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
7464 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
7465 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
7466 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
7467 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
7468 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
7469 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
7470
7471#ifndef NDEBUG
7472 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
7473#endif
7474};
7475/** Number of entries in g_aReplacements. */
7476KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
7477
7478
7479/**
7480 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
7481 * execution.
7482 */
7483KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
7484{
7485 /*
7486 * Kernel32.dll and friends.
7487 */
7488 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
7489 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
7490
7491#if 0
7492 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
7493#endif
7494
7495 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
7496 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
7497 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
7498 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
7499#ifdef WITH_TEMP_MEMORY_FILES
7500 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
7501 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
7502 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
7503 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
7504 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
7505 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
7506 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
7507 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
7508 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
7509#endif
7510 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
7511 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
7512 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
7513 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
7514 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
7515 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
7516 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
7517#ifdef WITH_TEMP_MEMORY_FILES
7518 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
7519#endif
7520 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
7521
7522 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
7523 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
7524
7525#ifdef WITH_HASH_MD5_CACHE
7526 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
7527 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
7528 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
7529 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
7530#endif
7531
7532 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
7533
7534
7535 /*
7536 * MS Visual C++ CRTs.
7537 */
7538 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
7539 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
7540 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
7541 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
7542 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
7543 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
7544
7545#if 0 /* used by mspdbXXX.dll */
7546 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
7547 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
7548#endif
7549};
7550/** Number of entries in g_aSandboxNativeReplacements. */
7551KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
7552
7553
7554/**
7555 * Functions that needs replacing when queried by GetProcAddress.
7556 */
7557KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
7558{
7559 /*
7560 * Kernel32.dll and friends.
7561 */
7562 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc },
7563 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree },
7564};
7565/** Number of entries in g_aSandboxGetProcReplacements. */
7566KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
7567
7568
7569/**
7570 * Control handler.
7571 *
7572 * @returns TRUE if handled, FALSE if not.
7573 * @param dwCtrlType The signal.
7574 */
7575static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
7576{
7577 switch (dwCtrlType)
7578 {
7579 case CTRL_C_EVENT:
7580 fprintf(stderr, "kWorker: Ctrl-C\n");
7581 exit(9);
7582 break;
7583
7584 case CTRL_BREAK_EVENT:
7585 fprintf(stderr, "kWorker: Ctrl-Break\n");
7586 exit(10);
7587 break;
7588
7589 case CTRL_CLOSE_EVENT:
7590 fprintf(stderr, "kWorker: console closed\n");
7591 exit(11);
7592 break;
7593
7594 case CTRL_LOGOFF_EVENT:
7595 fprintf(stderr, "kWorker: logoff event\n");
7596 exit(11);
7597 break;
7598
7599 case CTRL_SHUTDOWN_EVENT:
7600 fprintf(stderr, "kWorker: shutdown event\n");
7601 exit(11);
7602 break;
7603
7604 default:
7605 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
7606 break;
7607 }
7608 return TRUE;
7609}
7610
7611
7612/**
7613 * Used by kwSandboxExec to reset the state of the module tree.
7614 *
7615 * This is done recursively.
7616 *
7617 * @param pMod The root of the tree to consider.
7618 */
7619static void kwSandboxResetModuleState(PKWMODULE pMod)
7620{
7621 if ( !pMod->fNative
7622 && pMod->u.Manual.enmState != KWMODSTATE_NEEDS_BITS)
7623 {
7624 KSIZE iImp;
7625 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
7626 iImp = pMod->u.Manual.cImpMods;
7627 while (iImp-- > 0)
7628 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
7629 }
7630}
7631
7632static PPEB kwSandboxGetProcessEnvironmentBlock(void)
7633{
7634#if K_ARCH == K_ARCH_X86_32
7635 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
7636#elif K_ARCH == K_ARCH_AMD64
7637 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
7638#else
7639# error "Port me!"
7640#endif
7641}
7642
7643
7644#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
7645typedef struct _EXCEPTION_REGISTRATION_RECORD
7646{
7647 struct _EXCEPTION_REGISTRATION_RECORD * volatile PrevStructure;
7648 KU32 (__cdecl * volatile ExceptionHandler)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
7649 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
7650};
7651
7652/**
7653 * Vectored exception handler that emulates x86 chained exception handler.
7654 *
7655 * This is necessary because the RtlIsValidHandler check fails for self loaded
7656 * code and prevents cl.exe from working. (On AMD64 we can register function
7657 * tables, but on X86 cooking your own handling seems to be the only viabke
7658 * alternative.)
7659 *
7660 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
7661 * @param pXcptPtrs The exception details.
7662 */
7663static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
7664{
7665 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
7666 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
7667 if (g_Sandbox.fRunning)
7668 {
7669 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
7670 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
7671 struct _EXCEPTION_REGISTRATION_RECORD * volatile *ppRegRec = &pTib->ExceptionList;
7672 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = *ppRegRec;
7673 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
7674 {
7675#if 1
7676 /* This is a more robust version that isn't subject to calling
7677 convension cleanup disputes and such. */
7678 KU32 uSavedEdi;
7679 KU32 uSavedEsi;
7680 KU32 uSavedEbx;
7681 KU32 rcHandler;
7682 __asm
7683 {
7684 mov [uSavedEdi], edi
7685 mov [uSavedEsi], esi
7686 mov [uSavedEbx], ebx
7687 mov esi, esp
7688 mov edi, esp
7689 mov ecx, [pXcptRec]
7690 mov edx, [pRegRec]
7691 mov eax, [pXcptCtx]
7692 mov ebx, [ppRegRec]
7693 sub esp, 16
7694 and esp, 0fffffff0h
7695 mov [esp ], ecx
7696 mov [esp + 4], edx
7697 mov [esp + 8], eax
7698 mov [esp + 12], ebx
7699 call dword ptr [edx + 4]
7700 mov esp, esi
7701 cmp esp, edi
7702 je stack_ok
7703 int 3
7704 stack_ok:
7705 mov edi, [uSavedEdi]
7706 mov esi, [uSavedEsi]
7707 mov ebx, [uSavedEbx]
7708 mov [rcHandler], eax
7709 }
7710#else
7711 KU32 rcHandler = pRegRec->ExceptionHandler(pXcptPtrs->ExceptionRecord, pRegRec, pXcptPtrs->ContextRecord, ppRegRec);
7712#endif
7713 if (rcHandler == ExceptionContinueExecution)
7714 {
7715 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
7716 return EXCEPTION_CONTINUE_EXECUTION;
7717 }
7718 if (rcHandler == ExceptionContinueSearch)
7719 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
7720 else if (rcHandler == ExceptionNestedException)
7721 kHlpAssertMsgFailed(("Nested exceptions.\n"));
7722 else
7723 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
7724
7725 /*
7726 * Next.
7727 */
7728 ppRegRec = &pRegRec->PrevStructure;
7729 pRegRec = pRegRec->PrevStructure;
7730 }
7731 }
7732 return EXCEPTION_CONTINUE_SEARCH;
7733}
7734#endif /* WINDOWS + X86 */
7735
7736
7737/**
7738 * Enters the given handle into the handle table.
7739 *
7740 * @returns K_TRUE on success, K_FALSE on failure.
7741 * @param pSandbox The sandbox.
7742 * @param pHandle The handle.
7743 * @param hHandle The handle value to enter it under (for the
7744 * duplicate handle API).
7745 */
7746static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
7747{
7748 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
7749 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
7750
7751 /*
7752 * Grow handle table.
7753 */
7754 if (idxHandle >= pSandbox->cHandles)
7755 {
7756 void *pvNew;
7757 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
7758 while (cHandles <= idxHandle)
7759 cHandles *= 2;
7760 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
7761 if (!pvNew)
7762 {
7763 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
7764 return K_FALSE;
7765 }
7766 pSandbox->papHandles = (PKWHANDLE *)pvNew;
7767 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
7768 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
7769 pSandbox->cHandles = cHandles;
7770 }
7771
7772 /*
7773 * Check that the entry is unused then insert it.
7774 */
7775 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
7776 pSandbox->papHandles[idxHandle] = pHandle;
7777 pSandbox->cActiveHandles++;
7778 return K_TRUE;
7779}
7780
7781
7782/**
7783 * Creates a correctly quoted ANSI command line string from the given argv.
7784 *
7785 * @returns Pointer to the command line.
7786 * @param cArgs Number of arguments.
7787 * @param papszArgs The argument vector.
7788 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
7789 * @param pcbCmdLine Where to return the command line length,
7790 * including one terminator.
7791 */
7792static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
7793{
7794 KU32 i;
7795 KSIZE cbCmdLine;
7796 char *pszCmdLine;
7797
7798 /* Make a copy of the argument vector that we'll be quoting. */
7799 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
7800 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
7801
7802 /* Quote the arguments that need it. */
7803 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
7804
7805 /* figure out cmd line length. */
7806 cbCmdLine = 0;
7807 for (i = 0; i < cArgs; i++)
7808 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
7809 *pcbCmdLine = cbCmdLine;
7810
7811 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
7812 if (pszCmdLine)
7813 {
7814 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
7815 if (papszQuotedArgs[0] != papszArgs[0])
7816 free(papszQuotedArgs[0]);
7817
7818 for (i = 1; i < cArgs; i++)
7819 {
7820 *psz++ = ' ';
7821 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
7822 if (papszQuotedArgs[i] != papszArgs[i])
7823 free(papszQuotedArgs[i]);
7824 }
7825 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
7826
7827 *psz++ = '\0';
7828 *psz++ = '\0';
7829 }
7830
7831 return pszCmdLine;
7832}
7833
7834
7835
7836static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
7837 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
7838 KU32 cEnvVars, const char **papszEnvVars)
7839{
7840 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
7841 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
7842 wchar_t *pwcPool;
7843 KSIZE cbStrings;
7844 KSIZE cwc;
7845 KSIZE cbCmdLine;
7846 KU32 i;
7847
7848 /* Simple stuff. */
7849 pSandbox->rcExitCode = 256;
7850 pSandbox->pTool = pTool;
7851 pSandbox->idMainThread = GetCurrentThreadId();
7852 pSandbox->pgmptr = (char *)pTool->pszPath;
7853 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
7854#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
7855 if (pSandbox->StdOut.fIsConsole)
7856 pSandbox->StdOut.u.Con.cwcBuf = 0;
7857 else
7858 pSandbox->StdOut.u.Fully.cchBuf = 0;
7859 if (pSandbox->StdErr.fIsConsole)
7860 pSandbox->StdErr.u.Con.cwcBuf = 0;
7861 else
7862 pSandbox->StdErr.u.Fully.cchBuf = 0;
7863 pSandbox->Combined.cwcBuf = 0;
7864 pSandbox->Combined.cFlushes = 0;
7865#endif
7866 pSandbox->cArgs = cArgs;
7867 pSandbox->papszArgs = (char **)papszArgs;
7868 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
7869 if (!pSandbox->pszCmdLine)
7870 return KERR_NO_MEMORY;
7871
7872 /*
7873 * Convert command line and argv to UTF-16.
7874 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
7875 */
7876 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
7877 if (!pSandbox->papwszArgs)
7878 return KERR_NO_MEMORY;
7879 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
7880 for (i = 0; i < cArgs; i++)
7881 {
7882 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
7883 pSandbox->papwszArgs[i] = pwcPool;
7884 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
7885 pwcPool++;
7886 }
7887 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
7888 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
7889
7890 /*
7891 * Convert the commandline string to UTF-16, same pessimistic approach as above.
7892 */
7893 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
7894 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
7895 if (!pSandbox->pwszCmdLine)
7896 return KERR_NO_MEMORY;
7897 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
7898
7899 pSandbox->SavedCommandLine = pProcParams->CommandLine;
7900 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
7901 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
7902
7903 /*
7904 * Setup the environment.
7905 */
7906 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
7907 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
7908 {
7909 KU32 iDst = 0;
7910 for (i = 0; i < cEnvVars; i++)
7911 {
7912 const char *pszVar = papszEnvVars[i];
7913 KSIZE cchVar = kHlpStrLen(pszVar);
7914 if ( cchVar > 0
7915 && kHlpMemChr(pszVar, '=', cchVar) != NULL)
7916 {
7917 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
7918 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
7919 if (pszCopy && pwszCopy)
7920 {
7921 pSandbox->papszEnvVars[iDst] = pszCopy;
7922 pSandbox->environ[iDst] = pszCopy;
7923 pSandbox->papwszEnvVars[iDst] = pwszCopy;
7924 pSandbox->wenviron[iDst] = pwszCopy;
7925 iDst++;
7926 }
7927 else
7928 {
7929 kHlpFree(pszCopy);
7930 kHlpFree(pwszCopy);
7931 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
7932 }
7933 }
7934 else
7935 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
7936 }
7937 pSandbox->papszEnvVars[iDst] = NULL;
7938 pSandbox->environ[iDst] = NULL;
7939 pSandbox->papwszEnvVars[iDst] = NULL;
7940 pSandbox->wenviron[iDst] = NULL;
7941 }
7942 else
7943 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
7944
7945 /*
7946 * Invalidate the volatile parts of cache (kBuild output directory,
7947 * temporary directory, whatever).
7948 */
7949 kFsCacheInvalidateCustomBoth(g_pFsCache);
7950 return 0;
7951}
7952
7953
7954/**
7955 * Does sandbox cleanup between jobs.
7956 *
7957 * We postpone whatever isn't externally visible (i.e. files) and doesn't
7958 * influence the result, so that kmk can get on with things ASAP.
7959 *
7960 * @param pSandbox The sandbox.
7961 */
7962static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
7963{
7964 PROCESS_MEMORY_COUNTERS MemInfo;
7965 PKWVIRTALLOC pTracker;
7966 PKWHEAP pHeap;
7967 PKWLOCALSTORAGE pLocalStorage;
7968#ifdef WITH_HASH_MD5_CACHE
7969 PKWHASHMD5 pHash;
7970#endif
7971#ifdef WITH_TEMP_MEMORY_FILES
7972 PKWFSTEMPFILE pTempFile;
7973#endif
7974 PKWEXITCALLACK pExitCallback;
7975
7976 /*
7977 * First stuff that may cause code to run.
7978 */
7979
7980 /* Do exit callback first. */
7981 pExitCallback = g_Sandbox.pExitCallbackHead;
7982 g_Sandbox.pExitCallbackHead = NULL;
7983 while (pExitCallback)
7984 {
7985 PKWEXITCALLACK pNext = pExitCallback->pNext;
7986 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
7987 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
7988 __try
7989 {
7990 pExitCallback->pfnCallback();
7991 }
7992 __except (EXCEPTION_EXECUTE_HANDLER)
7993 {
7994 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
7995 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
7996 kHlpAssertFailed();
7997 }
7998 kHlpFree(pExitCallback);
7999 pExitCallback = pNext;
8000 }
8001
8002 /* Free left behind FlsAlloc leaks. */
8003 pLocalStorage = g_Sandbox.pFlsAllocHead;
8004 g_Sandbox.pFlsAllocHead = NULL;
8005 while (pLocalStorage)
8006 {
8007 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
8008 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
8009 FlsFree(pLocalStorage->idx);
8010 kHlpFree(pLocalStorage);
8011 pLocalStorage = pNext;
8012 }
8013
8014 /* Free left behind TlsAlloc leaks. */
8015 pLocalStorage = g_Sandbox.pTlsAllocHead;
8016 g_Sandbox.pTlsAllocHead = NULL;
8017 while (pLocalStorage)
8018 {
8019 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
8020 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
8021 TlsFree(pLocalStorage->idx);
8022 kHlpFree(pLocalStorage);
8023 pLocalStorage = pNext;
8024 }
8025
8026
8027 /*
8028 * Then free resources associated with the sandbox run.
8029 */
8030
8031 /* Open handles, except fixed handles (stdout and stderr). */
8032 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
8033 {
8034 KU32 idxHandle = pSandbox->cHandles;
8035 while (idxHandle-- > 0)
8036 if (pSandbox->papHandles[idxHandle] == NULL)
8037 { /* likely */ }
8038 else
8039 {
8040 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
8041 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8042 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
8043 {
8044 pSandbox->papHandles[idxHandle] = NULL;
8045 pSandbox->cLeakedHandles++;
8046
8047 switch (pHandle->enmType)
8048 {
8049 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8050 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
8051 idxHandle, pHandle->hHandle, pHandle->cRefs));
8052 break;
8053 case KWHANDLETYPE_OUTPUT_BUF:
8054 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
8055 idxHandle, pHandle->hHandle, pHandle->cRefs));
8056 break;
8057 case KWHANDLETYPE_TEMP_FILE:
8058 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
8059 idxHandle, pHandle->hHandle, pHandle->cRefs));
8060 pHandle->u.pTempFile->cActiveHandles--;
8061 break;
8062 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8063 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
8064 idxHandle, pHandle->hHandle, pHandle->cRefs));
8065 pHandle->u.pTempFile->cActiveHandles--;
8066 break;
8067 default:
8068 kHlpAssertFailed();
8069 }
8070 if (--pHandle->cRefs == 0)
8071 kHlpFree(pHandle);
8072 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
8073 break;
8074 }
8075 }
8076 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
8077 }
8078
8079#ifdef WITH_TEMP_MEMORY_FILES
8080 /* The temporary files aren't externally visible, they're all in memory. */
8081 pTempFile = pSandbox->pTempFileHead;
8082 pSandbox->pTempFileHead = NULL;
8083 while (pTempFile)
8084 {
8085 PKWFSTEMPFILE pNext = pTempFile->pNext;
8086 KU32 iSeg = pTempFile->cSegs;
8087 while (iSeg-- > 0)
8088 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8089 kHlpFree(pTempFile->paSegs);
8090 pTempFile->pNext = NULL;
8091 kHlpFree(pTempFile);
8092
8093 pTempFile = pNext;
8094 }
8095#endif
8096
8097 /* Free left behind HeapCreate leaks. */
8098 pHeap = g_Sandbox.pHeapHead;
8099 g_Sandbox.pHeapHead = NULL;
8100 while (pHeap != NULL)
8101 {
8102 PKWHEAP pNext = pHeap->pNext;
8103 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
8104 HeapDestroy(pHeap->hHeap);
8105 pHeap = pNext;
8106 }
8107
8108 /* Free left behind VirtualAlloc leaks. */
8109 pTracker = g_Sandbox.pVirtualAllocHead;
8110 g_Sandbox.pVirtualAllocHead = NULL;
8111 while (pTracker)
8112 {
8113 PKWVIRTALLOC pNext = pTracker->pNext;
8114 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
8115 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
8116 kHlpFree(pTracker);
8117 pTracker = pNext;
8118 }
8119
8120 /* Free the environment. */
8121 if (pSandbox->papszEnvVars)
8122 {
8123 KU32 i;
8124 for (i = 0; pSandbox->papszEnvVars[i]; i++)
8125 kHlpFree(pSandbox->papszEnvVars[i]);
8126 pSandbox->environ[0] = NULL;
8127 pSandbox->papszEnvVars[0] = NULL;
8128
8129 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
8130 kHlpFree(pSandbox->papwszEnvVars[i]);
8131 pSandbox->wenviron[0] = NULL;
8132 pSandbox->papwszEnvVars[0] = NULL;
8133 }
8134
8135#ifdef WITH_HASH_MD5_CACHE
8136 /*
8137 * Hash handles.
8138 */
8139 pHash = pSandbox->pHashHead;
8140 pSandbox->pHashHead = NULL;
8141 while (pHash)
8142 {
8143 PKWHASHMD5 pNext = pHash->pNext;
8144 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
8145 kHlpFree(pHash);
8146 pHash = pNext;
8147 }
8148#endif
8149
8150 /*
8151 * Check the memory usage. If it's getting high, trigger a respawn
8152 * after the next job.
8153 */
8154 MemInfo.WorkingSetSize = 0;
8155 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
8156 {
8157 /** @todo make the limit dynamic and user configurable. */
8158#if K_ARCH_BITS >= 64
8159 if (MemInfo.WorkingSetSize >= 512*1024*1024)
8160#else
8161 if (MemInfo.WorkingSetSize >= 384*1024*1024)
8162#endif
8163 {
8164 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
8165 g_fRestart = K_TRUE;
8166 }
8167 }
8168
8169 /*
8170 * The CRT has a max of 8192 handles, so we better restart after a while if
8171 * someone is leaking handles or we risk running out of descriptors.
8172 *
8173 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
8174 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
8175 */
8176 if (pSandbox->cLeakedHandles > 6000)
8177 {
8178 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
8179 g_fRestart = K_TRUE;
8180 }
8181}
8182
8183
8184/**
8185 * Does essential cleanups and restoring, anything externally visible.
8186 *
8187 * All cleanups that aren't externally visible are postponed till after we've
8188 * informed kmk of the result, so it can be done in the dead time between jobs.
8189 *
8190 * @param pSandbox The sandbox.
8191 */
8192static void kwSandboxCleanup(PKWSANDBOX pSandbox)
8193{
8194 /*
8195 * Restore the parent command line string.
8196 */
8197 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
8198 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
8199 pProcParams->CommandLine = pSandbox->SavedCommandLine;
8200 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
8201 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
8202}
8203
8204
8205static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
8206 KU32 cEnvVars, const char **papszEnvVars)
8207{
8208 int rcExit = 42;
8209 int rc;
8210
8211 /*
8212 * Initialize the sandbox environment.
8213 */
8214 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars);
8215 if (rc == 0)
8216 {
8217 /*
8218 * Do module initialization.
8219 */
8220 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
8221 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
8222 if (rc == 0)
8223 {
8224 /*
8225 * Call the main function.
8226 */
8227#if K_ARCH == K_ARCH_AMD64
8228 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
8229#elif K_ARCH == K_ARCH_X86_32
8230 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
8231#else
8232# error "Port me!"
8233#endif
8234
8235 /* Save the NT TIB first (should do that here, not in some other function). */
8236 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
8237 pSandbox->TibMainThread = *pTib;
8238
8239 /* Make the call in a guarded fashion. */
8240#if K_ARCH == K_ARCH_AMD64
8241 /* AMD64 */
8242 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
8243 __try
8244 {
8245 pSandbox->pOutXcptListHead = pTib->ExceptionList;
8246 if (setjmp(pSandbox->JmpBuf) == 0)
8247 {
8248 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
8249 pSandbox->fRunning = K_TRUE;
8250 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
8251 pSandbox->fRunning = K_FALSE;
8252 }
8253 else
8254 rcExit = pSandbox->rcExitCode;
8255 }
8256#elif K_ARCH == K_ARCH_X86_32
8257 /* x86 (see _tmainCRTStartup) */
8258 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
8259 __try
8260 {
8261 pSandbox->pOutXcptListHead = pTib->ExceptionList;
8262 if (setjmp(pSandbox->JmpBuf) == 0)
8263 {
8264 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
8265 pSandbox->fRunning = K_TRUE;
8266 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
8267 pSandbox->fRunning = K_FALSE;
8268 }
8269 else
8270 rcExit = pSandbox->rcExitCode;
8271 }
8272#endif
8273 __except (EXCEPTION_EXECUTE_HANDLER)
8274 {
8275 rcExit = 512;
8276 }
8277 pSandbox->fRunning = K_FALSE;
8278
8279 /* Now, restore the NT TIB. */
8280 *pTib = pSandbox->TibMainThread;
8281 }
8282 else
8283 rcExit = 42 + 4;
8284
8285 /*
8286 * Flush and clean up the essential bits only, postpone whatever we
8287 * can till after we've replied to kmk.
8288 */
8289#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8290 kwSandboxConsoleFlushAll(&g_Sandbox);
8291#endif
8292 kwSandboxCleanup(&g_Sandbox);
8293 }
8294 else
8295 rcExit = 42 + 3;
8296
8297 return rcExit;
8298}
8299
8300
8301/**
8302 * Does the post command part of a job (optional).
8303 *
8304 * @returns The exit code of the job.
8305 * @param cPostCmdArgs Number of post command arguments (includes cmd).
8306 * @param papszPostCmdArgs The post command and its argument.
8307 */
8308static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
8309{
8310 const char *pszCmd = papszPostCmdArgs[0];
8311
8312 /* Allow the kmk builtin prefix. */
8313 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
8314 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
8315 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
8316
8317 /* Command switch. */
8318 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
8319 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL);
8320
8321 return kwErrPrintfRc(42 + 5 , "Unknown post command: '%s'\n", pszCmd);
8322}
8323
8324
8325/**
8326 * Part 2 of the "JOB" command handler.
8327 *
8328 * @returns The exit code of the job.
8329 * @param pszExecutable The executable to execute.
8330 * @param pszCwd The current working directory of the job.
8331 * @param cArgs The number of arguments.
8332 * @param papszArgs The argument vector.
8333 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
8334 * @param cEnvVars The number of environment variables.
8335 * @param papszEnvVars The environment vector.
8336 * @param cPostCmdArgs Number of post command arguments (includes cmd).
8337 * @param papszPostCmdArgs The post command and its argument.
8338 */
8339static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
8340 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
8341 KU32 cEnvVars, const char **papszEnvVars,
8342 KU32 cPostCmdArgs, const char **papszPostCmdArgs)
8343{
8344 int rcExit;
8345 PKWTOOL pTool;
8346
8347 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
8348 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
8349#ifdef KW_LOG_ENABLED
8350 {
8351 KU32 i;
8352 for (i = 0; i < cArgs; i++)
8353 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
8354 for (i = 0; i < cPostCmdArgs; i++)
8355 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
8356 }
8357#endif
8358
8359 /*
8360 * Lookup the tool.
8361 */
8362 pTool = kwToolLookup(pszExecutable);
8363 if (pTool)
8364 {
8365 /*
8366 * Change the directory if we're going to execute the job inside
8367 * this process. Then invoke the tool type specific handler.
8368 */
8369 switch (pTool->enmType)
8370 {
8371 case KWTOOLTYPE_SANDBOXED:
8372 case KWTOOLTYPE_WATCOM:
8373 {
8374 /* Change dir. */
8375 KFSLOOKUPERROR enmError;
8376 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
8377 if ( pNewCurDir == g_pCurDirObj
8378 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
8379 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
8380 else if (SetCurrentDirectoryA(pszCwd))
8381 {
8382 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
8383 g_pCurDirObj = pNewCurDir;
8384 }
8385 else
8386 {
8387 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
8388 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
8389 rcExit = 42 + 1;
8390 break;
8391 }
8392
8393 /* Call specific handler. */
8394 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
8395 {
8396 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
8397 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars);
8398 }
8399 else
8400 {
8401 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
8402 rcExit = 42 + 2;
8403 }
8404 break;
8405 }
8406
8407 case KWTOOLTYPE_EXEC:
8408 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
8409 rcExit = 42 + 2;
8410 break;
8411
8412 default:
8413 kHlpAssertFailed();
8414 kwErrPrintf("Internal tool type corruption!!\n");
8415 rcExit = 42 + 2;
8416 g_fRestart = K_TRUE;
8417 break;
8418 }
8419
8420 /*
8421 * Do the post command, if present.
8422 */
8423 if (cPostCmdArgs && rcExit == 0)
8424 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
8425 }
8426 else
8427 rcExit = 42 + 1;
8428 return rcExit;
8429}
8430
8431
8432/**
8433 * Handles a "JOB" command.
8434 *
8435 * @returns The exit code of the job.
8436 * @param pszMsg Points to the "JOB" command part of the message.
8437 * @param cbMsg Number of message bytes at @a pszMsg. There are
8438 * 4 more zero bytes after the message body to
8439 * simplify parsing.
8440 */
8441static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
8442{
8443 int rcExit = 42;
8444
8445 /*
8446 * Unpack the message.
8447 */
8448 const char *pszExecutable;
8449 KSIZE cbTmp;
8450
8451 pszMsg += sizeof("JOB");
8452 cbMsg -= sizeof("JOB");
8453
8454 /* Executable name. */
8455 pszExecutable = pszMsg;
8456 cbTmp = kHlpStrLen(pszMsg) + 1;
8457 pszMsg += cbTmp;
8458 if ( cbTmp < cbMsg
8459 && cbTmp > 2)
8460 {
8461 const char *pszCwd;
8462 cbMsg -= cbTmp;
8463
8464 /* Current working directory. */
8465 pszCwd = pszMsg;
8466 cbTmp = kHlpStrLen(pszMsg) + 1;
8467 pszMsg += cbTmp;
8468 if ( cbTmp + sizeof(KU32) < cbMsg
8469 && cbTmp >= 2)
8470 {
8471 KU32 cArgs;
8472 cbMsg -= cbTmp;
8473
8474 /* Argument count. */
8475 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
8476 pszMsg += sizeof(cArgs);
8477 cbMsg -= sizeof(cArgs);
8478
8479 if (cArgs > 0 && cArgs < 4096)
8480 {
8481 /* The argument vector. */
8482 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
8483 if (papszArgs)
8484 {
8485 KU32 i;
8486 for (i = 0; i < cArgs; i++)
8487 {
8488 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
8489 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
8490 pszMsg += cbTmp;
8491 if (cbTmp < cbMsg)
8492 cbMsg -= cbTmp;
8493 else
8494 {
8495 cbMsg = 0;
8496 break;
8497 }
8498
8499 }
8500 papszArgs[cArgs] = 0;
8501
8502 /* Environment variable count. */
8503 if (cbMsg > sizeof(KU32))
8504 {
8505 KU32 cEnvVars;
8506 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
8507 pszMsg += sizeof(cEnvVars);
8508 cbMsg -= sizeof(cEnvVars);
8509
8510 if (cEnvVars >= 0 && cEnvVars < 4096)
8511 {
8512 /* The argument vector. */
8513 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
8514 if (papszEnvVars)
8515 {
8516 for (i = 0; i < cEnvVars; i++)
8517 {
8518 papszEnvVars[i] = pszMsg;
8519 cbTmp = kHlpStrLen(pszMsg) + 1;
8520 pszMsg += cbTmp;
8521 if (cbTmp < cbMsg)
8522 cbMsg -= cbTmp;
8523 else
8524 {
8525 cbMsg = 0;
8526 break;
8527 }
8528 }
8529 papszEnvVars[cEnvVars] = 0;
8530
8531 /* Flags (currently just watcom argument brain damanage). */
8532 if (cbMsg >= sizeof(KU8))
8533 {
8534 KBOOL fWatcomBrainDamange = *pszMsg++;
8535 cbMsg--;
8536
8537 /* Post command argument count (can be zero). */
8538 if (cbMsg >= sizeof(KU32))
8539 {
8540 KU32 cPostCmdArgs;
8541 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
8542 pszMsg += sizeof(cPostCmdArgs);
8543 cbMsg -= sizeof(cPostCmdArgs);
8544
8545 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
8546 {
8547 char const *apszPostCmdArgs[32+1];
8548 for (i = 0; i < cPostCmdArgs; i++)
8549 {
8550 apszPostCmdArgs[i] = pszMsg;
8551 cbTmp = kHlpStrLen(pszMsg) + 1;
8552 pszMsg += cbTmp;
8553 if ( cbTmp < cbMsg
8554 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
8555 cbMsg -= cbTmp;
8556 else
8557 {
8558 cbMsg = KSIZE_MAX;
8559 break;
8560 }
8561 }
8562 if (cbMsg == 0)
8563 {
8564 apszPostCmdArgs[cPostCmdArgs] = NULL;
8565
8566 /*
8567 * The next step.
8568 */
8569 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
8570 cArgs, papszArgs, fWatcomBrainDamange,
8571 cEnvVars, papszEnvVars,
8572 cPostCmdArgs, apszPostCmdArgs);
8573 }
8574 else if (cbMsg == KSIZE_MAX)
8575 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
8576 else
8577 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
8578 }
8579 else
8580 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
8581 }
8582 else
8583 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
8584 }
8585 else
8586 kwErrPrintf("Detected bogus message unpacking environment variables!\n");
8587 kHlpFree((void *)papszEnvVars);
8588 }
8589 else
8590 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
8591 }
8592 else
8593 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
8594 }
8595 else
8596 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
8597 kHlpFree((void *)papszArgs);
8598 }
8599 else
8600 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
8601 }
8602 else
8603 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
8604 }
8605 else
8606 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
8607 }
8608 else
8609 kwErrPrintf("Detected bogus message unpacking executable path!\n");
8610 return rcExit;
8611}
8612
8613
8614/**
8615 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
8616 *
8617 * @retval 0 on success.
8618 * @retval -1 on error (fully bitched).
8619 *
8620 * @param hPipe The pipe handle.
8621 * @param pvBuf The buffer to write out out.
8622 * @param cbToWrite The number of bytes to write.
8623 */
8624static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
8625{
8626 KU8 const *pbBuf = (KU8 const *)pvBuf;
8627 KU32 cbLeft = cbToWrite;
8628 for (;;)
8629 {
8630 DWORD cbActuallyWritten = 0;
8631 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
8632 {
8633 cbLeft -= cbActuallyWritten;
8634 if (!cbLeft)
8635 return 0;
8636 pbBuf += cbActuallyWritten;
8637 }
8638 else
8639 {
8640 DWORD dwErr = GetLastError();
8641 if (cbLeft == cbToWrite)
8642 kwErrPrintf("WriteFile failed: %u\n", dwErr);
8643 else
8644 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
8645 return -1;
8646 }
8647 }
8648}
8649
8650
8651/**
8652 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
8653 *
8654 * @retval 0 on success.
8655 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
8656 * @retval -1 on error (fully bitched).
8657 * @param hPipe The pipe handle.
8658 * @param pvBuf The buffer to read into.
8659 * @param cbToRead The number of bytes to read.
8660 * @param fShutdownOkay Whether connection shutdown while reading the
8661 * first byte is okay or not.
8662 */
8663static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
8664{
8665 KU8 *pbBuf = (KU8 *)pvBuf;
8666 KU32 cbLeft = cbToRead;
8667 for (;;)
8668 {
8669 DWORD cbActuallyRead = 0;
8670 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
8671 {
8672 cbLeft -= cbActuallyRead;
8673 if (!cbLeft)
8674 return 0;
8675 pbBuf += cbActuallyRead;
8676 }
8677 else
8678 {
8679 DWORD dwErr = GetLastError();
8680 if (cbLeft == cbToRead)
8681 {
8682 if ( fMayShutdown
8683 && dwErr == ERROR_BROKEN_PIPE)
8684 return 1;
8685 kwErrPrintf("ReadFile failed: %u\n", dwErr);
8686 }
8687 else
8688 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
8689 return -1;
8690 }
8691 }
8692}
8693
8694
8695/**
8696 * Handles what comes after --test.
8697 *
8698 * @returns Exit code.
8699 * @param argc Number of arguments after --test.
8700 * @param argv Arguments after --test.
8701 */
8702static int kwTestRun(int argc, char **argv)
8703{
8704 int i;
8705 int j;
8706 int rcExit;
8707 int cRepeats;
8708 char szCwd[MAX_PATH];
8709 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
8710 KU32 cEnvVars;
8711 KBOOL fWatcomBrainDamange = K_FALSE;
8712
8713 /*
8714 * Parse arguments.
8715 */
8716 /* Repeat count. */
8717 i = 0;
8718 if (i >= argc)
8719 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
8720 if (strcmp(argv[i], "--") != 0)
8721 {
8722 cRepeats = atoi(argv[i]);
8723 if (cRepeats <= 0)
8724 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
8725 i++;
8726
8727 /* Optional directory change. */
8728 if ( i < argc
8729 && ( strcmp(argv[i], "--chdir") == 0
8730 || strcmp(argv[i], "-C") == 0 ) )
8731 {
8732 i++;
8733 if (i >= argc)
8734 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
8735 pszCwd = argv[i++];
8736 }
8737
8738 /* Optional watcom flag directory change. */
8739 if ( i < argc
8740 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
8741 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
8742 {
8743 fWatcomBrainDamange = K_TRUE;
8744 i++;
8745 }
8746
8747 /* Trigger breakpoint */
8748 if ( i < argc
8749 && strcmp(argv[i], "--breakpoint") == 0)
8750 {
8751 __debugbreak();
8752 i++;
8753 }
8754
8755 /* Check for '--'. */
8756 if (i >= argc)
8757 return kwErrPrintfRc(2, "Missing '--'\n");
8758 if (strcmp(argv[i], "--") != 0)
8759 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
8760 i++;
8761 }
8762 else
8763 {
8764 cRepeats = 1;
8765 i++;
8766 }
8767 if (i >= argc)
8768 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
8769
8770 /*
8771 * Do the job.
8772 */
8773 cEnvVars = 0;
8774 while (environ[cEnvVars] != NULL)
8775 cEnvVars++;
8776
8777 for (j = 0; j < cRepeats; j++)
8778 {
8779 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
8780 argc - i, &argv[i], fWatcomBrainDamange,
8781 cEnvVars, environ,
8782 0, NULL);
8783 KW_LOG(("rcExit=%d\n", rcExit));
8784 kwSandboxCleanupLate(&g_Sandbox);
8785 }
8786
8787# ifdef WITH_LOG_FILE
8788 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
8789 CloseHandle(g_hLogFile);
8790# endif
8791 return rcExit;
8792}
8793
8794
8795int main(int argc, char **argv)
8796{
8797 KSIZE cbMsgBuf = 0;
8798 KU8 *pbMsgBuf = NULL;
8799 int i;
8800 HANDLE hPipe = INVALID_HANDLE_VALUE;
8801 const char *pszTmp;
8802 KFSLOOKUPERROR enmIgnored;
8803#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
8804 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/, kwSandboxVecXcptEmulateChained);
8805#endif
8806#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8807 HANDLE hCurProc = GetCurrentProcess();
8808 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
8809 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
8810 DWORD dwType;
8811#endif
8812
8813 /*
8814 * Register our Control-C and Control-Break handlers.
8815 */
8816 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
8817 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
8818
8819 /*
8820 * Create the cache and mark the temporary directory as using the custom revision.
8821 */
8822 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
8823 if (!g_pFsCache)
8824 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
8825
8826 pszTmp = getenv("TEMP");
8827 if (pszTmp && *pszTmp != '\0')
8828 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
8829 pszTmp = getenv("TMP");
8830 if (pszTmp && *pszTmp != '\0')
8831 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
8832 pszTmp = getenv("TMPDIR");
8833 if (pszTmp && *pszTmp != '\0')
8834 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
8835
8836 /*
8837 * Make g_abDefLdBuf executable.
8838 */
8839 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
8840 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
8841 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
8842
8843#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8844 /*
8845 * Get and duplicate the console handles.
8846 */
8847 /* Standard output. */
8848 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
8849 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
8850 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
8851 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
8852 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
8853 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
8854 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
8855 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
8856 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
8857 g_Sandbox.HandleStdOut.cRefs = 0x10001;
8858 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
8859 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
8860 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
8861 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
8862 {
8863 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
8864 g_Sandbox.cFixedHandles++;
8865 else
8866 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
8867 }
8868 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
8869 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
8870
8871 /* Standard error. */
8872 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
8873 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
8874 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
8875 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
8876 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
8877 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
8878 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
8879 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
8880 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
8881 g_Sandbox.HandleStdErr.cRefs = 0x10001;
8882 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
8883 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
8884 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
8885 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
8886 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
8887 {
8888 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
8889 g_Sandbox.cFixedHandles++;
8890 else
8891 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
8892 }
8893 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
8894 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
8895
8896 /* Combined console buffer. */
8897 if (g_Sandbox.StdErr.fIsConsole)
8898 {
8899 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
8900 g_Sandbox.Combined.uCodepage = GetConsoleCP();
8901 }
8902 else if (g_Sandbox.StdOut.fIsConsole)
8903 {
8904 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
8905 g_Sandbox.Combined.uCodepage = GetConsoleCP();
8906 }
8907 else
8908 {
8909 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
8910 g_Sandbox.Combined.uCodepage = CP_ACP;
8911 }
8912 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
8913#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8914
8915
8916 /*
8917 * Parse arguments.
8918 */
8919 for (i = 1; i < argc; i++)
8920 {
8921 if (strcmp(argv[i], "--pipe") == 0)
8922 {
8923 i++;
8924 if (i < argc)
8925 {
8926 char *pszEnd = NULL;
8927 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
8928 if ( *argv[i]
8929 && pszEnd != NULL
8930 && *pszEnd == '\0'
8931 && u64Value != 0
8932 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
8933 && (uintptr_t)u64Value == u64Value)
8934 hPipe = (HANDLE)(uintptr_t)u64Value;
8935 else
8936 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
8937 }
8938 else
8939 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
8940 }
8941 else if (strcmp(argv[i], "--volatile") == 0)
8942 {
8943 i++;
8944 if (i < argc)
8945 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
8946 else
8947 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
8948 }
8949 else if (strcmp(argv[i], "--test") == 0)
8950 return kwTestRun(argc - i - 1, &argv[i + 1]);
8951 else if ( strcmp(argv[i], "--help") == 0
8952 || strcmp(argv[i], "-h") == 0
8953 || strcmp(argv[i], "-?") == 0)
8954 {
8955 printf("usage: kWorker [--volatile dir] --pipe <pipe-handle>\n"
8956 "usage: kWorker <--help|-h>\n"
8957 "usage: kWorker <--version|-V>\n"
8958 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
8959 "\n"
8960 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
8961 return 0;
8962 }
8963 else if ( strcmp(argv[i], "--version") == 0
8964 || strcmp(argv[i], "-V") == 0)
8965 return kbuild_version(argv[0]);
8966 else
8967 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
8968 }
8969
8970 if (hPipe == INVALID_HANDLE_VALUE)
8971 return kwErrPrintfRc(2, "Missing --pipe <pipe-handle> argument!\n");
8972
8973 /*
8974 * Serve the pipe.
8975 */
8976 for (;;)
8977 {
8978 KU32 cbMsg = 0;
8979 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
8980 if (rc == 0)
8981 {
8982 /* Make sure the message length is within sane bounds. */
8983 if ( cbMsg > 4
8984 && cbMsg <= 256*1024*1024)
8985 {
8986 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
8987 if (cbMsg + 4 <= cbMsgBuf)
8988 { /* likely */ }
8989 else
8990 {
8991 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
8992 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
8993 if (!pbMsgBuf)
8994 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
8995 }
8996
8997 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
8998 *(KU32 *)pbMsgBuf = cbMsg;
8999 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
9000 if (rc == 0)
9001 {
9002 const char *psz;
9003
9004 pbMsgBuf[cbMsg] = '\0';
9005 pbMsgBuf[cbMsg + 1] = '\0';
9006 pbMsgBuf[cbMsg + 2] = '\0';
9007 pbMsgBuf[cbMsg + 3] = '\0';
9008
9009 /* The first string after the header is the command. */
9010 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
9011 if (strcmp(psz, "JOB") == 0)
9012 {
9013 struct
9014 {
9015 KI32 rcExitCode;
9016 KU8 bExiting;
9017 KU8 abZero[3];
9018 } Reply;
9019 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
9020 Reply.bExiting = g_fRestart;
9021 Reply.abZero[0] = 0;
9022 Reply.abZero[1] = 0;
9023 Reply.abZero[2] = 0;
9024 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
9025 if ( rc == 0
9026 && !g_fRestart)
9027 {
9028 kwSandboxCleanupLate(&g_Sandbox);
9029 continue;
9030 }
9031 }
9032 else
9033 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
9034 }
9035 }
9036 else
9037 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
9038 }
9039
9040 /*
9041 * If we're exitting because we're restarting, we need to delay till
9042 * kmk/kSubmit has read the result. Windows documentation says it
9043 * immediately discards pipe buffers once the pipe is broken by the
9044 * server (us). So, We flush the buffer and queues a 1 byte read
9045 * waiting for kSubmit to close the pipe when it receives the
9046 * bExiting = K_TRUE result.
9047 */
9048 if (g_fRestart)
9049 {
9050 KU8 b;
9051 FlushFileBuffers(hPipe);
9052 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
9053 }
9054
9055 CloseHandle(hPipe);
9056#ifdef WITH_LOG_FILE
9057 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
9058 CloseHandle(g_hLogFile);
9059#endif
9060 return rc > 0 ? 0 : 1;
9061 }
9062}
9063
9064
9065/** @page pg_kWorker kSubmit / kWorker
9066 *
9067 * @section sec_kWorker_Motivation Motivation / Inspiration
9068 *
9069 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
9070 * builds on machines "infested" by Anti Virus protection and disk encryption
9071 * software. Build times jumping from 35-40 min to 77-82 min after the machine
9072 * got "infected".
9073 *
9074 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
9075 * mainly a bunch of tiny assembly and C files being compiler a million times.
9076 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
9077 * own toolchain from within the same process, saving a lot of process creation
9078 * and teardown overhead.
9079 *
9080 *
9081 * @section sec_kWorker_kSubmit About kSubmit
9082 *
9083 * When wanting to execute a job in a kWorker instance, it must be submitted
9084 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
9085 * built into kmk and does not exist as an external program. The reason for
9086 * this is that it keep track of the kWorker instances.
9087 *
9088 * The kSubmit command has the --32-bit and --64-bit options for selecting
9089 * between 32-bit and 64-bit worker instance. We generally assume the user of
9090 * the command knows which bit count the executable has, so kSubmit is spared
9091 * the extra work of finding out.
9092 *
9093 * The kSubmit command shares a environment and current directory manipulation
9094 * with the kRedirect command, but not the file redirection. So long no file
9095 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
9096 * hand for tools like OpenWatcom, NASM and YASM which all require environment
9097 * and/or current directory changes to work.
9098 *
9099 * Unlike the kRedirect command, the kSubmit command can also specify an
9100 * internall post command to be executed after the main command succeeds.
9101 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
9102 * information from Microsoft COFF object files and Watcom OMF object files and
9103 * is scheduled to replace kDepIDB.
9104 *
9105 *
9106 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
9107 *
9108 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
9109 * A job request is written by kSubmit and kWorker read, unpacks it and executes
9110 * it. When the job is completed, kWorker writes a short reply with the exit
9111 * code and an internal status indicating whether it is going to restart.
9112 *
9113 * The kWorker intance will reply to kSubmit before completing all the internal
9114 * cleanup work, so as not to delay the next job execution unnecessarily. This
9115 * includes checking its own memory consumption and checking whether it needs
9116 * restarting. So, a decision to restart unfortunately have to wait till after
9117 * the next job has completed. This is a little bit unfortunate if the next job
9118 * requires a lot of memory and kWorker has already leaked/used a lot.
9119 *
9120 *
9121 * @section sec_kWorker_How_Works How kWorker Works
9122 *
9123 * kWorker will load the executable specified by kSubmit into memory and call
9124 * it's entrypoint in a lightly sandbox'ed environment.
9125 *
9126 *
9127 * @subsection ssec_kWorker_Loaing Image loading
9128 *
9129 * kWorker will manually load all the executable images into memory, fix them
9130 * up, and make a copy of the virgin image so it can be restored using memcpy
9131 * the next time it is used.
9132 *
9133 * Imported functions are monitored and replacements used for a few of them.
9134 * These replacements are serve the following purposes:
9135 * - Provide a different command line.
9136 * - Provide a different environment.
9137 * - Intercept process termination.
9138 * - Intercept thread creation (only linker is allowed to create threads).
9139 * - Intercept file reading for caching (header files, ++) as file system
9140 * access is made even slower by anti-virus software.
9141 * - Intercept crypto hash APIs to cache MD5 digests of header files
9142 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
9143 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
9144 * in memory as writing files grows expensive with encryption and
9145 * anti-virus software active.
9146 * - Intercept some file system queries to use the kFsCache instead of
9147 * going to the kernel and slowly worm thru the AV filter driver.
9148 * - Intercept standard output/error and console writes to aggressivly
9149 * buffer the output. The MS CRT does not buffer either when it goes to
9150 * the console, resulting in terrible performance and mixing up output
9151 * with other compile jobs.
9152 * This also allows us to filter out the annoying source file announcements
9153 * by cl.exe.
9154 * - Intercept VirtualAlloc and VirtualFree to prevent
9155 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
9156 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
9157 * the callbacks run after each job.
9158 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
9159 * executables and tools using custom heaps (like the microsoft linker).
9160 * [exectuable images only]
9161 * - Intercept atexit and _onexit registration to be able run them after
9162 * each job instead of crashing as kWorker exits. This also helps avoid
9163 * some leaks. [executable image only]
9164 *
9165 * DLLs falls into two categories, system DLLs which we always load using the
9166 * native loader, and tool DLLs which can be handled like the executable or
9167 * optionally using the native loader. We maintain a hardcoded white listing of
9168 * tool DLLs we trust to load using the native loader.
9169 *
9170 * Imports of natively loaded DLLs are processed too, but we only replace a
9171 * subset of the functions compared to natively loaded excutable and DLL images.
9172 *
9173 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
9174 * This is to speed up job execution.
9175 *
9176 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
9177 * for each job run, but so far this hasn't been necessary.
9178 *
9179 *
9180 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
9181 *
9182 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
9183 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
9184 * intermediate representation between the first (c1/c1xx.dll) and second pass
9185 * (c2.dll).
9186 *
9187 * kWorker helps the compiler as best as it can. Given a little knowledge about
9188 * stable and volatile file system areas, it can do a lot of caching that a
9189 * normal compiler driver cannot easily do when given a single file.
9190 *
9191 *
9192 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
9193 *
9194 * The preprocessor part will open and process header files exactly as they are
9195 * encountered in the source files. If string.h is included by the main source
9196 * and five other header files, it will be searched for (include path), opened,
9197 * read, MD5-summed, and pre-processed six times. The last five times is just a
9198 * waste of time because of the guards or \#pragma once. A smart compiler would
9199 * make a little extra effort and realize this.
9200 *
9201 * kWorker will cache help the preprocessor by remembering places where the
9202 * header was not found with help of kFsCache, and cache the file in memory when
9203 * found. The first part is taken care of by intercepting GetFileAttributesW,
9204 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
9205 * cached, the file is kept open and the CreateFileW call returns a duplicate of
9206 * that handle. An internal handle table is used by ReadFile and CloseFile to
9207 * keep track of intercepted handles (also used for temporary file, temporary
9208 * file mappings, console buffering, and standard out/err buffering).
9209 *
9210 * PS. The header search optimization also comes in handy when cl.exe goes on
9211 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
9212 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
9213 * optionally compile the three pass DLLs as executables during development
9214 * and problem analysis.
9215 *
9216 *
9217 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
9218 *
9219 * The issues of the temporary files is pretty severe on the Dell machine used
9220 * for benchmarking with full AV and encryption. The synthetic benchmark
9221 * improved by 30% when kWorker implemented measures to keep them entirely in
9222 * memory.
9223 *
9224 * kWorker implement these by recognizing the filename pattern in CreateFileW
9225 * and creating/opening the given file as needed. The handle returned is a
9226 * duplicate of the current process, thus giving us a good chance of catching
9227 * API calls we're not intercepting.
9228 *
9229 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
9230 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
9231 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
9232 * UnmapViewOfFile.
9233 *
9234 *
9235 * @section sec_kWorker_Numbers Some measurements.
9236 *
9237 * - r2881 building src/VBox/Runtime:
9238 * - without: 2m01.016388s = 120.016388 s
9239 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
9240 * - r2884 building vbox/debug (r110512):
9241 * - without: 11m14.446609s = 674.446609 s
9242 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
9243 * - r2896 building vbox/debug (r110577):
9244 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
9245 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
9246 * MS Defender as AV):
9247 * - without: 10m24.990389s = 624.990389s
9248 * - with: 08m04.738184s = 484.738184s
9249 * - delta: 624.99s - 484.74s = 140.25s
9250 * - saved: 140.25/624.99 = 22% faster
9251 *
9252 *
9253 * @subsection subsec_kWorker_Early_Numbers Early Experiments
9254 *
9255 * These are some early experiments doing 1024 compilations of
9256 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
9257 * main function:
9258 *
9259 * Skylake (W10/amd64, only stdandard MS defender):
9260 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
9261 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
9262 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
9263 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
9264 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
9265 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
9266 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
9267 *
9268 * Dell (W7/amd64, infected by mcafee):
9269 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
9270 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
9271 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
9272 *
9273 * The command line:
9274 * @code{.cpp}
9275 "\"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"
9276 * @endcode
9277 */
9278
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