VirtualBox

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

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

kWorker: Some cleanups

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 332.3 KB
Line 
1/* $Id: kWorker.c 2934 2016-09-18 16:47:16Z 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 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
4508 *
4509 * @returns K_TRUE if cacheable, K_FALSE if not.
4510 * @param wcFirst The first extension character.
4511 * @param wcSecond The second extension character.
4512 * @param wcThird The third extension character.
4513 * @param fAttrQuery Set if it's for an attribute query, clear if for
4514 * file creation.
4515 */
4516static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
4517{
4518 /* C++ header without an extension or a directory. */
4519 if (wcFirst == '\0')
4520 {
4521 /** @todo exclude temporary files... */
4522 return K_TRUE;
4523 }
4524
4525 /* C Header: .h */
4526 if (wcFirst == 'h' || wcFirst == 'H')
4527 {
4528 if (wcSecond == '\0')
4529 return K_TRUE;
4530
4531 /* C++ Header: .hpp, .hxx */
4532 if ( (wcSecond == 'p' || wcSecond == 'P')
4533 && (wcThird == 'p' || wcThird == 'P'))
4534 return K_TRUE;
4535 if ( (wcSecond == 'x' || wcSecond == 'X')
4536 && (wcThird == 'x' || wcThird == 'X'))
4537 return K_TRUE;
4538 }
4539 /* Misc starting with i. */
4540 else if (wcFirst == 'i' || wcFirst == 'I')
4541 {
4542 if (wcSecond != '\0')
4543 {
4544 if (wcSecond == 'n' || wcSecond == 'N')
4545 {
4546 /* C++ inline header: .inl */
4547 if (wcThird == 'l' || wcThird == 'L')
4548 return K_TRUE;
4549
4550 /* Assembly include file: .inc */
4551 if (wcThird == 'c' || wcThird == 'C')
4552 return K_TRUE;
4553 }
4554 }
4555 }
4556 /* Assembly header: .mac */
4557 else if (wcFirst == 'm' || wcFirst == 'M')
4558 {
4559 if (wcSecond == 'a' || wcSecond == 'A')
4560 {
4561 if (wcThird == 'c' || wcThird == 'C')
4562 return K_TRUE;
4563 }
4564 }
4565 else if (fAttrQuery)
4566 {
4567 /* Dynamic link library: .dll */
4568 if (wcFirst == 'd' || wcFirst == 'D')
4569 {
4570 if (wcSecond == 'l' || wcSecond == 'L')
4571 {
4572 if (wcThird == 'l' || wcThird == 'L')
4573 return K_TRUE;
4574 }
4575 }
4576 /* Executable file: .exe */
4577 else if (wcFirst == 'e' || wcFirst == 'E')
4578 {
4579 if (wcSecond == 'x' || wcSecond == 'X')
4580 {
4581 if (wcThird == 'e' || wcThird == 'e')
4582 return K_TRUE;
4583 }
4584 }
4585 }
4586
4587 return K_FALSE;
4588}
4589
4590
4591/**
4592 * Checks if the file extension indicates that the file/dir is something we
4593 * ought to cache.
4594 *
4595 * @returns K_TRUE if cachable, K_FALSE if not.
4596 * @param pszExt The kHlpGetExt result.
4597 * @param fAttrQuery Set if it's for an attribute query, clear if for
4598 * file creation.
4599 */
4600static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
4601{
4602 wchar_t const wcFirst = *pszExt;
4603 if (wcFirst)
4604 {
4605 wchar_t const wcSecond = pszExt[1];
4606 if (wcSecond)
4607 {
4608 wchar_t const wcThird = pszExt[2];
4609 if (pszExt[3] == '\0')
4610 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
4611 return K_FALSE;
4612 }
4613 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
4614 }
4615 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
4616}
4617
4618
4619/**
4620 * Checks if the extension of the given UTF-16 path indicates that the file/dir
4621 * should be cached.
4622 *
4623 * @returns K_TRUE if cachable, K_FALSE if not.
4624 * @param pwszPath The UTF-16 path to examine.
4625 * @param fAttrQuery Set if it's for an attribute query, clear if for
4626 * file creation.
4627 */
4628static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
4629{
4630 KSIZE cwcExt;
4631 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
4632 switch (cwcExt)
4633 {
4634 case 3: kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
4635 case 2: kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
4636 case 1: kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
4637 case 0: kwFsIsCacheableExtensionCommon(0, 0, 0, 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 (kwFsIsCacheableExtensionA(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 (kwFsIsCacheablePathExtensionW(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
5140#ifdef WITH_HASH_MD5_CACHE
5141 if (g_Sandbox.pHashHead)
5142 {
5143 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
5144 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
5145 g_Sandbox.LastHashRead.cbRead = cbActually;
5146 g_Sandbox.LastHashRead.pvRead = pvBuffer;
5147 }
5148#endif
5149
5150 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
5151 pHandle->offFile += cbActually;
5152
5153 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
5154 *pcbActuallyRead = cbActually;
5155
5156 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
5157 return TRUE;
5158 }
5159
5160#ifdef WITH_TEMP_MEMORY_FILES
5161 case KWHANDLETYPE_TEMP_FILE:
5162 {
5163 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5164 KU32 cbActually;
5165 if (pHandle->offFile < pTempFile->cbFile)
5166 {
5167 cbActually = pTempFile->cbFile - pHandle->offFile;
5168 if (cbActually > cbToRead)
5169 cbActually = cbToRead;
5170
5171 /* Copy the data. */
5172 if (cbActually > 0)
5173 {
5174 KU32 cbLeft;
5175 KU32 offSeg;
5176 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
5177
5178 /* Locate the segment containing the byte at offFile. */
5179 KU32 iSeg = pTempFile->cSegs - 1;
5180 kHlpAssert(pTempFile->cSegs > 0);
5181 while (paSegs[iSeg].offData > pHandle->offFile)
5182 iSeg--;
5183
5184 /* Copy out the data. */
5185 cbLeft = cbActually;
5186 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
5187 for (;;)
5188 {
5189 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
5190 if (cbAvail >= cbLeft)
5191 {
5192 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
5193 break;
5194 }
5195
5196 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
5197 cbLeft -= cbAvail;
5198 offSeg = 0;
5199 iSeg++;
5200 kHlpAssert(iSeg < pTempFile->cSegs);
5201 }
5202
5203 /* Update the file offset. */
5204 pHandle->offFile += cbActually;
5205 }
5206 }
5207 /* Read does not commit file space, so return zero bytes. */
5208 else
5209 cbActually = 0;
5210
5211 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
5212 *pcbActuallyRead = cbActually;
5213
5214 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
5215 return TRUE;
5216 }
5217#endif /* WITH_TEMP_MEMORY_FILES */
5218
5219 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5220 case KWHANDLETYPE_OUTPUT_BUF:
5221 default:
5222 kHlpAssertFailed();
5223 SetLastError(ERROR_INVALID_FUNCTION);
5224 *pcbActuallyRead = 0;
5225 return FALSE;
5226 }
5227 }
5228 }
5229
5230 KWFS_LOG(("ReadFile(%p)\n", hFile));
5231 return ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
5232}
5233
5234
5235/** Kernel32 - ReadFileEx */
5236static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
5237 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
5238{
5239 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5240 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5241 if (idxHandle < g_Sandbox.cHandles)
5242 {
5243 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5244 if (pHandle != NULL)
5245 {
5246 kHlpAssertFailed();
5247 }
5248 }
5249
5250 KWFS_LOG(("ReadFile(%p)\n", hFile));
5251 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
5252}
5253
5254#ifdef WITH_STD_OUT_ERR_BUFFERING
5255
5256/**
5257 * Write something to a handle, making sure everything is actually written.
5258 *
5259 * @param hHandle Where to write it to.
5260 * @param pchBuf What to write
5261 * @param cchToWrite How much to write.
5262 */
5263static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
5264{
5265 if (cchToWrite > 0)
5266 {
5267 DWORD cchWritten = 0;
5268 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
5269 {
5270 if (cchWritten == cchToWrite)
5271 { /* likely */ }
5272 else
5273 {
5274 do
5275 {
5276 pchBuf += cchWritten;
5277 cchToWrite -= cchWritten;
5278 cchWritten = 0;
5279 } while ( cchToWrite > 0
5280 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
5281 }
5282 }
5283 else
5284 kHlpAssertFailed();
5285 }
5286}
5287
5288
5289/**
5290 * Worker for WriteFile when the output isn't going to the console.
5291 *
5292 * @param pSandbox The sandbox.
5293 * @param pOutBuf The output buffer.
5294 * @param pchBuffer What to write.
5295 * @param cchToWrite How much to write.
5296 */
5297static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
5298{
5299 if (pOutBuf->u.Fully.cchBufAlloc > 0)
5300 { /* likely */ }
5301 else
5302 {
5303 /* No realloc, max size is 64KB. */
5304 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
5305 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
5306 if (!pOutBuf->u.Fully.pchBuf)
5307 {
5308 while ( !pOutBuf->u.Fully.pchBuf
5309 && pOutBuf->u.Fully.cchBufAlloc > 64)
5310 {
5311 pOutBuf->u.Fully.cchBufAlloc /= 2;
5312 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
5313 }
5314 if (!pOutBuf->u.Fully.pchBuf)
5315 {
5316 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
5317 pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
5318 }
5319 }
5320 }
5321
5322 /*
5323 * Special case: Output ends with newline and fits in the buffer.
5324 */
5325 if ( cchToWrite > 1
5326 && pchBuffer[cchToWrite - 1] == '\n'
5327 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
5328 {
5329 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
5330 pOutBuf->u.Fully.cchBuf += cchToWrite;
5331 }
5332 else
5333 {
5334 /*
5335 * Work thru the text line by line, flushing the buffer when
5336 * appropriate. The buffer is not a line buffer here, it's a
5337 * full buffer.
5338 */
5339 while (cchToWrite > 0)
5340 {
5341 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
5342 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
5343 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
5344 {
5345 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
5346 pOutBuf->u.Fully.cchBuf += cchLine;
5347 }
5348 /*
5349 * Option one: Flush the buffer and the current line.
5350 *
5351 * We choose this one when the line won't ever fit, or when we have
5352 * an incomplete line in the buffer.
5353 */
5354 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
5355 || pOutBuf->u.Fully.cchBuf == 0
5356 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
5357 {
5358 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
5359 if (pOutBuf->u.Fully.cchBuf > 0)
5360 {
5361 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
5362 pOutBuf->u.Fully.cchBuf = 0;
5363 }
5364 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
5365 }
5366 /*
5367 * Option two: Only flush the lines in the buffer.
5368 */
5369 else
5370 {
5371 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
5372 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
5373 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
5374 pOutBuf->u.Fully.cchBuf = cchLine;
5375 }
5376
5377 /* advance */
5378 pchBuffer += cchLine;
5379 cchToWrite -= cchLine;
5380 }
5381 }
5382}
5383
5384#endif /* WITH_STD_OUT_ERR_BUFFERING */
5385
5386#ifdef WITH_TEMP_MEMORY_FILES
5387static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
5388{
5389 KU32 cbMinFile = offFile + cbNeeded;
5390 if (cbMinFile >= offFile)
5391 {
5392 /* Calc how much space we've already allocated and */
5393 if (cbMinFile <= pTempFile->cbFileAllocated)
5394 return K_TRUE;
5395
5396 /* Grow the file. */
5397 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
5398 {
5399 int rc;
5400 KU32 cSegs = pTempFile->cSegs;
5401 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
5402 do
5403 {
5404 /* grow the segment array? */
5405 if ((cSegs % 16) == 0)
5406 {
5407 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
5408 if (!pvNew)
5409 return K_FALSE;
5410 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
5411 }
5412
5413 /* Use page alloc here to simplify mapping later. */
5414 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
5415 if (rc == 0)
5416 { /* likely */ }
5417 else
5418 {
5419 cbNewSeg = 64*1024;
5420 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
5421 if (rc != 0)
5422 {
5423 kHlpAssertFailed();
5424 return K_FALSE;
5425 }
5426 }
5427 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
5428 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
5429 pTempFile->cbFileAllocated += cbNewSeg;
5430 pTempFile->cSegs = ++cSegs;
5431
5432 } while (pTempFile->cbFileAllocated < cbMinFile);
5433
5434 return K_TRUE;
5435 }
5436 }
5437
5438 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
5439 return K_FALSE;
5440}
5441#endif /* WITH_TEMP_MEMORY_FILES */
5442
5443
5444#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
5445/** Kernel32 - WriteFile */
5446static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
5447 LPOVERLAPPED pOverlapped)
5448{
5449 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5450 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5451 if (idxHandle < g_Sandbox.cHandles)
5452 {
5453 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5454 if (pHandle != NULL)
5455 {
5456 switch (pHandle->enmType)
5457 {
5458# ifdef WITH_TEMP_MEMORY_FILES
5459 case KWHANDLETYPE_TEMP_FILE:
5460 {
5461 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5462
5463 kHlpAssert(!pOverlapped);
5464 kHlpAssert(pcbActuallyWritten);
5465
5466 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
5467 {
5468 KU32 cbLeft;
5469 KU32 offSeg;
5470
5471 /* Locate the segment containing the byte at offFile. */
5472 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
5473 KU32 iSeg = pTempFile->cSegs - 1;
5474 kHlpAssert(pTempFile->cSegs > 0);
5475 while (paSegs[iSeg].offData > pHandle->offFile)
5476 iSeg--;
5477
5478 /* Copy in the data. */
5479 cbLeft = cbToWrite;
5480 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
5481 for (;;)
5482 {
5483 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
5484 if (cbAvail >= cbLeft)
5485 {
5486 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
5487 break;
5488 }
5489
5490 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
5491 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
5492 cbLeft -= cbAvail;
5493 offSeg = 0;
5494 iSeg++;
5495 kHlpAssert(iSeg < pTempFile->cSegs);
5496 }
5497
5498 /* Update the file offset. */
5499 pHandle->offFile += cbToWrite;
5500 if (pHandle->offFile > pTempFile->cbFile)
5501 pTempFile->cbFile = pHandle->offFile;
5502
5503 *pcbActuallyWritten = cbToWrite;
5504 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
5505 return TRUE;
5506 }
5507
5508 kHlpAssertFailed();
5509 *pcbActuallyWritten = 0;
5510 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5511 return FALSE;
5512 }
5513# endif
5514
5515 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5516 kHlpAssertFailed();
5517 SetLastError(ERROR_ACCESS_DENIED);
5518 *pcbActuallyWritten = 0;
5519 return FALSE;
5520
5521# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
5522 /*
5523 * Standard output & error.
5524 */
5525 case KWHANDLETYPE_OUTPUT_BUF:
5526 {
5527 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
5528 if (pOutBuf->fIsConsole)
5529 {
5530 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
5531 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
5532 }
5533 else
5534 {
5535# ifdef WITH_STD_OUT_ERR_BUFFERING
5536 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
5537 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
5538 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
5539# else
5540 kHlpAssertFailed();
5541# endif
5542 }
5543 if (pcbActuallyWritten)
5544 *pcbActuallyWritten = cbToWrite;
5545 return TRUE;
5546 }
5547# endif
5548
5549 default:
5550 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5551 kHlpAssertFailed();
5552 SetLastError(ERROR_INVALID_FUNCTION);
5553 *pcbActuallyWritten = 0;
5554 return FALSE;
5555 }
5556 }
5557 }
5558
5559 KWFS_LOG(("WriteFile(%p,,%#x)\n", hFile, cbToWrite));
5560 return WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
5561}
5562
5563
5564/** Kernel32 - WriteFileEx */
5565static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
5566 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
5567{
5568 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5569 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5570 if (idxHandle < g_Sandbox.cHandles)
5571 {
5572 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5573 if (pHandle != NULL)
5574 {
5575 kHlpAssertFailed();
5576 }
5577 }
5578
5579 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
5580 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
5581}
5582
5583#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
5584
5585#ifdef WITH_TEMP_MEMORY_FILES
5586
5587/** Kernel32 - SetEndOfFile; */
5588static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
5589{
5590 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5591 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5592 if (idxHandle < g_Sandbox.cHandles)
5593 {
5594 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5595 if (pHandle != NULL)
5596 {
5597 switch (pHandle->enmType)
5598 {
5599 case KWHANDLETYPE_TEMP_FILE:
5600 {
5601 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5602 if ( pHandle->offFile > pTempFile->cbFile
5603 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
5604 {
5605 kHlpAssertFailed();
5606 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5607 return FALSE;
5608 }
5609
5610 pTempFile->cbFile = pHandle->offFile;
5611 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
5612 return TRUE;
5613 }
5614
5615 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5616 kHlpAssertFailed();
5617 SetLastError(ERROR_ACCESS_DENIED);
5618 return FALSE;
5619
5620 case KWHANDLETYPE_OUTPUT_BUF:
5621 kHlpAssertFailed();
5622 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
5623 return FALSE;
5624
5625 default:
5626 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5627 kHlpAssertFailed();
5628 SetLastError(ERROR_INVALID_FUNCTION);
5629 return FALSE;
5630 }
5631 }
5632 }
5633
5634 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
5635 return SetEndOfFile(hFile);
5636}
5637
5638
5639/** Kernel32 - GetFileType */
5640static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
5641{
5642 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5643 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5644 if (idxHandle < g_Sandbox.cHandles)
5645 {
5646 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5647 if (pHandle != NULL)
5648 {
5649 switch (pHandle->enmType)
5650 {
5651 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5652 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
5653 return FILE_TYPE_DISK;
5654
5655 case KWHANDLETYPE_TEMP_FILE:
5656 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
5657 return FILE_TYPE_DISK;
5658
5659 case KWHANDLETYPE_OUTPUT_BUF:
5660 {
5661 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
5662 DWORD fRet;
5663 if (pOutBuf->fFileType != KU8_MAX)
5664 {
5665 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
5666 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
5667 }
5668 else
5669 {
5670 fRet = GetFileType(hFile);
5671 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
5672 }
5673 return fRet;
5674 }
5675
5676 }
5677 }
5678 }
5679
5680 KWFS_LOG(("GetFileType(%p)\n", hFile));
5681 return GetFileType(hFile);
5682}
5683
5684
5685/** Kernel32 - GetFileSize */
5686static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
5687{
5688 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5689 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5690 if (idxHandle < g_Sandbox.cHandles)
5691 {
5692 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5693 if (pHandle != NULL)
5694 {
5695 if (pcbHighDword)
5696 *pcbHighDword = 0;
5697 SetLastError(NO_ERROR);
5698 switch (pHandle->enmType)
5699 {
5700 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5701 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
5702 return pHandle->u.pCachedFile->cbCached;
5703
5704 case KWHANDLETYPE_TEMP_FILE:
5705 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
5706 return pHandle->u.pTempFile->cbFile;
5707
5708 case KWHANDLETYPE_OUTPUT_BUF:
5709 /* do default */
5710 break;
5711
5712 default:
5713 kHlpAssertFailed();
5714 SetLastError(ERROR_INVALID_FUNCTION);
5715 return INVALID_FILE_SIZE;
5716 }
5717 }
5718 }
5719
5720 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
5721 return GetFileSize(hFile, pcbHighDword);
5722}
5723
5724
5725/** Kernel32 - GetFileSizeEx */
5726static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
5727{
5728 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5729 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5730 if (idxHandle < g_Sandbox.cHandles)
5731 {
5732 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5733 if (pHandle != NULL)
5734 {
5735 switch (pHandle->enmType)
5736 {
5737 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5738 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
5739 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
5740 return TRUE;
5741
5742 case KWHANDLETYPE_TEMP_FILE:
5743 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
5744 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
5745 return TRUE;
5746
5747 case KWHANDLETYPE_OUTPUT_BUF:
5748 /* do default */
5749 break;
5750
5751 default:
5752 kHlpAssertFailed();
5753 SetLastError(ERROR_INVALID_FUNCTION);
5754 return INVALID_FILE_SIZE;
5755 }
5756 }
5757 }
5758
5759 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
5760 return GetFileSizeEx(hFile, pcbFile);
5761}
5762
5763
5764/** Kernel32 - CreateFileMapping */
5765static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
5766 DWORD fProtect, DWORD dwMaximumSizeHigh,
5767 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
5768{
5769 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
5770 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5771 if (idxHandle < g_Sandbox.cHandles)
5772 {
5773 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5774 if (pHandle != NULL)
5775 {
5776 switch (pHandle->enmType)
5777 {
5778 case KWHANDLETYPE_TEMP_FILE:
5779 {
5780 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5781 if ( ( fProtect == PAGE_READONLY
5782 || fProtect == PAGE_EXECUTE_READ)
5783 && dwMaximumSizeHigh == 0
5784 && ( dwMaximumSizeLow == 0
5785 || dwMaximumSizeLow == pTempFile->cbFile)
5786 && pwszName == NULL)
5787 {
5788 HANDLE hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
5789 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
5790 return hMapping;
5791 }
5792 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
5793 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
5794 SetLastError(ERROR_ACCESS_DENIED);
5795 return INVALID_HANDLE_VALUE;
5796 }
5797 }
5798 }
5799 }
5800
5801 KWFS_LOG(("CreateFileMappingW(%p)\n", hFile));
5802 return CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
5803}
5804
5805/** Kernel32 - MapViewOfFile */
5806static HANDLE WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
5807 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
5808{
5809 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
5810 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5811 if (idxHandle < g_Sandbox.cHandles)
5812 {
5813 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5814 if (pHandle != NULL)
5815 {
5816 switch (pHandle->enmType)
5817 {
5818 case KWHANDLETYPE_FSOBJ_READ_CACHE:
5819 case KWHANDLETYPE_TEMP_FILE:
5820 case KWHANDLETYPE_OUTPUT_BUF:
5821 kHlpAssertFailed();
5822 SetLastError(ERROR_INVALID_OPERATION);
5823 return NULL;
5824
5825 case KWHANDLETYPE_TEMP_FILE_MAPPING:
5826 {
5827 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
5828 if ( dwDesiredAccess == FILE_MAP_READ
5829 && offFileHigh == 0
5830 && offFileLow == 0
5831 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
5832 {
5833 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
5834 if (pTempFile->cSegs != 1)
5835 {
5836 KU32 iSeg;
5837 KU32 cbLeft;
5838 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
5839 KU8 *pbAll = NULL;
5840 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
5841 if (rc != 0)
5842 {
5843 kHlpAssertFailed();
5844 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5845 return NULL;
5846 }
5847
5848 cbLeft = pTempFile->cbFile;
5849 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
5850 {
5851 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
5852 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
5853 cbLeft -= cbToCopy;
5854 }
5855
5856 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
5857 {
5858 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
5859 pTempFile->paSegs[iSeg].pbData = NULL;
5860 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
5861 }
5862
5863 pTempFile->cSegs = 1;
5864 pTempFile->cbFileAllocated = cbAll;
5865 pTempFile->paSegs[0].cbDataAlloc = cbAll;
5866 pTempFile->paSegs[0].pbData = pbAll;
5867 pTempFile->paSegs[0].offData = 0;
5868 }
5869
5870 pTempFile->cMappings++;
5871 kHlpAssert(pTempFile->cMappings == 1);
5872
5873 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pTempFile->paSegs[0].pbData));
5874 return pTempFile->paSegs[0].pbData;
5875 }
5876
5877 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
5878 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
5879 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5880 return NULL;
5881 }
5882 }
5883 }
5884 }
5885
5886 KWFS_LOG(("MapViewOfFile(%p)\n", hSection));
5887 return MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
5888}
5889/** @todo MapViewOfFileEx */
5890
5891
5892/** Kernel32 - UnmapViewOfFile */
5893static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
5894{
5895 /* Is this one of our temporary mappings? */
5896 PKWFSTEMPFILE pCur = g_Sandbox.pTempFileHead;
5897 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5898 while (pCur)
5899 {
5900 if ( pCur->cMappings > 0
5901 && pCur->paSegs[0].pbData == (KU8 *)pvBase)
5902 {
5903 pCur->cMappings--;
5904 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
5905 return TRUE;
5906 }
5907 pCur = pCur->pNext;
5908 }
5909
5910 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
5911 return UnmapViewOfFile(pvBase);
5912}
5913
5914/** @todo UnmapViewOfFileEx */
5915
5916#endif /* WITH_TEMP_MEMORY_FILES */
5917
5918
5919/** Kernel32 - DuplicateHandle */
5920static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
5921 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
5922{
5923 BOOL fRet;
5924
5925 /*
5926 * We must catch our handles being duplicated.
5927 */
5928 if (hSrcProc == GetCurrentProcess())
5929 {
5930 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
5931 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5932 if (idxHandle < g_Sandbox.cHandles)
5933 {
5934 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5935 if (pHandle)
5936 {
5937 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
5938 if (fRet)
5939 {
5940 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
5941 {
5942 pHandle->cRefs++;
5943 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
5944 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
5945 pHandle->enmType, pHandle->cRefs));
5946 }
5947 else
5948 {
5949 fRet = FALSE;
5950 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5951 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
5952 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
5953 }
5954 }
5955 else
5956 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
5957 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
5958 return fRet;
5959 }
5960 }
5961 }
5962
5963 /*
5964 * Not one of ours, just do what the caller asks and log it.
5965 */
5966 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
5967 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
5968 fInheritHandle, dwOptions, fRet, *phNew));
5969 return fRet;
5970}
5971
5972
5973/** Kernel32 - CloseHandle */
5974static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
5975{
5976 BOOL fRet;
5977 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
5978 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5979 if (idxHandle < g_Sandbox.cHandles)
5980 {
5981 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5982 if (pHandle)
5983 {
5984 /* Prevent the closing of the standard output and error handles. */
5985 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
5986 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
5987 {
5988 fRet = CloseHandle(hObject);
5989 if (fRet)
5990 {
5991 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
5992 g_Sandbox.papHandles[idxHandle] = NULL;
5993 g_Sandbox.cActiveHandles--;
5994 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
5995 if (--pHandle->cRefs == 0)
5996 {
5997#ifdef WITH_TEMP_MEMORY_FILES
5998 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
5999 {
6000 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
6001 pHandle->u.pTempFile->cActiveHandles--;
6002 }
6003#endif
6004 kHlpFree(pHandle);
6005 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
6006 }
6007 else
6008 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
6009 }
6010 else
6011 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
6012 }
6013 else
6014 {
6015 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
6016 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
6017 fRet = TRUE;
6018 }
6019 return fRet;
6020 }
6021 }
6022
6023 fRet = CloseHandle(hObject);
6024 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
6025 return fRet;
6026}
6027
6028
6029/** Kernel32 - GetFileAttributesA. */
6030static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
6031{
6032 DWORD fRet;
6033 const char *pszExt = kHlpGetExt(pszFilename);
6034 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
6035 {
6036 KFSLOOKUPERROR enmError;
6037 PKFSOBJ pFsObj;
6038 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6039
6040 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
6041 if (pFsObj)
6042 {
6043 kHlpAssert(pFsObj->fHaveStats);
6044 fRet = pFsObj->Stats.st_attribs;
6045 kFsCacheObjRelease(g_pFsCache, pFsObj);
6046 }
6047 else
6048 {
6049 SetLastError(kwFsLookupErrorToWindowsError(enmError));
6050 fRet = INVALID_FILE_ATTRIBUTES;
6051 }
6052
6053 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
6054 return fRet;
6055 }
6056
6057 fRet = GetFileAttributesA(pszFilename);
6058 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
6059 return fRet;
6060}
6061
6062
6063/** Kernel32 - GetFileAttributesW. */
6064static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
6065{
6066 DWORD fRet;
6067 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
6068 {
6069 KFSLOOKUPERROR enmError;
6070 PKFSOBJ pFsObj;
6071 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6072
6073 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6074 if (pFsObj)
6075 {
6076 kHlpAssert(pFsObj->fHaveStats);
6077 fRet = pFsObj->Stats.st_attribs;
6078 kFsCacheObjRelease(g_pFsCache, pFsObj);
6079 }
6080 else
6081 {
6082 SetLastError(kwFsLookupErrorToWindowsError(enmError));
6083 fRet = INVALID_FILE_ATTRIBUTES;
6084 }
6085
6086 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
6087 return fRet;
6088 }
6089
6090 fRet = GetFileAttributesW(pwszFilename);
6091 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
6092 return fRet;
6093}
6094
6095
6096/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
6097 * directory containing each include file. We cache the result to speed
6098 * things up a little. */
6099static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
6100{
6101 DWORD cwcRet;
6102 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
6103 {
6104 KFSLOOKUPERROR enmError;
6105 PKFSOBJ pObj;
6106 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6107
6108 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
6109 if (pObj)
6110 {
6111 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
6112 {
6113 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
6114 {
6115 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
6116
6117 /* Should preserve trailing slash on directory paths. */
6118 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
6119 {
6120 if ( cwcRet + 1 < cwcShortPath
6121 && pwszShortPath[cwcRet - 1] != '\\')
6122 {
6123 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
6124 if ( cwcIn > 0
6125 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
6126 {
6127 pwszShortPath[cwcRet++] = '\\';
6128 pwszShortPath[cwcRet] = '\0';
6129 }
6130 }
6131 }
6132
6133 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
6134 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
6135 kFsCacheObjRelease(g_pFsCache, pObj);
6136 return cwcRet;
6137 }
6138
6139 /* fall back for complicated cases. */
6140 }
6141 kFsCacheObjRelease(g_pFsCache, pObj);
6142 }
6143 }
6144 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
6145 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
6146 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
6147 return cwcRet;
6148}
6149
6150
6151#ifdef WITH_TEMP_MEMORY_FILES
6152/** Kernel32 - DeleteFileW
6153 * Skip deleting the in-memory files. */
6154static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
6155{
6156 BOOL fRc;
6157 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6158 && kwFsIsClTempFileW(pwszFilename))
6159 {
6160 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6161 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
6162 fRc = TRUE;
6163 }
6164 else
6165 {
6166 fRc = DeleteFileW(pwszFilename);
6167 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
6168 }
6169 return fRc;
6170}
6171#endif /* WITH_TEMP_MEMORY_FILES */
6172
6173
6174
6175#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
6176
6177/*
6178 *
6179 * Console output buffering.
6180 * Console output buffering.
6181 * Console output buffering.
6182 *
6183 */
6184
6185
6186/**
6187 * Write a wide char string to the console.
6188 *
6189 * @param pSandbox The sandbox which output buffer to flush.
6190 */
6191static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
6192{
6193 if (cwcToWrite > 0)
6194 {
6195 DWORD cwcWritten = 0;
6196 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
6197 {
6198 if (cwcWritten == cwcToWrite)
6199 { /* likely */ }
6200 else
6201 {
6202 DWORD off = 0;
6203 do
6204 {
6205 off += cwcWritten;
6206 cwcWritten = 0;
6207 } while ( off < cwcToWrite
6208 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
6209 kHlpAssert(off == cwcWritten);
6210 }
6211 }
6212 else
6213 kHlpAssertFailed();
6214 pSandbox->Combined.cFlushes++;
6215 }
6216}
6217
6218
6219/**
6220 * Flushes the combined console output buffer.
6221 *
6222 * @param pSandbox The sandbox which output buffer to flush.
6223 */
6224static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
6225{
6226 if (pSandbox->Combined.cwcBuf > 0)
6227 {
6228 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
6229 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
6230 pSandbox->Combined.cwcBuf = 0;
6231 }
6232}
6233
6234
6235/**
6236 * For handling combined buffer overflow cases line by line.
6237 *
6238 * @param pSandbox The sandbox.
6239 * @param pwcBuf What to add to the combined buffer. Usually a
6240 * line, unless we're really low on buffer space.
6241 * @param cwcBuf The length of what to add.
6242 * @param fBrokenLine Whether this is a broken line.
6243 */
6244static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
6245{
6246 if (fBrokenLine)
6247 kwSandboxConsoleFlushCombined(pSandbox);
6248 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
6249 {
6250 kwSandboxConsoleFlushCombined(pSandbox);
6251 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
6252 }
6253 else
6254 {
6255 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
6256 pSandbox->Combined.cwcBuf += cwcBuf;
6257 }
6258}
6259
6260
6261/**
6262 * Called to final flush a line buffer via the combined buffer (if applicable).
6263 *
6264 * @param pSandbox The sandbox.
6265 * @param pLineBuf The line buffer.
6266 * @param pszName The line buffer name (for logging)
6267 */
6268static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
6269{
6270 if (pLineBuf->fIsConsole)
6271 {
6272 if (pLineBuf->u.Con.cwcBuf > 0)
6273 {
6274 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
6275
6276 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
6277 {
6278 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
6279 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
6280 }
6281 else
6282 {
6283 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
6284 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
6285 }
6286 pLineBuf->u.Con.cwcBuf = 0;
6287 }
6288 }
6289#ifdef WITH_STD_OUT_ERR_BUFFERING
6290 else if (pLineBuf->u.Fully.cchBuf > 0)
6291 {
6292 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
6293
6294 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
6295 pLineBuf->u.Fully.cchBuf = 0;
6296 }
6297#endif
6298}
6299
6300
6301/**
6302 * Called at the end of sandboxed execution to flush both stream buffers.
6303 *
6304 * @param pSandbox The sandbox.
6305 */
6306static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
6307{
6308 /*
6309 * First do the cl.exe source file supression trick, if applicable.
6310 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
6311 * handle.
6312 */
6313 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6314 && pSandbox->Combined.cFlushes == 0)
6315 {
6316 if ( pSandbox->StdOut.fIsConsole
6317 || pSandbox->StdErr.fIsConsole)
6318 {
6319 if ( pSandbox->Combined.cwcBuf >= 3
6320 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
6321 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
6322 {
6323 KI32 off = pSandbox->Combined.cwcBuf - 1;
6324 if (pSandbox->Combined.wszBuf[off] == '\n')
6325 {
6326 KBOOL fOk = K_TRUE;
6327 while (off-- > 0)
6328 {
6329 wchar_t const wc = pSandbox->Combined.wszBuf[off];
6330 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
6331 { /* likely */ }
6332 else
6333 {
6334 fOk = K_FALSE;
6335 break;
6336 }
6337 }
6338 if (fOk)
6339 {
6340 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
6341 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
6342 pSandbox->Combined.cwcBuf = 0;
6343 return;
6344 }
6345 }
6346 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
6347 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
6348 }
6349 }
6350#ifdef WITH_STD_OUT_ERR_BUFFERING
6351 /*
6352 * Otherwise, it goes to standard output (redirected).
6353 */
6354 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
6355 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
6356 {
6357 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
6358 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
6359 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
6360
6361 if (pchBuf[off] == '\n')
6362 {
6363 KBOOL fOk = K_TRUE;
6364 if (pchBuf[off - 1] == '\r')
6365 off--;
6366 while (off-- > 0)
6367 {
6368 char const ch = pchBuf[off];
6369 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
6370 { /* likely */ }
6371 else
6372 {
6373 fOk = K_FALSE;
6374 break;
6375 }
6376 }
6377 if (fOk)
6378 {
6379 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
6380 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
6381 pSandbox->StdOut.u.Fully.cchBuf = 0;
6382 return;
6383 }
6384 }
6385 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
6386 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
6387 }
6388#endif
6389 }
6390
6391 /*
6392 * Flush the two line buffer, the the combined buffer.
6393 */
6394 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
6395 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
6396 kwSandboxConsoleFlushCombined(pSandbox);
6397}
6398
6399
6400/**
6401 * Writes a string to the given output stream.
6402 *
6403 * @param pSandbox The sandbox.
6404 * @param pLineBuf The line buffer for the output stream.
6405 * @param pwcBuffer The buffer to write.
6406 * @param cwcToWrite The number of wchar_t's in the buffer.
6407 */
6408static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
6409{
6410 kHlpAssert(pLineBuf->fIsConsole);
6411 if (cwcToWrite > 0)
6412 {
6413 /*
6414 * First, find the start of the last incomplete line so we can figure
6415 * out how much line buffering we need to do.
6416 */
6417 KU32 cchLastIncompleteLine;
6418 KU32 offLastIncompleteLine = cwcToWrite;
6419 while ( offLastIncompleteLine > 0
6420 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
6421 offLastIncompleteLine--;
6422 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
6423
6424 /* Was there anything to line buffer? */
6425 if (offLastIncompleteLine < cwcToWrite)
6426 {
6427 /* Need to grow the line buffer? */
6428 KU32 cwcNeeded = offLastIncompleteLine != 0 ? offLastIncompleteLine : cchLastIncompleteLine + pLineBuf->u.Con.cwcBuf;
6429 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
6430 {
6431 void *pvNew;
6432 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
6433 while (cwcNew < cwcNeeded)
6434 cwcNew *= 2;
6435 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
6436 if (pvNew)
6437 {
6438 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
6439 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
6440 }
6441 else
6442 {
6443 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
6444 if (pvNew)
6445 {
6446 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
6447 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
6448 }
6449 else
6450 {
6451 /* This isn't perfect, but it will have to do for now. */
6452 if (pLineBuf->u.Con.cwcBuf > 0)
6453 {
6454 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
6455 K_TRUE /*fBrokenLine*/);
6456 pLineBuf->u.Con.cwcBuf = 0;
6457 }
6458 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
6459 return;
6460 }
6461 }
6462 }
6463
6464 /*
6465 * Handle the case where we only add to the line buffer.
6466 */
6467 if (offLastIncompleteLine == 0)
6468 {
6469 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
6470 pLineBuf->u.Con.cwcBuf += cwcToWrite;
6471 return;
6472 }
6473 }
6474
6475 /*
6476 * If there is sufficient combined buffer to handle this request, this are rather simple.
6477 */
6478 if (pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
6479 {
6480 if (pLineBuf->u.Con.cwcBuf > 0)
6481 {
6482 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
6483 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
6484 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
6485 pLineBuf->u.Con.cwcBuf = 0;
6486 }
6487
6488 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
6489 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
6490 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
6491 }
6492 else
6493 {
6494 /*
6495 * Do line-by-line processing of the input, flusing the combined buffer
6496 * when it becomes necessary. We may have to write lines
6497 */
6498 KU32 off = 0;
6499 KU32 offNextLine = 0;
6500
6501 /* If there is buffered chars, we handle the first line outside the
6502 main loop. We must try our best outputting it as a complete line. */
6503 if (pLineBuf->u.Con.cwcBuf > 0)
6504 {
6505 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
6506 offNextLine++;
6507 offNextLine++;
6508 kHlpAssert(offNextLine <= offLastIncompleteLine);
6509
6510 if (pLineBuf->u.Con.cwcBuf + offNextLine + pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf))
6511 {
6512 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
6513 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
6514 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
6515 pLineBuf->u.Con.cwcBuf = 0;
6516
6517 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
6518 pSandbox->Combined.cwcBuf += offNextLine;
6519 }
6520 else
6521 {
6522 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
6523 if (cwcLeft > 0)
6524 {
6525 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
6526 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
6527 pLineBuf->u.Con.cwcBuf += cwcCopy;
6528 off += cwcCopy;
6529 }
6530 if (pLineBuf->u.Con.cwcBuf > 0)
6531 {
6532 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
6533 K_TRUE /*fBrokenLine*/);
6534 pLineBuf->u.Con.cwcBuf = 0;
6535 }
6536 if (off < offNextLine)
6537 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
6538 }
6539 off = offNextLine;
6540 }
6541
6542 /* Deal with the remaining lines */
6543 while (off < offLastIncompleteLine)
6544 {
6545 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
6546 offNextLine++;
6547 offNextLine++;
6548 kHlpAssert(offNextLine <= offLastIncompleteLine);
6549 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
6550 off = offNextLine;
6551 }
6552 }
6553
6554 /*
6555 * Buffer any remaining incomplete line chars.
6556 */
6557 if (offLastIncompleteLine < cwcToWrite)
6558 {
6559 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
6560 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
6561 }
6562 }
6563}
6564
6565
6566/**
6567 * Worker for WriteConsoleA and WriteFile.
6568 *
6569 * @param pSandbox The sandbox.
6570 * @param pLineBuf The line buffer.
6571 * @param pchBuffer What to write.
6572 * @param cchToWrite How much to write.
6573 */
6574static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
6575{
6576 /*
6577 * Convert it to wide char and use the 'W' to do the work.
6578 */
6579 int cwcRet;
6580 KU32 cwcBuf = cchToWrite * 2 + 1;
6581 wchar_t *pwcBufFree = NULL;
6582 wchar_t *pwcBuf;
6583 kHlpAssert(pLineBuf->fIsConsole);
6584
6585 if (cwcBuf <= 4096)
6586 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
6587 else
6588 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
6589
6590 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
6591 if (cwcRet > 0)
6592 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
6593 else
6594 {
6595 DWORD cchWritten;
6596 kHlpAssertFailed();
6597
6598 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
6599 if (pLineBuf->u.Con.cwcBuf > 0)
6600 {
6601 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
6602 pLineBuf->u.Con.cwcBuf = 0;
6603 }
6604 kwSandboxConsoleFlushCombined(pSandbox);
6605
6606 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
6607 {
6608 if (cchWritten >= cchToWrite)
6609 { /* likely */ }
6610 else
6611 {
6612 KU32 off = 0;
6613 do
6614 {
6615 off += cchWritten;
6616 cchWritten = 0;
6617 } while ( off < cchToWrite
6618 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
6619 }
6620 }
6621 }
6622
6623 if (pwcBufFree)
6624 kHlpFree(pwcBufFree);
6625}
6626
6627
6628/** Kernel32 - WriteConsoleA */
6629BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
6630 PVOID pvReserved)
6631{
6632 BOOL fRc;
6633 PKWOUTPUTSTREAMBUF pLineBuf;
6634 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6635
6636 if (hConOutput == g_Sandbox.StdErr.hOutput)
6637 pLineBuf = &g_Sandbox.StdErr;
6638 else
6639 pLineBuf = &g_Sandbox.StdOut;
6640 if (pLineBuf->fIsConsole)
6641 {
6642 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
6643
6644 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
6645 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
6646 if (pcbWritten)
6647 *pcbWritten = cbToWrite;
6648 fRc = TRUE;
6649 }
6650 else
6651 {
6652 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
6653 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
6654 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
6655 }
6656 return fRc;
6657}
6658
6659
6660/** Kernel32 - WriteConsoleW */
6661BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
6662 PVOID pvReserved)
6663{
6664 BOOL fRc;
6665 PKWOUTPUTSTREAMBUF pLineBuf;
6666 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6667
6668 if (hConOutput == g_Sandbox.StdErr.hOutput)
6669 pLineBuf = &g_Sandbox.StdErr;
6670 else if (hConOutput == g_Sandbox.StdOut.hOutput)
6671 pLineBuf = &g_Sandbox.StdOut;
6672 else
6673 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
6674 if (pLineBuf->fIsConsole)
6675 {
6676 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
6677
6678 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
6679 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
6680 if (pcwcWritten)
6681 *pcwcWritten = cwcToWrite;
6682 fRc = TRUE;
6683 }
6684 else
6685 {
6686 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
6687 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
6688 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
6689 }
6690 return fRc;
6691}
6692
6693#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
6694
6695
6696
6697/*
6698 *
6699 * Virtual memory leak prevension.
6700 * Virtual memory leak prevension.
6701 * Virtual memory leak prevension.
6702 *
6703 */
6704
6705/** Kernel32 - VirtualAlloc - for c1[xx].dll 78GB leaks. */
6706static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
6707{
6708 PVOID pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
6709 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
6710 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
6711 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6712 && pvMem)
6713 {
6714 PKWVIRTALLOC pTracker;
6715 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6716
6717 pTracker = g_Sandbox.pVirtualAllocHead;
6718 while ( pTracker
6719 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
6720 pTracker = pTracker->pNext;
6721 if (!pTracker)
6722 {
6723 DWORD dwErr = GetLastError();
6724 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
6725 if (pTracker)
6726 {
6727 pTracker->pvAlloc = pvMem;
6728 pTracker->cbAlloc = cb;
6729 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
6730 g_Sandbox.pVirtualAllocHead = pTracker;
6731 }
6732 SetLastError(dwErr);
6733 }
6734 }
6735 return pvMem;
6736}
6737
6738
6739/** Kernel32 - VirtualFree. */
6740static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
6741{
6742 BOOL fRc = VirtualFree(pvAddr, cb, dwFreeType);
6743 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
6744 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
6745 {
6746 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6747 if (dwFreeType & MEM_RELEASE)
6748 {
6749 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
6750 if (pTracker)
6751 {
6752 if (pTracker->pvAlloc == pvAddr)
6753 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
6754 else
6755 {
6756 PKWVIRTALLOC pPrev;
6757 do
6758 {
6759 pPrev = pTracker;
6760 pTracker = pTracker->pNext;
6761 } while (pTracker && pTracker->pvAlloc != pvAddr);
6762 if (pTracker)
6763 pPrev->pNext = pTracker->pNext;
6764 }
6765 if (pTracker)
6766 kHlpFree(pTracker);
6767 else
6768 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
6769 }
6770 }
6771 }
6772 return fRc;
6773}
6774
6775
6776/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
6777HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
6778{
6779 HANDLE hHeap;
6780 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6781
6782 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
6783 if (hHeap != NULL)
6784 {
6785 DWORD dwErr = GetLastError();
6786 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
6787 if (pTracker)
6788 {
6789 pTracker->hHeap = hHeap;
6790 pTracker->pNext = g_Sandbox.pHeapHead;
6791 g_Sandbox.pHeapHead = pTracker;
6792 }
6793
6794 SetLastError(dwErr);
6795 }
6796 return hHeap;
6797
6798}
6799
6800
6801/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
6802BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
6803{
6804 BOOL fRc = HeapDestroy(hHeap);
6805 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
6806 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6807 if (fRc)
6808 {
6809 PKWHEAP pTracker = g_Sandbox.pHeapHead;
6810 if (pTracker)
6811 {
6812 if (pTracker->hHeap == hHeap)
6813 g_Sandbox.pHeapHead = pTracker->pNext;
6814 else
6815 {
6816 PKWHEAP pPrev;
6817 do
6818 {
6819 pPrev = pTracker;
6820 pTracker = pTracker->pNext;
6821 } while (pTracker && pTracker->hHeap == hHeap);
6822 if (pTracker)
6823 pPrev->pNext = pTracker->pNext;
6824 }
6825 if (pTracker)
6826 kHlpFree(pTracker);
6827 else
6828 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
6829 }
6830 }
6831
6832 return fRc;
6833}
6834
6835
6836
6837/*
6838 *
6839 * Thread/Fiber local storage leak prevention.
6840 * Thread/Fiber local storage leak prevention.
6841 * Thread/Fiber local storage leak prevention.
6842 *
6843 * Note! The FlsAlloc/Free causes problems for statically linked VS2010
6844 * code like VBoxBs3ObjConverter.exe. One thing is that we're
6845 * leaking these indexes, but more importantely we crash during
6846 * worker exit since the callback is triggered multiple times.
6847 */
6848
6849
6850/** Kernel32 - FlsAlloc */
6851DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
6852{
6853 DWORD idxFls = FlsAlloc(pfnCallback);
6854 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
6855 if (idxFls != FLS_OUT_OF_INDEXES)
6856 {
6857 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
6858 if (pTracker)
6859 {
6860 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6861 pTracker->idx = idxFls;
6862 pTracker->pNext = g_Sandbox.pFlsAllocHead;
6863 g_Sandbox.pFlsAllocHead = pTracker;
6864 }
6865 }
6866
6867 return idxFls;
6868}
6869
6870/** Kernel32 - FlsFree */
6871BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
6872{
6873 BOOL fRc = FlsFree(idxFls);
6874 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
6875 if (fRc)
6876 {
6877 PKWLOCALSTORAGE pTracker;
6878 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6879
6880 pTracker = g_Sandbox.pFlsAllocHead;
6881 if (pTracker)
6882 {
6883 if (pTracker->idx == idxFls)
6884 g_Sandbox.pFlsAllocHead = pTracker->pNext;
6885 else
6886 {
6887 PKWLOCALSTORAGE pPrev;
6888 do
6889 {
6890 pPrev = pTracker;
6891 pTracker = pTracker->pNext;
6892 } while (pTracker && pTracker->idx != idxFls);
6893 if (pTracker)
6894 pPrev->pNext = pTracker->pNext;
6895 }
6896 if (pTracker)
6897 {
6898 pTracker->idx = FLS_OUT_OF_INDEXES;
6899 pTracker->pNext = NULL;
6900 kHlpFree(pTracker);
6901 }
6902 }
6903 }
6904 return fRc;
6905}
6906
6907
6908
6909/*
6910 *
6911 * Header file hashing.
6912 * Header file hashing.
6913 * Header file hashing.
6914 *
6915 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
6916 * indicated that ~12% of the time was spent doing MD5 caluclation when
6917 * rebuiling openssl. The hashing it done right after reading the source
6918 * via ReadFile, same buffers and sizes.
6919 */
6920
6921#ifdef WITH_HASH_MD5_CACHE
6922
6923/** Advapi32 - CryptCreateHash */
6924static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
6925 HCRYPTHASH *phHash)
6926{
6927 BOOL fRc;
6928
6929 /*
6930 * Only do this for cl.exe when it request normal MD5.
6931 */
6932 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
6933 {
6934 if (idAlg == CALG_MD5)
6935 {
6936 if (hKey == 0)
6937 {
6938 if (dwFlags == 0)
6939 {
6940 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
6941 if (pHash)
6942 {
6943 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6944 pHash->uMagic = KWHASHMD5_MAGIC;
6945 pHash->cbHashed = 0;
6946 pHash->fGoneBad = K_FALSE;
6947 pHash->fFallbackMode = K_FALSE;
6948 pHash->fFinal = K_FALSE;
6949
6950 /* link it. */
6951 pHash->pNext = g_Sandbox.pHashHead;
6952 g_Sandbox.pHashHead = pHash;
6953
6954 *phHash = (KUPTR)pHash;
6955 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
6956 hProv, *phHash, TRUE));
6957 return TRUE;
6958 }
6959
6960 kwErrPrintf("CryptCreateHash: out of memory!\n");
6961 }
6962 else
6963 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
6964 }
6965 else
6966 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
6967 }
6968 else
6969 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
6970 }
6971
6972 /*
6973 * Fallback.
6974 */
6975 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
6976 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
6977 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
6978 return fRc;
6979}
6980
6981
6982/** Advapi32 - CryptHashData */
6983static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
6984{
6985 BOOL fRc;
6986 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
6987 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6988 while (pHash && (KUPTR)pHash != hHash)
6989 pHash = pHash->pNext;
6990 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
6991 hHash, pHash, pbData, cbData, dwFlags));
6992 if (pHash)
6993 {
6994 /*
6995 * Validate the state.
6996 */
6997 if ( pHash->uMagic == KWHASHMD5_MAGIC
6998 && !pHash->fFinal)
6999 {
7000 if (!pHash->fFallbackMode)
7001 {
7002 /*
7003 * Does this match the previous ReadFile call to a cached file?
7004 * If it doesn't, try falling back.
7005 */
7006 if ( g_Sandbox.LastHashRead.cbRead == cbData
7007 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
7008 {
7009 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
7010 if ( pCachedFile
7011 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
7012 {
7013
7014 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
7015 {
7016 if ( pHash->pCachedFile == NULL
7017 && pHash->cbHashed == 0)
7018 pHash->pCachedFile = pCachedFile;
7019 if (pHash->pCachedFile == pCachedFile)
7020 {
7021 pHash->cbHashed += cbData;
7022 g_Sandbox.LastHashRead.pCachedFile = NULL;
7023 g_Sandbox.LastHashRead.pvRead = NULL;
7024 g_Sandbox.LastHashRead.cbRead = 0;
7025 g_Sandbox.LastHashRead.offRead = 0;
7026 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
7027 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
7028 return TRUE;
7029 }
7030
7031 /* Note! it's possible to fall back here too, if necessary. */
7032 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
7033 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
7034 }
7035 else
7036 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
7037 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
7038 }
7039 else if (!pCachedFile)
7040 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
7041 else
7042 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
7043 }
7044 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
7045 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
7046 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
7047 if (pHash->cbHashed == 0)
7048 pHash->fFallbackMode = K_TRUE;
7049 if (pHash->fFallbackMode)
7050 {
7051 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
7052 pHash->fFallbackMode = K_TRUE;
7053 MD5Init(&pHash->Md5Ctx);
7054 MD5Update(&pHash->Md5Ctx, pbData, cbData);
7055 pHash->cbHashed = cbData;
7056 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
7057 hHash, pbData, cbData, dwFlags));
7058 return TRUE;
7059 }
7060 pHash->fGoneBad = K_TRUE;
7061 SetLastError(ERROR_INVALID_PARAMETER);
7062 fRc = FALSE;
7063 }
7064 else
7065 {
7066 /* fallback. */
7067 MD5Update(&pHash->Md5Ctx, pbData, cbData);
7068 pHash->cbHashed += cbData;
7069 fRc = TRUE;
7070 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
7071 hHash, pbData, cbData, dwFlags));
7072 }
7073 }
7074 /*
7075 * Bad handle state.
7076 */
7077 else
7078 {
7079 if (pHash->uMagic != KWHASHMD5_MAGIC)
7080 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
7081 else
7082 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
7083 SetLastError(NTE_BAD_HASH);
7084 fRc = FALSE;
7085 }
7086 }
7087 else
7088 {
7089
7090 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
7091 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
7092 }
7093 return fRc;
7094}
7095
7096
7097/** Advapi32 - CryptGetHashParam */
7098static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
7099 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
7100{
7101 BOOL fRc;
7102 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
7103 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7104 while (pHash && (KUPTR)pHash != hHash)
7105 pHash = pHash->pNext;
7106 if (pHash)
7107 {
7108 if (pHash->uMagic == KWHASHMD5_MAGIC)
7109 {
7110 if (dwFlags == 0)
7111 {
7112 DWORD cbRet;
7113 void *pvRet;
7114 union
7115 {
7116 DWORD dw;
7117 } uBuf;
7118
7119 switch (dwParam)
7120 {
7121 case HP_HASHVAL:
7122 {
7123 /* Check the hash progress. */
7124 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
7125 if (pCachedFile)
7126 {
7127 if ( pCachedFile->cbCached == pHash->cbHashed
7128 && !pHash->fGoneBad)
7129 {
7130 if (pCachedFile->fValidMd5)
7131 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
7132 else
7133 {
7134 MD5Init(&pHash->Md5Ctx);
7135 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
7136 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
7137 pCachedFile->fValidMd5 = K_TRUE;
7138 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
7139 }
7140 pvRet = pCachedFile->abMd5Digest;
7141 }
7142 else
7143 {
7144 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
7145 from what I can tell, so just deal with it. */
7146 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
7147 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
7148 pHash, pCachedFile, pCachedFile->szPath));
7149 pHash->fFallbackMode = K_TRUE;
7150 pHash->pCachedFile = NULL;
7151 MD5Init(&pHash->Md5Ctx);
7152 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
7153 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
7154 pvRet = pHash->abDigest;
7155 }
7156 pHash->fFinal = K_TRUE;
7157 cbRet = 16;
7158 break;
7159 }
7160 else if (pHash->fFallbackMode)
7161 {
7162 if (!pHash->fFinal)
7163 {
7164 pHash->fFinal = K_TRUE;
7165 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
7166 }
7167 pvRet = pHash->abDigest;
7168 cbRet = 16;
7169 break;
7170 }
7171 else
7172 {
7173 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
7174 SetLastError(ERROR_INVALID_SERVER_STATE);
7175 }
7176 return FALSE;
7177 }
7178
7179 case HP_HASHSIZE:
7180 uBuf.dw = 16;
7181 pvRet = &uBuf;
7182 cbRet = sizeof(DWORD);
7183 break;
7184
7185 case HP_ALGID:
7186 uBuf.dw = CALG_MD5;
7187 pvRet = &uBuf;
7188 cbRet = sizeof(DWORD);
7189 break;
7190
7191 default:
7192 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
7193 SetLastError(NTE_BAD_TYPE);
7194 return FALSE;
7195 }
7196
7197 /*
7198 * Copy out cbRet from pvRet.
7199 */
7200 if (pbData)
7201 {
7202 if (*pcbData >= cbRet)
7203 {
7204 *pcbData = cbRet;
7205 kHlpMemCopy(pbData, pvRet, cbRet);
7206 if (cbRet == 4)
7207 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
7208 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
7209 else if (cbRet == 16)
7210 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",
7211 dwParam, pHash, pHash->pCachedFile, cbRet,
7212 pbData[0], pbData[1], pbData[2], pbData[3],
7213 pbData[4], pbData[5], pbData[6], pbData[7],
7214 pbData[8], pbData[9], pbData[10], pbData[11],
7215 pbData[12], pbData[13], pbData[14], pbData[15]));
7216 else
7217 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
7218 dwParam, pHash, pHash->pCachedFile, cbRet));
7219 return TRUE;
7220 }
7221
7222 kHlpMemCopy(pbData, pvRet, *pcbData);
7223 }
7224 SetLastError(ERROR_MORE_DATA);
7225 *pcbData = cbRet;
7226 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
7227 }
7228 else
7229 {
7230 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
7231 SetLastError(NTE_BAD_FLAGS);
7232 }
7233 }
7234 else
7235 {
7236 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
7237 SetLastError(NTE_BAD_HASH);
7238 }
7239 fRc = FALSE;
7240 }
7241 /*
7242 * Regular handle.
7243 */
7244 else
7245 {
7246 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
7247 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
7248 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
7249 }
7250
7251 return fRc;
7252}
7253
7254
7255/** Advapi32 - CryptDestroyHash */
7256static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
7257{
7258 BOOL fRc;
7259 PKWHASHMD5 pPrev = NULL;
7260 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
7261 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7262 while (pHash && (KUPTR)pHash != hHash)
7263 {
7264 pPrev = pHash;
7265 pHash = pHash->pNext;
7266 }
7267 if (pHash)
7268 {
7269 if (pHash->uMagic == KWHASHMD5_MAGIC)
7270 {
7271 pHash->uMagic = 0;
7272 if (!pPrev)
7273 g_Sandbox.pHashHead = pHash->pNext;
7274 else
7275 pPrev->pNext = pHash->pNext;
7276 kHlpFree(pHash);
7277 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
7278 fRc = TRUE;
7279 }
7280 else
7281 {
7282 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
7283 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
7284 SetLastError(ERROR_INVALID_HANDLE);
7285 fRc = FALSE;
7286 }
7287 }
7288 /*
7289 * Regular handle.
7290 */
7291 else
7292 {
7293 fRc = CryptDestroyHash(hHash);
7294 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
7295 }
7296 return fRc;
7297}
7298
7299#endif /* WITH_HASH_MD5_CACHE */
7300
7301
7302/*
7303 *
7304 * Misc function only intercepted while debugging.
7305 * Misc function only intercepted while debugging.
7306 * Misc function only intercepted while debugging.
7307 *
7308 */
7309
7310#ifndef NDEBUG
7311
7312/** CRT - memcpy */
7313static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
7314{
7315 KU8 const *pbSrc = (KU8 const *)pvSrc;
7316 KU8 *pbDst = (KU8 *)pvDst;
7317 KSIZE cbLeft = cb;
7318 while (cbLeft-- > 0)
7319 *pbDst++ = *pbSrc++;
7320 return pvDst;
7321}
7322
7323#endif /* NDEBUG */
7324
7325
7326
7327/**
7328 * Functions that needs replacing for sandboxed execution.
7329 */
7330KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
7331{
7332 /*
7333 * Kernel32.dll and friends.
7334 */
7335 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
7336 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
7337
7338 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
7339 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
7340 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
7341 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
7342 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
7343 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
7344 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
7345 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
7346 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
7347 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
7348 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
7349
7350 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
7351 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
7352 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
7353 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
7354
7355 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
7356
7357 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
7358 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
7359 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
7360 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
7361 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
7362 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
7363 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
7364 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
7365 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
7366 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
7367 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
7368
7369 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
7370 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
7371 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
7372 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
7373#ifdef WITH_TEMP_MEMORY_FILES
7374 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
7375 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
7376 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
7377 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
7378 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
7379 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
7380 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
7381 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
7382 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
7383#endif
7384 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
7385 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
7386 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
7387 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
7388 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
7389 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
7390 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
7391#ifdef WITH_TEMP_MEMORY_FILES
7392 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
7393#endif
7394
7395 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
7396 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
7397
7398 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
7399 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
7400
7401 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
7402 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
7403
7404 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc },
7405 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree },
7406
7407 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
7408
7409#ifdef WITH_HASH_MD5_CACHE
7410 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
7411 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
7412 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
7413 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
7414#endif
7415
7416 /*
7417 * MS Visual C++ CRTs.
7418 */
7419 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
7420 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
7421 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
7422 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
7423 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
7424 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
7425
7426 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
7427 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
7428 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
7429
7430 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
7431 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
7432
7433 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
7434 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
7435 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
7436 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
7437 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
7438 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
7439 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
7440 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
7441 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
7442 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
7443 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
7444 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
7445 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
7446 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
7447 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
7448 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
7449 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
7450 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
7451 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
7452 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
7453
7454 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
7455 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
7456 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
7457 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
7458 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
7459 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
7460 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
7461 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
7462 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
7463 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
7464 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
7465 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
7466 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
7467 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
7468
7469#ifndef NDEBUG
7470 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
7471#endif
7472};
7473/** Number of entries in g_aReplacements. */
7474KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
7475
7476
7477/**
7478 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
7479 * execution.
7480 */
7481KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
7482{
7483 /*
7484 * Kernel32.dll and friends.
7485 */
7486 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
7487 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
7488
7489#if 0
7490 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
7491#endif
7492
7493 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
7494 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
7495 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
7496 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
7497#ifdef WITH_TEMP_MEMORY_FILES
7498 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
7499 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
7500 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
7501 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
7502 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
7503 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
7504 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
7505 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
7506 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
7507#endif
7508 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
7509 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
7510 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
7511 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
7512 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
7513 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
7514 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
7515#ifdef WITH_TEMP_MEMORY_FILES
7516 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
7517#endif
7518 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
7519
7520 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
7521 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
7522
7523#ifdef WITH_HASH_MD5_CACHE
7524 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
7525 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
7526 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
7527 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
7528#endif
7529
7530 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
7531
7532
7533 /*
7534 * MS Visual C++ CRTs.
7535 */
7536 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
7537 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
7538 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
7539 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
7540 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
7541 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
7542
7543#if 0 /* used by mspdbXXX.dll */
7544 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
7545 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
7546#endif
7547};
7548/** Number of entries in g_aSandboxNativeReplacements. */
7549KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
7550
7551
7552/**
7553 * Functions that needs replacing when queried by GetProcAddress.
7554 */
7555KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
7556{
7557 /*
7558 * Kernel32.dll and friends.
7559 */
7560 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc },
7561 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree },
7562};
7563/** Number of entries in g_aSandboxGetProcReplacements. */
7564KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
7565
7566
7567/**
7568 * Control handler.
7569 *
7570 * @returns TRUE if handled, FALSE if not.
7571 * @param dwCtrlType The signal.
7572 */
7573static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
7574{
7575 switch (dwCtrlType)
7576 {
7577 case CTRL_C_EVENT:
7578 fprintf(stderr, "kWorker: Ctrl-C\n");
7579 exit(9);
7580 break;
7581
7582 case CTRL_BREAK_EVENT:
7583 fprintf(stderr, "kWorker: Ctrl-Break\n");
7584 exit(10);
7585 break;
7586
7587 case CTRL_CLOSE_EVENT:
7588 fprintf(stderr, "kWorker: console closed\n");
7589 exit(11);
7590 break;
7591
7592 case CTRL_LOGOFF_EVENT:
7593 fprintf(stderr, "kWorker: logoff event\n");
7594 exit(11);
7595 break;
7596
7597 case CTRL_SHUTDOWN_EVENT:
7598 fprintf(stderr, "kWorker: shutdown event\n");
7599 exit(11);
7600 break;
7601
7602 default:
7603 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
7604 break;
7605 }
7606 return TRUE;
7607}
7608
7609
7610/**
7611 * Used by kwSandboxExec to reset the state of the module tree.
7612 *
7613 * This is done recursively.
7614 *
7615 * @param pMod The root of the tree to consider.
7616 */
7617static void kwSandboxResetModuleState(PKWMODULE pMod)
7618{
7619 if ( !pMod->fNative
7620 && pMod->u.Manual.enmState != KWMODSTATE_NEEDS_BITS)
7621 {
7622 KSIZE iImp;
7623 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
7624 iImp = pMod->u.Manual.cImpMods;
7625 while (iImp-- > 0)
7626 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
7627 }
7628}
7629
7630static PPEB kwSandboxGetProcessEnvironmentBlock(void)
7631{
7632#if K_ARCH == K_ARCH_X86_32
7633 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
7634#elif K_ARCH == K_ARCH_AMD64
7635 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
7636#else
7637# error "Port me!"
7638#endif
7639}
7640
7641
7642#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
7643typedef struct _EXCEPTION_REGISTRATION_RECORD
7644{
7645 struct _EXCEPTION_REGISTRATION_RECORD * volatile PrevStructure;
7646 KU32 (__cdecl * volatile ExceptionHandler)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
7647 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
7648};
7649
7650/**
7651 * Vectored exception handler that emulates x86 chained exception handler.
7652 *
7653 * This is necessary because the RtlIsValidHandler check fails for self loaded
7654 * code and prevents cl.exe from working. (On AMD64 we can register function
7655 * tables, but on X86 cooking your own handling seems to be the only viabke
7656 * alternative.)
7657 *
7658 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
7659 * @param pXcptPtrs The exception details.
7660 */
7661static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
7662{
7663 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
7664 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
7665 if (g_Sandbox.fRunning)
7666 {
7667 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
7668 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
7669 struct _EXCEPTION_REGISTRATION_RECORD * volatile *ppRegRec = &pTib->ExceptionList;
7670 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = *ppRegRec;
7671 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
7672 {
7673#if 1
7674 /* This is a more robust version that isn't subject to calling
7675 convension cleanup disputes and such. */
7676 KU32 uSavedEdi;
7677 KU32 uSavedEsi;
7678 KU32 uSavedEbx;
7679 KU32 rcHandler;
7680 __asm
7681 {
7682 mov [uSavedEdi], edi
7683 mov [uSavedEsi], esi
7684 mov [uSavedEbx], ebx
7685 mov esi, esp
7686 mov edi, esp
7687 mov ecx, [pXcptRec]
7688 mov edx, [pRegRec]
7689 mov eax, [pXcptCtx]
7690 mov ebx, [ppRegRec]
7691 sub esp, 16
7692 and esp, 0fffffff0h
7693 mov [esp ], ecx
7694 mov [esp + 4], edx
7695 mov [esp + 8], eax
7696 mov [esp + 12], ebx
7697 call dword ptr [edx + 4]
7698 mov esp, esi
7699 cmp esp, edi
7700 je stack_ok
7701 int 3
7702 stack_ok:
7703 mov edi, [uSavedEdi]
7704 mov esi, [uSavedEsi]
7705 mov ebx, [uSavedEbx]
7706 mov [rcHandler], eax
7707 }
7708#else
7709 KU32 rcHandler = pRegRec->ExceptionHandler(pXcptPtrs->ExceptionRecord, pRegRec, pXcptPtrs->ContextRecord, ppRegRec);
7710#endif
7711 if (rcHandler == ExceptionContinueExecution)
7712 {
7713 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
7714 return EXCEPTION_CONTINUE_EXECUTION;
7715 }
7716 if (rcHandler == ExceptionContinueSearch)
7717 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
7718 else if (rcHandler == ExceptionNestedException)
7719 kHlpAssertMsgFailed(("Nested exceptions.\n"));
7720 else
7721 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
7722
7723 /*
7724 * Next.
7725 */
7726 ppRegRec = &pRegRec->PrevStructure;
7727 pRegRec = pRegRec->PrevStructure;
7728 }
7729 }
7730 return EXCEPTION_CONTINUE_SEARCH;
7731}
7732#endif /* WINDOWS + X86 */
7733
7734
7735/**
7736 * Enters the given handle into the handle table.
7737 *
7738 * @returns K_TRUE on success, K_FALSE on failure.
7739 * @param pSandbox The sandbox.
7740 * @param pHandle The handle.
7741 * @param hHandle The handle value to enter it under (for the
7742 * duplicate handle API).
7743 */
7744static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
7745{
7746 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
7747 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
7748
7749 /*
7750 * Grow handle table.
7751 */
7752 if (idxHandle >= pSandbox->cHandles)
7753 {
7754 void *pvNew;
7755 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
7756 while (cHandles <= idxHandle)
7757 cHandles *= 2;
7758 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
7759 if (!pvNew)
7760 {
7761 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
7762 return K_FALSE;
7763 }
7764 pSandbox->papHandles = (PKWHANDLE *)pvNew;
7765 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
7766 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
7767 pSandbox->cHandles = cHandles;
7768 }
7769
7770 /*
7771 * Check that the entry is unused then insert it.
7772 */
7773 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
7774 pSandbox->papHandles[idxHandle] = pHandle;
7775 pSandbox->cActiveHandles++;
7776 return K_TRUE;
7777}
7778
7779
7780/**
7781 * Creates a correctly quoted ANSI command line string from the given argv.
7782 *
7783 * @returns Pointer to the command line.
7784 * @param cArgs Number of arguments.
7785 * @param papszArgs The argument vector.
7786 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
7787 * @param pcbCmdLine Where to return the command line length,
7788 * including one terminator.
7789 */
7790static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
7791{
7792 KU32 i;
7793 KSIZE cbCmdLine;
7794 char *pszCmdLine;
7795
7796 /* Make a copy of the argument vector that we'll be quoting. */
7797 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
7798 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
7799
7800 /* Quote the arguments that need it. */
7801 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
7802
7803 /* figure out cmd line length. */
7804 cbCmdLine = 0;
7805 for (i = 0; i < cArgs; i++)
7806 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
7807 *pcbCmdLine = cbCmdLine;
7808
7809 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
7810 if (pszCmdLine)
7811 {
7812 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
7813 if (papszQuotedArgs[0] != papszArgs[0])
7814 free(papszQuotedArgs[0]);
7815
7816 for (i = 1; i < cArgs; i++)
7817 {
7818 *psz++ = ' ';
7819 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
7820 if (papszQuotedArgs[i] != papszArgs[i])
7821 free(papszQuotedArgs[i]);
7822 }
7823 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
7824
7825 *psz++ = '\0';
7826 *psz++ = '\0';
7827 }
7828
7829 return pszCmdLine;
7830}
7831
7832
7833
7834static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
7835 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
7836 KU32 cEnvVars, const char **papszEnvVars)
7837{
7838 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
7839 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
7840 wchar_t *pwcPool;
7841 KSIZE cbStrings;
7842 KSIZE cwc;
7843 KSIZE cbCmdLine;
7844 KU32 i;
7845
7846 /* Simple stuff. */
7847 pSandbox->rcExitCode = 256;
7848 pSandbox->pTool = pTool;
7849 pSandbox->idMainThread = GetCurrentThreadId();
7850 pSandbox->pgmptr = (char *)pTool->pszPath;
7851 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
7852#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
7853 if (pSandbox->StdOut.fIsConsole)
7854 pSandbox->StdOut.u.Con.cwcBuf = 0;
7855 else
7856 pSandbox->StdOut.u.Fully.cchBuf = 0;
7857 if (pSandbox->StdErr.fIsConsole)
7858 pSandbox->StdErr.u.Con.cwcBuf = 0;
7859 else
7860 pSandbox->StdErr.u.Fully.cchBuf = 0;
7861 pSandbox->Combined.cwcBuf = 0;
7862 pSandbox->Combined.cFlushes = 0;
7863#endif
7864 pSandbox->cArgs = cArgs;
7865 pSandbox->papszArgs = (char **)papszArgs;
7866 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
7867 if (!pSandbox->pszCmdLine)
7868 return KERR_NO_MEMORY;
7869
7870 /*
7871 * Convert command line and argv to UTF-16.
7872 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
7873 */
7874 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
7875 if (!pSandbox->papwszArgs)
7876 return KERR_NO_MEMORY;
7877 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
7878 for (i = 0; i < cArgs; i++)
7879 {
7880 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
7881 pSandbox->papwszArgs[i] = pwcPool;
7882 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
7883 pwcPool++;
7884 }
7885 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
7886 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
7887
7888 /*
7889 * Convert the commandline string to UTF-16, same pessimistic approach as above.
7890 */
7891 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
7892 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
7893 if (!pSandbox->pwszCmdLine)
7894 return KERR_NO_MEMORY;
7895 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
7896
7897 pSandbox->SavedCommandLine = pProcParams->CommandLine;
7898 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
7899 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
7900
7901 /*
7902 * Setup the environment.
7903 */
7904 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
7905 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
7906 {
7907 KU32 iDst = 0;
7908 for (i = 0; i < cEnvVars; i++)
7909 {
7910 const char *pszVar = papszEnvVars[i];
7911 KSIZE cchVar = kHlpStrLen(pszVar);
7912 if ( cchVar > 0
7913 && kHlpMemChr(pszVar, '=', cchVar) != NULL)
7914 {
7915 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
7916 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
7917 if (pszCopy && pwszCopy)
7918 {
7919 pSandbox->papszEnvVars[iDst] = pszCopy;
7920 pSandbox->environ[iDst] = pszCopy;
7921 pSandbox->papwszEnvVars[iDst] = pwszCopy;
7922 pSandbox->wenviron[iDst] = pwszCopy;
7923 iDst++;
7924 }
7925 else
7926 {
7927 kHlpFree(pszCopy);
7928 kHlpFree(pwszCopy);
7929 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
7930 }
7931 }
7932 else
7933 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
7934 }
7935 pSandbox->papszEnvVars[iDst] = NULL;
7936 pSandbox->environ[iDst] = NULL;
7937 pSandbox->papwszEnvVars[iDst] = NULL;
7938 pSandbox->wenviron[iDst] = NULL;
7939 }
7940 else
7941 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
7942
7943 /*
7944 * Invalidate the volatile parts of cache (kBuild output directory,
7945 * temporary directory, whatever).
7946 */
7947 kFsCacheInvalidateCustomBoth(g_pFsCache);
7948 return 0;
7949}
7950
7951
7952/**
7953 * Does sandbox cleanup between jobs.
7954 *
7955 * We postpone whatever isn't externally visible (i.e. files) and doesn't
7956 * influence the result, so that kmk can get on with things ASAP.
7957 *
7958 * @param pSandbox The sandbox.
7959 */
7960static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
7961{
7962 PROCESS_MEMORY_COUNTERS MemInfo;
7963 PKWVIRTALLOC pTracker;
7964 PKWHEAP pHeap;
7965 PKWLOCALSTORAGE pLocalStorage;
7966#ifdef WITH_HASH_MD5_CACHE
7967 PKWHASHMD5 pHash;
7968#endif
7969#ifdef WITH_TEMP_MEMORY_FILES
7970 PKWFSTEMPFILE pTempFile;
7971#endif
7972 PKWEXITCALLACK pExitCallback;
7973
7974 /*
7975 * First stuff that may cause code to run.
7976 */
7977
7978 /* Do exit callback first. */
7979 pExitCallback = g_Sandbox.pExitCallbackHead;
7980 g_Sandbox.pExitCallbackHead = NULL;
7981 while (pExitCallback)
7982 {
7983 PKWEXITCALLACK pNext = pExitCallback->pNext;
7984 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
7985 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
7986 __try
7987 {
7988 pExitCallback->pfnCallback();
7989 }
7990 __except (EXCEPTION_EXECUTE_HANDLER)
7991 {
7992 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
7993 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
7994 kHlpAssertFailed();
7995 }
7996 kHlpFree(pExitCallback);
7997 pExitCallback = pNext;
7998 }
7999
8000 /* Free left behind FlsAlloc leaks. */
8001 pLocalStorage = g_Sandbox.pFlsAllocHead;
8002 g_Sandbox.pFlsAllocHead = NULL;
8003 while (pLocalStorage)
8004 {
8005 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
8006 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
8007 FlsFree(pLocalStorage->idx);
8008 kHlpFree(pLocalStorage);
8009 pLocalStorage = pNext;
8010 }
8011
8012 /* Free left behind TlsAlloc leaks. */
8013 pLocalStorage = g_Sandbox.pTlsAllocHead;
8014 g_Sandbox.pTlsAllocHead = NULL;
8015 while (pLocalStorage)
8016 {
8017 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
8018 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
8019 TlsFree(pLocalStorage->idx);
8020 kHlpFree(pLocalStorage);
8021 pLocalStorage = pNext;
8022 }
8023
8024
8025 /*
8026 * Then free resources associated with the sandbox run.
8027 */
8028
8029 /* Open handles, except fixed handles (stdout and stderr). */
8030 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
8031 {
8032 KU32 idxHandle = pSandbox->cHandles;
8033 while (idxHandle-- > 0)
8034 if (pSandbox->papHandles[idxHandle] == NULL)
8035 { /* likely */ }
8036 else
8037 {
8038 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
8039 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8040 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
8041 {
8042 pSandbox->papHandles[idxHandle] = NULL;
8043 pSandbox->cLeakedHandles++;
8044
8045 switch (pHandle->enmType)
8046 {
8047 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8048 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
8049 idxHandle, pHandle->hHandle, pHandle->cRefs));
8050 break;
8051 case KWHANDLETYPE_OUTPUT_BUF:
8052 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
8053 idxHandle, pHandle->hHandle, pHandle->cRefs));
8054 break;
8055 case KWHANDLETYPE_TEMP_FILE:
8056 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
8057 idxHandle, pHandle->hHandle, pHandle->cRefs));
8058 pHandle->u.pTempFile->cActiveHandles--;
8059 break;
8060 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8061 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
8062 idxHandle, pHandle->hHandle, pHandle->cRefs));
8063 pHandle->u.pTempFile->cActiveHandles--;
8064 break;
8065 default:
8066 kHlpAssertFailed();
8067 }
8068 if (--pHandle->cRefs == 0)
8069 kHlpFree(pHandle);
8070 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
8071 break;
8072 }
8073 }
8074 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
8075 }
8076
8077#ifdef WITH_TEMP_MEMORY_FILES
8078 /* The temporary files aren't externally visible, they're all in memory. */
8079 pTempFile = pSandbox->pTempFileHead;
8080 pSandbox->pTempFileHead = NULL;
8081 while (pTempFile)
8082 {
8083 PKWFSTEMPFILE pNext = pTempFile->pNext;
8084 KU32 iSeg = pTempFile->cSegs;
8085 while (iSeg-- > 0)
8086 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8087 kHlpFree(pTempFile->paSegs);
8088 pTempFile->pNext = NULL;
8089 kHlpFree(pTempFile);
8090
8091 pTempFile = pNext;
8092 }
8093#endif
8094
8095 /* Free left behind HeapCreate leaks. */
8096 pHeap = g_Sandbox.pHeapHead;
8097 g_Sandbox.pHeapHead = NULL;
8098 while (pHeap != NULL)
8099 {
8100 PKWHEAP pNext = pHeap->pNext;
8101 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
8102 HeapDestroy(pHeap->hHeap);
8103 pHeap = pNext;
8104 }
8105
8106 /* Free left behind VirtualAlloc leaks. */
8107 pTracker = g_Sandbox.pVirtualAllocHead;
8108 g_Sandbox.pVirtualAllocHead = NULL;
8109 while (pTracker)
8110 {
8111 PKWVIRTALLOC pNext = pTracker->pNext;
8112 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
8113 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
8114 kHlpFree(pTracker);
8115 pTracker = pNext;
8116 }
8117
8118 /* Free the environment. */
8119 if (pSandbox->papszEnvVars)
8120 {
8121 KU32 i;
8122 for (i = 0; pSandbox->papszEnvVars[i]; i++)
8123 kHlpFree(pSandbox->papszEnvVars[i]);
8124 pSandbox->environ[0] = NULL;
8125 pSandbox->papszEnvVars[0] = NULL;
8126
8127 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
8128 kHlpFree(pSandbox->papwszEnvVars[i]);
8129 pSandbox->wenviron[0] = NULL;
8130 pSandbox->papwszEnvVars[0] = NULL;
8131 }
8132
8133#ifdef WITH_HASH_MD5_CACHE
8134 /*
8135 * Hash handles.
8136 */
8137 pHash = pSandbox->pHashHead;
8138 pSandbox->pHashHead = NULL;
8139 while (pHash)
8140 {
8141 PKWHASHMD5 pNext = pHash->pNext;
8142 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
8143 kHlpFree(pHash);
8144 pHash = pNext;
8145 }
8146#endif
8147
8148 /*
8149 * Check the memory usage. If it's getting high, trigger a respawn
8150 * after the next job.
8151 */
8152 MemInfo.WorkingSetSize = 0;
8153 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
8154 {
8155 /** @todo make the limit dynamic and user configurable. */
8156#if K_ARCH_BITS >= 64
8157 if (MemInfo.WorkingSetSize >= 512*1024*1024)
8158#else
8159 if (MemInfo.WorkingSetSize >= 384*1024*1024)
8160#endif
8161 {
8162 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
8163 g_fRestart = K_TRUE;
8164 }
8165 }
8166
8167 /*
8168 * The CRT has a max of 8192 handles, so we better restart after a while if
8169 * someone is leaking handles or we risk running out of descriptors.
8170 *
8171 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
8172 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
8173 */
8174 if (pSandbox->cLeakedHandles > 6000)
8175 {
8176 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
8177 g_fRestart = K_TRUE;
8178 }
8179}
8180
8181
8182/**
8183 * Does essential cleanups and restoring, anything externally visible.
8184 *
8185 * All cleanups that aren't externally visible are postponed till after we've
8186 * informed kmk of the result, so it can be done in the dead time between jobs.
8187 *
8188 * @param pSandbox The sandbox.
8189 */
8190static void kwSandboxCleanup(PKWSANDBOX pSandbox)
8191{
8192 /*
8193 * Restore the parent command line string.
8194 */
8195 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
8196 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
8197 pProcParams->CommandLine = pSandbox->SavedCommandLine;
8198 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
8199 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
8200}
8201
8202
8203static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
8204 KU32 cEnvVars, const char **papszEnvVars)
8205{
8206 int rcExit = 42;
8207 int rc;
8208
8209 /*
8210 * Initialize the sandbox environment.
8211 */
8212 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars);
8213 if (rc == 0)
8214 {
8215 /*
8216 * Do module initialization.
8217 */
8218 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
8219 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
8220 if (rc == 0)
8221 {
8222 /*
8223 * Call the main function.
8224 */
8225#if K_ARCH == K_ARCH_AMD64
8226 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
8227#elif K_ARCH == K_ARCH_X86_32
8228 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
8229#else
8230# error "Port me!"
8231#endif
8232
8233 /* Save the NT TIB first (should do that here, not in some other function). */
8234 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
8235 pSandbox->TibMainThread = *pTib;
8236
8237 /* Make the call in a guarded fashion. */
8238#if K_ARCH == K_ARCH_AMD64
8239 /* AMD64 */
8240 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
8241 __try
8242 {
8243 pSandbox->pOutXcptListHead = pTib->ExceptionList;
8244 if (setjmp(pSandbox->JmpBuf) == 0)
8245 {
8246 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
8247 pSandbox->fRunning = K_TRUE;
8248 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
8249 pSandbox->fRunning = K_FALSE;
8250 }
8251 else
8252 rcExit = pSandbox->rcExitCode;
8253 }
8254#elif K_ARCH == K_ARCH_X86_32
8255 /* x86 (see _tmainCRTStartup) */
8256 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
8257 __try
8258 {
8259 pSandbox->pOutXcptListHead = pTib->ExceptionList;
8260 if (setjmp(pSandbox->JmpBuf) == 0)
8261 {
8262 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
8263 pSandbox->fRunning = K_TRUE;
8264 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
8265 pSandbox->fRunning = K_FALSE;
8266 }
8267 else
8268 rcExit = pSandbox->rcExitCode;
8269 }
8270#endif
8271 __except (EXCEPTION_EXECUTE_HANDLER)
8272 {
8273 rcExit = 512;
8274 }
8275 pSandbox->fRunning = K_FALSE;
8276
8277 /* Now, restore the NT TIB. */
8278 *pTib = pSandbox->TibMainThread;
8279 }
8280 else
8281 rcExit = 42 + 4;
8282
8283 /*
8284 * Flush and clean up the essential bits only, postpone whatever we
8285 * can till after we've replied to kmk.
8286 */
8287#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8288 kwSandboxConsoleFlushAll(&g_Sandbox);
8289#endif
8290 kwSandboxCleanup(&g_Sandbox);
8291 }
8292 else
8293 rcExit = 42 + 3;
8294
8295 return rcExit;
8296}
8297
8298
8299/**
8300 * Does the post command part of a job (optional).
8301 *
8302 * @returns The exit code of the job.
8303 * @param cPostCmdArgs Number of post command arguments (includes cmd).
8304 * @param papszPostCmdArgs The post command and its argument.
8305 */
8306static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
8307{
8308 const char *pszCmd = papszPostCmdArgs[0];
8309
8310 /* Allow the kmk builtin prefix. */
8311 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
8312 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
8313 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
8314
8315 /* Command switch. */
8316 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
8317 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL);
8318
8319 return kwErrPrintfRc(42 + 5 , "Unknown post command: '%s'\n", pszCmd);
8320}
8321
8322
8323/**
8324 * Part 2 of the "JOB" command handler.
8325 *
8326 * @returns The exit code of the job.
8327 * @param pszExecutable The executable to execute.
8328 * @param pszCwd The current working directory of the job.
8329 * @param cArgs The number of arguments.
8330 * @param papszArgs The argument vector.
8331 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
8332 * @param cEnvVars The number of environment variables.
8333 * @param papszEnvVars The environment vector.
8334 * @param cPostCmdArgs Number of post command arguments (includes cmd).
8335 * @param papszPostCmdArgs The post command and its argument.
8336 */
8337static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
8338 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
8339 KU32 cEnvVars, const char **papszEnvVars,
8340 KU32 cPostCmdArgs, const char **papszPostCmdArgs)
8341{
8342 int rcExit;
8343 PKWTOOL pTool;
8344
8345 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
8346 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
8347#ifdef KW_LOG_ENABLED
8348 {
8349 KU32 i;
8350 for (i = 0; i < cArgs; i++)
8351 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
8352 for (i = 0; i < cPostCmdArgs; i++)
8353 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
8354 }
8355#endif
8356
8357 /*
8358 * Lookup the tool.
8359 */
8360 pTool = kwToolLookup(pszExecutable);
8361 if (pTool)
8362 {
8363 /*
8364 * Change the directory if we're going to execute the job inside
8365 * this process. Then invoke the tool type specific handler.
8366 */
8367 switch (pTool->enmType)
8368 {
8369 case KWTOOLTYPE_SANDBOXED:
8370 case KWTOOLTYPE_WATCOM:
8371 {
8372 /* Change dir. */
8373 KFSLOOKUPERROR enmError;
8374 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
8375 if ( pNewCurDir == g_pCurDirObj
8376 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
8377 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
8378 else if (SetCurrentDirectoryA(pszCwd))
8379 {
8380 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
8381 g_pCurDirObj = pNewCurDir;
8382 }
8383 else
8384 {
8385 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
8386 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
8387 rcExit = 42 + 1;
8388 break;
8389 }
8390
8391 /* Call specific handler. */
8392 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
8393 {
8394 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
8395 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars);
8396 }
8397 else
8398 {
8399 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
8400 rcExit = 42 + 2;
8401 }
8402 break;
8403 }
8404
8405 case KWTOOLTYPE_EXEC:
8406 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
8407 rcExit = 42 + 2;
8408 break;
8409
8410 default:
8411 kHlpAssertFailed();
8412 kwErrPrintf("Internal tool type corruption!!\n");
8413 rcExit = 42 + 2;
8414 g_fRestart = K_TRUE;
8415 break;
8416 }
8417
8418 /*
8419 * Do the post command, if present.
8420 */
8421 if (cPostCmdArgs && rcExit == 0)
8422 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
8423 }
8424 else
8425 rcExit = 42 + 1;
8426 return rcExit;
8427}
8428
8429
8430/**
8431 * Handles a "JOB" command.
8432 *
8433 * @returns The exit code of the job.
8434 * @param pszMsg Points to the "JOB" command part of the message.
8435 * @param cbMsg Number of message bytes at @a pszMsg. There are
8436 * 4 more zero bytes after the message body to
8437 * simplify parsing.
8438 */
8439static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
8440{
8441 int rcExit = 42;
8442
8443 /*
8444 * Unpack the message.
8445 */
8446 const char *pszExecutable;
8447 KSIZE cbTmp;
8448
8449 pszMsg += sizeof("JOB");
8450 cbMsg -= sizeof("JOB");
8451
8452 /* Executable name. */
8453 pszExecutable = pszMsg;
8454 cbTmp = kHlpStrLen(pszMsg) + 1;
8455 pszMsg += cbTmp;
8456 if ( cbTmp < cbMsg
8457 && cbTmp > 2)
8458 {
8459 const char *pszCwd;
8460 cbMsg -= cbTmp;
8461
8462 /* Current working directory. */
8463 pszCwd = pszMsg;
8464 cbTmp = kHlpStrLen(pszMsg) + 1;
8465 pszMsg += cbTmp;
8466 if ( cbTmp + sizeof(KU32) < cbMsg
8467 && cbTmp >= 2)
8468 {
8469 KU32 cArgs;
8470 cbMsg -= cbTmp;
8471
8472 /* Argument count. */
8473 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
8474 pszMsg += sizeof(cArgs);
8475 cbMsg -= sizeof(cArgs);
8476
8477 if (cArgs > 0 && cArgs < 4096)
8478 {
8479 /* The argument vector. */
8480 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
8481 if (papszArgs)
8482 {
8483 KU32 i;
8484 for (i = 0; i < cArgs; i++)
8485 {
8486 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
8487 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
8488 pszMsg += cbTmp;
8489 if (cbTmp < cbMsg)
8490 cbMsg -= cbTmp;
8491 else
8492 {
8493 cbMsg = 0;
8494 break;
8495 }
8496
8497 }
8498 papszArgs[cArgs] = 0;
8499
8500 /* Environment variable count. */
8501 if (cbMsg > sizeof(KU32))
8502 {
8503 KU32 cEnvVars;
8504 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
8505 pszMsg += sizeof(cEnvVars);
8506 cbMsg -= sizeof(cEnvVars);
8507
8508 if (cEnvVars >= 0 && cEnvVars < 4096)
8509 {
8510 /* The argument vector. */
8511 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
8512 if (papszEnvVars)
8513 {
8514 for (i = 0; i < cEnvVars; i++)
8515 {
8516 papszEnvVars[i] = pszMsg;
8517 cbTmp = kHlpStrLen(pszMsg) + 1;
8518 pszMsg += cbTmp;
8519 if (cbTmp < cbMsg)
8520 cbMsg -= cbTmp;
8521 else
8522 {
8523 cbMsg = 0;
8524 break;
8525 }
8526 }
8527 papszEnvVars[cEnvVars] = 0;
8528
8529 /* Flags (currently just watcom argument brain damanage). */
8530 if (cbMsg >= sizeof(KU8))
8531 {
8532 KBOOL fWatcomBrainDamange = *pszMsg++;
8533 cbMsg--;
8534
8535 /* Post command argument count (can be zero). */
8536 if (cbMsg >= sizeof(KU32))
8537 {
8538 KU32 cPostCmdArgs;
8539 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
8540 pszMsg += sizeof(cPostCmdArgs);
8541 cbMsg -= sizeof(cPostCmdArgs);
8542
8543 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
8544 {
8545 char const *apszPostCmdArgs[32+1];
8546 for (i = 0; i < cPostCmdArgs; i++)
8547 {
8548 apszPostCmdArgs[i] = pszMsg;
8549 cbTmp = kHlpStrLen(pszMsg) + 1;
8550 pszMsg += cbTmp;
8551 if ( cbTmp < cbMsg
8552 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
8553 cbMsg -= cbTmp;
8554 else
8555 {
8556 cbMsg = KSIZE_MAX;
8557 break;
8558 }
8559 }
8560 if (cbMsg == 0)
8561 {
8562 apszPostCmdArgs[cPostCmdArgs] = NULL;
8563
8564 /*
8565 * The next step.
8566 */
8567 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
8568 cArgs, papszArgs, fWatcomBrainDamange,
8569 cEnvVars, papszEnvVars,
8570 cPostCmdArgs, apszPostCmdArgs);
8571 }
8572 else if (cbMsg == KSIZE_MAX)
8573 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
8574 else
8575 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
8576 }
8577 else
8578 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
8579 }
8580 else
8581 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
8582 }
8583 else
8584 kwErrPrintf("Detected bogus message unpacking environment variables!\n");
8585 kHlpFree((void *)papszEnvVars);
8586 }
8587 else
8588 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
8589 }
8590 else
8591 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
8592 }
8593 else
8594 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
8595 kHlpFree((void *)papszArgs);
8596 }
8597 else
8598 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
8599 }
8600 else
8601 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
8602 }
8603 else
8604 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
8605 }
8606 else
8607 kwErrPrintf("Detected bogus message unpacking executable path!\n");
8608 return rcExit;
8609}
8610
8611
8612/**
8613 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
8614 *
8615 * @retval 0 on success.
8616 * @retval -1 on error (fully bitched).
8617 *
8618 * @param hPipe The pipe handle.
8619 * @param pvBuf The buffer to write out out.
8620 * @param cbToWrite The number of bytes to write.
8621 */
8622static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
8623{
8624 KU8 const *pbBuf = (KU8 const *)pvBuf;
8625 KU32 cbLeft = cbToWrite;
8626 for (;;)
8627 {
8628 DWORD cbActuallyWritten = 0;
8629 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
8630 {
8631 cbLeft -= cbActuallyWritten;
8632 if (!cbLeft)
8633 return 0;
8634 pbBuf += cbActuallyWritten;
8635 }
8636 else
8637 {
8638 DWORD dwErr = GetLastError();
8639 if (cbLeft == cbToWrite)
8640 kwErrPrintf("WriteFile failed: %u\n", dwErr);
8641 else
8642 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
8643 return -1;
8644 }
8645 }
8646}
8647
8648
8649/**
8650 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
8651 *
8652 * @retval 0 on success.
8653 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
8654 * @retval -1 on error (fully bitched).
8655 * @param hPipe The pipe handle.
8656 * @param pvBuf The buffer to read into.
8657 * @param cbToRead The number of bytes to read.
8658 * @param fShutdownOkay Whether connection shutdown while reading the
8659 * first byte is okay or not.
8660 */
8661static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
8662{
8663 KU8 *pbBuf = (KU8 *)pvBuf;
8664 KU32 cbLeft = cbToRead;
8665 for (;;)
8666 {
8667 DWORD cbActuallyRead = 0;
8668 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
8669 {
8670 cbLeft -= cbActuallyRead;
8671 if (!cbLeft)
8672 return 0;
8673 pbBuf += cbActuallyRead;
8674 }
8675 else
8676 {
8677 DWORD dwErr = GetLastError();
8678 if (cbLeft == cbToRead)
8679 {
8680 if ( fMayShutdown
8681 && dwErr == ERROR_BROKEN_PIPE)
8682 return 1;
8683 kwErrPrintf("ReadFile failed: %u\n", dwErr);
8684 }
8685 else
8686 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
8687 return -1;
8688 }
8689 }
8690}
8691
8692
8693/**
8694 * Handles what comes after --test.
8695 *
8696 * @returns Exit code.
8697 * @param argc Number of arguments after --test.
8698 * @param argv Arguments after --test.
8699 */
8700static int kwTestRun(int argc, char **argv)
8701{
8702 int i;
8703 int j;
8704 int rcExit;
8705 int cRepeats;
8706 char szCwd[MAX_PATH];
8707 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
8708 KU32 cEnvVars;
8709 KBOOL fWatcomBrainDamange = K_FALSE;
8710
8711 /*
8712 * Parse arguments.
8713 */
8714 /* Repeat count. */
8715 i = 0;
8716 if (i >= argc)
8717 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
8718 if (strcmp(argv[i], "--") != 0)
8719 {
8720 cRepeats = atoi(argv[i]);
8721 if (cRepeats <= 0)
8722 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
8723 i++;
8724
8725 /* Optional directory change. */
8726 if ( i < argc
8727 && ( strcmp(argv[i], "--chdir") == 0
8728 || strcmp(argv[i], "-C") == 0 ) )
8729 {
8730 i++;
8731 if (i >= argc)
8732 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
8733 pszCwd = argv[i++];
8734 }
8735
8736 /* Optional watcom flag directory change. */
8737 if ( i < argc
8738 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
8739 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
8740 {
8741 fWatcomBrainDamange = K_TRUE;
8742 i++;
8743 }
8744
8745 /* Trigger breakpoint */
8746 if ( i < argc
8747 && strcmp(argv[i], "--breakpoint") == 0)
8748 {
8749 __debugbreak();
8750 i++;
8751 }
8752
8753 /* Check for '--'. */
8754 if (i >= argc)
8755 return kwErrPrintfRc(2, "Missing '--'\n");
8756 if (strcmp(argv[i], "--") != 0)
8757 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
8758 i++;
8759 }
8760 else
8761 {
8762 cRepeats = 1;
8763 i++;
8764 }
8765 if (i >= argc)
8766 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
8767
8768 /*
8769 * Do the job.
8770 */
8771 cEnvVars = 0;
8772 while (environ[cEnvVars] != NULL)
8773 cEnvVars++;
8774
8775 for (j = 0; j < cRepeats; j++)
8776 {
8777 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
8778 argc - i, &argv[i], fWatcomBrainDamange,
8779 cEnvVars, environ,
8780 0, NULL);
8781 KW_LOG(("rcExit=%d\n", rcExit));
8782 kwSandboxCleanupLate(&g_Sandbox);
8783 }
8784
8785# ifdef WITH_LOG_FILE
8786 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
8787 CloseHandle(g_hLogFile);
8788# endif
8789 return rcExit;
8790}
8791
8792
8793int main(int argc, char **argv)
8794{
8795 KSIZE cbMsgBuf = 0;
8796 KU8 *pbMsgBuf = NULL;
8797 int i;
8798 HANDLE hPipe = INVALID_HANDLE_VALUE;
8799 const char *pszTmp;
8800 KFSLOOKUPERROR enmIgnored;
8801#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
8802 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/, kwSandboxVecXcptEmulateChained);
8803#endif
8804#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8805 HANDLE hCurProc = GetCurrentProcess();
8806 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
8807 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
8808 DWORD dwType;
8809#endif
8810
8811 /*
8812 * Register our Control-C and Control-Break handlers.
8813 */
8814 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
8815 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
8816
8817 /*
8818 * Create the cache and mark the temporary directory as using the custom revision.
8819 */
8820 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
8821 if (!g_pFsCache)
8822 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
8823
8824 pszTmp = getenv("TEMP");
8825 if (pszTmp && *pszTmp != '\0')
8826 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
8827 pszTmp = getenv("TMP");
8828 if (pszTmp && *pszTmp != '\0')
8829 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
8830 pszTmp = getenv("TMPDIR");
8831 if (pszTmp && *pszTmp != '\0')
8832 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
8833
8834 /*
8835 * Make g_abDefLdBuf executable.
8836 */
8837 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
8838 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
8839 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
8840
8841#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8842 /*
8843 * Get and duplicate the console handles.
8844 */
8845 /* Standard output. */
8846 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
8847 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
8848 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
8849 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
8850 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
8851 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
8852 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
8853 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
8854 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
8855 g_Sandbox.HandleStdOut.cRefs = 0x10001;
8856 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
8857 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
8858 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
8859 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
8860 {
8861 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
8862 g_Sandbox.cFixedHandles++;
8863 else
8864 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
8865 }
8866 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
8867 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
8868
8869 /* Standard error. */
8870 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
8871 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
8872 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
8873 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
8874 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
8875 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
8876 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
8877 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
8878 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
8879 g_Sandbox.HandleStdErr.cRefs = 0x10001;
8880 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
8881 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
8882 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
8883 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
8884 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
8885 {
8886 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
8887 g_Sandbox.cFixedHandles++;
8888 else
8889 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
8890 }
8891 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
8892 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
8893
8894 /* Combined console buffer. */
8895 if (g_Sandbox.StdErr.fIsConsole)
8896 {
8897 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
8898 g_Sandbox.Combined.uCodepage = GetConsoleCP();
8899 }
8900 else if (g_Sandbox.StdOut.fIsConsole)
8901 {
8902 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
8903 g_Sandbox.Combined.uCodepage = GetConsoleCP();
8904 }
8905 else
8906 {
8907 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
8908 g_Sandbox.Combined.uCodepage = CP_ACP;
8909 }
8910 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
8911#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8912
8913
8914 /*
8915 * Parse arguments.
8916 */
8917 for (i = 1; i < argc; i++)
8918 {
8919 if (strcmp(argv[i], "--pipe") == 0)
8920 {
8921 i++;
8922 if (i < argc)
8923 {
8924 char *pszEnd = NULL;
8925 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
8926 if ( *argv[i]
8927 && pszEnd != NULL
8928 && *pszEnd == '\0'
8929 && u64Value != 0
8930 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
8931 && (uintptr_t)u64Value == u64Value)
8932 hPipe = (HANDLE)(uintptr_t)u64Value;
8933 else
8934 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
8935 }
8936 else
8937 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
8938 }
8939 else if (strcmp(argv[i], "--volatile") == 0)
8940 {
8941 i++;
8942 if (i < argc)
8943 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
8944 else
8945 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
8946 }
8947 else if (strcmp(argv[i], "--test") == 0)
8948 return kwTestRun(argc - i - 1, &argv[i + 1]);
8949 else if ( strcmp(argv[i], "--help") == 0
8950 || strcmp(argv[i], "-h") == 0
8951 || strcmp(argv[i], "-?") == 0)
8952 {
8953 printf("usage: kWorker [--volatile dir] --pipe <pipe-handle>\n"
8954 "usage: kWorker <--help|-h>\n"
8955 "usage: kWorker <--version|-V>\n"
8956 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
8957 "\n"
8958 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
8959 return 0;
8960 }
8961 else if ( strcmp(argv[i], "--version") == 0
8962 || strcmp(argv[i], "-V") == 0)
8963 return kbuild_version(argv[0]);
8964 else
8965 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
8966 }
8967
8968 if (hPipe == INVALID_HANDLE_VALUE)
8969 return kwErrPrintfRc(2, "Missing --pipe <pipe-handle> argument!\n");
8970
8971 /*
8972 * Serve the pipe.
8973 */
8974 for (;;)
8975 {
8976 KU32 cbMsg = 0;
8977 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
8978 if (rc == 0)
8979 {
8980 /* Make sure the message length is within sane bounds. */
8981 if ( cbMsg > 4
8982 && cbMsg <= 256*1024*1024)
8983 {
8984 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
8985 if (cbMsg + 4 <= cbMsgBuf)
8986 { /* likely */ }
8987 else
8988 {
8989 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
8990 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
8991 if (!pbMsgBuf)
8992 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
8993 }
8994
8995 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
8996 *(KU32 *)pbMsgBuf = cbMsg;
8997 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
8998 if (rc == 0)
8999 {
9000 const char *psz;
9001
9002 pbMsgBuf[cbMsg] = '\0';
9003 pbMsgBuf[cbMsg + 1] = '\0';
9004 pbMsgBuf[cbMsg + 2] = '\0';
9005 pbMsgBuf[cbMsg + 3] = '\0';
9006
9007 /* The first string after the header is the command. */
9008 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
9009 if (strcmp(psz, "JOB") == 0)
9010 {
9011 struct
9012 {
9013 KI32 rcExitCode;
9014 KU8 bExiting;
9015 KU8 abZero[3];
9016 } Reply;
9017 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
9018 Reply.bExiting = g_fRestart;
9019 Reply.abZero[0] = 0;
9020 Reply.abZero[1] = 0;
9021 Reply.abZero[2] = 0;
9022 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
9023 if ( rc == 0
9024 && !g_fRestart)
9025 {
9026 kwSandboxCleanupLate(&g_Sandbox);
9027 continue;
9028 }
9029 }
9030 else
9031 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
9032 }
9033 }
9034 else
9035 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
9036 }
9037
9038 /*
9039 * If we're exitting because we're restarting, we need to delay till
9040 * kmk/kSubmit has read the result. Windows documentation says it
9041 * immediately discards pipe buffers once the pipe is broken by the
9042 * server (us). So, We flush the buffer and queues a 1 byte read
9043 * waiting for kSubmit to close the pipe when it receives the
9044 * bExiting = K_TRUE result.
9045 */
9046 if (g_fRestart)
9047 {
9048 KU8 b;
9049 FlushFileBuffers(hPipe);
9050 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
9051 }
9052
9053 CloseHandle(hPipe);
9054#ifdef WITH_LOG_FILE
9055 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
9056 CloseHandle(g_hLogFile);
9057#endif
9058 return rc > 0 ? 0 : 1;
9059 }
9060}
9061
9062
9063/** @page pg_kWorker kSubmit / kWorker
9064 *
9065 * @section sec_kWorker_Motivation Motivation / Inspiration
9066 *
9067 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
9068 * builds on machines "infested" by Anti Virus protection and disk encryption
9069 * software. Build times jumping from 35-40 min to 77-82 min after the machine
9070 * got "infected".
9071 *
9072 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
9073 * mainly a bunch of tiny assembly and C files being compiler a million times.
9074 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
9075 * own toolchain from within the same process, saving a lot of process creation
9076 * and teardown overhead.
9077 *
9078 *
9079 * @section sec_kWorker_kSubmit About kSubmit
9080 *
9081 * When wanting to execute a job in a kWorker instance, it must be submitted
9082 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
9083 * built into kmk and does not exist as an external program. The reason for
9084 * this is that it keep track of the kWorker instances.
9085 *
9086 * The kSubmit command has the --32-bit and --64-bit options for selecting
9087 * between 32-bit and 64-bit worker instance. We generally assume the user of
9088 * the command knows which bit count the executable has, so kSubmit is spared
9089 * the extra work of finding out.
9090 *
9091 * The kSubmit command shares a environment and current directory manipulation
9092 * with the kRedirect command, but not the file redirection. So long no file
9093 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
9094 * hand for tools like OpenWatcom, NASM and YASM which all require environment
9095 * and/or current directory changes to work.
9096 *
9097 * Unlike the kRedirect command, the kSubmit command can also specify an
9098 * internall post command to be executed after the main command succeeds.
9099 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
9100 * information from Microsoft COFF object files and Watcom OMF object files and
9101 * is scheduled to replace kDepIDB.
9102 *
9103 *
9104 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
9105 *
9106 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
9107 * A job request is written by kSubmit and kWorker read, unpacks it and executes
9108 * it. When the job is completed, kWorker writes a short reply with the exit
9109 * code and an internal status indicating whether it is going to restart.
9110 *
9111 * The kWorker intance will reply to kSubmit before completing all the internal
9112 * cleanup work, so as not to delay the next job execution unnecessarily. This
9113 * includes checking its own memory consumption and checking whether it needs
9114 * restarting. So, a decision to restart unfortunately have to wait till after
9115 * the next job has completed. This is a little bit unfortunate if the next job
9116 * requires a lot of memory and kWorker has already leaked/used a lot.
9117 *
9118 *
9119 * @section sec_kWorker_How_Works How kWorker Works
9120 *
9121 * kWorker will load the executable specified by kSubmit into memory and call
9122 * it's entrypoint in a lightly sandbox'ed environment.
9123 *
9124 *
9125 * @subsection ssec_kWorker_Loaing Image loading
9126 *
9127 * kWorker will manually load all the executable images into memory, fix them
9128 * up, and make a copy of the virgin image so it can be restored using memcpy
9129 * the next time it is used.
9130 *
9131 * Imported functions are monitored and replacements used for a few of them.
9132 * These replacements are serve the following purposes:
9133 * - Provide a different command line.
9134 * - Provide a different environment.
9135 * - Intercept process termination.
9136 * - Intercept thread creation (only linker is allowed to create threads).
9137 * - Intercept file reading for caching (header files, ++) as file system
9138 * access is made even slower by anti-virus software.
9139 * - Intercept crypto hash APIs to cache MD5 digests of header files
9140 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
9141 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
9142 * in memory as writing files grows expensive with encryption and
9143 * anti-virus software active.
9144 * - Intercept some file system queries to use the kFsCache instead of
9145 * going to the kernel and slowly worm thru the AV filter driver.
9146 * - Intercept standard output/error and console writes to aggressivly
9147 * buffer the output. The MS CRT does not buffer either when it goes to
9148 * the console, resulting in terrible performance and mixing up output
9149 * with other compile jobs.
9150 * This also allows us to filter out the annoying source file announcements
9151 * by cl.exe.
9152 * - Intercept VirtualAlloc and VirtualFree to prevent
9153 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
9154 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
9155 * the callbacks run after each job.
9156 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
9157 * executables and tools using custom heaps (like the microsoft linker).
9158 * [exectuable images only]
9159 * - Intercept atexit and _onexit registration to be able run them after
9160 * each job instead of crashing as kWorker exits. This also helps avoid
9161 * some leaks. [executable image only]
9162 *
9163 * DLLs falls into two categories, system DLLs which we always load using the
9164 * native loader, and tool DLLs which can be handled like the executable or
9165 * optionally using the native loader. We maintain a hardcoded white listing of
9166 * tool DLLs we trust to load using the native loader.
9167 *
9168 * Imports of natively loaded DLLs are processed too, but we only replace a
9169 * subset of the functions compared to natively loaded excutable and DLL images.
9170 *
9171 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
9172 * This is to speed up job execution.
9173 *
9174 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
9175 * for each job run, but so far this hasn't been necessary.
9176 *
9177 *
9178 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
9179 *
9180 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
9181 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
9182 * intermediate representation between the first (c1/c1xx.dll) and second pass
9183 * (c2.dll).
9184 *
9185 * kWorker helps the compiler as best as it can. Given a little knowledge about
9186 * stable and volatile file system areas, it can do a lot of caching that a
9187 * normal compiler driver cannot easily do when given a single file.
9188 *
9189 *
9190 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
9191 *
9192 * The preprocessor part will open and process header files exactly as they are
9193 * encountered in the source files. If string.h is included by the main source
9194 * and five other header files, it will be searched for (include path), opened,
9195 * read, MD5-summed, and pre-processed six times. The last five times is just a
9196 * waste of time because of the guards or \#pragma once. A smart compiler would
9197 * make a little extra effort and realize this.
9198 *
9199 * kWorker will cache help the preprocessor by remembering places where the
9200 * header was not found with help of kFsCache, and cache the file in memory when
9201 * found. The first part is taken care of by intercepting GetFileAttributesW,
9202 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
9203 * cached, the file is kept open and the CreateFileW call returns a duplicate of
9204 * that handle. An internal handle table is used by ReadFile and CloseFile to
9205 * keep track of intercepted handles (also used for temporary file, temporary
9206 * file mappings, console buffering, and standard out/err buffering).
9207 *
9208 * PS. The header search optimization also comes in handy when cl.exe goes on
9209 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
9210 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
9211 * optionally compile the three pass DLLs as executables during development
9212 * and problem analysis.
9213 *
9214 *
9215 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
9216 *
9217 * The issues of the temporary files is pretty severe on the Dell machine used
9218 * for benchmarking with full AV and encryption. The synthetic benchmark
9219 * improved by 30% when kWorker implemented measures to keep them entirely in
9220 * memory.
9221 *
9222 * kWorker implement these by recognizing the filename pattern in CreateFileW
9223 * and creating/opening the given file as needed. The handle returned is a
9224 * duplicate of the current process, thus giving us a good chance of catching
9225 * API calls we're not intercepting.
9226 *
9227 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
9228 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
9229 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
9230 * UnmapViewOfFile.
9231 *
9232 *
9233 * @section sec_kWorker_Numbers Some measurements.
9234 *
9235 * - r2881 building src/VBox/Runtime:
9236 * - without: 2m01.016388s = 120.016388 s
9237 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
9238 * - r2884 building vbox/debug (r110512):
9239 * - without: 11m14.446609s = 674.446609 s
9240 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
9241 * - r2896 building vbox/debug (r110577):
9242 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
9243 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
9244 * MS Defender as AV):
9245 * - without: 10m24.990389s = 624.990389s
9246 * - with: 08m04.738184s = 484.738184s
9247 * - delta: 624.99s - 484.74s = 140.25s
9248 * - saved: 140.25/624.99 = 22% faster
9249 *
9250 *
9251 * @subsection subsec_kWorker_Early_Numbers Early Experiments
9252 *
9253 * These are some early experiments doing 1024 compilations of
9254 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
9255 * main function:
9256 *
9257 * Skylake (W10/amd64, only stdandard MS defender):
9258 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
9259 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
9260 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
9261 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
9262 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
9263 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
9264 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
9265 *
9266 * Dell (W7/amd64, infected by mcafee):
9267 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
9268 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
9269 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
9270 *
9271 * The command line:
9272 * @code{.cpp}
9273 "\"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"
9274 * @endcode
9275 */
9276
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