VirtualBox

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

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

fixes

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