VirtualBox

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

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

kWorker: small executable

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