VirtualBox

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

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

kWorker: Fully buffered output to pipes and files too (for build box).

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