VirtualBox

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

Last change on this file since 2885 was 2885, checked in by bird, 9 years ago

stats

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette