VirtualBox

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

Last change on this file since 3347 was 3347, checked in by bird, 5 years ago

kWorker: Don't complain about not finding mscoree.dll. The CRT exit code checks for it to allow it before doing ExitProcess, so that mscoree.dll can do ExitProcess if present.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 483.6 KB
Line 
1/* $Id: kWorker.c 3347 2020-05-23 08:28:25Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <[email protected]>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33//#undef NDEBUG
34//#define K_STRICT 1
35//#define KW_LOG_ENABLED
36
37#define PSAPI_VERSION 1
38#include <k/kHlp.h>
39#include <k/kLdr.h>
40
41#include <stdio.h>
42#include <intrin.h>
43#include <setjmp.h>
44#include <ctype.h>
45#include <errno.h>
46#include <process.h>
47
48#include "nt/ntstat.h"
49#include "kbuild_version.h"
50
51#include "nt/ntstuff.h"
52#include "nt/nthlp.h"
53#include <psapi.h>
54
55#include "nt/kFsCache.h"
56#include "nt_fullpath.h"
57#include "quote_argv.h"
58#include "md5.h"
59#include "console.h"
60
61#include "../kmk/kmkbuiltin.h"
62
63
64/*********************************************************************************************************************************
65* Defined Constants And Macros *
66*********************************************************************************************************************************/
67/** @def WITH_TEMP_MEMORY_FILES
68 * Enables temporary memory files for cl.exe. */
69#define WITH_TEMP_MEMORY_FILES
70
71/** @def WITH_HASH_MD5_CACHE
72 * Enables caching of MD5 sums for cl.exe.
73 * This prevents wasting time on rehashing common headers each time
74 * they are included. */
75#define WITH_HASH_MD5_CACHE
76
77/** @def WITH_CRYPT_CTX_REUSE
78 * Enables reusing crypt contexts. The Visual C++ compiler always creates a
79 * context which is only used for MD5 and maybe some random bytes (VS 2010).
80 * So, only create it once and add a reference to it instead of creating new
81 * ones. Saves registry access among other things. */
82#define WITH_CRYPT_CTX_REUSE
83
84/** @def WITH_CONSOLE_OUTPUT_BUFFERING
85 * Enables buffering of all console output as well as removal of annoying
86 * source file echo by cl.exe. */
87#define WITH_CONSOLE_OUTPUT_BUFFERING
88
89/** @def WITH_STD_OUT_ERR_BUFFERING
90 * Enables buffering of standard output and standard error buffer as well as
91 * removal of annoying source file echo by cl.exe. */
92#define WITH_STD_OUT_ERR_BUFFERING
93
94/** @def WITH_LOG_FILE
95 * Log to file instead of stderr. */
96#define WITH_LOG_FILE
97
98/** @def WITH_HISTORY
99 * Keep history of the last jobs. For debugging. */
100#define WITH_HISTORY
101
102/** @def WITH_FIXED_VIRTUAL_ALLOCS
103 * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
104 * there is only one, but an important one, from cl.exe).
105 */
106#if K_ARCH == K_ARCH_X86_32
107# define WITH_FIXED_VIRTUAL_ALLOCS
108#endif
109
110/** @def WITH_PCH_CACHING
111 * Enables read caching of precompiled header files. */
112#if K_ARCH_BITS >= 64
113# define WITH_PCH_CACHING
114#endif
115
116
117#ifndef NDEBUG
118# define KW_LOG_ENABLED
119#endif
120
121/** @def KW_LOG
122 * Generic logging.
123 * @param a Argument list for kwDbgPrintf */
124#ifdef KW_LOG_ENABLED
125# define KW_LOG(a) kwDbgPrintf a
126#else
127# define KW_LOG(a) do { } while (0)
128#endif
129
130/** @def KWLDR_LOG
131 * Loader related logging.
132 * @param a Argument list for kwDbgPrintf */
133#ifdef KW_LOG_ENABLED
134# define KWLDR_LOG(a) kwDbgPrintf a
135#else
136# define KWLDR_LOG(a) do { } while (0)
137#endif
138
139
140/** @def KWFS_LOG
141 * FS cache logging.
142 * @param a Argument list for kwDbgPrintf */
143#ifdef KW_LOG_ENABLED
144# define KWFS_LOG(a) kwDbgPrintf a
145#else
146# define KWFS_LOG(a) do { } while (0)
147#endif
148
149/** @def KWOUT_LOG
150 * Output related logging.
151 * @param a Argument list for kwDbgPrintf */
152#ifdef KW_LOG_ENABLED
153# define KWOUT_LOG(a) kwDbgPrintf a
154#else
155# define KWOUT_LOG(a) do { } while (0)
156#endif
157
158/** @def KWCRYPT_LOG
159 * FS cache logging.
160 * @param a Argument list for kwDbgPrintf */
161#ifdef KW_LOG_ENABLED
162# define KWCRYPT_LOG(a) kwDbgPrintf a
163#else
164# define KWCRYPT_LOG(a) do { } while (0)
165#endif
166
167/** Converts a windows handle to a handle table index.
168 * @note We currently just mask off the 31th bit, and do no shifting or anything
169 * else to create an index of the handle.
170 * @todo consider shifting by 2 or 3. */
171#define KW_HANDLE_TO_INDEX(a_hHandle) ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
172/** Maximum handle value we can deal with. */
173#define KW_HANDLE_MAX 0x20000
174
175/** Max temporary file size (memory backed). */
176#if K_ARCH_BITS >= 64
177# define KWFS_TEMP_FILE_MAX (256*1024*1024)
178#else
179# define KWFS_TEMP_FILE_MAX (64*1024*1024)
180#endif
181
182/** Marks unfinished code. */
183#if 1
184# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
185#else
186# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
187#endif
188
189/** User data key for tools. */
190#define KW_DATA_KEY_TOOL (~(KUPTR)16381)
191/** User data key for a cached file. */
192#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
193
194/** String constant comma length. */
195#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
196
197
198/**
199 * Generate CRT slot wrapper functions.
200 */
201#define CRT_SLOT_FUNCTION_WRAPPER(a_RetTypeAndCallConv, a_FnName, a_aArgsDecl, a_aArgCall) \
202 static a_RetTypeAndCallConv a_FnName##00 a_aArgsDecl { const unsigned iCrtSlot = 0; return a_FnName##_wrapped a_aArgCall; } \
203 static a_RetTypeAndCallConv a_FnName##01 a_aArgsDecl { const unsigned iCrtSlot = 1; return a_FnName##_wrapped a_aArgCall; } \
204 static a_RetTypeAndCallConv a_FnName##02 a_aArgsDecl { const unsigned iCrtSlot = 2; return a_FnName##_wrapped a_aArgCall; } \
205 static a_RetTypeAndCallConv a_FnName##03 a_aArgsDecl { const unsigned iCrtSlot = 3; return a_FnName##_wrapped a_aArgCall; } \
206 static a_RetTypeAndCallConv a_FnName##04 a_aArgsDecl { const unsigned iCrtSlot = 4; return a_FnName##_wrapped a_aArgCall; } \
207 static a_RetTypeAndCallConv a_FnName##05 a_aArgsDecl { const unsigned iCrtSlot = 5; return a_FnName##_wrapped a_aArgCall; } \
208 static a_RetTypeAndCallConv a_FnName##06 a_aArgsDecl { const unsigned iCrtSlot = 6; return a_FnName##_wrapped a_aArgCall; } \
209 static a_RetTypeAndCallConv a_FnName##07 a_aArgsDecl { const unsigned iCrtSlot = 7; return a_FnName##_wrapped a_aArgCall; } \
210 static a_RetTypeAndCallConv a_FnName##08 a_aArgsDecl { const unsigned iCrtSlot = 8; return a_FnName##_wrapped a_aArgCall; } \
211 static a_RetTypeAndCallConv a_FnName##09 a_aArgsDecl { const unsigned iCrtSlot = 9; return a_FnName##_wrapped a_aArgCall; } \
212 static a_RetTypeAndCallConv a_FnName##10 a_aArgsDecl { const unsigned iCrtSlot = 10; return a_FnName##_wrapped a_aArgCall; } \
213 static a_RetTypeAndCallConv a_FnName##11 a_aArgsDecl { const unsigned iCrtSlot = 11; return a_FnName##_wrapped a_aArgCall; } \
214 static a_RetTypeAndCallConv a_FnName##12 a_aArgsDecl { const unsigned iCrtSlot = 12; return a_FnName##_wrapped a_aArgCall; } \
215 static a_RetTypeAndCallConv a_FnName##13 a_aArgsDecl { const unsigned iCrtSlot = 13; return a_FnName##_wrapped a_aArgCall; } \
216 static a_RetTypeAndCallConv a_FnName##14 a_aArgsDecl { const unsigned iCrtSlot = 14; return a_FnName##_wrapped a_aArgCall; } \
217 static a_RetTypeAndCallConv a_FnName##15 a_aArgsDecl { const unsigned iCrtSlot = 15; return a_FnName##_wrapped a_aArgCall; } \
218 static a_RetTypeAndCallConv a_FnName##16 a_aArgsDecl { const unsigned iCrtSlot = 16; return a_FnName##_wrapped a_aArgCall; } \
219 static a_RetTypeAndCallConv a_FnName##17 a_aArgsDecl { const unsigned iCrtSlot = 17; return a_FnName##_wrapped a_aArgCall; } \
220 static a_RetTypeAndCallConv a_FnName##18 a_aArgsDecl { const unsigned iCrtSlot = 18; return a_FnName##_wrapped a_aArgCall; } \
221 static a_RetTypeAndCallConv a_FnName##19 a_aArgsDecl { const unsigned iCrtSlot = 19; return a_FnName##_wrapped a_aArgCall; } \
222 static a_RetTypeAndCallConv a_FnName##20 a_aArgsDecl { const unsigned iCrtSlot = 20; return a_FnName##_wrapped a_aArgCall; } \
223 static a_RetTypeAndCallConv a_FnName##21 a_aArgsDecl { const unsigned iCrtSlot = 21; return a_FnName##_wrapped a_aArgCall; } \
224 static a_RetTypeAndCallConv a_FnName##22 a_aArgsDecl { const unsigned iCrtSlot = 22; return a_FnName##_wrapped a_aArgCall; } \
225 static a_RetTypeAndCallConv a_FnName##23 a_aArgsDecl { const unsigned iCrtSlot = 23; return a_FnName##_wrapped a_aArgCall; } \
226 static a_RetTypeAndCallConv a_FnName##24 a_aArgsDecl { const unsigned iCrtSlot = 24; return a_FnName##_wrapped a_aArgCall; } \
227 static a_RetTypeAndCallConv a_FnName##25 a_aArgsDecl { const unsigned iCrtSlot = 25; return a_FnName##_wrapped a_aArgCall; } \
228 static a_RetTypeAndCallConv a_FnName##26 a_aArgsDecl { const unsigned iCrtSlot = 26; return a_FnName##_wrapped a_aArgCall; } \
229 static a_RetTypeAndCallConv a_FnName##27 a_aArgsDecl { const unsigned iCrtSlot = 27; return a_FnName##_wrapped a_aArgCall; } \
230 static a_RetTypeAndCallConv a_FnName##28 a_aArgsDecl { const unsigned iCrtSlot = 28; return a_FnName##_wrapped a_aArgCall; } \
231 static a_RetTypeAndCallConv a_FnName##29 a_aArgsDecl { const unsigned iCrtSlot = 29; return a_FnName##_wrapped a_aArgCall; } \
232 static a_RetTypeAndCallConv a_FnName##30 a_aArgsDecl { const unsigned iCrtSlot = 30; return a_FnName##_wrapped a_aArgCall; } \
233 static a_RetTypeAndCallConv a_FnName##31 a_aArgsDecl { const unsigned iCrtSlot = 31; return a_FnName##_wrapped a_aArgCall; } \
234 static const KUPTR a_FnName[] = \
235 { \
236 (KUPTR)a_FnName##00, \
237 (KUPTR)a_FnName##01, \
238 (KUPTR)a_FnName##02, \
239 (KUPTR)a_FnName##03, \
240 (KUPTR)a_FnName##04, \
241 (KUPTR)a_FnName##05, \
242 (KUPTR)a_FnName##06, \
243 (KUPTR)a_FnName##07, \
244 (KUPTR)a_FnName##08, \
245 (KUPTR)a_FnName##09, \
246 (KUPTR)a_FnName##10, \
247 (KUPTR)a_FnName##11, \
248 (KUPTR)a_FnName##12, \
249 (KUPTR)a_FnName##13, \
250 (KUPTR)a_FnName##14, \
251 (KUPTR)a_FnName##15, \
252 (KUPTR)a_FnName##16, \
253 (KUPTR)a_FnName##17, \
254 (KUPTR)a_FnName##18, \
255 (KUPTR)a_FnName##19, \
256 (KUPTR)a_FnName##20, \
257 (KUPTR)a_FnName##21, \
258 (KUPTR)a_FnName##22, \
259 (KUPTR)a_FnName##23, \
260 (KUPTR)a_FnName##24, \
261 (KUPTR)a_FnName##25, \
262 (KUPTR)a_FnName##26, \
263 (KUPTR)a_FnName##27, \
264 (KUPTR)a_FnName##28, \
265 (KUPTR)a_FnName##29, \
266 (KUPTR)a_FnName##30, \
267 (KUPTR)a_FnName##31, \
268 }
269
270
271/*********************************************************************************************************************************
272* Structures and Typedefs *
273*********************************************************************************************************************************/
274typedef enum KWLOCATION
275{
276 KWLOCATION_INVALID = 0,
277 KWLOCATION_EXE_DIR,
278 KWLOCATION_IMPORTER_DIR,
279 KWLOCATION_SYSTEM32,
280 KWLOCATION_UNKNOWN_NATIVE,
281 KWLOCATION_UNKNOWN,
282} KWLOCATION;
283
284typedef enum KWMODSTATE
285{
286 KWMODSTATE_INVALID = 0,
287 KWMODSTATE_NEEDS_BITS,
288 KWMODSTATE_NEEDS_INIT,
289 KWMODSTATE_BEING_INITED,
290 KWMODSTATE_INIT_FAILED,
291 KWMODSTATE_READY,
292} KWMODSTATE;
293
294typedef struct KWMODULE *PKWMODULE;
295typedef struct KWMODULE
296{
297 /** Pointer to the next image withe the same hash. */
298 PKWMODULE pNextHash;
299 /** Pointer to the next image in the global list. */
300 PKWMODULE pNextList;
301 /** The normalized path to the image. */
302 const char *pszPath;
303 /** The hash of the program path. */
304 KU32 uHashPath;
305 /** Number of references. */
306 KU32 cRefs;
307 /** UTF-16 version of pszPath. */
308 const wchar_t *pwszPath;
309 /** The offset of the filename in pszPath. */
310 KU16 offFilename;
311 /** The offset of the filename in pwszPath. */
312 KU16 offFilenameW;
313 /** Set if executable. */
314 KBOOL fExe;
315 /** Set if native module entry. */
316 KBOOL fNative;
317 /** Loader module handle. */
318 PKLDRMOD pLdrMod;
319 /** The windows module handle. */
320 HMODULE hOurMod;
321 /** The of the loaded image bits. */
322 KSIZE cbImage;
323 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
324 KU8 iCrtSlot;
325 /** Loop prevention when working the tree. */
326 KBOOL fVisited;
327 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
328 KBOOL fNeedReInit;
329 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
330 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
331 KU8 fReInitOnMsPdbSrvEndpointChange;
332 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
333 char *pszMsPdbSrvEndpoint;
334
335 union
336 {
337 /** Data for a manually loaded image. */
338 struct
339 {
340 /** Where we load the image. */
341 KU8 *pbLoad;
342 /** Virgin copy of the image. */
343 KU8 *pbCopy;
344 /** Ldr pvBits argument. This is NULL till we've successfully resolved
345 * the imports. */
346 void *pvBits;
347 /** The state. */
348 KWMODSTATE enmState;
349#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
350 /** The number of entries in the table. */
351 KU32 cFunctions;
352 /** The function table address (in the copy). */
353 PRUNTIME_FUNCTION paFunctions;
354 /** Set if we've already registered a function table already. */
355 KBOOL fRegisteredFunctionTable;
356#endif
357 /** Set if we share memory with other executables. */
358 KBOOL fUseLdBuf;
359 /** Set after the first whole image copy is done. */
360 KBOOL fCanDoQuick;
361 /** Number of quick copy chunks. */
362 KU8 cQuickCopyChunks;
363 /** Number of quick zero chunks. */
364 KU8 cQuickZeroChunks;
365 /** Quicker image copy instructions that skips non-writable parts when
366 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
367 * image. */
368 struct
369 {
370 /** The copy destination. */
371 KU8 *pbDst;
372 /** The copy source. */
373 KU8 const *pbSrc;
374 /** How much to copy. */
375 KSIZE cbToCopy;
376 } aQuickCopyChunks[3];
377 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
378 struct
379 {
380 /** Where to start zeroing. */
381 KU8 *pbDst;
382 /** How much to zero. */
383 KSIZE cbToZero;
384 } aQuickZeroChunks[3];
385
386 /** TLS index if one was allocated, otherwise KU32_MAX. */
387 KU32 idxTls;
388 /** Offset (RVA) of the TLS initialization data. */
389 KU32 offTlsInitData;
390 /** Number of bytes of TLS initialization data. */
391 KU32 cbTlsInitData;
392 /** Number of allocated bytes for TLS. */
393 KU32 cbTlsAlloc;
394 /** Number of TLS callbacks. */
395 KU32 cTlsCallbacks;
396 /** Offset (RVA) of the TLS callback table. */
397 KU32 offTlsCallbacks;
398
399 /** Number of imported modules. */
400 KSIZE cImpMods;
401 /** Import array (variable size). */
402 PKWMODULE apImpMods[1];
403 } Manual;
404 } u;
405} KWMODULE;
406
407
408typedef struct KWDYNLOAD *PKWDYNLOAD;
409typedef struct KWDYNLOAD
410{
411 /** Pointer to the next in the list. */
412 PKWDYNLOAD pNext;
413
414 /** The module handle we present to the application.
415 * This is the LoadLibraryEx return value for special modules and the
416 * KWMODULE.hOurMod value for the others. */
417 HMODULE hmod;
418
419 /** The module for non-special resource stuff, NULL if special. */
420 PKWMODULE pMod;
421
422 /** The length of the LoadLibary filename. */
423 KSIZE cchRequest;
424 /** The LoadLibrary filename. */
425 char szRequest[1];
426} KWDYNLOAD;
427
428
429/**
430 * GetModuleHandle cache for system modules frequently queried.
431 */
432typedef struct KWGETMODULEHANDLECACHE
433{
434 const char *pszName;
435 const wchar_t *pwszName;
436 KU8 cchName;
437 KU8 cwcName;
438 KBOOL fAlwaysPresent;
439 HANDLE hmod;
440} KWGETMODULEHANDLECACHE;
441typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
442
443
444/**
445 * A cached file.
446 */
447typedef struct KFSWCACHEDFILE
448{
449 /** The user data core. */
450 KFSUSERDATA Core;
451
452 /** Cached file handle. */
453 HANDLE hCached;
454 /** Cached file section handle. */
455 HANDLE hSection;
456 /** Cached file content. */
457 KU8 *pbCached;
458 /** The file size. */
459 KU32 cbCached;
460#ifdef WITH_HASH_MD5_CACHE
461 /** Set if we've got a valid MD5 hash in abMd5Digest. */
462 KBOOL fValidMd5;
463 /** The MD5 digest if fValidMd5 is set. */
464 KU8 abMd5Digest[16];
465#endif
466
467 /** Circular self reference. Prevents the object from ever going away and
468 * keeps it handy for debugging. */
469 PKFSOBJ pFsObj;
470 /** The file path (for debugging). */
471 char szPath[1];
472} KFSWCACHEDFILE;
473/** Pointer to a cached filed. */
474typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
475
476#ifdef WITH_HASH_MD5_CACHE
477
478/** Pointer to a MD5 hash instance. */
479typedef struct KWHASHMD5 *PKWHASHMD5;
480/**
481 * A MD5 hash instance.
482 */
483typedef struct KWHASHMD5
484{
485 /** The magic value. */
486 KUPTR uMagic;
487 /** Pointer to the next hash handle. */
488 PKWHASHMD5 pNext;
489 /** The cached file we've associated this handle with. */
490 PKFSWCACHEDFILE pCachedFile;
491 /** The number of bytes we've hashed. */
492 KU32 cbHashed;
493 /** Set if this has gone wrong. */
494 KBOOL fGoneBad;
495 /** Set if we're in fallback mode (file not cached). */
496 KBOOL fFallbackMode;
497 /** Set if we've already finalized the digest. */
498 KBOOL fFinal;
499 /** The MD5 fallback context. */
500 struct MD5Context Md5Ctx;
501 /** The finalized digest. */
502 KU8 abDigest[16];
503
504} KWHASHMD5;
505/** Magic value for KWHASHMD5::uMagic (Les McCann). */
506# define KWHASHMD5_MAGIC KUPTR_C(0x19350923)
507
508#endif /* WITH_HASH_MD5_CACHE */
509#ifdef WITH_TEMP_MEMORY_FILES
510
511typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
512typedef struct KWFSTEMPFILESEG
513{
514 /** File offset of data. */
515 KU32 offData;
516 /** The size of the buffer pbData points to. */
517 KU32 cbDataAlloc;
518 /** The segment data. */
519 KU8 *pbData;
520} KWFSTEMPFILESEG;
521
522typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
523typedef struct KWFSTEMPFILE
524{
525 /** Pointer to the next temporary file for this run. */
526 PKWFSTEMPFILE pNext;
527 /** The UTF-16 path. (Allocated after this structure.) */
528 const wchar_t *pwszPath;
529 /** The path length. */
530 KU16 cwcPath;
531 /** Number of active handles using this file/mapping (<= 2). */
532 KU8 cActiveHandles;
533 /** Number of active mappings (mapped views) (0 or 1). */
534 KU8 cMappings;
535 /** The amount of space allocated in the segments. */
536 KU32 cbFileAllocated;
537 /** The current file size. */
538 KU32 cbFile;
539 /** The number of segments. */
540 KU32 cSegs;
541 /** Segments making up the file. */
542 PKWFSTEMPFILESEG paSegs;
543} KWFSTEMPFILE;
544
545#endif /* WITH_TEMP_MEMORY_FILES */
546#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
547
548/**
549 * Console line buffer or output full buffer.
550 */
551typedef struct KWOUTPUTSTREAMBUF
552{
553 /** The main output handle. */
554 HANDLE hOutput;
555 /** Our backup handle. */
556 HANDLE hBackup;
557 /** Set if this is a console handle and we're in line buffered mode.
558 * When clear, we may buffer multiple lines, though try flush on line
559 * boundraries when ever possible. */
560 KBOOL fIsConsole;
561 /** Compressed GetFileType result. */
562 KU8 fFileType;
563 KU8 abPadding[2];
564 union
565 {
566 /** Line buffer mode (fIsConsole == K_TRUE). */
567 struct
568 {
569 /** Amount of pending console output in wchar_t's. */
570 KU32 cwcBuf;
571 /** The allocated buffer size. */
572 KU32 cwcBufAlloc;
573 /** Pending console output. */
574 wchar_t *pwcBuf;
575 } Con;
576 /** Fully buffered mode (fIsConsole == K_FALSE). */
577 struct
578 {
579 /** Amount of pending output (in chars). */
580 KU32 cchBuf;
581#ifdef WITH_STD_OUT_ERR_BUFFERING
582 /** The allocated buffer size (in chars). */
583 KU32 cchBufAlloc;
584 /** Pending output. */
585 char *pchBuf;
586#endif
587 } Fully;
588 } u;
589} KWOUTPUTSTREAMBUF;
590/** Pointer to a console line buffer. */
591typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
592
593/**
594 * Combined console buffer of complete lines.
595 */
596typedef struct KWCONSOLEOUTPUT
597{
598 /** The console output handle.
599 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
600 * combined output buffering. */
601 HANDLE hOutput;
602 /** The current code page for the console. */
603 KU32 uCodepage;
604 /** Amount of pending console output in wchar_t's. */
605 KU32 cwcBuf;
606 /** Number of times we've flushed it in any way (for cl.exe hack). */
607 KU32 cFlushes;
608 /** Pending console output. */
609 wchar_t wszBuf[8192];
610} KWCONSOLEOUTPUT;
611/** Pointer to a combined console buffer. */
612typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
613
614#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
615
616/** Handle type. */
617typedef enum KWHANDLETYPE
618{
619 KWHANDLETYPE_INVALID = 0,
620 KWHANDLETYPE_FSOBJ_READ_CACHE,
621 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
622 KWHANDLETYPE_TEMP_FILE,
623 KWHANDLETYPE_TEMP_FILE_MAPPING,
624 KWHANDLETYPE_OUTPUT_BUF
625} KWHANDLETYPE;
626
627/** Handle data. */
628typedef struct KWHANDLE
629{
630 KWHANDLETYPE enmType;
631 /** Number of references */
632 KU32 cRefs;
633 /** The current file offset. */
634 KU32 offFile;
635 /** Handle access. */
636 KU32 dwDesiredAccess;
637 /** The handle. */
638 HANDLE hHandle;
639
640 /** Type specific data. */
641 union
642 {
643 /** The file system object. */
644 PKFSWCACHEDFILE pCachedFile;
645 /** Temporary file handle or mapping handle. */
646 PKWFSTEMPFILE pTempFile;
647#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
648 /** Buffered output stream. */
649 PKWOUTPUTSTREAMBUF pOutBuf;
650#endif
651 } u;
652} KWHANDLE;
653typedef KWHANDLE *PKWHANDLE;
654
655/**
656 * Tracking one of our memory mappings.
657 */
658typedef struct KWMEMMAPPING
659{
660 /** Number of references. */
661 KU32 cRefs;
662 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
663 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
664 KWHANDLETYPE enmType;
665 /** The mapping address. */
666 PVOID pvMapping;
667 /** Type specific data. */
668 union
669 {
670 /** The file system object. */
671 PKFSWCACHEDFILE pCachedFile;
672 /** Temporary file handle or mapping handle. */
673 PKWFSTEMPFILE pTempFile;
674 } u;
675} KWMEMMAPPING;
676/** Pointer to a memory mapping tracker. */
677typedef KWMEMMAPPING *PKWMEMMAPPING;
678
679
680/** Pointer to a VirtualAlloc tracker entry. */
681typedef struct KWVIRTALLOC *PKWVIRTALLOC;
682/**
683 * Tracking an VirtualAlloc allocation.
684 */
685typedef struct KWVIRTALLOC
686{
687 PKWVIRTALLOC pNext;
688 void *pvAlloc;
689 KSIZE cbAlloc;
690 /** This is KU32_MAX if not a preallocated chunk. */
691 KU32 idxPreAllocated;
692} KWVIRTALLOC;
693
694
695/** Pointer to a heap (HeapCreate) tracker entry. */
696typedef struct KWHEAP *PKWHEAP;
697/**
698 * Tracking an heap (HeapCreate)
699 */
700typedef struct KWHEAP
701{
702 PKWHEAP pNext;
703 HANDLE hHeap;
704} KWHEAP;
705
706
707/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
708typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
709/**
710 * Tracking an FlsAlloc/TlsAlloc index.
711 */
712typedef struct KWLOCALSTORAGE
713{
714 PKWLOCALSTORAGE pNext;
715 KU32 idx;
716} KWLOCALSTORAGE;
717
718
719/** Pointer to an at exit callback record */
720typedef struct KWEXITCALLACK *PKWEXITCALLACK;
721/**
722 * At exit callback record.
723 */
724typedef struct KWEXITCALLACK
725{
726 PKWEXITCALLACK pNext;
727 _onexit_t pfnCallback;
728 /** At exit doesn't have an exit code. */
729 KBOOL fAtExit;
730} KWEXITCALLACK;
731
732
733typedef enum KWTOOLTYPE
734{
735 KWTOOLTYPE_INVALID = 0,
736 KWTOOLTYPE_SANDBOXED,
737 KWTOOLTYPE_WATCOM,
738 KWTOOLTYPE_EXEC,
739 KWTOOLTYPE_END
740} KWTOOLTYPE;
741
742typedef enum KWTOOLHINT
743{
744 KWTOOLHINT_INVALID = 0,
745 KWTOOLHINT_NONE,
746 KWTOOLHINT_VISUAL_CPP_CL,
747 KWTOOLHINT_VISUAL_CPP_LINK,
748 KWTOOLHINT_END
749} KWTOOLHINT;
750
751
752/**
753 * A kWorker tool.
754 */
755typedef struct KWTOOL
756{
757 /** The user data core structure. */
758 KFSUSERDATA Core;
759
760 /** The normalized path to the program. */
761 const char *pszPath;
762 /** UTF-16 version of pszPath. */
763 wchar_t const *pwszPath;
764 /** The kind of tool. */
765 KWTOOLTYPE enmType;
766
767 union
768 {
769 struct
770 {
771 /** The main entry point. */
772 KUPTR uMainAddr;
773 /** The executable. */
774 PKWMODULE pExe;
775 /** List of dynamically loaded modules.
776 * These will be kept loaded till the tool is destroyed (if we ever do that). */
777 PKWDYNLOAD pDynLoadHead;
778 /** Module array sorted by hOurMod. */
779 PKWMODULE *papModules;
780 /** Number of entries in papModules. */
781 KU32 cModules;
782
783 /** Tool hint (for hacks and such). */
784 KWTOOLHINT enmHint;
785 } Sandboxed;
786 } u;
787} KWTOOL;
788/** Pointer to a tool. */
789typedef struct KWTOOL *PKWTOOL;
790
791
792typedef struct KWSANDBOX *PKWSANDBOX;
793typedef struct KWSANDBOX
794{
795 /** The tool currently running in the sandbox. */
796 PKWTOOL pTool;
797 /** Jump buffer. */
798 jmp_buf JmpBuf;
799 /** The thread ID of the main thread (owner of JmpBuf). */
800 DWORD idMainThread;
801 /** Copy of the NT TIB of the main thread. */
802 NT_TIB TibMainThread;
803 /** The NT_TIB::ExceptionList value inside the try case.
804 * We restore this prior to the longjmp. */
805 void *pOutXcptListHead;
806 /** The exit code in case of longjmp. */
807 int rcExitCode;
808 /** Set if we're running. */
809 KBOOL fRunning;
810 /** Whether to disable caching of ".pch" files. */
811 KBOOL fNoPchCaching;
812
813 /** The command line. */
814 char *pszCmdLine;
815 /** The UTF-16 command line. */
816 wchar_t *pwszCmdLine;
817 /** Number of arguments in papszArgs. */
818 int cArgs;
819 /** The argument vector. */
820 char **papszArgs;
821 /** The argument vector. */
822 wchar_t **papwszArgs;
823
824 /** The _pgmptr msvcrt variable. */
825 char *pgmptr;
826 /** The _wpgmptr msvcrt variable. */
827 wchar_t *wpgmptr;
828
829 /** The _initenv msvcrt variable. */
830 char **initenv;
831 /** The _winitenv msvcrt variable. */
832 wchar_t **winitenv;
833
834 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
835 KSIZE cEnvVarsAllocated;
836 /** The _environ msvcrt variable. */
837 char **environ;
838 /** The _wenviron msvcrt variable. */
839 wchar_t **wenviron;
840 /** The shadow _environ msvcrt variable. */
841 char **papszEnvVars;
842 /** The shadow _wenviron msvcrt variable. */
843 wchar_t **papwszEnvVars;
844
845
846 /** Handle table. */
847 PKWHANDLE *papHandles;
848 /** Size of the handle table. */
849 KU32 cHandles;
850 /** Number of active handles in the table. */
851 KU32 cActiveHandles;
852 /** Number of handles in the handle table that will not be freed. */
853 KU32 cFixedHandles;
854 /** Total number of leaked handles. */
855 KU32 cLeakedHandles;
856
857 /** Number of active memory mappings in paMemMappings. */
858 KU32 cMemMappings;
859 /** The allocated size of paMemMappings. */
860 KU32 cMemMappingsAlloc;
861 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
862 PKWMEMMAPPING paMemMappings;
863
864 /** Head of the list of temporary file. */
865 PKWFSTEMPFILE pTempFileHead;
866
867 /** Head of the virtual alloc allocations. */
868 PKWVIRTALLOC pVirtualAllocHead;
869 /** Head of the heap list (HeapCreate).
870 * This is only done from images we forcibly restore. */
871 PKWHEAP pHeapHead;
872 /** Head of the FlsAlloc indexes. */
873 PKWLOCALSTORAGE pFlsAllocHead;
874 /** Head of the TlsAlloc indexes. */
875 PKWLOCALSTORAGE pTlsAllocHead;
876
877 /** The at exit callback head.
878 * This is only done from images we forcibly restore. */
879 PKWEXITCALLACK pExitCallbackHead;
880
881 MY_UNICODE_STRING SavedCommandLine;
882
883#ifdef WITH_HASH_MD5_CACHE
884 /** The special MD5 hash instance. */
885 PKWHASHMD5 pHashHead;
886 /** ReadFile sets these while CryptHashData claims and clears them.
887 *
888 * This is part of the heuristics we use for MD5 caching for header files. The
889 * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
890 * header, then passes the same buffer and read byte count to CryptHashData.
891 */
892 struct
893 {
894 /** The cached file last read from. */
895 PKFSWCACHEDFILE pCachedFile;
896 /** The file offset of the last cached read. */
897 KU32 offRead;
898 /** The number of bytes read last. */
899 KU32 cbRead;
900 /** The buffer pointer of the last read. */
901 void *pvRead;
902 } LastHashRead;
903#endif
904
905#ifdef WITH_CRYPT_CTX_REUSE
906 /** Reusable crypt contexts. */
907 struct
908 {
909 /** The creation provider type. */
910 KU32 dwProvType;
911 /** The creation flags. */
912 KU32 dwFlags;
913 /** The length of the container name. */
914 KU32 cwcContainer;
915 /** The length of the provider name. */
916 KU32 cwcProvider;
917 /** The container name string. */
918 wchar_t *pwszContainer;
919 /** The provider name string. */
920 wchar_t *pwszProvider;
921 /** The context handle. */
922 HCRYPTPROV hProv;
923 } aCryptCtxs[4];
924 /** Number of reusable crypt conexts in aCryptCtxs. */
925 KU32 cCryptCtxs;
926#endif
927
928
929#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
930 /** The internal standard output handle. */
931 KWHANDLE HandleStdOut;
932 /** The internal standard error handle. */
933 KWHANDLE HandleStdErr;
934 /** Standard output (and whatever else) buffer. */
935 KWOUTPUTSTREAMBUF StdOut;
936 /** Standard error buffer. */
937 KWOUTPUTSTREAMBUF StdErr;
938 /** Combined buffer of completed lines. */
939 KWCONSOLEOUTPUT Combined;
940#endif
941} KWSANDBOX;
942
943
944/** A CRT slot. */
945typedef struct KWCRTSLOT
946{
947 KU32 iSlot;
948
949 /** The CRT module data. */
950 PKWMODULE pModule;
951 /** Pointer to the malloc function. */
952 void * (__cdecl *pfnMalloc)(size_t);
953
954} KWCRTSLOT;
955typedef KWCRTSLOT *PKWCRTSLOT;
956
957
958/** Replacement function entry. */
959typedef struct KWREPLACEMENTFUNCTION
960{
961 /** The function name. */
962 const char *pszFunction;
963 /** The length of the function name. */
964 KSIZE cchFunction;
965 /** The module name (optional). */
966 const char *pszModule;
967 /** The replacement function, data address or CRT slot function array. */
968 KUPTR pfnReplacement;
969 /** Only replace in the executable.
970 * @todo fix the reinitialization of non-native DLLs! */
971 KBOOL fOnlyExe;
972 /** Set if pfnReplacement points to a CRT slot function array. */
973 KBOOL fCrtSlotArray;
974} KWREPLACEMENTFUNCTION;
975typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
976
977#if 0
978/** Replacement function entry. */
979typedef struct KWREPLACEMENTDATA
980{
981 /** The function name. */
982 const char *pszFunction;
983 /** The length of the function name. */
984 KSIZE cchFunction;
985 /** The module name (optional). */
986 const char *pszModule;
987 /** Function providing the replacement. */
988 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
989} KWREPLACEMENTDATA;
990typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
991#endif
992
993/**
994 * One test job (--full-test).
995 */
996typedef struct KWONETEST
997{
998 /** Where this job originated. */
999 const char *pszJobSrc;
1000 /** The argument number it started with. */
1001 unsigned iJobSrc;
1002 /** Set if virgin, clear if modified. */
1003 KBOOL fVirgin;
1004
1005 /** Number of runs to give it. */
1006 unsigned cRuns;
1007
1008 /** @name kSubmitHandleJobUnpacked arguments
1009 * @{ */
1010 const char *pszExecutable;
1011 const char *pszCwd;
1012 KU32 cArgs;
1013 const char **papszArgs;
1014 KU32 cEnvVars;
1015 const char **papszEnvVars;
1016 const char *pszSpecialEnv;
1017 KBOOL fWatcomBrainDamange;
1018 KBOOL fNoPchCaching;
1019 KU32 cPostCmdArgs;
1020 const char **papszPostCmdArgs;
1021 /** @} */
1022
1023 /** Pointer to the next one. */
1024 struct KWONETEST *pNext;
1025} KWONETEST;
1026/** Pointer to one test job. */
1027typedef KWONETEST *PKWONETEST;
1028
1029
1030/*********************************************************************************************************************************
1031* Global Variables *
1032*********************************************************************************************************************************/
1033/** The sandbox data. */
1034static KWSANDBOX g_Sandbox;
1035
1036/** The module currently occupying g_abDefLdBuf. */
1037static PKWMODULE g_pModInLdBuf = NULL;
1038
1039/** The module that previuosly occupied g_abDefLdBuf. */
1040static PKWMODULE g_pModPrevInLdBuf = NULL;
1041
1042/** Module list head. */
1043static PKWMODULE g_pModuleHead = NULL;
1044/** Where to insert the next module. */
1045static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1046
1047/** Module hash table. */
1048static PKWMODULE g_apModules[127];
1049
1050/** GetModuleHandle cache. */
1051static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1052{
1053#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1054 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1055#if 1
1056 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1057 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1058#endif
1059 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1060};
1061
1062/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1063static PKWMODULE g_pModPendingTlsAlloc = NULL;
1064
1065/** CRT slots.
1066 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1067static KWCRTSLOT g_aCrtSlots[32];
1068
1069/** The file system cache. */
1070static PKFSCACHE g_pFsCache;
1071/** The current directory (referenced). */
1072static PKFSOBJ g_pCurDirObj = NULL;
1073#ifdef KBUILD_OS_WINDOWS
1074/** The windows system32 directory (referenced). */
1075static PKFSDIR g_pWinSys32 = NULL;
1076#endif
1077
1078/** Verbosity level. */
1079static int g_cVerbose = 2;
1080
1081/** Whether we should restart the worker. */
1082static KBOOL g_fRestart = K_FALSE;
1083
1084/** The process group this worker is tied to (--group option), -1 if none. */
1085static KI32 g_iProcessGroup = -1;
1086
1087/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1088static int volatile g_rcCtrlC = 0;
1089
1090/** The communication pipe handle. We break this when we see Ctrl-C such. */
1091#ifdef KBUILD_OS_WINDOWS
1092static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1093#else
1094static int g_hPipe = -1;
1095#endif
1096
1097
1098/* Further down. */
1099extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1100extern KU32 const g_cSandboxReplacements;
1101
1102extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1103extern KU32 const g_cSandboxNativeReplacements;
1104
1105extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1106extern KU32 const g_cSandboxGetProcReplacements;
1107
1108
1109/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1110 * cover the default executable link address of 0x400000.
1111 * @remarks Early main() makes it read+write+executable. Attempts as having
1112 * it as a separate section failed because the linker insists on
1113 * writing out every zero in the uninitialized section, resulting in
1114 * really big binaries. */
1115__declspec(align(0x1000))
1116static KU8 g_abDefLdBuf[16*1024*1024];
1117
1118#ifdef WITH_LOG_FILE
1119/** Log file handle. */
1120static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1121#endif
1122
1123
1124#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1125/** Virtual address space reserved for CL.EXE heap manager.
1126 *
1127 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1128 * address. It's among other things used for precompiled headers, which
1129 * seemingly have addresses hardcoded into them and won't work if mapped
1130 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1131 * for it. (The /Zm option may affect this allocation.)
1132 */
1133static struct
1134{
1135 /** The memory address we need. */
1136 KUPTR const uFixed;
1137 /** How much we need to fix. */
1138 KSIZE const cbFixed;
1139 /** What we actually got, NULL if given back. */
1140 void *pvReserved;
1141 /** Whether it is in use or not. */
1142 KBOOL fInUse;
1143} g_aFixedVirtualAllocs[] =
1144{
1145# if K_ARCH == K_ARCH_X86_32
1146 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1147 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1148 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1149# else
1150 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1151# endif
1152};
1153#endif
1154
1155
1156#ifdef WITH_HISTORY
1157/** The job history. */
1158static char *g_apszHistory[32];
1159/** Index of the next history entry. */
1160static unsigned g_iHistoryNext = 0;
1161#endif
1162
1163
1164/** Number of jobs executed. */
1165static KU32 g_cJobs;
1166/** Number of tools. */
1167static KU32 g_cTools;
1168/** Number of modules. */
1169static KU32 g_cModules;
1170/** Number of non-native modules. */
1171static KU32 g_cNonNativeModules;
1172/** Number of read-cached files. */
1173static KU32 g_cReadCachedFiles;
1174/** Total size of read-cached files. */
1175static KSIZE g_cbReadCachedFiles;
1176
1177/** Total number of ReadFile calls. */
1178static KSIZE g_cReadFileCalls;
1179/** Total bytes read via ReadFile. */
1180static KSIZE g_cbReadFileTotal;
1181/** Total number of read from read-cached files. */
1182static KSIZE g_cReadFileFromReadCached;
1183/** Total bytes read from read-cached files. */
1184static KSIZE g_cbReadFileFromReadCached;
1185/** Total number of read from in-memory temporary files. */
1186static KSIZE g_cReadFileFromInMemTemp;
1187/** Total bytes read from in-memory temporary files. */
1188static KSIZE g_cbReadFileFromInMemTemp;
1189
1190/** Total number of WriteFile calls. */
1191static KSIZE g_cWriteFileCalls;
1192/** Total bytes written via WriteFile. */
1193static KSIZE g_cbWriteFileTotal;
1194/** Total number of written to from in-memory temporary files. */
1195static KSIZE g_cWriteFileToInMemTemp;
1196/** Total bytes written to in-memory temporary files. */
1197static KSIZE g_cbWriteFileToInMemTemp;
1198
1199
1200/*********************************************************************************************************************************
1201* Internal Functions *
1202*********************************************************************************************************************************/
1203static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1204static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1205 const char *pszSearchPath, PKWMODULE *ppMod);
1206static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1207static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1208static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1209#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1210static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1211#endif
1212static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1213
1214
1215
1216
1217/**
1218 * Debug printing.
1219 * @param pszFormat Debug format string.
1220 * @param ... Format argument.
1221 */
1222static void kwDbgPrintfV(const char *pszFormat, va_list va)
1223{
1224 if (g_cVerbose >= 2)
1225 {
1226 DWORD const dwSavedErr = GetLastError();
1227#ifdef WITH_LOG_FILE
1228 DWORD dwIgnored;
1229 char szTmp[2048];
1230 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1231 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1232 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1233 cch += cchPrefix;
1234 else
1235 {
1236 cch = sizeof(szTmp) - 1;
1237 szTmp[cch] = '\0';
1238 }
1239
1240 if (g_hLogFile == INVALID_HANDLE_VALUE)
1241 {
1242 wchar_t wszFilename[128];
1243 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1244 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1245 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1246 }
1247
1248 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1249#else
1250 fprintf(stderr, "debug: ");
1251 vfprintf(stderr, pszFormat, va);
1252#endif
1253
1254 SetLastError(dwSavedErr);
1255 }
1256}
1257
1258
1259/**
1260 * Debug printing.
1261 * @param pszFormat Debug format string.
1262 * @param ... Format argument.
1263 */
1264static void kwDbgPrintf(const char *pszFormat, ...)
1265{
1266 if (g_cVerbose >= 2)
1267 {
1268 va_list va;
1269 va_start(va, pszFormat);
1270 kwDbgPrintfV(pszFormat, va);
1271 va_end(va);
1272 }
1273}
1274
1275
1276/**
1277 * Debugger printing.
1278 * @param pszFormat Debug format string.
1279 * @param ... Format argument.
1280 */
1281static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1282{
1283 if (IsDebuggerPresent())
1284 {
1285 DWORD const dwSavedErr = GetLastError();
1286 char szTmp[2048];
1287
1288 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1289 OutputDebugStringA(szTmp);
1290
1291 SetLastError(dwSavedErr);
1292 }
1293}
1294
1295
1296/**
1297 * Debugger printing.
1298 * @param pszFormat Debug format string.
1299 * @param ... Format argument.
1300 */
1301static void kwDebuggerPrintf(const char *pszFormat, ...)
1302{
1303 va_list va;
1304 va_start(va, pszFormat);
1305 kwDebuggerPrintfV(pszFormat, va);
1306 va_end(va);
1307}
1308
1309
1310
1311/**
1312 * Error printing.
1313 * @param pszFormat Message format string.
1314 * @param ... Format argument.
1315 */
1316static void kwErrPrintfV(const char *pszFormat, va_list va)
1317{
1318 DWORD const dwSavedErr = GetLastError();
1319
1320 fprintf(stderr, "kWorker: error: ");
1321 vfprintf(stderr, pszFormat, va);
1322 fflush(stderr); /* In case it's a pipe. */
1323
1324 SetLastError(dwSavedErr);
1325}
1326
1327
1328/**
1329 * Error printing.
1330 * @param pszFormat Message format string.
1331 * @param ... Format argument.
1332 */
1333static void kwErrPrintf(const char *pszFormat, ...)
1334{
1335 va_list va;
1336 va_start(va, pszFormat);
1337 kwErrPrintfV(pszFormat, va);
1338 va_end(va);
1339}
1340
1341
1342/**
1343 * Error printing.
1344 * @return rc;
1345 * @param rc Return value
1346 * @param pszFormat Message format string.
1347 * @param ... Format argument.
1348 */
1349static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1350{
1351 va_list va;
1352 va_start(va, pszFormat);
1353 kwErrPrintfV(pszFormat, va);
1354 va_end(va);
1355 return rc;
1356}
1357
1358
1359#ifdef K_STRICT
1360
1361KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1362{
1363 DWORD const dwSavedErr = GetLastError();
1364
1365 fprintf(stderr,
1366 "\n"
1367 "!!Assertion failed!!\n"
1368 "Expression: %s\n"
1369 "Function : %s\n"
1370 "File: %s\n"
1371 "Line: %d\n"
1372 , pszExpr, pszFunction, pszFile, iLine);
1373
1374 SetLastError(dwSavedErr);
1375}
1376
1377
1378KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1379{
1380 DWORD const dwSavedErr = GetLastError();
1381 va_list va;
1382
1383 va_start(va, pszFormat);
1384 fprintf(stderr, pszFormat, va);
1385 va_end(va);
1386
1387 SetLastError(dwSavedErr);
1388}
1389
1390#endif /* K_STRICT */
1391
1392
1393/**
1394 * Hashes a string.
1395 *
1396 * @returns 32-bit string hash.
1397 * @param pszString String to hash.
1398 */
1399static KU32 kwStrHash(const char *pszString)
1400{
1401 /* This algorithm was created for sdbm (a public-domain reimplementation of
1402 ndbm) database library. it was found to do well in scrambling bits,
1403 causing better distribution of the keys and fewer splits. it also happens
1404 to be a good general hashing function with good distribution. the actual
1405 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1406 is the faster version used in gawk. [there is even a faster, duff-device
1407 version] the magic constant 65599 was picked out of thin air while
1408 experimenting with different constants, and turns out to be a prime.
1409 this is one of the algorithms used in berkeley db (see sleepycat) and
1410 elsewhere. */
1411 KU32 uHash = 0;
1412 KU32 uChar;
1413 while ((uChar = (unsigned char)*pszString++) != 0)
1414 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1415 return uHash;
1416}
1417
1418
1419/**
1420 * Hashes a string.
1421 *
1422 * @returns The string length.
1423 * @param pszString String to hash.
1424 * @param puHash Where to return the 32-bit string hash.
1425 */
1426static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1427{
1428 const char * const pszStart = pszString;
1429 KU32 uHash = 0;
1430 KU32 uChar;
1431 while ((uChar = (unsigned char)*pszString) != 0)
1432 {
1433 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1434 pszString++;
1435 }
1436 *puHash = uHash;
1437 return pszString - pszStart;
1438}
1439
1440
1441/**
1442 * Hashes a string.
1443 *
1444 * @returns The string length in wchar_t units.
1445 * @param pwszString String to hash.
1446 * @param puHash Where to return the 32-bit string hash.
1447 */
1448static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1449{
1450 const wchar_t * const pwszStart = pwszString;
1451 KU32 uHash = 0;
1452 KU32 uChar;
1453 while ((uChar = *pwszString) != 0)
1454 {
1455 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1456 pwszString++;
1457 }
1458 *puHash = uHash;
1459 return pwszString - pwszStart;
1460}
1461
1462
1463/**
1464 * Converts the given string to unicode.
1465 *
1466 * @returns Length of the resulting string in wchar_t's.
1467 * @param pszSrc The source string.
1468 * @param pwszDst The destination buffer.
1469 * @param cwcDst The size of the destination buffer in wchar_t's.
1470 */
1471static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1472{
1473 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1474 KSIZE offDst = 0;
1475 while (offDst < cwcDst)
1476 {
1477 char ch = *pszSrc++;
1478 pwszDst[offDst++] = ch;
1479 if (!ch)
1480 return offDst - 1;
1481 kHlpAssert((unsigned)ch < 127);
1482 }
1483
1484 pwszDst[offDst - 1] = '\0';
1485 return offDst;
1486}
1487
1488
1489/**
1490 * Converts the given string to UTF-16, allocating the buffer.
1491 *
1492 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1493 * the source string.
1494 * @param pchSrc The source string.
1495 * @param cchSrc The length of the source string.
1496 */
1497static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1498{
1499 DWORD const dwErrSaved = GetLastError();
1500 KSIZE cwcBuf = cchSrc + 1;
1501 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1502 if (pwszBuf)
1503 {
1504 if (cchSrc > 0)
1505 {
1506 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1507 if (cwcRet > 0)
1508 {
1509 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1510 pwszBuf[cwcRet] = '\0';
1511 }
1512 else
1513 {
1514 kHlpFree(pwszBuf);
1515
1516 /* Figure the length and allocate the right buffer size. */
1517 SetLastError(NO_ERROR);
1518 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1519 if (cwcRet)
1520 {
1521 cwcBuf = cwcRet + 2;
1522 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1523 if (pwszBuf)
1524 {
1525 SetLastError(NO_ERROR);
1526 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1527 if (cwcRet)
1528 {
1529 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1530 pwszBuf[cwcRet] = '\0';
1531 }
1532 else
1533 {
1534 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1535 kHlpFree(pwszBuf);
1536 pwszBuf = NULL;
1537 }
1538 }
1539 }
1540 else
1541 {
1542 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1543 pwszBuf = NULL;
1544 }
1545 }
1546 }
1547 else
1548 pwszBuf[0] = '\0';
1549 }
1550 SetLastError(dwErrSaved);
1551 return pwszBuf;
1552}
1553
1554
1555/**
1556 * Converts the given UTF-16 to a normal string.
1557 *
1558 * @returns Length of the resulting string.
1559 * @param pwszSrc The source UTF-16 string.
1560 * @param pszDst The destination buffer.
1561 * @param cbDst The size of the destination buffer in bytes.
1562 */
1563static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1564{
1565 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1566 KSIZE offDst = 0;
1567 while (offDst < cbDst)
1568 {
1569 wchar_t wc = *pwszSrc++;
1570 pszDst[offDst++] = (char)wc;
1571 if (!wc)
1572 return offDst - 1;
1573 kHlpAssert((unsigned)wc < 127);
1574 }
1575
1576 pszDst[offDst - 1] = '\0';
1577 return offDst;
1578}
1579
1580
1581/**
1582 * Converts the given UTF-16 to ASSI, allocating the buffer.
1583 *
1584 * @returns Pointer to the new heap allocation containing the ANSI version of
1585 * the source string.
1586 * @param pwcSrc The source string.
1587 * @param cwcSrc The length of the source string.
1588 */
1589static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1590{
1591 DWORD const dwErrSaved = GetLastError();
1592 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1593 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1594 if (pszBuf)
1595 {
1596 if (cwcSrc > 0)
1597 {
1598 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1599 if (cchRet > 0)
1600 {
1601 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1602 pszBuf[cchRet] = '\0';
1603 }
1604 else
1605 {
1606 kHlpFree(pszBuf);
1607
1608 /* Figure the length and allocate the right buffer size. */
1609 SetLastError(NO_ERROR);
1610 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1611 if (cchRet)
1612 {
1613 cbBuf = cchRet + 2;
1614 pszBuf = (char *)kHlpAlloc(cbBuf);
1615 if (pszBuf)
1616 {
1617 SetLastError(NO_ERROR);
1618 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1619 if (cchRet)
1620 {
1621 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1622 pszBuf[cchRet] = '\0';
1623 }
1624 else
1625 {
1626 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1627 kHlpFree(pszBuf);
1628 pszBuf = NULL;
1629 }
1630 }
1631 }
1632 else
1633 {
1634 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1635 pszBuf = NULL;
1636 }
1637 }
1638 }
1639 else
1640 pszBuf[0] = '\0';
1641 }
1642 SetLastError(dwErrSaved);
1643 return pszBuf;
1644}
1645
1646
1647
1648/** UTF-16 string length. */
1649static KSIZE kwUtf16Len(wchar_t const *pwsz)
1650{
1651 KSIZE cwc = 0;
1652 while (*pwsz != '\0')
1653 cwc++, pwsz++;
1654 return cwc;
1655}
1656
1657/**
1658 * Copy out the UTF-16 string following the convension of GetModuleFileName
1659 */
1660static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1661{
1662 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1663 if (cwcSrc + 1 <= cwcDst)
1664 {
1665 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1666 return (DWORD)cwcSrc;
1667 }
1668 if (cwcDst > 0)
1669 {
1670 KSIZE cwcDstTmp = cwcDst - 1;
1671 pwszDst[cwcDstTmp] = '\0';
1672 if (cwcDstTmp > 0)
1673 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1674 }
1675 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1676 return (DWORD)cwcDst;
1677}
1678
1679
1680/**
1681 * Copy out the ANSI string following the convension of GetModuleFileName
1682 */
1683static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1684{
1685 KSIZE cchSrc = kHlpStrLen(pszSrc);
1686 if (cchSrc + 1 <= cbDst)
1687 {
1688 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1689 return (DWORD)cchSrc;
1690 }
1691 if (cbDst > 0)
1692 {
1693 KSIZE cbDstTmp = cbDst - 1;
1694 pszDst[cbDstTmp] = '\0';
1695 if (cbDstTmp > 0)
1696 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1697 }
1698 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1699 return (DWORD)cbDst;
1700}
1701
1702
1703/**
1704 * Normalizes the path so we get a consistent hash.
1705 *
1706 * @returns status code.
1707 * @param pszPath The path.
1708 * @param pszNormPath The output buffer.
1709 * @param cbNormPath The size of the output buffer.
1710 */
1711static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1712{
1713 KFSLOOKUPERROR enmError;
1714 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1715 if (pFsObj)
1716 {
1717 KBOOL fRc;
1718 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1719 kFsCacheObjRelease(g_pFsCache, pFsObj);
1720 if (fRc)
1721 return 0;
1722 return KERR_BUFFER_OVERFLOW;
1723 }
1724 return KERR_FILE_NOT_FOUND;
1725}
1726
1727
1728/**
1729 * Get the pointer to the filename part of the path.
1730 *
1731 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1732 * @returns Pointer to the terminator char if no filename.
1733 * @param pszPath The path to parse.
1734 */
1735static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1736{
1737 const wchar_t *pwszLast = NULL;
1738 for (;;)
1739 {
1740 wchar_t wc = *pwszPath;
1741#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1742 if (wc == '/' || wc == '\\' || wc == ':')
1743 {
1744 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1745 /* nothing */;
1746 pwszLast = pwszPath;
1747 }
1748#else
1749 if (wc == '/')
1750 {
1751 while ((wc = *++pszFilename) == '/')
1752 /* betsuni */;
1753 pwszLast = pwszPath;
1754 }
1755#endif
1756 if (!wc)
1757 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1758 pwszPath++;
1759 }
1760}
1761
1762
1763
1764/**
1765 * Retains a new reference to the given module
1766 * @returns pMod
1767 * @param pMod The module to retain.
1768 */
1769static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1770{
1771 kHlpAssert(pMod->cRefs > 0);
1772 kHlpAssert(pMod->cRefs < 64);
1773 pMod->cRefs++;
1774 return pMod;
1775}
1776
1777
1778/**
1779 * Releases a module reference.
1780 *
1781 * @param pMod The module to release.
1782 */
1783static void kwLdrModuleRelease(PKWMODULE pMod)
1784{
1785 if (--pMod->cRefs == 0)
1786 {
1787 /* Unlink it from the hash table. */
1788 if (!pMod->fExe)
1789 {
1790 PKWMODULE pPrev = NULL;
1791 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1792 if (g_apModules[idx] == pMod)
1793 g_apModules[idx] = pMod->pNextHash;
1794 else
1795 {
1796 PKWMODULE pPrev = g_apModules[idx];
1797 kHlpAssert(pPrev != NULL);
1798 while (pPrev->pNextHash != pMod)
1799 {
1800 pPrev = pPrev->pNextHash;
1801 kHlpAssert(pPrev != NULL);
1802 }
1803 pPrev->pNextHash = pMod->pNextHash;
1804 }
1805 }
1806
1807 /* Unlink it from the list. */
1808 if (pMod != g_pModuleHead)
1809 {
1810 PKWMODULE pPrev = g_pModuleHead;
1811 while (pPrev)
1812 {
1813 if (pPrev->pNextList == pMod)
1814 {
1815 pPrev->pNextList = pMod->pNextList;
1816 if (!pMod->pNextList)
1817 g_ppModuleNext = &pPrev->pNextList;
1818 break;
1819 }
1820 pPrev = pPrev->pNextList;
1821 }
1822 kHlpAssert(pPrev != NULL);
1823 }
1824 else
1825 {
1826 g_pModuleHead = pMod->pNextList;
1827 if (!pMod->pNextList)
1828 g_ppModuleNext = &g_pModuleHead;
1829 }
1830
1831 /* Release import modules. */
1832 if (!pMod->fNative)
1833 {
1834 KSIZE idx = pMod->u.Manual.cImpMods;
1835 while (idx-- > 0)
1836 if (pMod->u.Manual.apImpMods[idx])
1837 {
1838 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1839 pMod->u.Manual.apImpMods[idx] = NULL;
1840 }
1841 }
1842
1843 /* Free our resources. */
1844 kLdrModClose(pMod->pLdrMod);
1845 pMod->pLdrMod = NULL;
1846
1847 if (!pMod->fNative)
1848 {
1849 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1850 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1851 }
1852
1853 if (pMod->iCrtSlot != KU8_MAX)
1854 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
1855
1856 if (pMod->pszMsPdbSrvEndpoint)
1857 {
1858 kHlpFree(pMod->pszMsPdbSrvEndpoint);
1859 pMod->pszMsPdbSrvEndpoint = NULL;
1860 }
1861
1862 kHlpFree(pMod);
1863 }
1864 else
1865 kHlpAssert(pMod->cRefs < 64);
1866}
1867
1868
1869/**
1870 * Links the module into the module hash table.
1871 *
1872 * @returns pMod
1873 * @param pMod The module to link.
1874 */
1875static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
1876{
1877 if (!pMod->fExe)
1878 {
1879 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1880 pMod->pNextHash = g_apModules[idx];
1881 g_apModules[idx] = pMod;
1882 }
1883
1884 pMod->pNextList = NULL;
1885 *g_ppModuleNext = pMod;
1886 g_ppModuleNext = &pMod->pNextList;
1887
1888 return pMod;
1889}
1890
1891
1892/**
1893 * Replaces imports for this module according to g_aSandboxNativeReplacements.
1894 *
1895 * @param pMod The natively loaded module to process.
1896 */
1897static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
1898{
1899 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
1900 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
1901 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
1902 IMAGE_NT_HEADERS const *pNtHdrs;
1903 IMAGE_DATA_DIRECTORY const *pDirEnt;
1904
1905 kHlpAssert(pMod->fNative);
1906
1907 /*
1908 * Locate the export descriptors.
1909 */
1910 /* MZ header. */
1911 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
1912 {
1913 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
1914 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
1915 }
1916 else
1917 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
1918
1919 /* Check PE header. */
1920 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
1921 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
1922
1923 /* Locate the import descriptor array. */
1924 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
1925 if ( pDirEnt->Size > 0
1926 && pDirEnt->VirtualAddress != 0)
1927 {
1928 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
1929 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
1930 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
1931 KU8 *pbProtRange = NULL;
1932 SIZE_T cbProtRange = 0;
1933 DWORD fOldProt = 0;
1934 KU32 const cbPage = 0x1000;
1935 BOOL fRc;
1936
1937
1938 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
1939 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
1940 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
1941
1942 /*
1943 * Walk the import descriptor array.
1944 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
1945 */
1946 while ( cLeft-- > 0
1947 && pImpDesc->Name > 0
1948 && pImpDesc->FirstThunk > 0)
1949 {
1950 KU32 iThunk;
1951 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
1952 PKWMODULE pImportMod = NULL;
1953 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
1954 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
1955 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
1956 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
1957 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
1958 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
1959 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
1960
1961 /* Iterate the thunks. */
1962 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
1963 {
1964 KUPTR const off = paOrgThunks[iThunk].u1.Function;
1965 kHlpAssertReturnVoid(off < cbImage);
1966 if (!IMAGE_SNAP_BY_ORDINAL(off))
1967 {
1968 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
1969 KSIZE const cchSymbol = kHlpStrLen(pName->Name);
1970 KU32 i = g_cSandboxNativeReplacements;
1971 while (i-- > 0)
1972 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
1973 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
1974 {
1975 if ( !g_aSandboxNativeReplacements[i].pszModule
1976 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
1977 {
1978 KW_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
1979
1980 /* The .rdata section is normally read-only, so we need to make it writable first. */
1981 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
1982 {
1983 /* Restore previous .rdata page. */
1984 if (fOldProt)
1985 {
1986 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
1987 kHlpAssert(fRc);
1988 fOldProt = 0;
1989 }
1990
1991 /* Query attributes for the current .rdata page. */
1992 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
1993 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
1994 kHlpAssert(cbProtRange);
1995 if (cbProtRange)
1996 {
1997 switch (ProtInfo.Protect)
1998 {
1999 case PAGE_READWRITE:
2000 case PAGE_WRITECOPY:
2001 case PAGE_EXECUTE_READWRITE:
2002 case PAGE_EXECUTE_WRITECOPY:
2003 /* Already writable, nothing to do. */
2004 break;
2005
2006 default:
2007 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2008 case PAGE_READONLY:
2009 cbProtRange = cbPage;
2010 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2011 break;
2012
2013 case PAGE_EXECUTE:
2014 case PAGE_EXECUTE_READ:
2015 cbProtRange = cbPage;
2016 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2017 break;
2018 }
2019 kHlpAssertStmt(fRc, fOldProt = 0);
2020 }
2021 }
2022
2023 /*
2024 * Unslotted replacements are simple.
2025 */
2026 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2027 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2028 else
2029 {
2030 /*
2031 * Must find our module entry for this module, possibly creating one.
2032 */
2033 if (!pImportMod)
2034 {
2035 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2036 K_TRUE /*fAlwaysPresent*/);
2037 if (!pImportMod)
2038 {
2039 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2040 pszImport, pMod->pszPath);
2041 break;
2042 }
2043 }
2044 paThunks[iThunk].u1.AddressOfData
2045 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2046 }
2047 break;
2048 }
2049 }
2050 }
2051 }
2052
2053
2054 /* Next import descriptor. */
2055 pImpDesc++;
2056 }
2057
2058
2059 if (fOldProt)
2060 {
2061 DWORD fIgnore = 0;
2062 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2063 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2064 }
2065 }
2066
2067}
2068
2069
2070/**
2071 * Creates a module from a native kLdr module handle.
2072 *
2073 * @returns Module w/ 1 reference on success, NULL on failure.
2074 * @param pLdrMod The native kLdr module.
2075 * @param pszPath The normalized path to the module.
2076 * @param cbPath The module path length with terminator.
2077 * @param uHashPath The module path hash.
2078 * @param fDoReplacements Whether to do import replacements on this
2079 * module.
2080 */
2081static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2082 KBOOL fDoReplacements)
2083{
2084 /*
2085 * Create the entry.
2086 */
2087 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2088 if (pMod)
2089 {
2090 pMod->pwszPath = (wchar_t *)(pMod + 1);
2091 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2092 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2093 pMod->uHashPath = uHashPath;
2094 pMod->cRefs = 1;
2095 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2096 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2097 pMod->fExe = K_FALSE;
2098 pMod->fNative = K_TRUE;
2099 pMod->pLdrMod = pLdrMod;
2100 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2101 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2102 pMod->iCrtSlot = KU8_MAX;
2103 pMod->fNeedReInit = K_FALSE;
2104 pMod->pszMsPdbSrvEndpoint = NULL;
2105 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2106
2107 if (fDoReplacements)
2108 {
2109 DWORD const dwSavedErr = GetLastError();
2110 kwLdrModuleDoNativeImportReplacements(pMod);
2111 SetLastError(dwSavedErr);
2112 }
2113
2114 KW_LOG(("New module: %p LB %#010x %s (native)\n",
2115 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath));
2116 g_cModules++;
2117 return kwLdrModuleLink(pMod);
2118 }
2119 return NULL;
2120}
2121
2122
2123
2124/**
2125 * Creates a module using the native loader.
2126 *
2127 * @returns Module w/ 1 reference on success, NULL on failure.
2128 * @param pszPath The normalized path to the module.
2129 * @param uHashPath The module path hash.
2130 * @param fDoReplacements Whether to do import replacements on this
2131 * module.
2132 */
2133static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2134{
2135 /*
2136 * Open the module and check the type.
2137 */
2138 PKLDRMOD pLdrMod;
2139 int rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2140 if (rc == 0)
2141 {
2142 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, kHlpStrLen(pszPath) + 1,
2143 uHashPath, fDoReplacements);
2144 if (pMod)
2145 return pMod;
2146 kLdrModClose(pLdrMod);
2147 }
2148 return NULL;
2149}
2150
2151
2152/**
2153 * Sets up the quick zero & copy tables for the non-native module.
2154 *
2155 * This is a worker for kwLdrModuleCreateNonNative.
2156 *
2157 * @param pMod The module.
2158 */
2159static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2160{
2161 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2162 KU32 cSegs = pMod->pLdrMod->cSegments;
2163 KU32 iSeg;
2164
2165 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2166 pMod->u.Manual.cQuickCopyChunks = 0;
2167 pMod->u.Manual.cQuickZeroChunks = 0;
2168
2169 for (iSeg = 0; iSeg < cSegs; iSeg++)
2170 switch (paSegs[iSeg].enmProt)
2171 {
2172 case KPROT_READWRITE:
2173 case KPROT_WRITECOPY:
2174 case KPROT_EXECUTE_READWRITE:
2175 case KPROT_EXECUTE_WRITECOPY:
2176 if (paSegs[iSeg].cbMapped)
2177 {
2178 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2179 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2180 {
2181 /*
2182 * Check for trailing zero words.
2183 */
2184 KSIZE cbTrailingZeros;
2185 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2186 && (paSegs[iSeg].cbMapped & 7) == 0
2187 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2188 {
2189 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2190 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2191 KSIZE idxFirstZero = cNatural;
2192 while (idxFirstZero > 0)
2193 if (pauNatural[--idxFirstZero] == 0)
2194 { /* likely */ }
2195 else
2196 {
2197 idxFirstZero++;
2198 break;
2199 }
2200 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2201 if (cbTrailingZeros < 128)
2202 cbTrailingZeros = 0;
2203 }
2204 else
2205 cbTrailingZeros = 0;
2206
2207 /*
2208 * Add quick copy entry.
2209 */
2210 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2211 {
2212 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2213 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2214 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2215 pMod->u.Manual.cQuickCopyChunks = iChunk + 1;
2216 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2217 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2218 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2219 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2220 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2221 }
2222
2223 /*
2224 * Add quick zero entry.
2225 */
2226 if (cbTrailingZeros)
2227 {
2228 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2229 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2230 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2231 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2232 pMod->u.Manual.cQuickZeroChunks = iZero + 1;
2233 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2234 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2235 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2236 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2237 }
2238 }
2239 else
2240 {
2241 /*
2242 * We're out of quick copy table entries, so just copy the whole darn thing.
2243 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2244 */
2245 kHlpAssertFailed();
2246 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2247 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2248 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2249 pMod->u.Manual.cQuickCopyChunks = 1;
2250 KWLDR_LOG(("Quick copy not possible!\n"));
2251 return;
2252 }
2253 }
2254 break;
2255
2256 default:
2257 break;
2258 }
2259}
2260
2261
2262/**
2263 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2264 *
2265 * @param hDll The DLL handle.
2266 * @param idxTls The allocated TLS index.
2267 * @param ppfnTlsCallback Pointer to the TLS callback table entry.
2268 */
2269__declspec(dllexport) void kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, PIMAGE_TLS_CALLBACK *ppfnTlsCallback)
2270{
2271 /*
2272 * Do the module initialization thing first.
2273 */
2274 PKWMODULE pMod = g_pModPendingTlsAlloc;
2275 if (pMod)
2276 {
2277 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2278 LIST_ENTRY *pHead;
2279 LIST_ENTRY *pCur;
2280
2281 pMod->u.Manual.idxTls = idxTls;
2282 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2283
2284 /*
2285 * Try sabotage the DLL name so we can load this module again.
2286 */
2287 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2288 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2289 {
2290 LDR_DATA_TABLE_ENTRY *pMte;
2291 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2292 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2293 {
2294 PUNICODE_STRING pStr = &pMte->FullDllName;
2295 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2296 pStr->Buffer[--off]++;
2297 pStr->Buffer[--off]++;
2298 pStr->Buffer[--off]++;
2299 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2300 break;
2301 }
2302 }
2303 }
2304}
2305
2306
2307/**
2308 * Allocates and initializes TLS variables.
2309 *
2310 * @returns 0 on success, non-zero failure.
2311 * @param pMod The module.
2312 */
2313static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2314{
2315 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2316 IMAGE_NT_HEADERS const *pNtHdrs;
2317 IMAGE_DATA_DIRECTORY const *pTlsDir;
2318
2319 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2320 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2321 else
2322 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2323 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2324
2325 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2326 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2327 {
2328 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2329 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2330 KU32 iEntry;
2331 KUPTR offIndex;
2332 KUPTR offCallbacks;
2333 KUPTR const *puCallbacks;
2334 KSIZE cbData;
2335 const wchar_t *pwszTlsDll;
2336 HMODULE hmodTlsDll;
2337
2338 /*
2339 * Check and log.
2340 */
2341 for (iEntry = 0; iEntry < cEntries; iEntry++)
2342 {
2343 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2344 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2345 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2346 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2347 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2348 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2349 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2350
2351 if (offIndex >= pMod->cbImage)
2352 {
2353 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2354 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2355 return -1;
2356 }
2357 if (offCallbacks >= pMod->cbImage)
2358 {
2359 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2360 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2361 return -1;
2362 }
2363 while (*puCallbacks != 0)
2364 {
2365 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2366 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2367 puCallbacks++;
2368 }
2369 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2370 {
2371 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2372 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2373 return -1;
2374 }
2375 }
2376
2377 if (cEntries > 1)
2378 {
2379 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2380 return -1;
2381 }
2382
2383 /*
2384 * Make the allocation by loading a new instance of one of the TLS dlls.
2385 * The DLL will make a call to
2386 */
2387 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2388 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2389 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2390 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2391 if (cbData <= 1024)
2392 pwszTlsDll = L"kWorkerTls1K.dll";
2393 else if (cbData <= 65536)
2394 pwszTlsDll = L"kWorkerTls64K.dll";
2395 else if (cbData <= 524288)
2396 pwszTlsDll = L"kWorkerTls512K.dll";
2397 else
2398 {
2399 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2400 return -1;
2401 }
2402
2403 pMod->u.Manual.idxTls = KU32_MAX;
2404 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2405 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2406 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2407 pMod->u.Manual.cTlsCallbacks = 0;
2408 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2409 pMod->u.Manual.cTlsCallbacks++;
2410 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2411
2412 g_pModPendingTlsAlloc = pMod;
2413 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2414 g_pModPendingTlsAlloc = NULL;
2415 if (hmodTlsDll == NULL)
2416 {
2417 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2418 return -1;
2419 }
2420 if (pMod->u.Manual.idxTls == KU32_MAX)
2421 {
2422 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2423 return -1;
2424 }
2425
2426 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2427 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x\n",
2428 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData));
2429 }
2430 return 0;
2431}
2432
2433
2434/**
2435 * Creates a module using the our own loader.
2436 *
2437 * @returns Module w/ 1 reference on success, NULL on failure.
2438 * @param pszPath The normalized path to the module.
2439 * @param uHashPath The module path hash.
2440 * @param fExe K_TRUE if this is an executable image, K_FALSE
2441 * if not. Executable images does not get entered
2442 * into the global module table.
2443 * @param pExeMod The executable module of the process (for
2444 * resolving imports). NULL if fExe is set.
2445 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2446 */
2447static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2448 PKWMODULE pExeMod, const char *pszSearchPath)
2449{
2450 /*
2451 * Open the module and check the type.
2452 */
2453 PKLDRMOD pLdrMod;
2454 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2455 if (rc == 0)
2456 {
2457 switch (pLdrMod->enmType)
2458 {
2459 case KLDRTYPE_EXECUTABLE_FIXED:
2460 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2461 case KLDRTYPE_EXECUTABLE_PIC:
2462 if (!fExe)
2463 rc = KERR_GENERAL_FAILURE;
2464 break;
2465
2466 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2467 case KLDRTYPE_SHARED_LIBRARY_PIC:
2468 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2469 if (fExe)
2470 rc = KERR_GENERAL_FAILURE;
2471 break;
2472
2473 default:
2474 rc = KERR_GENERAL_FAILURE;
2475 break;
2476 }
2477 if (rc == 0)
2478 {
2479 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2480 if (cImports >= 0)
2481 {
2482 /*
2483 * Create the entry.
2484 */
2485 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2486 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2487 + sizeof(pMod) * cImports
2488 + cbPath
2489 + cbPath * 2 * sizeof(wchar_t));
2490 if (pMod)
2491 {
2492 KBOOL fFixed;
2493
2494 pMod->cRefs = 1;
2495 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2496 pMod->uHashPath = uHashPath;
2497 pMod->fExe = fExe;
2498 pMod->fNative = K_FALSE;
2499 pMod->pLdrMod = pLdrMod;
2500 pMod->iCrtSlot = KU8_MAX;
2501 pMod->fNeedReInit = K_FALSE;
2502 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2503 pMod->pszMsPdbSrvEndpoint = NULL;
2504 pMod->u.Manual.cImpMods = (KU32)cImports;
2505#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2506 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2507#endif
2508 pMod->u.Manual.fUseLdBuf = K_FALSE;
2509 pMod->u.Manual.fCanDoQuick = K_FALSE;
2510 pMod->u.Manual.cQuickZeroChunks = 0;
2511 pMod->u.Manual.cQuickCopyChunks = 0;
2512 pMod->u.Manual.idxTls = KU32_MAX;
2513 pMod->u.Manual.offTlsInitData = KU32_MAX;
2514 pMod->u.Manual.cbTlsInitData = 0;
2515 pMod->u.Manual.cbTlsAlloc = 0;
2516 pMod->u.Manual.cTlsCallbacks = 0;
2517 pMod->u.Manual.offTlsCallbacks = 0;
2518 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2519 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2520 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2521 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2522
2523 /*
2524 * Figure out where to load it and get memory there.
2525 */
2526 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2527 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2528 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2529 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2530 if ( !fFixed
2531 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2532 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2533 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2534 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2535 else
2536 pMod->u.Manual.fUseLdBuf = K_TRUE;
2537 if (rc == 0)
2538 {
2539 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2540 if (rc == 0)
2541 {
2542 KI32 iImp;
2543
2544 /*
2545 * Link the module (unless it's an executable image) and process the imports.
2546 */
2547 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2548 kwLdrModuleLink(pMod);
2549 KW_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2550 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2551 KW_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2552 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2553
2554 for (iImp = 0; iImp < cImports; iImp++)
2555 {
2556 char szName[1024];
2557 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2558 if (rc == 0)
2559 {
2560 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2561 &pMod->u.Manual.apImpMods[iImp]);
2562 if (rc == 0)
2563 continue;
2564 }
2565 break;
2566 }
2567
2568 if (rc == 0)
2569 {
2570 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2571 kwLdrModuleGetImportCallback, pMod);
2572 if (rc == 0)
2573 {
2574#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2575 /*
2576 * Find the function table. No validation here because the
2577 * loader did that already, right...
2578 */
2579 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2580 IMAGE_NT_HEADERS const *pNtHdrs;
2581 IMAGE_DATA_DIRECTORY const *pXcptDir;
2582 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2583 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2584 else
2585 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2586 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2587 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2588 if (pXcptDir->Size > 0)
2589 {
2590 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2591 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2592 == pXcptDir->Size);
2593 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2594 }
2595 else
2596 {
2597 pMod->u.Manual.cFunctions = 0;
2598 pMod->u.Manual.paFunctions = NULL;
2599 }
2600#endif
2601
2602 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2603
2604 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2605 if (rc == 0)
2606 {
2607 /*
2608 * Final finish.
2609 */
2610 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2611 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2612 g_cModules++;
2613 g_cNonNativeModules++;
2614 return pMod;
2615 }
2616 }
2617 else
2618 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
2619 }
2620
2621 kwLdrModuleRelease(pMod);
2622 return NULL;
2623 }
2624
2625 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2626 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2627 }
2628 else if (fFixed)
2629 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
2630 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
2631 else
2632 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2633 }
2634 }
2635 }
2636 kLdrModClose(pLdrMod);
2637 }
2638 else
2639 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
2640 return NULL;
2641}
2642
2643
2644/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
2645static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
2646 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
2647{
2648 PKWMODULE pCurMod = (PKWMODULE)pvUser;
2649 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
2650 int rc;
2651 K_NOREF(pMod);
2652
2653 if (pImpMod->fNative)
2654 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
2655 iSymbol, pchSymbol, cchSymbol, pszVersion,
2656 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2657 puValue, pfKind);
2658 else
2659 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
2660 iSymbol, pchSymbol, cchSymbol, pszVersion,
2661 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2662 puValue, pfKind);
2663 if (rc == 0)
2664 {
2665 KU32 i = g_cSandboxReplacements;
2666 while (i-- > 0)
2667 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
2668 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
2669 {
2670 if ( !g_aSandboxReplacements[i].pszModule
2671 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
2672 {
2673 if ( pCurMod->fExe
2674 || !g_aSandboxReplacements[i].fOnlyExe)
2675 {
2676 KW_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
2677 *puValue = g_aSandboxReplacements[i].pfnReplacement;
2678 }
2679 break;
2680 }
2681 }
2682 }
2683
2684 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
2685 KW_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
2686 return rc;
2687
2688}
2689
2690
2691/**
2692 * Gets the main entrypoint for a module.
2693 *
2694 * @returns 0 on success, KERR on failure
2695 * @param pMod The module.
2696 * @param puAddrMain Where to return the address.
2697 */
2698static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
2699{
2700 KLDRADDR uLdrAddrMain;
2701 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
2702 if (rc == 0)
2703 {
2704 *puAddrMain = (KUPTR)uLdrAddrMain;
2705 return 0;
2706 }
2707 return rc;
2708}
2709
2710
2711/**
2712 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
2713 *
2714 * @returns K_TRUE/K_FALSE.
2715 * @param pszFilename The filename (no path).
2716 * @param enmLocation The location.
2717 */
2718static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
2719{
2720 if (enmLocation != KWLOCATION_SYSTEM32)
2721 return K_TRUE;
2722 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2723 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2724 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2725}
2726
2727
2728/**
2729 * Lazily initializes the g_pWinSys32 variable.
2730 */
2731static PKFSDIR kwLdrResolveWinSys32(void)
2732{
2733 KFSLOOKUPERROR enmError;
2734 PKFSDIR pWinSys32;
2735
2736 /* Get the path first. */
2737 char szSystem32[MAX_PATH];
2738 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
2739 {
2740 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
2741 strcpy(szSystem32, "C:\\Windows\\System32");
2742 }
2743
2744 /* Look it up and verify it. */
2745 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
2746 if (pWinSys32)
2747 {
2748 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
2749 {
2750 g_pWinSys32 = pWinSys32;
2751 return pWinSys32;
2752 }
2753
2754 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
2755 }
2756 else
2757 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
2758 return NULL;
2759}
2760
2761
2762/**
2763 * Whether we can load this DLL natively or not.
2764 *
2765 * @returns K_TRUE/K_FALSE.
2766 * @param pszFilename The filename (no path).
2767 * @param enmLocation The location.
2768 * @param pszFullPath The full filename and path.
2769 */
2770static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
2771{
2772 if (enmLocation == KWLOCATION_SYSTEM32)
2773 return K_TRUE;
2774 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
2775 return K_TRUE;
2776
2777 /* If the location is unknown, we must check if it's some dynamic loading
2778 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
2779 if (enmLocation == KWLOCATION_UNKNOWN)
2780 {
2781 PKFSDIR pWinSys32 = g_pWinSys32;
2782 if (!pWinSys32)
2783 pWinSys32 = kwLdrResolveWinSys32();
2784 if (pWinSys32)
2785 {
2786 KFSLOOKUPERROR enmError;
2787 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
2788 if (pFsObj)
2789 {
2790 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
2791 kFsCacheObjRelease(g_pFsCache, pFsObj);
2792 if (fInWinSys32)
2793 return K_TRUE;
2794 }
2795 }
2796 }
2797
2798 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2799 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2800 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2801}
2802
2803
2804/**
2805 * Check if the path leads to a regular file (that exists).
2806 *
2807 * @returns K_TRUE / K_FALSE
2808 * @param pszPath Path to the file to check out.
2809 */
2810static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
2811{
2812 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
2813 KSIZE cchPath = kHlpStrLen(pszPath);
2814 if ( cchPath > 3
2815 && pszPath[cchPath - 4] == '.'
2816 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
2817 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
2818 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
2819 {
2820 KFSLOOKUPERROR enmError;
2821 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
2822 if (pFsObj)
2823 {
2824 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
2825 kFsCacheObjRelease(g_pFsCache, pFsObj);
2826 return fRc;
2827 }
2828 }
2829 else
2830 {
2831 BirdStat_T Stat;
2832 int rc = birdStatFollowLink(pszPath, &Stat);
2833 if (rc == 0)
2834 {
2835 if (S_ISREG(Stat.st_mode))
2836 return K_TRUE;
2837 }
2838 }
2839 return K_FALSE;
2840}
2841
2842
2843/**
2844 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
2845 *
2846 * If the file exists, we consult the module hash table before trying to load it
2847 * off the disk.
2848 *
2849 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
2850 * failure.
2851 * @param pszPath The name of the import module.
2852 * @param enmLocation The location we're searching. This is used in
2853 * the heuristics for determining if we can use the
2854 * native loader or need to sandbox the DLL.
2855 * @param pExe The executable (optional).
2856 * @param pszSearchPath The PATH to search (optional).
2857 */
2858static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
2859{
2860 /*
2861 * Does the file exists and is it a regular file?
2862 */
2863 if (kwLdrModuleIsRegularFile(pszPath))
2864 {
2865 /*
2866 * Yes! Normalize it and look it up in the hash table.
2867 */
2868 char szNormPath[1024];
2869 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
2870 if (rc == 0)
2871 {
2872 const char *pszName;
2873 KU32 const uHashPath = kwStrHash(szNormPath);
2874 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2875 PKWMODULE pMod = g_apModules[idxHash];
2876 if (pMod)
2877 {
2878 do
2879 {
2880 if ( pMod->uHashPath == uHashPath
2881 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
2882 return kwLdrModuleRetain(pMod);
2883 pMod = pMod->pNextHash;
2884 } while (pMod);
2885 }
2886
2887 /*
2888 * Not in the hash table, so we have to load it from scratch.
2889 */
2890 pszName = kHlpGetFilename(szNormPath);
2891 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
2892 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
2893 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
2894 else
2895 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
2896 if (pMod)
2897 return pMod;
2898 return (PKWMODULE)~(KUPTR)0;
2899 }
2900 }
2901 return NULL;
2902}
2903
2904
2905/**
2906 * Gets a reference to the module by the given name.
2907 *
2908 * We must do the search path thing, as our hash table may multiple DLLs with
2909 * the same base name due to different tools version and similar. We'll use a
2910 * modified search sequence, though. No point in searching the current
2911 * directory for instance.
2912 *
2913 * @returns 0 on success, KERR on failure.
2914 * @param pszName The name of the import module.
2915 * @param pExe The executable (optional).
2916 * @param pImporter The module doing the importing (optional).
2917 * @param pszSearchPath The PATH to search (optional).
2918 * @param ppMod Where to return the module pointer w/ reference.
2919 */
2920static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
2921 const char *pszSearchPath, PKWMODULE *ppMod)
2922{
2923 KSIZE const cchName = kHlpStrLen(pszName);
2924 char szPath[1024];
2925 char *psz;
2926 PKWMODULE pMod = NULL;
2927 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
2928 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
2929
2930
2931 /* The import path. */
2932 if (pMod == NULL && pImporter != NULL)
2933 {
2934 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
2935 return KERR_BUFFER_OVERFLOW;
2936
2937 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
2938 if (fNeedSuffix)
2939 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2940 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
2941 }
2942
2943 /* Application directory first. */
2944 if (pMod == NULL && pExe != NULL && pExe != pImporter)
2945 {
2946 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
2947 return KERR_BUFFER_OVERFLOW;
2948 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
2949 if (fNeedSuffix)
2950 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2951 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
2952 }
2953
2954 /* The windows directory. */
2955 if (pMod == NULL)
2956 {
2957 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
2958 if ( cchDir <= 2
2959 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
2960 return KERR_BUFFER_OVERFLOW;
2961 szPath[cchDir++] = '\\';
2962 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
2963 if (fNeedSuffix)
2964 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2965 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2966 }
2967
2968 /* The path. */
2969 if ( pMod == NULL
2970 && pszSearchPath)
2971 {
2972 const char *pszCur = pszSearchPath;
2973 while (*pszCur != '\0')
2974 {
2975 /* Find the end of the component */
2976 KSIZE cch = 0;
2977 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
2978 cch++;
2979
2980 if ( cch > 0 /* wrong, but whatever */
2981 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
2982 {
2983 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
2984 if ( szPath[cch - 1] != ':'
2985 && szPath[cch - 1] != '/'
2986 && szPath[cch - 1] != '\\')
2987 *pszDst++ = '\\';
2988 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
2989 if (fNeedSuffix)
2990 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
2991 *pszDst = '\0';
2992
2993 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2994 if (pMod)
2995 break;
2996 }
2997
2998 /* Advance */
2999 pszCur += cch;
3000 while (*pszCur == ';')
3001 pszCur++;
3002 }
3003 }
3004
3005 /* Return. */
3006 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3007 {
3008 *ppMod = pMod;
3009 return 0;
3010 }
3011 *ppMod = NULL;
3012 return KERR_GENERAL_FAILURE;
3013}
3014
3015
3016/**
3017 * Creates a CRT slot for the given module.
3018 *
3019 * @returns 0 on success, non-zero on failure.
3020 * @param pModule The module.
3021 */
3022static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3023{
3024 KSIZE iSlot;
3025 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3026 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3027 if (g_aCrtSlots[iSlot].pModule == NULL)
3028 {
3029 KLDRADDR uAddr;
3030 int rc;
3031
3032 /* Do the linking: */
3033 g_aCrtSlots[iSlot].pModule = pModule;
3034 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3035 pModule->iCrtSlot = (KU8)iSlot;
3036
3037 /* resolve symbols: */
3038 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3039 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3040 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3041 if (rc != 0)
3042 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3043
3044 return 0;
3045 }
3046 kwErrPrintf("Out of CRT slots!\n");
3047 return KERR_NO_MEMORY;
3048}
3049
3050
3051/**
3052 * Locates the module structure for an already loaded native module.
3053 *
3054 * This will create a module structure if needed.
3055 *
3056 * @returns Pointer to the module structure on success, NULL on failure.
3057 * @param pszName The name of the module.
3058 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3059 * @param fAlwaysPresent Whether the module is expected to always be present,
3060 * or not. If not, complain less.
3061 */
3062static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3063{
3064 /*
3065 * Locate the module and get a normalized path for it.
3066 */
3067 HANDLE hModule = GetModuleHandleA(pszName);
3068 if (hModule)
3069 {
3070 char szModPath[1024];
3071 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3072 {
3073 char szNormPath[1024];
3074 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3075 if (rc == 0)
3076 {
3077 /*
3078 * Hash the path and look it up.
3079 */
3080 KU32 uHashPath;
3081 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3082 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3083 PKWMODULE pMod = g_apModules[idxHash];
3084 if (pMod)
3085 {
3086 do
3087 {
3088 if ( pMod->uHashPath == uHashPath
3089 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3090 {
3091 kwLdrModuleRetain(pMod);
3092 break;
3093 }
3094 pMod = pMod->pNextHash;
3095 } while (pMod);
3096 }
3097
3098 /*
3099 * If not in the hash table, so create a module entry.
3100 */
3101 if (!pMod)
3102 {
3103 PKLDRMOD pLdrMod;
3104 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3105 if (rc == 0)
3106 {
3107 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3108 K_FALSE /*fDoReplacements*/);
3109 if (!pMod)
3110 {
3111 kLdrModClose(pLdrMod);
3112 kwErrPrintf("out of memory\n");
3113 }
3114 }
3115 else
3116 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
3117 }
3118 if (pMod)
3119 {
3120 /*
3121 * Create a CRT slot for the module if necessary.
3122 */
3123 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3124 return pMod;
3125 rc = kwLdrModuleCreateCrtSlot(pMod);
3126 if (rc == 0)
3127 return pMod;
3128 kwLdrModuleRelease(pMod);
3129 }
3130 }
3131 else
3132 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszName, GetLastError());
3133 }
3134 else
3135 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszName, GetLastError());
3136 }
3137 else if (fAlwaysPresent)
3138 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3139 return NULL;
3140}
3141
3142
3143/**
3144 * Does the TLS memory initialization for a module on the current thread.
3145 *
3146 * @returns 0 on success, error on failure.
3147 * @param pMod The module.
3148 */
3149static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3150{
3151 if (pMod->u.Manual.idxTls != KU32_MAX)
3152 {
3153 PTEB pTeb = NtCurrentTeb();
3154 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3155 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3156 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3157 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3158 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3159 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3160 if (pMod->u.Manual.cbTlsInitData)
3161 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3162 }
3163 return 0;
3164}
3165
3166
3167/**
3168 * Does the TLS callbacks for a module.
3169 *
3170 * @param pMod The module.
3171 * @param dwReason The callback reason.
3172 */
3173static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3174{
3175 if (pMod->u.Manual.cTlsCallbacks)
3176 {
3177 PIMAGE_TLS_CALLBACK *pCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3178 do
3179 {
3180 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *pCallback, pMod->hOurMod, dwReason));
3181 (*pCallback)(pMod->hOurMod, dwReason, 0);
3182 } while (*++pCallback);
3183 }
3184}
3185
3186
3187/**
3188 * Does module initialization starting at @a pMod.
3189 *
3190 * This is initially used on the executable. Later it is used by the
3191 * LoadLibrary interceptor.
3192 *
3193 * @returns 0 on success, error on failure.
3194 * @param pMod The module to initialize.
3195 */
3196static int kwLdrModuleInitTree(PKWMODULE pMod)
3197{
3198 int rc = 0;
3199 if (!pMod->fNative)
3200 {
3201 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3202 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3203
3204 /*
3205 * Need to copy bits?
3206 */
3207 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3208 {
3209 if (pMod->u.Manual.fUseLdBuf)
3210 {
3211#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3212 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3213 {
3214 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3215 kHlpAssert(fRc); K_NOREF(fRc);
3216 }
3217#endif
3218 g_pModPrevInLdBuf = g_pModInLdBuf;
3219 g_pModInLdBuf = pMod;
3220 }
3221
3222 /* Do quick zeroing and copying when we can. */
3223 pMod->u.Manual.fCanDoQuick = K_FALSE;
3224 if ( pMod->u.Manual.fCanDoQuick
3225 && ( !pMod->u.Manual.fUseLdBuf
3226 || g_pModPrevInLdBuf == pMod))
3227 {
3228 /* Zero first. */
3229 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3230 switch (pMod->u.Manual.cQuickZeroChunks)
3231 {
3232 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3233 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3234 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3235 case 0: break;
3236 }
3237
3238 /* Then copy. */
3239 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3240 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3241 switch (pMod->u.Manual.cQuickCopyChunks)
3242 {
3243 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3244 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3245 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3246 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3247 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3248 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3249 case 0: break;
3250 }
3251 }
3252 /* Must copy the whole image. */
3253 else
3254 {
3255 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3256 pMod->u.Manual.fCanDoQuick = K_TRUE;
3257 }
3258 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3259 }
3260
3261#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3262 /*
3263 * Need to register function table?
3264 */
3265 if ( !pMod->u.Manual.fRegisteredFunctionTable
3266 && pMod->u.Manual.cFunctions > 0)
3267 {
3268 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3269 pMod->u.Manual.cFunctions,
3270 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3271 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3272 }
3273#endif
3274
3275
3276 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3277 {
3278 /*
3279 * Must do imports first, but mark our module as being initialized to avoid
3280 * endless recursion should there be a dependency loop.
3281 */
3282 KSIZE iImp;
3283 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3284
3285 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3286 {
3287 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3288 if (rc != 0)
3289 return rc;
3290 }
3291
3292 /* Do TLS allocations for module init? */
3293 rc = kwLdrCallTlsAllocateAndInit(pMod);
3294 if (rc != 0)
3295 return rc;
3296 if (pMod->u.Manual.cTlsCallbacks > 0)
3297 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3298
3299 /* Finally call the entry point. */
3300 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3301 if (rc == 0)
3302 pMod->u.Manual.enmState = KWMODSTATE_READY;
3303 else
3304 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3305 }
3306 }
3307 /*
3308 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3309 * _MSPDBSRV_ENDPOINT_ changes value.
3310 */
3311 else if (pMod->fNeedReInit)
3312 {
3313 int rc2;
3314 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3315 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3316 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3317 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3318 if (!rc && !rc2)
3319 { /* likely */ }
3320 else
3321 {
3322 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3323 if (rc2 && !rc)
3324 rc = rc2;
3325 }
3326 pMod->fNeedReInit = K_FALSE;
3327 }
3328 return rc;
3329}
3330
3331
3332/**
3333 * Looks up a module handle for a tool.
3334 *
3335 * @returns Referenced loader module on success, NULL on if not found.
3336 * @param pTool The tool.
3337 * @param hmod The module handle.
3338 */
3339static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3340{
3341 KUPTR const uHMod = (KUPTR)hmod;
3342 PKWMODULE *papMods;
3343 KU32 iEnd;
3344 KU32 i;
3345 PKWDYNLOAD pDynLoad;
3346
3347 /* The executable. */
3348 if ( hmod == NULL
3349 || pTool->u.Sandboxed.pExe->hOurMod == hmod)
3350 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3351
3352 /*
3353 * Binary lookup using the module table.
3354 */
3355 papMods = pTool->u.Sandboxed.papModules;
3356 iEnd = pTool->u.Sandboxed.cModules;
3357 if (iEnd)
3358 {
3359 KU32 iStart = 0;
3360 i = iEnd / 2;
3361 for (;;)
3362 {
3363 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3364 if (uHMod < uHModThis)
3365 {
3366 iEnd = i--;
3367 if (iStart <= i)
3368 { }
3369 else
3370 break;
3371 }
3372 else if (uHMod != uHModThis)
3373 {
3374 iStart = ++i;
3375 if (i < iEnd)
3376 { }
3377 else
3378 break;
3379 }
3380 else
3381 return kwLdrModuleRetain(papMods[i]);
3382
3383 i = iStart + (iEnd - iStart) / 2;
3384 }
3385
3386#ifndef NDEBUG
3387 iStart = pTool->u.Sandboxed.cModules;
3388 while (--iStart > 0)
3389 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3390 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3391#endif
3392 }
3393
3394 /*
3395 * Dynamically loaded images.
3396 */
3397 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3398 if (pDynLoad->hmod == hmod)
3399 {
3400 if (pDynLoad->pMod)
3401 return kwLdrModuleRetain(pDynLoad->pMod);
3402 KWFS_TODO();
3403 return NULL;
3404 }
3405
3406 return NULL;
3407}
3408
3409/**
3410 * Adds the given module to the tool import table.
3411 *
3412 * @returns 0 on success, non-zero on failure.
3413 * @param pTool The tool.
3414 * @param pMod The module.
3415 */
3416static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3417{
3418 /*
3419 * Binary lookup. Locating the right slot for it, return if already there.
3420 */
3421 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3422 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3423 KU32 iEnd = pTool->u.Sandboxed.cModules;
3424 KU32 i;
3425 if (iEnd)
3426 {
3427 KU32 iStart = 0;
3428 i = iEnd / 2;
3429 for (;;)
3430 {
3431 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3432 if (uHMod < uHModThis)
3433 {
3434 iEnd = i;
3435 if (iStart < i)
3436 { }
3437 else
3438 break;
3439 }
3440 else if (uHMod != uHModThis)
3441 {
3442 iStart = ++i;
3443 if (i < iEnd)
3444 { }
3445 else
3446 break;
3447 }
3448 else
3449 {
3450 /* Already there in the table. */
3451 return 0;
3452 }
3453
3454 i = iStart + (iEnd - iStart) / 2;
3455 }
3456#ifndef NDEBUG
3457 iStart = pTool->u.Sandboxed.cModules;
3458 while (--iStart > 0)
3459 {
3460 kHlpAssert(papMods[iStart] != pMod);
3461 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3462 }
3463 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3464 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod > uHMod);
3465#endif
3466 }
3467 else
3468 i = 0;
3469
3470 /*
3471 * Grow the table?
3472 */
3473 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3474 {
3475 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3476 if (!pvNew)
3477 return KERR_NO_MEMORY;
3478 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3479 }
3480
3481 /* Insert it. */
3482 if (i != pTool->u.Sandboxed.cModules)
3483 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3484 papMods[i] = kwLdrModuleRetain(pMod);
3485 pTool->u.Sandboxed.cModules++;
3486 KW_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3487 return 0;
3488}
3489
3490
3491/**
3492 * Adds the given module and all its imports to the
3493 *
3494 * @returns 0 on success, non-zero on failure.
3495 * @param pTool The tool.
3496 * @param pMod The module.
3497 */
3498static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
3499{
3500 int rc = kwToolAddModule(pTool, pMod);
3501 if (!pMod->fNative && rc == 0)
3502 {
3503 KSIZE iImp = pMod->u.Manual.cImpMods;
3504 while (iImp-- > 0)
3505 {
3506 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
3507 if (rc == 0)
3508 { }
3509 else
3510 break;
3511 }
3512 }
3513 return 0;
3514}
3515
3516
3517/**
3518 * Creates a tool entry and inserts it.
3519 *
3520 * @returns Pointer to the tool entry. NULL on failure.
3521 * @param pToolFsObj The file object of the tool. The created tool
3522 * will be associated with it.
3523 *
3524 * A reference is donated by the caller and must be
3525 * released.
3526 * @param pszSearchPath The PATH environment variable value, or NULL.
3527 */
3528static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
3529{
3530 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
3531 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
3532 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
3533 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
3534 if (pTool)
3535 {
3536 KBOOL fRc;
3537 pTool->pwszPath = (wchar_t const *)(pTool + 1);
3538 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
3539 kHlpAssert(fRc); K_NOREF(fRc);
3540
3541 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
3542 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
3543 kHlpAssert(fRc);
3544
3545 pTool->enmType = KWTOOLTYPE_SANDBOXED;
3546 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
3547 NULL /*pEexeMod*/, pszSearchPath);
3548 if (pTool->u.Sandboxed.pExe)
3549 {
3550 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
3551 if (rc == 0)
3552 {
3553 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
3554 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
3555 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
3556 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
3557 else
3558 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
3559 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
3560 }
3561 else
3562 {
3563 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
3564 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
3565 pTool->u.Sandboxed.pExe = NULL;
3566 pTool->enmType = KWTOOLTYPE_EXEC;
3567 }
3568 }
3569 else
3570 pTool->enmType = KWTOOLTYPE_EXEC;
3571
3572 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3573 g_cTools++;
3574 return pTool;
3575 }
3576 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3577 return NULL;
3578}
3579
3580
3581/**
3582 * Looks up the given tool, creating a new tool table entry if necessary.
3583 *
3584 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
3585 * @param pszExe The executable for the tool (not normalized).
3586 * @param cEnvVars Number of environment varibles.
3587 * @param papszEnvVars Environment variables. For getting the PATH.
3588 */
3589static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
3590{
3591 /*
3592 * We associate the tools instances with the file system objects.
3593 *
3594 * We'd like to do the lookup without invaliding the volatile parts of the
3595 * cache, thus the double lookup here. The cache gets invalidate later on.
3596 */
3597 KFSLOOKUPERROR enmError;
3598 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3599 if ( !pToolFsObj
3600 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
3601 {
3602 kFsCacheInvalidateCustomBoth(g_pFsCache);
3603 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3604 }
3605 if (pToolFsObj)
3606 {
3607 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
3608 {
3609 const char *pszSearchPath;
3610 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
3611 if (pTool)
3612 {
3613 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3614 return pTool;
3615 }
3616
3617 /*
3618 * Need to create a new tool.
3619 */
3620 pszSearchPath = NULL;
3621 while (cEnvVars-- > 0)
3622 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
3623 {
3624 pszSearchPath = &papszEnvVars[cEnvVars][5];
3625 break;
3626 }
3627
3628 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
3629 if (pTool)
3630 return pTool;
3631
3632 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
3633 }
3634 else
3635 {
3636 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3637 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
3638 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
3639 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
3640 }
3641 }
3642 else
3643 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
3644 return NULL;
3645}
3646
3647
3648
3649/*
3650 *
3651 * File system cache.
3652 * File system cache.
3653 * File system cache.
3654 *
3655 */
3656
3657
3658/**
3659 * This is for kDep.
3660 */
3661int kwFsPathExists(const char *pszPath)
3662{
3663 BirdTimeSpec_T TsIgnored;
3664 KFSLOOKUPERROR enmError;
3665 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3666 if (pFsObj)
3667 {
3668 kFsCacheObjRelease(g_pFsCache, pFsObj);
3669 return 1;
3670 }
3671 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
3672}
3673
3674
3675/* duplicated in dir-nt-bird.c */
3676void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
3677{
3678 KFSLOOKUPERROR enmError;
3679 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
3680 if (pPathObj)
3681 {
3682 KSIZE off = pPathObj->cchParent;
3683 if (off > 0)
3684 {
3685 KSIZE offEnd = off + pPathObj->cchName;
3686 if (offEnd < cbFull)
3687 {
3688 PKFSDIR pAncestor;
3689
3690 pszFull[off + pPathObj->cchName] = '\0';
3691 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
3692
3693 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3694 {
3695 kHlpAssert(off > 1);
3696 kHlpAssert(pAncestor != NULL);
3697 kHlpAssert(pAncestor->Obj.cchName > 0);
3698 pszFull[--off] = '/';
3699 off -= pAncestor->Obj.cchName;
3700 kHlpAssert(pAncestor->Obj.cchParent == off);
3701 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3702 }
3703 kFsCacheObjRelease(g_pFsCache, pPathObj);
3704 return;
3705 }
3706 }
3707 else
3708 {
3709 if ((size_t)pPathObj->cchName + 1 < cbFull)
3710 {
3711 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
3712 pszFull[pPathObj->cchName] = '/';
3713 pszFull[pPathObj->cchName + 1] = '\0';
3714
3715 kFsCacheObjRelease(g_pFsCache, pPathObj);
3716 return;
3717 }
3718 }
3719
3720 /* do fallback. */
3721 kHlpAssertFailed();
3722 kFsCacheObjRelease(g_pFsCache, pPathObj);
3723 }
3724
3725 nt_fullpath(pszPath, pszFull, cbFull);
3726}
3727
3728
3729/**
3730 * Helper for getting the extension of a UTF-16 path.
3731 *
3732 * @returns Pointer to the extension or the terminator.
3733 * @param pwszPath The path.
3734 * @param pcwcExt Where to return the length of the extension.
3735 */
3736static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
3737{
3738 wchar_t const *pwszName = pwszPath;
3739 wchar_t const *pwszExt = NULL;
3740 for (;;)
3741 {
3742 wchar_t const wc = *pwszPath++;
3743 if (wc == '.')
3744 pwszExt = pwszPath;
3745 else if (wc == '/' || wc == '\\' || wc == ':')
3746 {
3747 pwszName = pwszPath;
3748 pwszExt = NULL;
3749 }
3750 else if (wc == '\0')
3751 {
3752 if (pwszExt)
3753 {
3754 *pcwcExt = pwszPath - pwszExt - 1;
3755 return pwszExt;
3756 }
3757 *pcwcExt = 0;
3758 return pwszPath - 1;
3759 }
3760 }
3761}
3762
3763
3764
3765/**
3766 * Parses the argument string passed in as pszSrc.
3767 *
3768 * @returns size of the processed arguments.
3769 * @param pszSrc Pointer to the commandline that's to be parsed.
3770 * @param pcArgs Where to return the number of arguments.
3771 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
3772 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
3773 *
3774 * @remarks Lifted from startuphacks-win.c
3775 */
3776static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
3777{
3778 int bs;
3779 char chQuote;
3780 char *pfFlags;
3781 int cbArgs;
3782 int cArgs;
3783
3784#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
3785#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
3786#define WHITE(c) ((c) == ' ' || (c) == '\t')
3787
3788#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
3789#define _ARG_RESPONSE 0x02 /* Argument read from response file */
3790#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
3791#define _ARG_ENV 0x08 /* Argument from environment */
3792#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
3793
3794 cArgs = 0;
3795 cbArgs = 0;
3796
3797#if 0
3798 /* argv[0] */
3799 PUTC((char)_ARG_NONZERO);
3800 PUTV;
3801 for (;;)
3802 {
3803 PUTC(*pszSrc);
3804 if (*pszSrc == 0)
3805 break;
3806 ++pszSrc;
3807 }
3808 ++pszSrc;
3809#endif
3810
3811 for (;;)
3812 {
3813 while (WHITE(*pszSrc))
3814 ++pszSrc;
3815 if (*pszSrc == 0)
3816 break;
3817 pfFlags = pchPool;
3818 PUTC((char)_ARG_NONZERO);
3819 PUTV;
3820 bs = 0; chQuote = 0;
3821 for (;;)
3822 {
3823 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
3824 {
3825 while (bs >= 2)
3826 {
3827 PUTC('\\');
3828 bs -= 2;
3829 }
3830 if (bs & 1)
3831 PUTC(*pszSrc);
3832 else
3833 {
3834 chQuote = chQuote ? 0 : *pszSrc;
3835 if (pfFlags != NULL)
3836 *pfFlags |= _ARG_DQUOTE;
3837 }
3838 bs = 0;
3839 }
3840 else if (*pszSrc == '\\')
3841 ++bs;
3842 else
3843 {
3844 while (bs != 0)
3845 {
3846 PUTC('\\');
3847 --bs;
3848 }
3849 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
3850 break;
3851 PUTC(*pszSrc);
3852 }
3853 ++pszSrc;
3854 }
3855 PUTC(0);
3856 }
3857
3858 *pcArgs = cArgs;
3859 return cbArgs;
3860}
3861
3862
3863
3864
3865/*
3866 *
3867 * Process and thread related APIs.
3868 * Process and thread related APIs.
3869 * Process and thread related APIs.
3870 *
3871 */
3872
3873/** Common worker for ExitProcess(), exit() and friends. */
3874static void WINAPI kwSandboxDoExit(int uExitCode)
3875{
3876 if (g_Sandbox.idMainThread == GetCurrentThreadId())
3877 {
3878 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
3879
3880 g_Sandbox.rcExitCode = (int)uExitCode;
3881
3882 /* Before we jump, restore the TIB as we're not interested in any
3883 exception chain stuff installed by the sandboxed executable. */
3884 *pTib = g_Sandbox.TibMainThread;
3885 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
3886
3887 longjmp(g_Sandbox.JmpBuf, 1);
3888 }
3889 KWFS_TODO();
3890}
3891
3892
3893/** ExitProcess replacement. */
3894static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
3895{
3896 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
3897 kwSandboxDoExit((int)uExitCode);
3898}
3899
3900
3901/** ExitProcess replacement. */
3902static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
3903{
3904 if (hProcess == GetCurrentProcess())
3905 kwSandboxDoExit(uExitCode);
3906 KWFS_TODO();
3907 return TerminateProcess(hProcess, uExitCode);
3908}
3909
3910
3911/** Normal CRT exit(). */
3912static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
3913{
3914 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
3915 kwSandboxDoExit(rcExitCode);
3916}
3917
3918
3919/** Quick CRT _exit(). */
3920static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
3921{
3922 /* Quick. */
3923 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
3924 kwSandboxDoExit(rcExitCode);
3925}
3926
3927
3928/** Return to caller CRT _cexit(). */
3929static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
3930{
3931 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
3932 kwSandboxDoExit(rcExitCode);
3933}
3934
3935
3936/** Quick return to caller CRT _c_exit(). */
3937static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
3938{
3939 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
3940 kwSandboxDoExit(rcExitCode);
3941}
3942
3943
3944/** Runtime error and exit _amsg_exit(). */
3945static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
3946{
3947 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
3948 kwSandboxDoExit(255);
3949}
3950
3951
3952/** CRT - terminate(). */
3953static void __cdecl kwSandbox_msvcrt_terminate(void)
3954{
3955 KW_LOG(("\nRuntime - terminate!\n"));
3956 kwSandboxDoExit(254);
3957}
3958
3959
3960/** CRT - _onexit */
3961static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
3962{
3963 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3964 {
3965 PKWEXITCALLACK pCallback;
3966 KW_LOG(("_onexit(%p)\n", pfnFunc));
3967 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3968
3969 pCallback = kHlpAlloc(sizeof(*pCallback));
3970 if (pCallback)
3971 {
3972 pCallback->pfnCallback = pfnFunc;
3973 pCallback->fAtExit = K_FALSE;
3974 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3975 g_Sandbox.pExitCallbackHead = pCallback;
3976 return pfnFunc;
3977 }
3978 return NULL;
3979 }
3980 KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
3981 return pfnFunc;
3982}
3983
3984
3985/** CRT - atexit */
3986static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
3987{
3988 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3989 {
3990 PKWEXITCALLACK pCallback;
3991 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3992 KW_LOG(("atexit(%p)\n", pfnFunc));
3993
3994 pCallback = kHlpAlloc(sizeof(*pCallback));
3995 if (pCallback)
3996 {
3997 pCallback->pfnCallback = (_onexit_t)pfnFunc;
3998 pCallback->fAtExit = K_TRUE;
3999 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4000 g_Sandbox.pExitCallbackHead = pCallback;
4001 return 0;
4002 }
4003 return -1;
4004 }
4005 KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4006 return 0;
4007}
4008
4009
4010/** Kernel32 - SetConsoleCtrlHandler(). */
4011static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4012{
4013 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4014 return TRUE;
4015}
4016
4017
4018/** The CRT internal __getmainargs() API. */
4019static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4020 int dowildcard, int const *piNewMode)
4021{
4022 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4023 *pargc = g_Sandbox.cArgs;
4024 *pargv = g_Sandbox.papszArgs;
4025 *penvp = g_Sandbox.environ;
4026
4027 /** @todo startinfo points at a newmode (setmode) value. */
4028 return 0;
4029}
4030
4031
4032/** The CRT internal __wgetmainargs() API. */
4033static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4034 int dowildcard, int const *piNewMode)
4035{
4036 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4037 *pargc = g_Sandbox.cArgs;
4038 *pargv = g_Sandbox.papwszArgs;
4039 *penvp = g_Sandbox.wenviron;
4040
4041 /** @todo startinfo points at a newmode (setmode) value. */
4042 return 0;
4043}
4044
4045
4046
4047/** Kernel32 - GetCommandLineA() */
4048static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4049{
4050 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4051 return g_Sandbox.pszCmdLine;
4052}
4053
4054
4055/** Kernel32 - GetCommandLineW() */
4056static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4057{
4058 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4059 return g_Sandbox.pwszCmdLine;
4060}
4061
4062
4063/** Kernel32 - GetStartupInfoA() */
4064static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4065{
4066 KW_LOG(("GetStartupInfoA\n"));
4067 GetStartupInfoA(pStartupInfo);
4068 pStartupInfo->lpReserved = NULL;
4069 pStartupInfo->lpTitle = NULL;
4070 pStartupInfo->lpReserved2 = NULL;
4071 pStartupInfo->cbReserved2 = 0;
4072}
4073
4074
4075/** Kernel32 - GetStartupInfoW() */
4076static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4077{
4078 KW_LOG(("GetStartupInfoW\n"));
4079 GetStartupInfoW(pStartupInfo);
4080 pStartupInfo->lpReserved = NULL;
4081 pStartupInfo->lpTitle = NULL;
4082 pStartupInfo->lpReserved2 = NULL;
4083 pStartupInfo->cbReserved2 = 0;
4084}
4085
4086
4087/** CRT - __p___argc(). */
4088static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4089{
4090 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4091 return &g_Sandbox.cArgs;
4092}
4093
4094
4095/** CRT - __p___argv(). */
4096static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4097{
4098 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4099 return &g_Sandbox.papszArgs;
4100}
4101
4102
4103/** CRT - __p___sargv(). */
4104static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4105{
4106 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4107 return &g_Sandbox.papwszArgs;
4108}
4109
4110
4111/** CRT - __p__acmdln(). */
4112static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4113{
4114 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4115 return (char **)&g_Sandbox.pszCmdLine;
4116}
4117
4118
4119/** CRT - __p__acmdln(). */
4120static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4121{
4122 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4123 return &g_Sandbox.pwszCmdLine;
4124}
4125
4126
4127/** CRT - __p__pgmptr(). */
4128static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4129{
4130 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4131 return &g_Sandbox.pgmptr;
4132}
4133
4134
4135/** CRT - __p__wpgmptr(). */
4136static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4137{
4138 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4139 return &g_Sandbox.wpgmptr;
4140}
4141
4142
4143/** CRT - _get_pgmptr(). */
4144static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4145{
4146 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4147 *ppszValue = g_Sandbox.pgmptr;
4148 return 0;
4149}
4150
4151
4152/** CRT - _get_wpgmptr(). */
4153static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4154{
4155 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4156 *ppwszValue = g_Sandbox.wpgmptr;
4157 return 0;
4158}
4159
4160/** Just in case. */
4161static void kwSandbox_msvcrt__wincmdln(void)
4162{
4163 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4164 KWFS_TODO();
4165}
4166
4167
4168/** Just in case. */
4169static void kwSandbox_msvcrt__wwincmdln(void)
4170{
4171 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4172 KWFS_TODO();
4173}
4174
4175/** CreateThread interceptor. */
4176static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4177 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4178 DWORD fFlags, PDWORD pidThread)
4179{
4180 HANDLE hThread = NULL;
4181 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4182 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4183 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4184 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4185 {
4186 /* Allow link::DbgThread. */
4187 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4188 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4189 }
4190 else
4191 KWFS_TODO();
4192 return hThread;
4193}
4194
4195
4196/** _beginthread - create a new thread. */
4197static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4198{
4199 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4200 KWFS_TODO();
4201 return 0;
4202}
4203
4204
4205/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4206static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4207 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4208 unsigned fCreate, unsigned *pidThread)
4209{
4210 /*
4211 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4212 * whatever it needs to.
4213 */
4214 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4215 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4216 pfnThreadProc, pvUser, fCreate, pidThread));
4217 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4218 {
4219 uintptr_t rcRet;
4220 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4221 if (!s_pfnReal)
4222 {
4223 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4224 if (!s_pfnReal)
4225 {
4226 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4227 __debugbreak();
4228 }
4229 }
4230 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4231 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4232 return rcRet;
4233 }
4234
4235 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4236 KWFS_TODO();
4237 return 0;
4238}
4239
4240
4241/** _beginthreadex - create a new thread. */
4242static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex(void *pvSecAttr, unsigned cbStack,
4243 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4244 unsigned fCreate, unsigned *pidThread)
4245{
4246 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4247 KWFS_TODO();
4248 return 0;
4249}
4250
4251
4252/*
4253 *
4254 * Environment related APIs.
4255 * Environment related APIs.
4256 * Environment related APIs.
4257 *
4258 */
4259
4260/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4261static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4262{
4263 char *pszzEnv;
4264 char *pszCur;
4265 KSIZE cbNeeded = 1;
4266 KSIZE iVar = 0;
4267
4268 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4269
4270 /* Figure how space much we need first. */
4271 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4272 cbNeeded += kHlpStrLen(pszCur) + 1;
4273
4274 /* Allocate it. */
4275 pszzEnv = kHlpAlloc(cbNeeded);
4276 if (pszzEnv)
4277 {
4278 char *psz = pszzEnv;
4279 iVar = 0;
4280 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4281 {
4282 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4283 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4284 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4285 }
4286 *psz++ = '\0';
4287 kHlpAssert(psz - pszzEnv == cbNeeded);
4288 }
4289
4290 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4291#if 0
4292 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4293 pszCur = pszzEnv;
4294 iVar = 0;
4295 while (*pszCur)
4296 {
4297 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4298 iVar++;
4299 pszCur += kHlpStrLen(pszCur) + 1;
4300 }
4301 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4302 pszCur++;
4303 fprintf(stderr, "ended at %p, after %u bytes (exepcted %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4304#endif
4305 return pszzEnv;
4306}
4307
4308
4309/** Kernel32 - GetEnvironmentStrings */
4310static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4311{
4312 KW_LOG(("GetEnvironmentStrings!\n"));
4313 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4314}
4315
4316
4317/** Kernel32 - GetEnvironmentStringsW */
4318static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4319{
4320 wchar_t *pwszzEnv;
4321 wchar_t *pwszCur;
4322 KSIZE cwcNeeded = 1;
4323 KSIZE iVar = 0;
4324
4325 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4326
4327 /* Figure how space much we need first. */
4328 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4329 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4330
4331 /* Allocate it. */
4332 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4333 if (pwszzEnv)
4334 {
4335 wchar_t *pwsz = pwszzEnv;
4336 iVar = 0;
4337 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4338 {
4339 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4340 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4341 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4342 }
4343 *pwsz++ = '\0';
4344 kHlpAssert(pwsz - pwszzEnv == cwcNeeded);
4345 }
4346
4347 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4348 return pwszzEnv;
4349}
4350
4351
4352/** Kernel32 - FreeEnvironmentStringsA */
4353static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4354{
4355 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4356 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4357 kHlpFree(pszzEnv);
4358 return TRUE;
4359}
4360
4361
4362/** Kernel32 - FreeEnvironmentStringsW */
4363static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4364{
4365 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4366 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4367 kHlpFree(pwszzEnv);
4368 return TRUE;
4369}
4370
4371
4372/**
4373 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4374 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4375 *
4376 * @returns 0 on success, non-zero on failure.
4377 * @param pSandbox The sandbox.
4378 * @param cMin Minimum size, including terminator.
4379 */
4380static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4381{
4382 void *pvNew;
4383 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4384 KSIZE cNew = cOld + 256;
4385 while (cNew < cMin)
4386 cNew += 256;
4387
4388 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4389 if (pvNew)
4390 {
4391 pSandbox->environ = (char **)pvNew;
4392 pSandbox->environ[cOld] = NULL;
4393
4394 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4395 if (pvNew)
4396 {
4397 pSandbox->papszEnvVars = (char **)pvNew;
4398 pSandbox->papszEnvVars[cOld] = NULL;
4399
4400 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4401 if (pvNew)
4402 {
4403 pSandbox->wenviron = (wchar_t **)pvNew;
4404 pSandbox->wenviron[cOld] = NULL;
4405
4406 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4407 if (pvNew)
4408 {
4409 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4410 pSandbox->papwszEnvVars[cOld] = NULL;
4411
4412 pSandbox->cEnvVarsAllocated = cNew;
4413 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4414 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4415 return 0;
4416 }
4417 }
4418 }
4419 }
4420 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4421 return KERR_NO_MEMORY;
4422}
4423
4424
4425/**
4426 * Sets an environment variable, ANSI style.
4427 *
4428 * @returns 0 on success, non-zero on failure.
4429 * @param pSandbox The sandbox.
4430 * @param pchVar The variable name.
4431 * @param cchVar The length of the name.
4432 * @param pszValue The value.
4433 */
4434static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4435{
4436 /* Allocate and construct the new strings. */
4437 KSIZE cchTmp = kHlpStrLen(pszValue);
4438 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4439 if (pszNew)
4440 {
4441 wchar_t *pwszNew;
4442 kHlpMemCopy(pszNew, pchVar, cchVar);
4443 pszNew[cchVar] = '=';
4444 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4445 cchTmp += cchVar + 1;
4446 pszNew[cchTmp] = '\0';
4447
4448 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4449 if (pwszNew)
4450 {
4451 /* Look it up. */
4452 KSIZE iVar = 0;
4453 char *pszEnv;
4454 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4455 {
4456 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4457 && pszEnv[cchVar] == '=')
4458 {
4459 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4460 " iVar=%d: %p='%s' and %p='%ls'\n",
4461 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4462 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4463 iVar, pszNew, pszNew, pwszNew, pwszNew));
4464
4465 kHlpFree(pSandbox->papszEnvVars[iVar]);
4466 pSandbox->papszEnvVars[iVar] = pszNew;
4467 pSandbox->environ[iVar] = pszNew;
4468
4469 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4470 pSandbox->papwszEnvVars[iVar] = pwszNew;
4471 pSandbox->wenviron[iVar] = pwszNew;
4472 return 0;
4473 }
4474 iVar++;
4475 }
4476
4477 /* Not found, do we need to grow the table first? */
4478 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4479 kwSandboxGrowEnv(pSandbox, iVar + 2);
4480 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4481 {
4482 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4483
4484 pSandbox->papszEnvVars[iVar + 1] = NULL;
4485 pSandbox->papszEnvVars[iVar] = pszNew;
4486 pSandbox->environ[iVar + 1] = NULL;
4487 pSandbox->environ[iVar] = pszNew;
4488
4489 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4490 pSandbox->papwszEnvVars[iVar] = pwszNew;
4491 pSandbox->wenviron[iVar + 1] = NULL;
4492 pSandbox->wenviron[iVar] = pwszNew;
4493 return 0;
4494 }
4495
4496 kHlpFree(pwszNew);
4497 }
4498 kHlpFree(pszNew);
4499 }
4500 KW_LOG(("Out of memory!\n"));
4501 return 0;
4502}
4503
4504
4505/**
4506 * Sets an environment variable, UTF-16 style.
4507 *
4508 * @returns 0 on success, non-zero on failure.
4509 * @param pSandbox The sandbox.
4510 * @param pwcVar The variable name.
4511 * @param cwcVar The length of the name.
4512 * @param pwszValue The value.
4513 */
4514static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4515{
4516 /* Allocate and construct the new strings. */
4517 KSIZE cwcTmp = kwUtf16Len(pwszValue);
4518 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4519 if (pwszNew)
4520 {
4521 char *pszNew;
4522 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4523 pwszNew[cwcVar] = '=';
4524 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4525 cwcTmp += cwcVar + 1;
4526 pwszNew[cwcVar] = '\0';
4527
4528 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4529 if (pszNew)
4530 {
4531 /* Look it up. */
4532 KSIZE iVar = 0;
4533 wchar_t *pwszEnv;
4534 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4535 {
4536 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4537 && pwszEnv[cwcVar] == '=')
4538 {
4539 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4540 " iVar=%d: %p='%s' and %p='%ls'\n",
4541 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4542 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4543 iVar, pszNew, pszNew, pwszNew, pwszNew));
4544
4545 kHlpFree(pSandbox->papszEnvVars[iVar]);
4546 pSandbox->papszEnvVars[iVar] = pszNew;
4547 pSandbox->environ[iVar] = pszNew;
4548
4549 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4550 pSandbox->papwszEnvVars[iVar] = pwszNew;
4551 pSandbox->wenviron[iVar] = pwszNew;
4552 return 0;
4553 }
4554 iVar++;
4555 }
4556
4557 /* Not found, do we need to grow the table first? */
4558 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4559 kwSandboxGrowEnv(pSandbox, iVar + 2);
4560 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4561 {
4562 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4563
4564 pSandbox->papszEnvVars[iVar + 1] = NULL;
4565 pSandbox->papszEnvVars[iVar] = pszNew;
4566 pSandbox->environ[iVar + 1] = NULL;
4567 pSandbox->environ[iVar] = pszNew;
4568
4569 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4570 pSandbox->papwszEnvVars[iVar] = pwszNew;
4571 pSandbox->wenviron[iVar + 1] = NULL;
4572 pSandbox->wenviron[iVar] = pwszNew;
4573 return 0;
4574 }
4575
4576 kHlpFree(pwszNew);
4577 }
4578 kHlpFree(pszNew);
4579 }
4580 KW_LOG(("Out of memory!\n"));
4581 return 0;
4582}
4583
4584
4585/** ANSI unsetenv worker. */
4586static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4587{
4588 KSIZE iVar = 0;
4589 char *pszEnv;
4590 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4591 {
4592 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4593 && pszEnv[cchVar] == '=')
4594 {
4595 KSIZE cVars = iVar;
4596 while (pSandbox->papszEnvVars[cVars])
4597 cVars++;
4598 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4599 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4600
4601 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4602 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4603 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4604
4605 kHlpFree(pSandbox->papszEnvVars[iVar]);
4606 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4607 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4608 pSandbox->papszEnvVars[cVars] = NULL;
4609 pSandbox->environ[cVars] = NULL;
4610
4611 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4612 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4613 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4614 pSandbox->papwszEnvVars[cVars] = NULL;
4615 pSandbox->wenviron[cVars] = NULL;
4616 return 0;
4617 }
4618 iVar++;
4619 }
4620 return KERR_ENVVAR_NOT_FOUND;
4621}
4622
4623
4624/** UTF-16 unsetenv worker. */
4625static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4626{
4627 KSIZE iVar = 0;
4628 wchar_t *pwszEnv;
4629 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4630 {
4631 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4632 && pwszEnv[cwcVar] == '=')
4633 {
4634 KSIZE cVars = iVar;
4635 while (pSandbox->papwszEnvVars[cVars])
4636 cVars++;
4637 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
4638 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
4639
4640 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4641 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4642 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4643
4644 kHlpFree(pSandbox->papszEnvVars[iVar]);
4645 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4646 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4647 pSandbox->papszEnvVars[cVars] = NULL;
4648 pSandbox->environ[cVars] = NULL;
4649
4650 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4651 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4652 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4653 pSandbox->papwszEnvVars[cVars] = NULL;
4654 pSandbox->wenviron[cVars] = NULL;
4655 return 0;
4656 }
4657 iVar++;
4658 }
4659 return KERR_ENVVAR_NOT_FOUND;
4660}
4661
4662
4663
4664/** ANSI getenv worker. */
4665static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4666{
4667 KSIZE iVar = 0;
4668 char *pszEnv;
4669 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
4670 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4671 && pszEnv[cchVar] == '=')
4672 return &pszEnv[cchVar + 1];
4673 return NULL;
4674}
4675
4676
4677/** UTF-16 getenv worker. */
4678static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4679{
4680 KSIZE iVar = 0;
4681 wchar_t *pwszEnv;
4682 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
4683 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4684 && pwszEnv[cwcVar] == '=')
4685 return &pwszEnv[cwcVar + 1];
4686 return NULL;
4687}
4688
4689
4690/** Kernel32 - GetEnvironmentVariableA() */
4691static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
4692{
4693 char *pszFoundValue;
4694 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4695
4696 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4697 if (pszFoundValue)
4698 {
4699 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
4700 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
4701 return cchRet;
4702 }
4703 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
4704 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4705 return 0;
4706}
4707
4708
4709/** Kernel32 - GetEnvironmentVariableW() */
4710static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
4711{
4712 wchar_t *pwszFoundValue;
4713 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4714
4715 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4716 if (pwszFoundValue)
4717 {
4718 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
4719 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
4720 return cchRet;
4721 }
4722 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
4723 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4724 return 0;
4725}
4726
4727
4728/** Kernel32 - SetEnvironmentVariableA() */
4729static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
4730{
4731 int rc;
4732 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4733
4734 if (pszValue)
4735 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4736 else
4737 {
4738 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4739 rc = 0; //??
4740 }
4741 if (rc == 0)
4742 {
4743 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
4744 return TRUE;
4745 }
4746 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4747 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
4748 return FALSE;
4749}
4750
4751
4752/** Kernel32 - SetEnvironmentVariableW() */
4753static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
4754{
4755 int rc;
4756 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4757
4758 if (pwszValue)
4759 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4760 else
4761 {
4762 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4763 rc = 0; //??
4764 }
4765 if (rc == 0)
4766 {
4767 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
4768 return TRUE;
4769 }
4770 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4771 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
4772 return FALSE;
4773}
4774
4775
4776/** Kernel32 - ExpandEnvironmentStringsA() */
4777static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
4778{
4779 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4780 KWFS_TODO();
4781 return 0;
4782}
4783
4784
4785/** Kernel32 - ExpandEnvironmentStringsW() */
4786static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
4787{
4788 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4789 KWFS_TODO();
4790 return 0;
4791}
4792
4793
4794/** CRT - _putenv(). */
4795static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
4796{
4797 int rc;
4798 char const *pszEqual;
4799 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4800
4801 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
4802 if (pszEqual)
4803 {
4804 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
4805 if (rc == 0)
4806 { }
4807 else
4808 rc = -1;
4809 }
4810 else
4811 {
4812 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
4813 rc = 0;
4814 }
4815 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
4816 return rc;
4817}
4818
4819
4820/** CRT - _wputenv(). */
4821static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
4822{
4823 int rc;
4824 wchar_t const *pwszEqual;
4825 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4826
4827 pwszEqual = wcschr(pwszVarEqualValue, '=');
4828 if (pwszEqual)
4829 {
4830 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
4831 if (rc == 0)
4832 { }
4833 else
4834 rc = -1;
4835 }
4836 else
4837 {
4838 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
4839 rc = 0;
4840 }
4841 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
4842 return rc;
4843}
4844
4845
4846/** CRT - _putenv_s(). */
4847static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
4848{
4849 char const *pszEqual;
4850 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4851
4852 pszEqual = kHlpStrChr(pszVar, '=');
4853 if (pszEqual == NULL)
4854 {
4855 if (pszValue)
4856 {
4857 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4858 if (rc == 0)
4859 {
4860 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
4861 return 0;
4862 }
4863 }
4864 else
4865 {
4866 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4867 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
4868 return 0;
4869 }
4870 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
4871 return ENOMEM;
4872 }
4873 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
4874 return EINVAL;
4875}
4876
4877
4878/** CRT - _wputenv_s(). */
4879static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
4880{
4881 wchar_t const *pwszEqual;
4882 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4883
4884 pwszEqual = wcschr(pwszVar, '=');
4885 if (pwszEqual == NULL)
4886 {
4887 if (pwszValue)
4888 {
4889 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4890 if (rc == 0)
4891 {
4892 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
4893 return 0;
4894 }
4895 }
4896 else
4897 {
4898 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4899 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
4900 return 0;
4901 }
4902 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
4903 return ENOMEM;
4904 }
4905 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
4906 return EINVAL;
4907}
4908
4909
4910/** CRT - get pointer to the __initenv variable (initial environment). */
4911static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
4912{
4913 KW_LOG(("__p___initenv\n"));
4914 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4915 KWFS_TODO();
4916 return &g_Sandbox.initenv;
4917}
4918
4919
4920/** CRT - get pointer to the __winitenv variable (initial environment). */
4921static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
4922{
4923 KW_LOG(("__p___winitenv\n"));
4924 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4925 KWFS_TODO();
4926 return &g_Sandbox.winitenv;
4927}
4928
4929
4930/** CRT - get pointer to the _environ variable (current environment). */
4931static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
4932{
4933 KW_LOG(("__p__environ\n"));
4934 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4935 return &g_Sandbox.environ;
4936}
4937
4938
4939/** CRT - get pointer to the _wenviron variable (current environment). */
4940static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
4941{
4942 KW_LOG(("__p__wenviron\n"));
4943 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4944 return &g_Sandbox.wenviron;
4945}
4946
4947
4948/** CRT - get the _environ variable (current environment).
4949 * @remarks Not documented or prototyped? */
4950static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
4951{
4952 KWFS_TODO(); /** @todo check the callers expectations! */
4953 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4954 *ppapszEnviron = g_Sandbox.environ;
4955 return 0;
4956}
4957
4958
4959/** CRT - get the _wenviron variable (current environment).
4960 * @remarks Not documented or prototyped? */
4961static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
4962{
4963 KWFS_TODO(); /** @todo check the callers expectations! */
4964 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4965 *ppapwszEnviron = g_Sandbox.wenviron;
4966 return 0;
4967}
4968
4969
4970/** CRT - _wdupenv_s() (see _tdupenv_s(). */
4971static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
4972 PKWCRTSLOT pSlot)
4973{
4974 errno_t rc;
4975 wchar_t *pwszValue;
4976 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4977
4978 if (ppwszValue)
4979 {
4980 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
4981 if (pwszValue)
4982 {
4983 size_t cwcValue = wcslen(pwszValue);
4984 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
4985 if (pwszDst)
4986 {
4987 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
4988 pwszDst[cwcValue] = '\0';
4989 *ppwszValue = pwszDst;
4990 if (pcwcValue)
4991 *pcwcValue = cwcValue;
4992 rc = 0;
4993 }
4994 else
4995 {
4996 *ppwszValue = NULL;
4997 if (pcwcValue)
4998 *pcwcValue = 0;
4999 rc = ENOMEM;
5000 }
5001 }
5002 else
5003 {
5004 *ppwszValue = NULL;
5005 if (pcwcValue)
5006 *pcwcValue = 0;
5007 rc = 0;
5008 }
5009 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5010 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5011 }
5012 else
5013 {
5014 /*
5015 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5016 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5017 * was set to a value.
5018 */
5019 if (pcwcValue)
5020 *pcwcValue = 0;
5021 rc = EINVAL;
5022 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5023 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5024 }
5025 return rc;
5026}
5027CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5028 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5029 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5030
5031
5032
5033/*
5034 *
5035 * Loader related APIs
5036 * Loader related APIs
5037 * Loader related APIs
5038 *
5039 */
5040
5041/**
5042 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5043 */
5044static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5045{
5046 /* Load it first. */
5047 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5048 if (hmod)
5049 {
5050 pDynLoad->hmod = hmod;
5051 pDynLoad->pMod = NULL; /* indicates special */
5052
5053 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5054 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5055 KW_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5056 }
5057 else
5058 kHlpFree(pDynLoad);
5059 return hmod;
5060}
5061
5062
5063/**
5064 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5065 */
5066static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5067{
5068 HMODULE hmod;
5069 PKWMODULE pMod;
5070 KU32 uHashPath;
5071 KSIZE idxHash;
5072 char szNormPath[256];
5073 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5074
5075 /*
5076 * Lower case it and make it ends with .dll.
5077 */
5078 if (cbFilename <= sizeof(szNormPath))
5079 {
5080 static const char s_szDll[] = ".dll";
5081 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5082 _strlwr(szNormPath);
5083 if ( ( cbFilename <= 4
5084 || strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5085 && cbFilename + sizeof(s_szDll) - 1 <= sizeof(szNormPath))
5086 {
5087 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5088 cbFilename += sizeof(s_szDll) - 1;
5089 }
5090 }
5091 else
5092 {
5093 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5094 return NULL;
5095 }
5096
5097 /*
5098 * Check if it has already been loaded so we don't create an unnecessary
5099 * loader module for it.
5100 */
5101 uHashPath = kwStrHash(szNormPath);
5102 idxHash = uHashPath % K_ELEMENTS(g_apModules);
5103 pMod = g_apModules[idxHash];
5104 if (pMod)
5105 {
5106 do
5107 {
5108 if ( pMod->uHashPath == uHashPath
5109 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
5110 {
5111 pDynLoad->pMod = kwLdrModuleRetain(pMod);
5112 pDynLoad->hmod = pMod->hOurMod;
5113
5114 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5115 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5116 KW_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - already loaded]\n", pDynLoad->szRequest, pDynLoad->hmod));
5117 return pDynLoad->hmod;
5118 }
5119 pMod = pMod->pNextHash;
5120 } while (pMod);
5121 }
5122
5123
5124 /*
5125 * Try load it and make a kLdr module for it.
5126 */
5127 hmod = LoadLibraryExA(szNormPath, NULL /*hFile*/, fFlags);
5128 if (hmod)
5129 {
5130 PKLDRMOD pLdrMod;
5131 int rc = kLdrModOpenNativeByHandle((KUPTR)hmod, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
5132 if (rc == 0)
5133 {
5134 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cbFilename, uHashPath,
5135 K_FALSE /*fDoReplacements*/);
5136 if (pMod)
5137 {
5138 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5139
5140 pDynLoad->pMod = pMod;
5141 pDynLoad->hmod = hmod;
5142
5143 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5144 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5145 KW_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5146 return hmod;
5147 }
5148 KWFS_TODO();
5149 }
5150 else
5151 KWFS_TODO();
5152 }
5153 kHlpFree(pDynLoad);
5154 return hmod;
5155}
5156
5157
5158/** Kernel32 - LoadLibraryExA() */
5159static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5160{
5161 KSIZE cchFilename = kHlpStrLen(pszFilename);
5162 const char *pszSearchPath;
5163 PKWDYNLOAD pDynLoad;
5164 PKWMODULE pMod;
5165 int rc;
5166 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5167
5168 /*
5169 * Deal with a couple of extremely unlikely special cases right away.
5170 */
5171 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5172 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5173 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5174 { /* likely */ }
5175 else
5176 {
5177 KWFS_TODO();
5178 return LoadLibraryExA(pszFilename, hFile, fFlags);
5179 }
5180
5181 /*
5182 * Check if we've already got a dynload entry for this one.
5183 */
5184 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5185 if ( pDynLoad->cchRequest == cchFilename
5186 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5187 {
5188 if (pDynLoad->pMod)
5189 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5190 else
5191 rc = 0;
5192 if (rc == 0)
5193 {
5194 KW_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5195 return pDynLoad->hmod;
5196 }
5197 SetLastError(ERROR_DLL_INIT_FAILED);
5198 return NULL;
5199 }
5200
5201 /*
5202 * Allocate a dynload entry for the request.
5203 */
5204 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5205 if (pDynLoad)
5206 {
5207 pDynLoad->cchRequest = cchFilename;
5208 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5209 }
5210 else
5211 {
5212 KW_LOG(("LoadLibraryExA: Out of memory!\n"));
5213 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5214 return NULL;
5215 }
5216
5217 /*
5218 * Deal with resource / data DLLs.
5219 */
5220 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5221 | LOAD_LIBRARY_AS_DATAFILE
5222 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5223 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5224 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5225
5226 /*
5227 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5228 */
5229 if ( strnicmp(pszFilename, TUPLE("api-ms-")) == 0
5230 && kHlpIsFilenameOnly(pszFilename))
5231 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5232
5233 /*
5234 * Normal library loading.
5235 * We start by being very lazy and reusing the code for resolving imports.
5236 */
5237 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5238 if (!kHlpIsFilenameOnly(pszFilename))
5239 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5240 else
5241 {
5242 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5243 if (rc != 0)
5244 pMod = NULL;
5245 }
5246 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5247 {
5248 /* Enter it into the tool module table and dynamic link request cache. */
5249 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5250
5251 pDynLoad->pMod = pMod;
5252 pDynLoad->hmod = pMod->hOurMod;
5253
5254 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5255 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5256
5257 /*
5258 * Make sure it's initialized (need to link it first since DllMain may
5259 * use loader APIs).
5260 */
5261 rc = kwLdrModuleInitTree(pMod);
5262 if (rc == 0)
5263 {
5264 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5265 return pDynLoad->hmod;
5266 }
5267
5268 SetLastError(ERROR_DLL_INIT_FAILED);
5269 }
5270 else
5271 {
5272 KWFS_TODO();
5273 kHlpFree(pDynLoad);
5274 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5275 }
5276 return NULL;
5277}
5278
5279
5280/** Kernel32 - LoadLibraryExA() for native overloads */
5281static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5282{
5283 char szPath[1024];
5284 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5285
5286 /*
5287 * We may have to help resolved unqualified DLLs living in the executable directory.
5288 */
5289 if (kHlpIsFilenameOnly(pszFilename))
5290 {
5291 KSIZE cchFilename = kHlpStrLen(pszFilename);
5292 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5293 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5294 {
5295 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5296 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5297 if (kwFsPathExists(szPath))
5298 {
5299 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5300 pszFilename = szPath;
5301 }
5302 }
5303
5304 if (pszFilename != szPath)
5305 {
5306 KSIZE cchSuffix = 0;
5307 KBOOL fNeedSuffix = K_FALSE;
5308 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5309 while (*pszCur != '\0')
5310 {
5311 /* Find the end of the component */
5312 KSIZE cch = 0;
5313 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5314 cch++;
5315
5316 if ( cch > 0 /* wrong, but whatever */
5317 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5318 {
5319 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5320 if ( szPath[cch - 1] != ':'
5321 && szPath[cch - 1] != '/'
5322 && szPath[cch - 1] != '\\')
5323 *pszDst++ = '\\';
5324 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5325 if (fNeedSuffix)
5326 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5327 *pszDst = '\0';
5328
5329 if (kwFsPathExists(szPath))
5330 {
5331 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5332 pszFilename = szPath;
5333 break;
5334 }
5335 }
5336
5337 /* Advance */
5338 pszCur += cch;
5339 while (*pszCur == ';')
5340 pszCur++;
5341 }
5342 }
5343 }
5344
5345 return LoadLibraryExA(pszFilename, hFile, fFlags);
5346}
5347
5348
5349/** Kernel32 - LoadLibraryExW() */
5350static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5351{
5352 char szTmp[4096];
5353 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5354 if (cchTmp < sizeof(szTmp))
5355 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5356
5357 KWFS_TODO();
5358 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5359 return NULL;
5360}
5361
5362/** Kernel32 - LoadLibraryA() */
5363static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5364{
5365 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5366}
5367
5368
5369/** Kernel32 - LoadLibraryW() */
5370static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5371{
5372 char szTmp[4096];
5373 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5374 if (cchTmp < sizeof(szTmp))
5375 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5376 KWFS_TODO();
5377 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5378 return NULL;
5379}
5380
5381
5382/** Kernel32 - FreeLibrary() */
5383static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5384{
5385 /* Ignored, we like to keep everything loaded. */
5386 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5387 return TRUE;
5388}
5389
5390
5391/** Worker for GetModuleHandleA/W for handling cached modules. */
5392static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5393{
5394 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5395 if (hmod)
5396 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5397 hmod, g_aGetModuleHandleCache[i].pszName));
5398 else
5399 {
5400 /*
5401 * The first time around we have to make sure we have a module table
5402 * entry for it, if not we add one. We need to add it to the tools
5403 * module list to for it to work.
5404 */
5405 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
5406 g_aGetModuleHandleCache[i].fAlwaysPresent);
5407 if (pMod)
5408 {
5409 hmod = pMod->hOurMod;
5410 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5411 {
5412 kwToolAddModule(g_Sandbox.pTool, pMod);
5413 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5414 hmod, g_aGetModuleHandleCache[i].pszName));
5415 }
5416 else
5417 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5418 hmod, g_aGetModuleHandleCache[i].pszName));
5419
5420 }
5421 }
5422 return hmod;
5423}
5424
5425
5426/** Kernel32 - GetModuleHandleA() */
5427static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5428{
5429 KSIZE i;
5430 KSIZE cchModule;
5431 PKWDYNLOAD pDynLoad;
5432 KSIZE cchSuffix;
5433 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5434 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5435
5436 /*
5437 * The executable.
5438 */
5439 if (pszModule == NULL)
5440 {
5441 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5442 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5443 }
5444
5445 /*
5446 * If no path of suffix, pretend it ends with .DLL.
5447 */
5448 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5449
5450 /*
5451 * Cache of system modules we've seen queried.
5452 */
5453 cchModule = kHlpStrLen(pszModule);
5454 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5455 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5456 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5457 || ( cchSuffix > 0
5458 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5459 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5460 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5461 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5462
5463 /*
5464 * Modules we've dynamically loaded.
5465 */
5466 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5467 if (pDynLoad->pMod)
5468 {
5469 const char *pszPath = pDynLoad->pMod->pszPath;
5470 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
5471 if ( stricmp(pszPath, pszModule) == 0
5472 || stricmp(pszName, pszModule) == 0
5473 || ( cchSuffix > 0
5474 && strnicmp(pszName, pszModule, cchModule) == 0
5475 && stricmp(&pszName[cchModule], ".dll") == 0))
5476 {
5477 if ( pDynLoad->pMod->fNative
5478 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5479 {
5480 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
5481 return pDynLoad->hmod;
5482 }
5483 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
5484 SetLastError(ERROR_MOD_NOT_FOUND);
5485 return NULL;
5486 }
5487 }
5488
5489 /*
5490 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5491 * to and go via the g_aGetModuleHandleCache cache.
5492 */
5493 if (strnicmp(pszModule, "api-ms-win-", 11) == 0)
5494 {
5495 HMODULE hmod = GetModuleHandleA(pszModule);
5496 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
5497 if (hmod)
5498 {
5499 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5500 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
5501 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5502 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
5503 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5504 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
5505 }
5506 else
5507 dwErr = GetLastError();
5508 }
5509
5510 kwErrPrintf("pszModule=%s\n", pszModule);
5511 KWFS_TODO();
5512 SetLastError(ERROR_MOD_NOT_FOUND);
5513 return NULL;
5514}
5515
5516
5517/** Kernel32 - GetModuleHandleW() */
5518static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
5519{
5520 KSIZE i;
5521 KSIZE cwcModule;
5522 PKWDYNLOAD pDynLoad;
5523 KSIZE cwcSuffix;
5524 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5525 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5526
5527 /*
5528 * The executable.
5529 */
5530 if (pwszModule == NULL)
5531 {
5532 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5533 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5534 }
5535
5536 /*
5537 * If no path of suffix, pretend it ends with .DLL.
5538 */
5539 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
5540
5541 /*
5542 * Cache of system modules we've seen queried.
5543 */
5544 cwcModule = kwUtf16Len(pwszModule);
5545 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5546 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
5547 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5548 || ( cwcSuffix > 0
5549 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
5550 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
5551 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
5552 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5553
5554 /*
5555 * Modules we've dynamically loaded.
5556 */
5557 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5558 if (pDynLoad->pMod)
5559 {
5560 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
5561 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
5562 if ( _wcsicmp(pwszPath, pwszModule) == 0
5563 || _wcsicmp(pwszName, pwszModule) == 0
5564 || ( cwcSuffix
5565 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
5566 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
5567 {
5568 if ( pDynLoad->pMod->fNative
5569 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5570 {
5571 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5572 return pDynLoad->hmod;
5573 }
5574 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
5575 SetLastError(ERROR_MOD_NOT_FOUND);
5576 return NULL;
5577 }
5578 }
5579
5580 /*
5581 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5582 * to and go via the g_aGetModuleHandleCache cache.
5583 */
5584 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
5585 {
5586 HMODULE hmod = GetModuleHandleW(pwszModule);
5587 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
5588 if (hmod)
5589 {
5590 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5591 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
5592 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5593 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
5594 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5595 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
5596 }
5597 else
5598 dwErr = GetLastError();
5599 }
5600
5601 kwErrPrintf("pwszModule=%ls\n", pwszModule);
5602 KWFS_TODO();
5603 SetLastError(dwErr);
5604 return NULL;
5605}
5606
5607
5608/** Used to debug dynamically resolved procedures. */
5609static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5610{
5611#ifdef _MSC_VER
5612 __debugbreak();
5613#else
5614 KWFS_TODO();
5615#endif
5616 return -1;
5617}
5618
5619
5620#ifndef NDEBUG
5621/*
5622 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5623 */
5624# if K_ARCH == K_ARCH_X86_32
5625static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5626# else
5627static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5628# endif
5629typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5630typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5631typedef struct KWCXINTERCEPTORENTRY
5632{
5633 PFNINVOKECOMPILERPASSW pfnOrg;
5634 PKWMODULE pModule;
5635 PFNINVOKECOMPILERPASSW pfnWrap;
5636} KWCXINTERCEPTORENTRY;
5637
5638static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5639 KWCXINTERCEPTORENTRY *pEntry)
5640{
5641 int i;
5642 KIPTR rcExit;
5643 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5644 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5645 for (i = 0; i < cArgs; i++)
5646 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5647
5648 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5649
5650 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5651 return rcExit;
5652}
5653
5654static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
5655static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
5656static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
5657
5658static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
5659{
5660 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
5661 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
5662 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
5663};
5664
5665static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5666{
5667 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
5668}
5669
5670static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5671{
5672 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
5673}
5674
5675static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5676{
5677 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
5678}
5679
5680#endif /* !NDEBUG */
5681
5682
5683/** Kernel32 - GetProcAddress() */
5684static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
5685{
5686 KSIZE i;
5687 PKWMODULE pMod;
5688 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5689
5690 /*
5691 * Try locate the module.
5692 */
5693 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5694 if (pMod)
5695 {
5696 KLDRADDR uValue;
5697 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
5698 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
5699 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
5700 KU32_MAX /*iSymbol*/,
5701 pszProc,
5702 kHlpStrLen(pszProc),
5703 NULL /*pszVersion*/,
5704 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
5705 &uValue,
5706 NULL /*pfKind*/);
5707 if (rc == 0)
5708 {
5709 //static int s_cDbgGets = 0;
5710 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
5711 KU32 i = g_cSandboxGetProcReplacements;
5712 while (i-- > 0)
5713 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
5714 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
5715 {
5716 if ( !g_aSandboxGetProcReplacements[i].pszModule
5717 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
5718 {
5719 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
5720 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
5721 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
5722 {
5723 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
5724 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5725 }
5726 kwLdrModuleRelease(pMod);
5727 return (FARPROC)(KUPTR)uValue;
5728 }
5729 }
5730
5731#ifndef NDEBUG
5732 /* Intercept the compiler pass method, dumping arguments. */
5733 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
5734 {
5735 KU32 i;
5736 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
5737 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
5738 {
5739 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5740 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
5741 break;
5742 }
5743 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
5744 while (i-- > 0)
5745 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
5746 {
5747 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
5748 g_aCxInterceptorEntries[i].pModule = pMod;
5749 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5750 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
5751 break;
5752 }
5753 }
5754#endif
5755 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5756 kwLdrModuleRelease(pMod);
5757 //s_cDbgGets++;
5758 //if (s_cGets >= 3)
5759 // return (FARPROC)kwSandbox_BreakIntoDebugger;
5760 return (FARPROC)(KUPTR)uValue;
5761 }
5762
5763 KWFS_TODO();
5764 SetLastError(ERROR_PROC_NOT_FOUND);
5765 kwLdrModuleRelease(pMod);
5766 return NULL;
5767 }
5768
5769 /*
5770 * Hmm... could be a cached module-by-name.
5771 */
5772 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5773 if (g_aGetModuleHandleCache[i].hmod == hmod)
5774 return GetProcAddress(hmod, pszProc);
5775
5776 KWFS_TODO();
5777 return GetProcAddress(hmod, pszProc);
5778}
5779
5780
5781/** Kernel32 - GetModuleFileNameA() */
5782static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
5783{
5784 PKWMODULE pMod;
5785 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5786
5787 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5788 if (pMod != NULL)
5789 {
5790 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
5791 kwLdrModuleRelease(pMod);
5792 return cbRet;
5793 }
5794 KWFS_TODO();
5795 return 0;
5796}
5797
5798
5799/** Kernel32 - GetModuleFileNameW() */
5800static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
5801{
5802 PKWMODULE pMod;
5803 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5804
5805 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5806 if (pMod)
5807 {
5808 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
5809 kwLdrModuleRelease(pMod);
5810 return cwcRet;
5811 }
5812
5813 KWFS_TODO();
5814 return 0;
5815}
5816
5817
5818/** NtDll - RtlPcToFileHeader
5819 * This is necessary for msvcr100.dll!CxxThrowException. */
5820static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
5821{
5822 PVOID pvRet;
5823
5824 /*
5825 * Do a binary lookup of the module table for the current tool.
5826 * This will give us a
5827 */
5828 if (g_Sandbox.fRunning)
5829 {
5830 KUPTR const uPC = (KUPTR)pvPC;
5831 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
5832 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
5833 KU32 i;
5834 if (iEnd)
5835 {
5836 KU32 iStart = 0;
5837 i = iEnd / 2;
5838 for (;;)
5839 {
5840 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
5841 if (uPC < uHModThis)
5842 {
5843 iEnd = i;
5844 if (iStart < i)
5845 { }
5846 else
5847 break;
5848 }
5849 else if (uPC != uHModThis)
5850 {
5851 iStart = ++i;
5852 if (i < iEnd)
5853 { }
5854 else
5855 break;
5856 }
5857 else
5858 {
5859 /* This isn't supposed to happen. */
5860 break;
5861 }
5862
5863 i = iStart + (iEnd - iStart) / 2;
5864 }
5865
5866 /* For reasons of simplicity (= copy & paste), we end up with the
5867 module after the one we're interested in here. */
5868 i--;
5869 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
5870 && papMods[i]->pLdrMod)
5871 {
5872 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
5873 if (uRvaPC < papMods[i]->cbImage)
5874 {
5875 *ppvImageBase = papMods[i]->hOurMod;
5876 pvRet = papMods[i]->hOurMod;
5877 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
5878 return pvRet;
5879 }
5880 }
5881 }
5882 else
5883 i = 0;
5884 }
5885
5886 /*
5887 * Call the regular API.
5888 */
5889 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
5890 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
5891 return pvRet;
5892}
5893
5894
5895/*
5896 *
5897 * File access APIs (for speeding them up).
5898 * File access APIs (for speeding them up).
5899 * File access APIs (for speeding them up).
5900 *
5901 */
5902
5903
5904/**
5905 * Converts a lookup error to a windows error code.
5906 *
5907 * @returns The windows error code.
5908 * @param enmError The lookup error.
5909 */
5910static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
5911{
5912 switch (enmError)
5913 {
5914 case KFSLOOKUPERROR_NOT_FOUND:
5915 case KFSLOOKUPERROR_NOT_DIR:
5916 return ERROR_FILE_NOT_FOUND;
5917
5918 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
5919 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
5920 return ERROR_PATH_NOT_FOUND;
5921
5922 case KFSLOOKUPERROR_PATH_TOO_LONG:
5923 return ERROR_FILENAME_EXCED_RANGE;
5924
5925 case KFSLOOKUPERROR_OUT_OF_MEMORY:
5926 return ERROR_NOT_ENOUGH_MEMORY;
5927
5928 default:
5929 return ERROR_PATH_NOT_FOUND;
5930 }
5931}
5932
5933#ifdef WITH_TEMP_MEMORY_FILES
5934
5935/**
5936 * Checks for a cl.exe temporary file.
5937 *
5938 * There are quite a bunch of these. They seems to be passing data between the
5939 * first and second compiler pass. Since they're on disk, they get subjected to
5940 * AV software screening and normal file consistency rules. So, not necessarily
5941 * a very efficient way of handling reasonably small amounts of data.
5942 *
5943 * We make the files live in virtual memory by intercepting their opening,
5944 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
5945 *
5946 * @returns K_TRUE / K_FALSE
5947 * @param pwszFilename The file name being accessed.
5948 */
5949static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
5950{
5951 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
5952 if (pwszName)
5953 {
5954 /* The name starts with _CL_... */
5955 if ( pwszName[0] == '_'
5956 && pwszName[1] == 'C'
5957 && pwszName[2] == 'L'
5958 && pwszName[3] == '_' )
5959 {
5960 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
5961 this check by just checking that it's alpha numerical ascii from here on. */
5962 wchar_t wc;
5963 pwszName += 4;
5964 while ((wc = *pwszName++) != '\0')
5965 {
5966 if (wc < 127 && iswalnum(wc))
5967 { /* likely */ }
5968 else
5969 return K_FALSE;
5970 }
5971 return K_TRUE;
5972 }
5973 }
5974 return K_FALSE;
5975}
5976
5977
5978/**
5979 * Creates a handle to a temporary file.
5980 *
5981 * @returns The handle on success.
5982 * INVALID_HANDLE_VALUE and SetLastError on failure.
5983 * @param pTempFile The temporary file.
5984 * @param dwDesiredAccess The desired access to the handle.
5985 * @param fMapping Whether this is a mapping (K_TRUE) or file
5986 * (K_FALSE) handle type.
5987 */
5988static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
5989{
5990 /*
5991 * Create a handle to the temporary file.
5992 */
5993 HANDLE hFile = INVALID_HANDLE_VALUE;
5994 HANDLE hProcSelf = GetCurrentProcess();
5995 if (DuplicateHandle(hProcSelf, hProcSelf,
5996 hProcSelf, &hFile,
5997 SYNCHRONIZE, FALSE,
5998 0 /*dwOptions*/))
5999 {
6000 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6001 if (pHandle)
6002 {
6003 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6004 pHandle->cRefs = 1;
6005 pHandle->offFile = 0;
6006 pHandle->hHandle = hFile;
6007 pHandle->dwDesiredAccess = dwDesiredAccess;
6008 pHandle->u.pTempFile = pTempFile;
6009 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6010 {
6011 pTempFile->cActiveHandles++;
6012 kHlpAssert(pTempFile->cActiveHandles >= 1);
6013 kHlpAssert(pTempFile->cActiveHandles <= 2);
6014 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6015 return hFile;
6016 }
6017
6018 kHlpFree(pHandle);
6019 }
6020 else
6021 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6022 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6023 }
6024 else
6025 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6026 return INVALID_HANDLE_VALUE;
6027}
6028
6029
6030static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
6031{
6032 HANDLE hFile;
6033 DWORD dwErr;
6034
6035 /*
6036 * Check if we've got an existing temp file.
6037 * ASSUME exact same path for now.
6038 */
6039 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6040 PKWFSTEMPFILE pTempFile;
6041 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6042 {
6043 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6044 if ( pTempFile->cwcPath == cwcFilename
6045 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6046 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6047 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6048 break;
6049 }
6050
6051 /*
6052 * Create a new temporary file instance if not found.
6053 */
6054 if (pTempFile == NULL)
6055 {
6056 KSIZE cbFilename;
6057
6058 switch (dwCreationDisposition)
6059 {
6060 case CREATE_ALWAYS:
6061 case OPEN_ALWAYS:
6062 dwErr = NO_ERROR;
6063 break;
6064
6065 case CREATE_NEW:
6066 kHlpAssertFailed();
6067 SetLastError(ERROR_ALREADY_EXISTS);
6068 return INVALID_HANDLE_VALUE;
6069
6070 case OPEN_EXISTING:
6071 case TRUNCATE_EXISTING:
6072 kHlpAssertFailed();
6073 SetLastError(ERROR_FILE_NOT_FOUND);
6074 return INVALID_HANDLE_VALUE;
6075
6076 default:
6077 kHlpAssertFailed();
6078 SetLastError(ERROR_INVALID_PARAMETER);
6079 return INVALID_HANDLE_VALUE;
6080 }
6081
6082 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6083 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6084 if (pTempFile)
6085 {
6086 pTempFile->cwcPath = (KU16)cwcFilename;
6087 pTempFile->cbFile = 0;
6088 pTempFile->cbFileAllocated = 0;
6089 pTempFile->cActiveHandles = 0;
6090 pTempFile->cMappings = 0;
6091 pTempFile->cSegs = 0;
6092 pTempFile->paSegs = NULL;
6093 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6094
6095 pTempFile->pNext = g_Sandbox.pTempFileHead;
6096 g_Sandbox.pTempFileHead = pTempFile;
6097 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6098 }
6099 else
6100 {
6101 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6102 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6103 return INVALID_HANDLE_VALUE;
6104 }
6105 }
6106 else
6107 {
6108 switch (dwCreationDisposition)
6109 {
6110 case OPEN_EXISTING:
6111 dwErr = NO_ERROR;
6112 break;
6113 case OPEN_ALWAYS:
6114 dwErr = ERROR_ALREADY_EXISTS ;
6115 break;
6116
6117 case TRUNCATE_EXISTING:
6118 case CREATE_ALWAYS:
6119 kHlpAssertFailed();
6120 pTempFile->cbFile = 0;
6121 dwErr = ERROR_ALREADY_EXISTS;
6122 break;
6123
6124 case CREATE_NEW:
6125 kHlpAssertFailed();
6126 SetLastError(ERROR_FILE_EXISTS);
6127 return INVALID_HANDLE_VALUE;
6128
6129 default:
6130 kHlpAssertFailed();
6131 SetLastError(ERROR_INVALID_PARAMETER);
6132 return INVALID_HANDLE_VALUE;
6133 }
6134 }
6135
6136 /*
6137 * Create a handle to the temporary file.
6138 */
6139 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6140 if (hFile != INVALID_HANDLE_VALUE)
6141 SetLastError(dwErr);
6142 return hFile;
6143}
6144
6145#endif /* WITH_TEMP_MEMORY_FILES */
6146
6147/**
6148 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6149 *
6150 * @returns K_TRUE if cacheable, K_FALSE if not.
6151 * @param wcFirst The first extension character.
6152 * @param wcSecond The second extension character.
6153 * @param wcThird The third extension character.
6154 * @param fAttrQuery Set if it's for an attribute query, clear if for
6155 * file creation.
6156 */
6157static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6158{
6159 /* C++ header without an extension or a directory. */
6160 if (wcFirst == '\0')
6161 {
6162 /** @todo exclude temporary files... */
6163 return K_TRUE;
6164 }
6165
6166 /* C Header: .h */
6167 if (wcFirst == 'h' || wcFirst == 'H')
6168 {
6169 if (wcSecond == '\0')
6170 return K_TRUE;
6171
6172 /* C++ Header: .hpp, .hxx */
6173 if ( (wcSecond == 'p' || wcSecond == 'P')
6174 && (wcThird == 'p' || wcThird == 'P'))
6175 return K_TRUE;
6176 if ( (wcSecond == 'x' || wcSecond == 'X')
6177 && (wcThird == 'x' || wcThird == 'X'))
6178 return K_TRUE;
6179 }
6180 /* Misc starting with i. */
6181 else if (wcFirst == 'i' || wcFirst == 'I')
6182 {
6183 if (wcSecond != '\0')
6184 {
6185 if (wcSecond == 'n' || wcSecond == 'N')
6186 {
6187 /* C++ inline header: .inl */
6188 if (wcThird == 'l' || wcThird == 'L')
6189 return K_TRUE;
6190
6191 /* Assembly include file: .inc */
6192 if (wcThird == 'c' || wcThird == 'C')
6193 return K_TRUE;
6194 }
6195 }
6196 }
6197 /* Assembly header: .mac */
6198 else if (wcFirst == 'm' || wcFirst == 'M')
6199 {
6200 if (wcSecond == 'a' || wcSecond == 'A')
6201 {
6202 if (wcThird == 'c' || wcThird == 'C')
6203 return K_TRUE;
6204 }
6205 }
6206#ifdef WITH_PCH_CACHING
6207 /* Precompiled header: .pch */
6208 else if (wcFirst == 'p' || wcFirst == 'P')
6209 {
6210 if (wcSecond == 'c' || wcSecond == 'C')
6211 {
6212 if (wcThird == 'h' || wcThird == 'H')
6213 return !g_Sandbox.fNoPchCaching;
6214 }
6215 }
6216#endif
6217#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6218 /* Linker - Object file: .obj */
6219 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6220 {
6221 if (wcSecond == 'b' || wcSecond == 'B')
6222 {
6223 if (wcThird == 'j' || wcThird == 'J')
6224 return K_TRUE;
6225 }
6226 }
6227#endif
6228 else if (fAttrQuery)
6229 {
6230 /* Dynamic link library: .dll */
6231 if (wcFirst == 'd' || wcFirst == 'D')
6232 {
6233 if (wcSecond == 'l' || wcSecond == 'L')
6234 {
6235 if (wcThird == 'l' || wcThird == 'L')
6236 return K_TRUE;
6237 }
6238 }
6239 /* Executable file: .exe */
6240 else if (wcFirst == 'e' || wcFirst == 'E')
6241 {
6242 if (wcSecond == 'x' || wcSecond == 'X')
6243 {
6244 if (wcThird == 'e' || wcThird == 'E')
6245 return K_TRUE;
6246 }
6247 }
6248 /* Response file: .rsp */
6249 else if (wcFirst == 'r' || wcFirst == 'R')
6250 {
6251 if (wcSecond == 's' || wcSecond == 'S')
6252 {
6253 if (wcThird == 'p' || wcThird == 'P')
6254 return !g_Sandbox.fNoPchCaching;
6255 }
6256 }
6257 /* Linker: */
6258 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6259 {
6260 /* Object file: .obj */
6261 if (wcFirst == 'o' || wcFirst == 'O')
6262 {
6263 if (wcSecond == 'b' || wcSecond == 'B')
6264 {
6265 if (wcThird == 'j' || wcThird == 'J')
6266 return K_TRUE;
6267 }
6268 }
6269 /* Library file: .lib */
6270 else if (wcFirst == 'l' || wcFirst == 'L')
6271 {
6272 if (wcSecond == 'i' || wcSecond == 'I')
6273 {
6274 if (wcThird == 'b' || wcThird == 'B')
6275 return K_TRUE;
6276 }
6277 }
6278 /* Linker definition file: .def */
6279 else if (wcFirst == 'd' || wcFirst == 'D')
6280 {
6281 if (wcSecond == 'e' || wcSecond == 'E')
6282 {
6283 if (wcThird == 'f' || wcThird == 'F')
6284 return K_TRUE;
6285 }
6286 }
6287 }
6288 }
6289
6290 return K_FALSE;
6291}
6292
6293
6294/**
6295 * Checks if the file extension indicates that the file/dir is something we
6296 * ought to cache.
6297 *
6298 * @returns K_TRUE if cachable, K_FALSE if not.
6299 * @param pszExt The kHlpGetExt result.
6300 * @param fAttrQuery Set if it's for an attribute query, clear if for
6301 * file creation.
6302 */
6303static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6304{
6305 wchar_t const wcFirst = *pszExt;
6306 if (wcFirst)
6307 {
6308 wchar_t const wcSecond = pszExt[1];
6309 if (wcSecond)
6310 {
6311 wchar_t const wcThird = pszExt[2];
6312 if (pszExt[3] == '\0')
6313 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6314 return K_FALSE;
6315 }
6316 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6317 }
6318 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6319}
6320
6321
6322/**
6323 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6324 * should be cached.
6325 *
6326 * @returns K_TRUE if cachable, K_FALSE if not.
6327 * @param pwszPath The UTF-16 path to examine.
6328 * @param fAttrQuery Set if it's for an attribute query, clear if for
6329 * file creation.
6330 */
6331static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6332{
6333 KSIZE cwcExt;
6334 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6335 switch (cwcExt)
6336 {
6337 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6338 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6339 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6340 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6341 }
6342 return K_FALSE;
6343}
6344
6345
6346
6347/**
6348 * Creates a new
6349 *
6350 * @returns
6351 * @param pFsObj .
6352 * @param pwszFilename .
6353 */
6354static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6355{
6356 HANDLE hFile;
6357 MY_IO_STATUS_BLOCK Ios;
6358 MY_OBJECT_ATTRIBUTES ObjAttr;
6359 MY_UNICODE_STRING UniStr;
6360 MY_NTSTATUS rcNt;
6361 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6362
6363 /*
6364 * Open the file relative to the parent directory.
6365 */
6366 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6367 kHlpAssert(pFsObj->pParent);
6368 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6369
6370 Ios.Information = -1;
6371 Ios.u.Status = -1;
6372
6373 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6374 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6375 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6376
6377 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6378
6379 rcNt = g_pfnNtCreateFile(&hFile,
6380 GENERIC_READ | SYNCHRONIZE,
6381 &ObjAttr,
6382 &Ios,
6383 NULL, /*cbFileInitialAlloc */
6384 FILE_ATTRIBUTE_NORMAL,
6385 FILE_SHARE_READ,
6386 FILE_OPEN,
6387 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6388 NULL, /*pEaBuffer*/
6389 0); /*cbEaBuffer*/
6390 if (MY_NT_SUCCESS(rcNt))
6391 {
6392 /*
6393 * Read the whole file into memory.
6394 */
6395 LARGE_INTEGER cbFile;
6396 if (GetFileSizeEx(hFile, &cbFile))
6397 {
6398 if ( cbFile.QuadPart >= 0
6399#ifdef WITH_PCH_CACHING
6400 && ( cbFile.QuadPart < 16*1024*1024
6401 || ( cbFile.QuadPart < 96*1024*1024
6402 && pFsObj->cchName > 4
6403 && !g_Sandbox.fNoPchCaching
6404 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
6405#endif
6406 )
6407 {
6408 KU32 cbCache = (KU32)cbFile.QuadPart;
6409 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
6410 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
6411 if (hMapping != NULL)
6412 {
6413 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
6414 if (pbCache)
6415 {
6416 /*
6417 * Create the cached file object.
6418 */
6419 PKFSWCACHEDFILE pCachedFile;
6420 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
6421 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
6422 sizeof(*pCachedFile) + cbPath);
6423 if (pCachedFile)
6424 {
6425 pCachedFile->hCached = hFile;
6426 pCachedFile->hSection = hMapping;
6427 pCachedFile->cbCached = cbCache;
6428 pCachedFile->pbCached = pbCache;
6429 pCachedFile->pFsObj = pFsObj;
6430 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
6431 kFsCacheObjRetain(pFsObj);
6432
6433 g_cReadCachedFiles++;
6434 g_cbReadCachedFiles += cbCache;
6435
6436 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
6437 return pCachedFile;
6438 }
6439
6440 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
6441 }
6442 else
6443 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
6444 CloseHandle(hMapping);
6445 }
6446 else
6447 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
6448 }
6449 else
6450 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
6451 }
6452 else
6453 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
6454 g_pfnNtClose(hFile);
6455 }
6456 else
6457 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
6458 return NULL;
6459}
6460
6461
6462/**
6463 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
6464 */
6465static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
6466 KBOOL fIsFileHandle, HANDLE *phFile)
6467{
6468 HANDLE hProcSelf = GetCurrentProcess();
6469 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
6470 hProcSelf, phFile,
6471 dwDesiredAccess, fInheritHandle,
6472 0 /*dwOptions*/))
6473 {
6474 /*
6475 * Create handle table entry for the duplicate handle.
6476 */
6477 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6478 if (pHandle)
6479 {
6480 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
6481 pHandle->cRefs = 1;
6482 pHandle->offFile = 0;
6483 pHandle->hHandle = *phFile;
6484 pHandle->dwDesiredAccess = dwDesiredAccess;
6485 pHandle->u.pCachedFile = pCachedFile;
6486 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
6487 return K_TRUE;
6488
6489 kHlpFree(pHandle);
6490 }
6491 else
6492 KWFS_LOG(("Out of memory for handle!\n"));
6493
6494 CloseHandle(*phFile);
6495 *phFile = INVALID_HANDLE_VALUE;
6496 }
6497 else
6498 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
6499 return K_FALSE;
6500}
6501
6502
6503/**
6504 * Kernel32 - Common code for CreateFileW and CreateFileA.
6505 */
6506static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
6507{
6508 *phFile = INVALID_HANDLE_VALUE;
6509
6510 /*
6511 * At the moment we only handle existing files.
6512 */
6513 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
6514 {
6515 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
6516 kHlpAssert(pFsObj->fHaveStats);
6517 if ( pCachedFile != NULL
6518 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
6519 {
6520 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
6521 return K_TRUE;
6522 }
6523 }
6524 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
6525
6526 /* Do fallback, please. */
6527 return K_FALSE;
6528}
6529
6530
6531/** Kernel32 - CreateFileA */
6532static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6533 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6534 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6535{
6536 HANDLE hFile;
6537
6538 /*
6539 * Check for include files and similar that we do read-only caching of.
6540 */
6541 if (dwCreationDisposition == FILE_OPEN_IF)
6542 {
6543 if ( dwDesiredAccess == GENERIC_READ
6544 || dwDesiredAccess == FILE_GENERIC_READ)
6545 {
6546 if (dwShareMode & FILE_SHARE_READ)
6547 {
6548 if ( !pSecAttrs
6549 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6550 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6551 {
6552 const char *pszExt = kHlpGetExt(pszFilename);
6553 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
6554 {
6555 KFSLOOKUPERROR enmError;
6556 PKFSOBJ pFsObj;
6557 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6558
6559 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
6560 if (pFsObj)
6561 {
6562 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6563 &hFile);
6564 kFsCacheObjRelease(g_pFsCache, pFsObj);
6565 if (fRc)
6566 {
6567 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6568 return hFile;
6569 }
6570 }
6571 /* These are for nasm and yasm header searching. Cache will already
6572 have checked the directories for the file, no need to call
6573 CreateFile to do it again. */
6574 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6575 {
6576 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6577 return INVALID_HANDLE_VALUE;
6578 }
6579 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6580 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6581 {
6582 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6583 return INVALID_HANDLE_VALUE;
6584 }
6585
6586 /* fallback */
6587 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6588 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6589 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
6590 return hFile;
6591 }
6592 }
6593 }
6594 }
6595 }
6596
6597 /*
6598 * Okay, normal.
6599 */
6600 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6601 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6602 if (hFile != INVALID_HANDLE_VALUE)
6603 {
6604 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6605 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6606 }
6607 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
6608 return hFile;
6609}
6610
6611
6612/** Kernel32 - CreateFileW */
6613static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6614 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6615 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6616{
6617 HANDLE hFile;
6618
6619#ifdef WITH_TEMP_MEMORY_FILES
6620 /*
6621 * Check for temporary files (cl.exe only).
6622 */
6623 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6624 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
6625 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
6626 && kwFsIsClTempFileW(pwszFilename))
6627 {
6628 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition);
6629 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
6630 return hFile;
6631 }
6632#endif
6633
6634 /*
6635 * Check for include files and similar that we do read-only caching of.
6636 */
6637 if (dwCreationDisposition == FILE_OPEN_IF)
6638 {
6639 if ( dwDesiredAccess == GENERIC_READ
6640 || dwDesiredAccess == FILE_GENERIC_READ)
6641 {
6642 if (dwShareMode & FILE_SHARE_READ)
6643 {
6644 if ( !pSecAttrs
6645 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6646 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6647 {
6648 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
6649 {
6650 KFSLOOKUPERROR enmError;
6651 PKFSOBJ pFsObj;
6652 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6653
6654 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6655 if (pFsObj)
6656 {
6657 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6658 &hFile);
6659 kFsCacheObjRelease(g_pFsCache, pFsObj);
6660 if (fRc)
6661 {
6662 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
6663 return hFile;
6664 }
6665 }
6666 /* These are for nasm and yasm style header searching. Cache will
6667 already have checked the directories for the file, no need to call
6668 CreateFile to do it again. */
6669 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6670 {
6671 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
6672 return INVALID_HANDLE_VALUE;
6673 }
6674 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6675 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6676 {
6677 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
6678 return INVALID_HANDLE_VALUE;
6679 }
6680
6681 /* fallback */
6682 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6683 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6684 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
6685 return hFile;
6686 }
6687 }
6688 else
6689 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
6690 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
6691 }
6692 else
6693 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
6694 }
6695 else
6696 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
6697 }
6698 else
6699 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
6700
6701 /*
6702 * Okay, normal.
6703 */
6704 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6705 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6706 if (hFile != INVALID_HANDLE_VALUE)
6707 {
6708 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6709 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6710 }
6711 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
6712 return hFile;
6713}
6714
6715
6716/** Kernel32 - SetFilePointer */
6717static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
6718{
6719 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6720 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6721 if (idxHandle < g_Sandbox.cHandles)
6722 {
6723 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6724 if (pHandle != NULL)
6725 {
6726 KU32 cbFile;
6727 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
6728 switch (pHandle->enmType)
6729 {
6730 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6731 cbFile = pHandle->u.pCachedFile->cbCached;
6732 break;
6733#ifdef WITH_TEMP_MEMORY_FILES
6734 case KWHANDLETYPE_TEMP_FILE:
6735 cbFile = pHandle->u.pTempFile->cbFile;
6736 break;
6737#endif
6738 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6739 case KWHANDLETYPE_OUTPUT_BUF:
6740 default:
6741 kHlpAssertFailed();
6742 SetLastError(ERROR_INVALID_FUNCTION);
6743 return INVALID_SET_FILE_POINTER;
6744 }
6745
6746 switch (dwMoveMethod)
6747 {
6748 case FILE_BEGIN:
6749 break;
6750 case FILE_CURRENT:
6751 offMove += pHandle->offFile;
6752 break;
6753 case FILE_END:
6754 offMove += cbFile;
6755 break;
6756 default:
6757 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6758 SetLastError(ERROR_INVALID_PARAMETER);
6759 return INVALID_SET_FILE_POINTER;
6760 }
6761 if (offMove >= 0)
6762 {
6763 if (offMove >= (KSSIZE)cbFile)
6764 {
6765 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6766 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6767 offMove = (KSSIZE)cbFile;
6768 /* For writable files, seeking beyond the end is fine, but check that we've got
6769 the type range for the request. */
6770 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
6771 {
6772 kHlpAssertMsgFailed(("%#llx\n", offMove));
6773 SetLastError(ERROR_SEEK);
6774 return INVALID_SET_FILE_POINTER;
6775 }
6776 }
6777 pHandle->offFile = (KU32)offMove;
6778 }
6779 else
6780 {
6781 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
6782 SetLastError(ERROR_NEGATIVE_SEEK);
6783 return INVALID_SET_FILE_POINTER;
6784 }
6785 if (pcbMoveHi)
6786 *pcbMoveHi = (KU64)offMove >> 32;
6787 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
6788 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6789 SetLastError(NO_ERROR);
6790 return (KU32)offMove;
6791 }
6792 }
6793 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
6794 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
6795}
6796
6797
6798/** Kernel32 - SetFilePointerEx */
6799static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
6800 DWORD dwMoveMethod)
6801{
6802 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6803 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6804 if (idxHandle < g_Sandbox.cHandles)
6805 {
6806 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6807 if (pHandle != NULL)
6808 {
6809 KI64 offMyMove = offMove.QuadPart;
6810 KU32 cbFile;
6811 switch (pHandle->enmType)
6812 {
6813 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6814 cbFile = pHandle->u.pCachedFile->cbCached;
6815 break;
6816#ifdef WITH_TEMP_MEMORY_FILES
6817 case KWHANDLETYPE_TEMP_FILE:
6818 cbFile = pHandle->u.pTempFile->cbFile;
6819 break;
6820#endif
6821 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6822 case KWHANDLETYPE_OUTPUT_BUF:
6823 default:
6824 kHlpAssertFailed();
6825 SetLastError(ERROR_INVALID_FUNCTION);
6826 return INVALID_SET_FILE_POINTER;
6827 }
6828
6829 switch (dwMoveMethod)
6830 {
6831 case FILE_BEGIN:
6832 break;
6833 case FILE_CURRENT:
6834 offMyMove += pHandle->offFile;
6835 break;
6836 case FILE_END:
6837 offMyMove += cbFile;
6838 break;
6839 default:
6840 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6841 SetLastError(ERROR_INVALID_PARAMETER);
6842 return INVALID_SET_FILE_POINTER;
6843 }
6844 if (offMyMove >= 0)
6845 {
6846 if (offMyMove >= (KSSIZE)cbFile)
6847 {
6848 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6849 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6850 offMyMove = (KSSIZE)cbFile;
6851 /* For writable files, seeking beyond the end is fine, but check that we've got
6852 the type range for the request. */
6853 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
6854 {
6855 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
6856 SetLastError(ERROR_SEEK);
6857 return INVALID_SET_FILE_POINTER;
6858 }
6859 }
6860 pHandle->offFile = (KU32)offMyMove;
6861 }
6862 else
6863 {
6864 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
6865 SetLastError(ERROR_NEGATIVE_SEEK);
6866 return INVALID_SET_FILE_POINTER;
6867 }
6868 if (poffNew)
6869 poffNew->QuadPart = offMyMove;
6870 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
6871 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6872 return TRUE;
6873 }
6874 }
6875 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
6876 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
6877}
6878
6879
6880/** Kernel32 - ReadFile */
6881static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
6882 LPOVERLAPPED pOverlapped)
6883{
6884 BOOL fRet;
6885 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6886 g_cReadFileCalls++;
6887 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6888 if (idxHandle < g_Sandbox.cHandles)
6889 {
6890 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6891 if (pHandle != NULL)
6892 {
6893 switch (pHandle->enmType)
6894 {
6895 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6896 {
6897 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
6898 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
6899 if (cbActually > cbToRead)
6900 cbActually = cbToRead;
6901
6902#ifdef WITH_HASH_MD5_CACHE
6903 if (g_Sandbox.pHashHead)
6904 {
6905 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
6906 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
6907 g_Sandbox.LastHashRead.cbRead = cbActually;
6908 g_Sandbox.LastHashRead.pvRead = pvBuffer;
6909 }
6910#endif
6911
6912 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
6913 pHandle->offFile += cbActually;
6914
6915 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6916 *pcbActuallyRead = cbActually;
6917
6918 g_cbReadFileFromReadCached += cbActually;
6919 g_cbReadFileTotal += cbActually;
6920 g_cReadFileFromReadCached++;
6921
6922 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
6923 return TRUE;
6924 }
6925
6926#ifdef WITH_TEMP_MEMORY_FILES
6927 case KWHANDLETYPE_TEMP_FILE:
6928 {
6929 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6930 KU32 cbActually;
6931 if (pHandle->offFile < pTempFile->cbFile)
6932 {
6933 cbActually = pTempFile->cbFile - pHandle->offFile;
6934 if (cbActually > cbToRead)
6935 cbActually = cbToRead;
6936
6937 /* Copy the data. */
6938 if (cbActually > 0)
6939 {
6940 KU32 cbLeft;
6941 KU32 offSeg;
6942 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
6943
6944 /* Locate the segment containing the byte at offFile. */
6945 KU32 iSeg = pTempFile->cSegs - 1;
6946 kHlpAssert(pTempFile->cSegs > 0);
6947 while (paSegs[iSeg].offData > pHandle->offFile)
6948 iSeg--;
6949
6950 /* Copy out the data. */
6951 cbLeft = cbActually;
6952 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6953 for (;;)
6954 {
6955 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6956 if (cbAvail >= cbLeft)
6957 {
6958 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
6959 break;
6960 }
6961
6962 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
6963 cbLeft -= cbAvail;
6964 offSeg = 0;
6965 iSeg++;
6966 kHlpAssert(iSeg < pTempFile->cSegs);
6967 }
6968
6969 /* Update the file offset. */
6970 pHandle->offFile += cbActually;
6971 }
6972 }
6973 /* Read does not commit file space, so return zero bytes. */
6974 else
6975 cbActually = 0;
6976
6977 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6978 *pcbActuallyRead = cbActually;
6979
6980 g_cbReadFileTotal += cbActually;
6981 g_cbReadFileFromInMemTemp += cbActually;
6982 g_cReadFileFromInMemTemp++;
6983
6984 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
6985 return TRUE;
6986 }
6987#endif /* WITH_TEMP_MEMORY_FILES */
6988
6989 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6990 case KWHANDLETYPE_OUTPUT_BUF:
6991 default:
6992 kHlpAssertFailed();
6993 SetLastError(ERROR_INVALID_FUNCTION);
6994 *pcbActuallyRead = 0;
6995 return FALSE;
6996 }
6997 }
6998 }
6999
7000 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7001 if (fRet && pcbActuallyRead)
7002 g_cbReadFileTotal += *pcbActuallyRead;
7003 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7004 return fRet;
7005}
7006
7007
7008/** Kernel32 - ReadFileEx */
7009static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7010 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7011{
7012 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7013 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7014 if (idxHandle < g_Sandbox.cHandles)
7015 {
7016 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7017 if (pHandle != NULL)
7018 {
7019 kHlpAssertFailed();
7020 }
7021 }
7022
7023 KWFS_LOG(("ReadFile(%p)\n", hFile));
7024 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7025}
7026
7027#ifdef WITH_STD_OUT_ERR_BUFFERING
7028
7029/**
7030 * Write something to a handle, making sure everything is actually written.
7031 *
7032 * @param hHandle Where to write it to.
7033 * @param pchBuf What to write
7034 * @param cchToWrite How much to write.
7035 */
7036static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7037{
7038 if (cchToWrite > 0)
7039 {
7040 DWORD cchWritten = 0;
7041 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7042 {
7043 if (cchWritten == cchToWrite)
7044 { /* likely */ }
7045 else
7046 {
7047 do
7048 {
7049 pchBuf += cchWritten;
7050 cchToWrite -= cchWritten;
7051 cchWritten = 0;
7052 } while ( cchToWrite > 0
7053 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7054 }
7055 }
7056 else
7057 kHlpAssertFailed();
7058 }
7059}
7060
7061
7062/**
7063 * Worker for WriteFile when the output isn't going to the console.
7064 *
7065 * @param pSandbox The sandbox.
7066 * @param pOutBuf The output buffer.
7067 * @param pchBuffer What to write.
7068 * @param cchToWrite How much to write.
7069 */
7070static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7071{
7072 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7073 { /* likely */ }
7074 else
7075 {
7076 /* No realloc, max size is 64KB. */
7077 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7078 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7079 if (!pOutBuf->u.Fully.pchBuf)
7080 {
7081 while ( !pOutBuf->u.Fully.pchBuf
7082 && pOutBuf->u.Fully.cchBufAlloc > 64)
7083 {
7084 pOutBuf->u.Fully.cchBufAlloc /= 2;
7085 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7086 }
7087 if (!pOutBuf->u.Fully.pchBuf)
7088 {
7089 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7090 pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
7091 }
7092 }
7093 }
7094
7095 /*
7096 * Special case: Output ends with newline and fits in the buffer.
7097 */
7098 if ( cchToWrite > 1
7099 && pchBuffer[cchToWrite - 1] == '\n'
7100 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7101 {
7102 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7103 pOutBuf->u.Fully.cchBuf += cchToWrite;
7104 }
7105 else
7106 {
7107 /*
7108 * Work thru the text line by line, flushing the buffer when
7109 * appropriate. The buffer is not a line buffer here, it's a
7110 * full buffer.
7111 */
7112 while (cchToWrite > 0)
7113 {
7114 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7115 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7116 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7117 {
7118 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7119 pOutBuf->u.Fully.cchBuf += cchLine;
7120 }
7121 /*
7122 * Option one: Flush the buffer and the current line.
7123 *
7124 * We choose this one when the line won't ever fit, or when we have
7125 * an incomplete line in the buffer.
7126 */
7127 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7128 || pOutBuf->u.Fully.cchBuf == 0
7129 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7130 {
7131 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7132 if (pOutBuf->u.Fully.cchBuf > 0)
7133 {
7134 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7135 pOutBuf->u.Fully.cchBuf = 0;
7136 }
7137 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7138 }
7139 /*
7140 * Option two: Only flush the lines in the buffer.
7141 */
7142 else
7143 {
7144 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7145 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7146 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7147 pOutBuf->u.Fully.cchBuf = cchLine;
7148 }
7149
7150 /* advance */
7151 pchBuffer += cchLine;
7152 cchToWrite -= cchLine;
7153 }
7154 }
7155}
7156
7157#endif /* WITH_STD_OUT_ERR_BUFFERING */
7158
7159#ifdef WITH_TEMP_MEMORY_FILES
7160static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7161{
7162 KU32 cbMinFile = offFile + cbNeeded;
7163 if (cbMinFile >= offFile)
7164 {
7165 /* Calc how much space we've already allocated and */
7166 if (cbMinFile <= pTempFile->cbFileAllocated)
7167 return K_TRUE;
7168
7169 /* Grow the file. */
7170 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7171 {
7172 int rc;
7173 KU32 cSegs = pTempFile->cSegs;
7174 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7175 do
7176 {
7177 /* grow the segment array? */
7178 if ((cSegs % 16) == 0)
7179 {
7180 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7181 if (!pvNew)
7182 return K_FALSE;
7183 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7184 }
7185
7186 /* Use page alloc here to simplify mapping later. */
7187 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7188 if (rc == 0)
7189 { /* likely */ }
7190 else
7191 {
7192 cbNewSeg = 64*1024;
7193 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7194 if (rc != 0)
7195 {
7196 kHlpAssertFailed();
7197 return K_FALSE;
7198 }
7199 }
7200 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7201 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7202 pTempFile->cbFileAllocated += cbNewSeg;
7203 pTempFile->cSegs = ++cSegs;
7204
7205 } while (pTempFile->cbFileAllocated < cbMinFile);
7206
7207 return K_TRUE;
7208 }
7209 }
7210
7211 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7212 return K_FALSE;
7213}
7214#endif /* WITH_TEMP_MEMORY_FILES */
7215
7216
7217#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7218/** Kernel32 - WriteFile */
7219static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7220 LPOVERLAPPED pOverlapped)
7221{
7222 BOOL fRet;
7223 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7224 g_cWriteFileCalls++;
7225 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7226 if (idxHandle < g_Sandbox.cHandles)
7227 {
7228 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7229 if (pHandle != NULL)
7230 {
7231 switch (pHandle->enmType)
7232 {
7233# ifdef WITH_TEMP_MEMORY_FILES
7234 case KWHANDLETYPE_TEMP_FILE:
7235 {
7236 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7237
7238 kHlpAssert(!pOverlapped);
7239 kHlpAssert(pcbActuallyWritten);
7240
7241 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7242 {
7243 KU32 cbLeft;
7244 KU32 offSeg;
7245
7246 /* Locate the segment containing the byte at offFile. */
7247 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7248 KU32 iSeg = pTempFile->cSegs - 1;
7249 kHlpAssert(pTempFile->cSegs > 0);
7250 while (paSegs[iSeg].offData > pHandle->offFile)
7251 iSeg--;
7252
7253 /* Copy in the data. */
7254 cbLeft = cbToWrite;
7255 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7256 for (;;)
7257 {
7258 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7259 if (cbAvail >= cbLeft)
7260 {
7261 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7262 break;
7263 }
7264
7265 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7266 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7267 cbLeft -= cbAvail;
7268 offSeg = 0;
7269 iSeg++;
7270 kHlpAssert(iSeg < pTempFile->cSegs);
7271 }
7272
7273 /* Update the file offset. */
7274 pHandle->offFile += cbToWrite;
7275 if (pHandle->offFile > pTempFile->cbFile)
7276 pTempFile->cbFile = pHandle->offFile;
7277
7278 *pcbActuallyWritten = cbToWrite;
7279
7280 g_cbWriteFileTotal += cbToWrite;
7281 g_cbWriteFileToInMemTemp += cbToWrite;
7282 g_cWriteFileToInMemTemp++;
7283
7284 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7285 return TRUE;
7286 }
7287
7288 kHlpAssertFailed();
7289 *pcbActuallyWritten = 0;
7290 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7291 return FALSE;
7292 }
7293# endif
7294
7295 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7296 kHlpAssertFailed();
7297 SetLastError(ERROR_ACCESS_DENIED);
7298 *pcbActuallyWritten = 0;
7299 return FALSE;
7300
7301# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7302 /*
7303 * Standard output & error.
7304 */
7305 case KWHANDLETYPE_OUTPUT_BUF:
7306 {
7307 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7308 if (pOutBuf->fIsConsole)
7309 {
7310 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7311 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7312 }
7313 else
7314 {
7315# ifdef WITH_STD_OUT_ERR_BUFFERING
7316 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7317 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7318 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7319# else
7320 kHlpAssertFailed();
7321# endif
7322 }
7323 if (pcbActuallyWritten)
7324 *pcbActuallyWritten = cbToWrite;
7325 g_cbWriteFileTotal += cbToWrite;
7326 return TRUE;
7327 }
7328# endif
7329
7330 default:
7331 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7332 kHlpAssertFailed();
7333 SetLastError(ERROR_INVALID_FUNCTION);
7334 *pcbActuallyWritten = 0;
7335 return FALSE;
7336 }
7337 }
7338 }
7339
7340 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7341 if (fRet && pcbActuallyWritten)
7342 g_cbWriteFileTotal += *pcbActuallyWritten;
7343 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7344 return fRet;
7345}
7346
7347
7348/** Kernel32 - WriteFileEx */
7349static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7350 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7351{
7352 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7353 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7354 if (idxHandle < g_Sandbox.cHandles)
7355 {
7356 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7357 if (pHandle != NULL)
7358 {
7359 kHlpAssertFailed();
7360 }
7361 }
7362
7363 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
7364 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
7365}
7366
7367#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
7368
7369#ifdef WITH_TEMP_MEMORY_FILES
7370
7371/** Kernel32 - SetEndOfFile; */
7372static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
7373{
7374 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7375 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7376 if (idxHandle < g_Sandbox.cHandles)
7377 {
7378 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7379 if (pHandle != NULL)
7380 {
7381 switch (pHandle->enmType)
7382 {
7383 case KWHANDLETYPE_TEMP_FILE:
7384 {
7385 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7386 if ( pHandle->offFile > pTempFile->cbFile
7387 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
7388 {
7389 kHlpAssertFailed();
7390 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7391 return FALSE;
7392 }
7393
7394 pTempFile->cbFile = pHandle->offFile;
7395 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
7396 return TRUE;
7397 }
7398
7399 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7400 kHlpAssertFailed();
7401 SetLastError(ERROR_ACCESS_DENIED);
7402 return FALSE;
7403
7404 case KWHANDLETYPE_OUTPUT_BUF:
7405 kHlpAssertFailed();
7406 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
7407 return FALSE;
7408
7409 default:
7410 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7411 kHlpAssertFailed();
7412 SetLastError(ERROR_INVALID_FUNCTION);
7413 return FALSE;
7414 }
7415 }
7416 }
7417
7418 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
7419 return SetEndOfFile(hFile);
7420}
7421
7422
7423/** Kernel32 - GetFileType */
7424static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
7425{
7426 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7427 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7428 if (idxHandle < g_Sandbox.cHandles)
7429 {
7430 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7431 if (pHandle != NULL)
7432 {
7433 switch (pHandle->enmType)
7434 {
7435 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7436 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
7437 return FILE_TYPE_DISK;
7438
7439 case KWHANDLETYPE_TEMP_FILE:
7440 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
7441 return FILE_TYPE_DISK;
7442
7443 case KWHANDLETYPE_OUTPUT_BUF:
7444 {
7445 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7446 DWORD fRet;
7447 if (pOutBuf->fFileType != KU8_MAX)
7448 {
7449 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
7450 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
7451 }
7452 else
7453 {
7454 fRet = GetFileType(hFile);
7455 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
7456 }
7457 return fRet;
7458 }
7459
7460 }
7461 }
7462 }
7463
7464 KWFS_LOG(("GetFileType(%p)\n", hFile));
7465 return GetFileType(hFile);
7466}
7467
7468
7469/** Kernel32 - GetFileSize */
7470static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
7471{
7472 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7473 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7474 if (idxHandle < g_Sandbox.cHandles)
7475 {
7476 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7477 if (pHandle != NULL)
7478 {
7479 if (pcbHighDword)
7480 *pcbHighDword = 0;
7481 SetLastError(NO_ERROR);
7482 switch (pHandle->enmType)
7483 {
7484 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7485 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7486 return pHandle->u.pCachedFile->cbCached;
7487
7488 case KWHANDLETYPE_TEMP_FILE:
7489 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7490 return pHandle->u.pTempFile->cbFile;
7491
7492 case KWHANDLETYPE_OUTPUT_BUF:
7493 /* do default */
7494 break;
7495
7496 default:
7497 kHlpAssertFailed();
7498 SetLastError(ERROR_INVALID_FUNCTION);
7499 return INVALID_FILE_SIZE;
7500 }
7501 }
7502 }
7503
7504 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
7505 return GetFileSize(hFile, pcbHighDword);
7506}
7507
7508
7509/** Kernel32 - GetFileSizeEx */
7510static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
7511{
7512 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7513 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7514 if (idxHandle < g_Sandbox.cHandles)
7515 {
7516 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7517 if (pHandle != NULL)
7518 {
7519 switch (pHandle->enmType)
7520 {
7521 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7522 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7523 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
7524 return TRUE;
7525
7526 case KWHANDLETYPE_TEMP_FILE:
7527 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7528 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
7529 return TRUE;
7530
7531 case KWHANDLETYPE_OUTPUT_BUF:
7532 /* do default */
7533 break;
7534
7535 default:
7536 kHlpAssertFailed();
7537 SetLastError(ERROR_INVALID_FUNCTION);
7538 return INVALID_FILE_SIZE;
7539 }
7540 }
7541 }
7542
7543 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
7544 return GetFileSizeEx(hFile, pcbFile);
7545}
7546
7547
7548/** Kernel32 - CreateFileMappingW */
7549static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
7550 DWORD fProtect, DWORD dwMaximumSizeHigh,
7551 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
7552{
7553 HANDLE hMapping;
7554 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7555 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7556 if (idxHandle < g_Sandbox.cHandles)
7557 {
7558 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7559 if (pHandle != NULL)
7560 {
7561 switch (pHandle->enmType)
7562 {
7563 case KWHANDLETYPE_TEMP_FILE:
7564 {
7565 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7566 if ( ( fProtect == PAGE_READONLY
7567 || fProtect == PAGE_EXECUTE_READ)
7568 && dwMaximumSizeHigh == 0
7569 && ( dwMaximumSizeLow == 0
7570 || dwMaximumSizeLow == pTempFile->cbFile)
7571 && pwszName == NULL)
7572 {
7573 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
7574 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
7575 return hMapping;
7576 }
7577 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
7578 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7579 SetLastError(ERROR_ACCESS_DENIED);
7580 return INVALID_HANDLE_VALUE;
7581 }
7582
7583 /* moc.exe benefits from this. */
7584 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7585 {
7586 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7587 if ( ( fProtect == PAGE_READONLY
7588 || fProtect == PAGE_EXECUTE_READ)
7589 && dwMaximumSizeHigh == 0
7590 && ( dwMaximumSizeLow == 0
7591 || dwMaximumSizeLow == pCachedFile->cbCached)
7592 && pwszName == NULL)
7593 {
7594 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
7595 K_FALSE /*fIsFileHandle*/, &hMapping))
7596 { /* likely */ }
7597 else
7598 hMapping = NULL;
7599 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
7600 return hMapping;
7601 }
7602
7603 /* Do fallback (for .pch). */
7604 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
7605 ("fProtect=%#x cb=%#x'%08x name=%p\n",
7606 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7607
7608 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7609 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
7610 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7611 return hMapping;
7612 }
7613
7614 /** @todo read cached memory mapped files too for moc. */
7615 }
7616 }
7617 }
7618
7619 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7620 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
7621 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7622 return hMapping;
7623}
7624
7625
7626/** Kernel32 - MapViewOfFile */
7627static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
7628 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
7629{
7630 PVOID pvRet;
7631 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7632 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7633 if (idxHandle < g_Sandbox.cHandles)
7634 {
7635 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7636 if (pHandle != NULL)
7637 {
7638 KU32 idxMapping;
7639
7640 /*
7641 * Ensure one free entry in the mapping tracking table first,
7642 * since this is common to both temporary and cached files.
7643 */
7644 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
7645 { /* likely */ }
7646 else
7647 {
7648 void *pvNew;
7649 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
7650 if (cNew)
7651 cNew *= 2;
7652 else
7653 cNew = 32;
7654 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
7655 if (pvNew)
7656 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
7657 else
7658 {
7659 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
7660 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7661 return NULL;
7662 }
7663 g_Sandbox.cMemMappingsAlloc = cNew;
7664 }
7665
7666 /*
7667 * Type specific work.
7668 */
7669 switch (pHandle->enmType)
7670 {
7671 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7672 case KWHANDLETYPE_TEMP_FILE:
7673 case KWHANDLETYPE_OUTPUT_BUF:
7674 default:
7675 kHlpAssertFailed();
7676 SetLastError(ERROR_INVALID_OPERATION);
7677 return NULL;
7678
7679 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7680 {
7681 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7682 if ( dwDesiredAccess == FILE_MAP_READ
7683 && offFileHigh == 0
7684 && offFileLow == 0
7685 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
7686 {
7687 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
7688 if (pTempFile->cSegs != 1)
7689 {
7690 KU32 iSeg;
7691 KU32 cbLeft;
7692 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
7693 KU8 *pbAll = NULL;
7694 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
7695 if (rc != 0)
7696 {
7697 kHlpAssertFailed();
7698 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7699 return NULL;
7700 }
7701
7702 cbLeft = pTempFile->cbFile;
7703 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
7704 {
7705 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
7706 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
7707 cbLeft -= cbToCopy;
7708 }
7709
7710 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
7711 {
7712 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
7713 pTempFile->paSegs[iSeg].pbData = NULL;
7714 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
7715 }
7716
7717 pTempFile->cSegs = 1;
7718 pTempFile->cbFileAllocated = cbAll;
7719 pTempFile->paSegs[0].cbDataAlloc = cbAll;
7720 pTempFile->paSegs[0].pbData = pbAll;
7721 pTempFile->paSegs[0].offData = 0;
7722 }
7723
7724 pTempFile->cMappings++;
7725 kHlpAssert(pTempFile->cMappings == 1);
7726
7727 pvRet = pTempFile->paSegs[0].pbData;
7728 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
7729 break;
7730 }
7731
7732 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7733 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
7734 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7735 return NULL;
7736 }
7737
7738 /*
7739 * This is simple in comparison to the above temporary file code.
7740 */
7741 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7742 {
7743 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7744 if ( dwDesiredAccess == FILE_MAP_READ
7745 && offFileHigh == 0
7746 && offFileLow == 0
7747 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
7748 {
7749 pvRet = pCachedFile->pbCached;
7750 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
7751 break;
7752 }
7753 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7754 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
7755 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7756 return NULL;
7757 }
7758 }
7759
7760 /*
7761 * Insert into the mapping tracking table. This is common
7762 * and we should only get here with a non-NULL pvRet.
7763 *
7764 * Note! We could look for duplicates and do ref counting, but it's
7765 * easier to just append for now.
7766 */
7767 kHlpAssert(pvRet != NULL);
7768 idxMapping = g_Sandbox.cMemMappings;
7769 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
7770
7771 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
7772 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
7773 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
7774 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
7775 g_Sandbox.cMemMappings++;
7776
7777 return pvRet;
7778 }
7779 }
7780
7781 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7782 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
7783 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
7784 return pvRet;
7785}
7786
7787
7788/** Kernel32 - MapViewOfFileEx */
7789static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
7790 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
7791{
7792 PVOID pvRet;
7793 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7794 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7795 if (idxHandle < g_Sandbox.cHandles)
7796 {
7797 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7798 if (pHandle != NULL)
7799 {
7800 switch (pHandle->enmType)
7801 {
7802 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7803 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
7804 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7805 if (!pvMapAddr)
7806 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7807 kHlpAssertFailed();
7808 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7809 return NULL;
7810
7811 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7812 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
7813 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7814 if (!pvMapAddr)
7815 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7816 /* We can use fallback here as the handle is an actual section handle. */
7817 break;
7818
7819 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7820 case KWHANDLETYPE_TEMP_FILE:
7821 case KWHANDLETYPE_OUTPUT_BUF:
7822 default:
7823 kHlpAssertFailed();
7824 SetLastError(ERROR_INVALID_OPERATION);
7825 return NULL;
7826 }
7827 }
7828 }
7829
7830 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
7831 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
7832 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
7833 return pvRet;
7834
7835}
7836
7837/** Kernel32 - UnmapViewOfFile */
7838static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
7839{
7840 /*
7841 * Consult the memory mapping tracker.
7842 */
7843 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
7844 KU32 idxMapping = g_Sandbox.cMemMappings;
7845 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7846 while (idxMapping-- > 0)
7847 if (paMemMappings[idxMapping].pvMapping == pvBase)
7848 {
7849 /* Type specific stuff. */
7850 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
7851 {
7852 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
7853 paMemMappings[idxMapping].u.pTempFile->cMappings--;
7854 }
7855 else
7856 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
7857
7858 /* Deref and probably free it. */
7859 if (--paMemMappings[idxMapping].cRefs == 0)
7860 {
7861 g_Sandbox.cMemMappings--;
7862 if (idxMapping != g_Sandbox.cMemMappings)
7863 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
7864 }
7865 return TRUE;
7866 }
7867
7868 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
7869 return UnmapViewOfFile(pvBase);
7870}
7871
7872/** @todo UnmapViewOfFileEx */
7873
7874#endif /* WITH_TEMP_MEMORY_FILES */
7875
7876
7877/** Kernel32 - DuplicateHandle */
7878static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
7879 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
7880{
7881 BOOL fRet;
7882
7883 /*
7884 * We must catch our handles being duplicated.
7885 */
7886 if (hSrcProc == GetCurrentProcess())
7887 {
7888 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
7889 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7890 if (idxHandle < g_Sandbox.cHandles)
7891 {
7892 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7893 if (pHandle)
7894 {
7895 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7896 if (fRet)
7897 {
7898 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
7899 {
7900 pHandle->cRefs++;
7901 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
7902 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
7903 pHandle->enmType, pHandle->cRefs));
7904 }
7905 else
7906 {
7907 fRet = FALSE;
7908 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7909 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
7910 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
7911 }
7912 }
7913 else
7914 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
7915 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
7916 return fRet;
7917 }
7918 }
7919 }
7920
7921 /*
7922 * Not one of ours, just do what the caller asks and log it.
7923 */
7924 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7925 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
7926 fInheritHandle, dwOptions, fRet, *phNew));
7927 return fRet;
7928}
7929
7930
7931/** Kernel32 - CloseHandle */
7932static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
7933{
7934 BOOL fRet;
7935 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
7936 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7937 if (idxHandle < g_Sandbox.cHandles)
7938 {
7939 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7940 if (pHandle)
7941 {
7942 /* Prevent the closing of the standard output and error handles. */
7943 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
7944 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
7945 {
7946 fRet = CloseHandle(hObject);
7947 if (fRet)
7948 {
7949 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7950 g_Sandbox.papHandles[idxHandle] = NULL;
7951 g_Sandbox.cActiveHandles--;
7952 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
7953 if (--pHandle->cRefs == 0)
7954 {
7955#ifdef WITH_TEMP_MEMORY_FILES
7956 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
7957 {
7958 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
7959 pHandle->u.pTempFile->cActiveHandles--;
7960 }
7961#endif
7962 kHlpFree(pHandle);
7963 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
7964 }
7965 else
7966 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
7967 }
7968 else
7969 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
7970 }
7971 else
7972 {
7973 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
7974 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
7975 fRet = TRUE;
7976 }
7977 return fRet;
7978 }
7979 }
7980
7981 fRet = CloseHandle(hObject);
7982 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
7983 return fRet;
7984}
7985
7986
7987/** Kernel32 - GetFileAttributesA. */
7988static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
7989{
7990 DWORD fRet;
7991 const char *pszExt = kHlpGetExt(pszFilename);
7992 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
7993 {
7994 KFSLOOKUPERROR enmError;
7995 PKFSOBJ pFsObj;
7996 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7997
7998 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
7999 if (pFsObj)
8000 {
8001 kHlpAssert(pFsObj->fHaveStats);
8002 fRet = pFsObj->Stats.st_attribs;
8003 kFsCacheObjRelease(g_pFsCache, pFsObj);
8004 }
8005 else
8006 {
8007 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8008 fRet = INVALID_FILE_ATTRIBUTES;
8009 }
8010
8011 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8012 return fRet;
8013 }
8014
8015 fRet = GetFileAttributesA(pszFilename);
8016 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8017 return fRet;
8018}
8019
8020
8021/** Kernel32 - GetFileAttributesW. */
8022static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8023{
8024 DWORD fRet;
8025 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8026 {
8027 KFSLOOKUPERROR enmError;
8028 PKFSOBJ pFsObj;
8029 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8030
8031 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8032 if (pFsObj)
8033 {
8034 kHlpAssert(pFsObj->fHaveStats);
8035 fRet = pFsObj->Stats.st_attribs;
8036 kFsCacheObjRelease(g_pFsCache, pFsObj);
8037 }
8038 else
8039 {
8040 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8041 fRet = INVALID_FILE_ATTRIBUTES;
8042 }
8043
8044 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8045 return fRet;
8046 }
8047
8048 fRet = GetFileAttributesW(pwszFilename);
8049 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8050 return fRet;
8051}
8052
8053
8054/** Kernel32 - GetFileAttributesExA. */
8055static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8056 WIN32_FILE_ATTRIBUTE_DATA *pData)
8057{
8058 BOOL fRet;
8059 const char *pszExt = kHlpGetExt(pszFilename);
8060 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8061 {
8062 KFSLOOKUPERROR enmError;
8063 PKFSOBJ pFsObj;
8064 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8065
8066 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8067 if (pFsObj)
8068 {
8069 kHlpAssert(pFsObj->fHaveStats);
8070 if (enmLevel == GetFileExInfoStandard)
8071 {
8072 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8073 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8074 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8075 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8076 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8077 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8078 kFsCacheObjRelease(g_pFsCache, pFsObj);
8079 }
8080 else
8081 {
8082 kFsCacheObjRelease(g_pFsCache, pFsObj);
8083 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8084 }
8085 }
8086 else
8087 {
8088 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8089 fRet = FALSE;
8090 }
8091
8092 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8093 return fRet;
8094 }
8095
8096 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8097 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8098 return fRet;
8099}
8100
8101
8102/** Kernel32 - GetFileAttributesExW. */
8103static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8104 WIN32_FILE_ATTRIBUTE_DATA *pData)
8105{
8106 BOOL fRet;
8107 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8108 {
8109 KFSLOOKUPERROR enmError;
8110 PKFSOBJ pFsObj;
8111 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8112
8113 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8114 if (pFsObj)
8115 {
8116 kHlpAssert(pFsObj->fHaveStats);
8117 if (enmLevel == GetFileExInfoStandard)
8118 {
8119 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8120 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8121 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8122 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8123 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8124 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8125 kFsCacheObjRelease(g_pFsCache, pFsObj);
8126 }
8127 else
8128 {
8129 kFsCacheObjRelease(g_pFsCache, pFsObj);
8130 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8131 }
8132 }
8133 else
8134 {
8135 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8136 fRet = FALSE;
8137 }
8138
8139 KWFS_LOG(("GetFileAttributesW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8140 return fRet;
8141 }
8142
8143 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8144 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8145 return fRet;
8146}
8147
8148
8149/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8150 * directory containing each include file. We cache the result to speed
8151 * things up a little. */
8152static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8153{
8154 DWORD cwcRet;
8155 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8156 {
8157 KFSLOOKUPERROR enmError;
8158 PKFSOBJ pObj;
8159 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8160
8161 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8162 if (pObj)
8163 {
8164 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8165 {
8166 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8167 {
8168 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8169
8170 /* Should preserve trailing slash on directory paths. */
8171 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8172 {
8173 if ( cwcRet + 1 < cwcShortPath
8174 && pwszShortPath[cwcRet - 1] != '\\')
8175 {
8176 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8177 if ( cwcIn > 0
8178 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8179 {
8180 pwszShortPath[cwcRet++] = '\\';
8181 pwszShortPath[cwcRet] = '\0';
8182 }
8183 }
8184 }
8185
8186 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8187 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8188 kFsCacheObjRelease(g_pFsCache, pObj);
8189 return cwcRet;
8190 }
8191
8192 /* fall back for complicated cases. */
8193 }
8194 kFsCacheObjRelease(g_pFsCache, pObj);
8195 }
8196 }
8197 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8198 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8199 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8200 return cwcRet;
8201}
8202
8203
8204#ifdef WITH_TEMP_MEMORY_FILES
8205/** Kernel32 - DeleteFileW
8206 * Skip deleting the in-memory files. */
8207static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8208{
8209 BOOL fRc;
8210 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8211 && kwFsIsClTempFileW(pwszFilename))
8212 {
8213 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8214 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8215 fRc = TRUE;
8216 }
8217 else
8218 {
8219 fRc = DeleteFileW(pwszFilename);
8220 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8221 }
8222 return fRc;
8223}
8224#endif /* WITH_TEMP_MEMORY_FILES */
8225
8226
8227
8228#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8229
8230/*
8231 *
8232 * Console output buffering.
8233 * Console output buffering.
8234 * Console output buffering.
8235 *
8236 */
8237
8238
8239/**
8240 * Write a wide char string to the console.
8241 *
8242 * @param pSandbox The sandbox which output buffer to flush.
8243 */
8244static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8245{
8246 if (cwcToWrite > 0)
8247 {
8248 DWORD cwcWritten = 0;
8249 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8250 {
8251 if (cwcWritten == cwcToWrite)
8252 { /* likely */ }
8253 else
8254 {
8255 DWORD off = 0;
8256 do
8257 {
8258 off += cwcWritten;
8259 cwcWritten = 0;
8260 } while ( off < cwcToWrite
8261 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8262 kHlpAssert(off == cwcWritten);
8263 }
8264 }
8265 else
8266 kHlpAssertFailed();
8267 pSandbox->Combined.cFlushes++;
8268 }
8269}
8270
8271
8272/**
8273 * Flushes the combined console output buffer.
8274 *
8275 * @param pSandbox The sandbox which output buffer to flush.
8276 */
8277static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8278{
8279 if (pSandbox->Combined.cwcBuf > 0)
8280 {
8281 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8282 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8283 pSandbox->Combined.cwcBuf = 0;
8284 }
8285}
8286
8287
8288/**
8289 * For handling combined buffer overflow cases line by line.
8290 *
8291 * @param pSandbox The sandbox.
8292 * @param pwcBuf What to add to the combined buffer. Usually a
8293 * line, unless we're really low on buffer space.
8294 * @param cwcBuf The length of what to add.
8295 * @param fBrokenLine Whether this is a broken line.
8296 */
8297static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8298{
8299 if (fBrokenLine)
8300 kwSandboxConsoleFlushCombined(pSandbox);
8301 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8302 {
8303 kwSandboxConsoleFlushCombined(pSandbox);
8304 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
8305 }
8306 else
8307 {
8308 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
8309 pSandbox->Combined.cwcBuf += cwcBuf;
8310 }
8311}
8312
8313
8314/**
8315 * Called to final flush a line buffer via the combined buffer (if applicable).
8316 *
8317 * @param pSandbox The sandbox.
8318 * @param pLineBuf The line buffer.
8319 * @param pszName The line buffer name (for logging)
8320 */
8321static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
8322{
8323 if (pLineBuf->fIsConsole)
8324 {
8325 if (pLineBuf->u.Con.cwcBuf > 0)
8326 {
8327 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
8328
8329 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
8330 {
8331 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
8332 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
8333 }
8334 else
8335 {
8336 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
8337 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
8338 }
8339 pLineBuf->u.Con.cwcBuf = 0;
8340 }
8341 }
8342#ifdef WITH_STD_OUT_ERR_BUFFERING
8343 else if (pLineBuf->u.Fully.cchBuf > 0)
8344 {
8345 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
8346
8347 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
8348 pLineBuf->u.Fully.cchBuf = 0;
8349 }
8350#endif
8351}
8352
8353
8354/**
8355 * Called at the end of sandboxed execution to flush both stream buffers.
8356 *
8357 * @param pSandbox The sandbox.
8358 */
8359static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
8360{
8361 /*
8362 * First do the cl.exe source file supression trick, if applicable.
8363 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
8364 * handle.
8365 */
8366 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8367 && pSandbox->Combined.cFlushes == 0)
8368 {
8369 if ( pSandbox->StdOut.fIsConsole
8370 || pSandbox->StdErr.fIsConsole)
8371 {
8372 if ( pSandbox->Combined.cwcBuf >= 3
8373 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
8374 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
8375 {
8376 KI32 off = pSandbox->Combined.cwcBuf - 1;
8377 if (pSandbox->Combined.wszBuf[off] == '\n')
8378 {
8379 KBOOL fOk = K_TRUE;
8380 while (off-- > 0)
8381 {
8382 wchar_t const wc = pSandbox->Combined.wszBuf[off];
8383 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
8384 { /* likely */ }
8385 else
8386 {
8387 fOk = K_FALSE;
8388 break;
8389 }
8390 }
8391 if (fOk)
8392 {
8393 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
8394 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8395 pSandbox->Combined.cwcBuf = 0;
8396 return;
8397 }
8398 }
8399 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
8400 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8401 }
8402 }
8403#ifdef WITH_STD_OUT_ERR_BUFFERING
8404 /*
8405 * Otherwise, it goes to standard output (redirected).
8406 */
8407 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
8408 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
8409 {
8410 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
8411 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
8412 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
8413
8414 if (pchBuf[off] == '\n')
8415 {
8416 KBOOL fOk = K_TRUE;
8417 if (pchBuf[off - 1] == '\r')
8418 off--;
8419 while (off-- > 0)
8420 {
8421 char const ch = pchBuf[off];
8422 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
8423 { /* likely */ }
8424 else
8425 {
8426 fOk = K_FALSE;
8427 break;
8428 }
8429 }
8430 if (fOk)
8431 {
8432 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
8433 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8434 pSandbox->StdOut.u.Fully.cchBuf = 0;
8435 return;
8436 }
8437 }
8438 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
8439 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8440 }
8441#endif
8442 }
8443
8444 /*
8445 * Flush the two line buffer, then the combined buffer.
8446 */
8447 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
8448 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
8449 kwSandboxConsoleFlushCombined(pSandbox);
8450}
8451
8452
8453/**
8454 * Writes a string to the given output stream.
8455 *
8456 * @param pSandbox The sandbox.
8457 * @param pLineBuf The line buffer for the output stream.
8458 * @param pwcBuffer The buffer to write.
8459 * @param cwcToWrite The number of wchar_t's in the buffer.
8460 */
8461static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
8462{
8463 kHlpAssert(pLineBuf->fIsConsole);
8464 if (cwcToWrite > 0)
8465 {
8466 /*
8467 * First, find the start of the last incomplete line so we can figure
8468 * out how much line buffering we need to do.
8469 */
8470 KU32 cchLastIncompleteLine;
8471 KU32 offLastIncompleteLine = cwcToWrite;
8472 while ( offLastIncompleteLine > 0
8473 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
8474 offLastIncompleteLine--;
8475 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
8476
8477 /* Was there anything to line buffer? */
8478 if (offLastIncompleteLine < cwcToWrite)
8479 {
8480 /* Need to grow the line buffer? */
8481 KU32 cwcNeeded = offLastIncompleteLine == 0
8482 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
8483 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
8484 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
8485 {
8486 void *pvNew;
8487 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
8488 while (cwcNew < cwcNeeded)
8489 cwcNew *= 2;
8490 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
8491 if (pvNew)
8492 {
8493 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8494 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
8495 }
8496 else
8497 {
8498 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
8499 if (pvNew)
8500 {
8501 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8502 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
8503 }
8504 else
8505 {
8506 /* This isn't perfect, but it will have to do for now. */
8507 if (pLineBuf->u.Con.cwcBuf > 0)
8508 {
8509 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8510 K_TRUE /*fBrokenLine*/);
8511 pLineBuf->u.Con.cwcBuf = 0;
8512 }
8513 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
8514 return;
8515 }
8516 }
8517 }
8518
8519 /*
8520 * Handle the case where we only add to the line buffer.
8521 */
8522 if (offLastIncompleteLine == 0)
8523 {
8524 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
8525 pLineBuf->u.Con.cwcBuf += cwcToWrite;
8526 return;
8527 }
8528 }
8529
8530 /*
8531 * If there is sufficient combined buffer to handle this request, this is rather simple.
8532 */
8533 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
8534 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8535 {
8536 if (pLineBuf->u.Con.cwcBuf > 0)
8537 {
8538 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8539 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8540 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8541 pLineBuf->u.Con.cwcBuf = 0;
8542 }
8543
8544 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8545 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
8546 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
8547 }
8548 else
8549 {
8550 /*
8551 * Do line-by-line processing of the input, flusing the combined buffer
8552 * when it becomes necessary. We may have to write lines
8553 */
8554 KU32 off = 0;
8555 KU32 offNextLine = 0;
8556
8557 /* If there are buffered chars, we handle the first line outside the
8558 main loop. We must try our best outputting it as a complete line. */
8559 if (pLineBuf->u.Con.cwcBuf > 0)
8560 {
8561 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
8562 offNextLine++;
8563 offNextLine++;
8564 kHlpAssert(offNextLine <= offLastIncompleteLine);
8565
8566 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8567 {
8568 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8569 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8570 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8571 pLineBuf->u.Con.cwcBuf = 0;
8572
8573 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
8574 pSandbox->Combined.cwcBuf += offNextLine;
8575 }
8576 else
8577 {
8578 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
8579 if (cwcLeft > 0)
8580 {
8581 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
8582 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
8583 pLineBuf->u.Con.cwcBuf += cwcCopy;
8584 off += cwcCopy;
8585 }
8586 if (pLineBuf->u.Con.cwcBuf > 0)
8587 {
8588 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8589 K_TRUE /*fBrokenLine*/);
8590 pLineBuf->u.Con.cwcBuf = 0;
8591 }
8592 if (off < offNextLine)
8593 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
8594 }
8595 off = offNextLine;
8596 }
8597
8598 /* Deal with the remaining lines */
8599 while (off < offLastIncompleteLine)
8600 {
8601 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
8602 offNextLine++;
8603 offNextLine++;
8604 kHlpAssert(offNextLine <= offLastIncompleteLine);
8605 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
8606 off = offNextLine;
8607 }
8608 }
8609
8610 /*
8611 * Buffer any remaining incomplete line chars.
8612 */
8613 if (cchLastIncompleteLine)
8614 {
8615 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
8616 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
8617 }
8618 }
8619}
8620
8621
8622/**
8623 * Worker for WriteConsoleA and WriteFile.
8624 *
8625 * @param pSandbox The sandbox.
8626 * @param pLineBuf The line buffer.
8627 * @param pchBuffer What to write.
8628 * @param cchToWrite How much to write.
8629 */
8630static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
8631{
8632 /*
8633 * Convert it to wide char and use the 'W' to do the work.
8634 */
8635 int cwcRet;
8636 KU32 cwcBuf = cchToWrite * 2 + 1;
8637 wchar_t *pwcBufFree = NULL;
8638 wchar_t *pwcBuf;
8639 kHlpAssert(pLineBuf->fIsConsole);
8640
8641 if (cwcBuf <= 4096)
8642 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
8643 else
8644 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
8645
8646 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
8647 if (cwcRet > 0)
8648 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
8649 else
8650 {
8651 DWORD cchWritten;
8652 kHlpAssertFailed();
8653
8654 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
8655 if (pLineBuf->u.Con.cwcBuf > 0)
8656 {
8657 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
8658 pLineBuf->u.Con.cwcBuf = 0;
8659 }
8660 kwSandboxConsoleFlushCombined(pSandbox);
8661
8662 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
8663 {
8664 if (cchWritten >= cchToWrite)
8665 { /* likely */ }
8666 else
8667 {
8668 KU32 off = 0;
8669 do
8670 {
8671 off += cchWritten;
8672 cchWritten = 0;
8673 } while ( off < cchToWrite
8674 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
8675 }
8676 }
8677 }
8678
8679 if (pwcBufFree)
8680 kHlpFree(pwcBufFree);
8681}
8682
8683
8684/** Kernel32 - WriteConsoleA */
8685BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
8686 PVOID pvReserved)
8687{
8688 BOOL fRc;
8689 PKWOUTPUTSTREAMBUF pLineBuf;
8690 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8691
8692 if (hConOutput == g_Sandbox.StdErr.hOutput)
8693 pLineBuf = &g_Sandbox.StdErr;
8694 else
8695 pLineBuf = &g_Sandbox.StdOut;
8696 if (pLineBuf->fIsConsole)
8697 {
8698 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
8699
8700 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
8701 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
8702 if (pcbWritten)
8703 *pcbWritten = cbToWrite;
8704 fRc = TRUE;
8705 }
8706 else
8707 {
8708 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
8709 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
8710 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
8711 }
8712 return fRc;
8713}
8714
8715
8716/** Kernel32 - WriteConsoleW */
8717BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
8718 PVOID pvReserved)
8719{
8720 BOOL fRc;
8721 PKWOUTPUTSTREAMBUF pLineBuf;
8722 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8723
8724 if (hConOutput == g_Sandbox.StdErr.hOutput)
8725 pLineBuf = &g_Sandbox.StdErr;
8726 else if (hConOutput == g_Sandbox.StdOut.hOutput)
8727 pLineBuf = &g_Sandbox.StdOut;
8728 else
8729 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
8730 if (pLineBuf->fIsConsole)
8731 {
8732 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
8733
8734 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
8735 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
8736 if (pcwcWritten)
8737 *pcwcWritten = cwcToWrite;
8738 fRc = TRUE;
8739 }
8740 else
8741 {
8742 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
8743 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
8744 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
8745 }
8746 return fRc;
8747}
8748
8749#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8750
8751
8752
8753/*
8754 *
8755 * Virtual memory leak prevension.
8756 * Virtual memory leak prevension.
8757 * Virtual memory leak prevension.
8758 *
8759 */
8760
8761#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8762
8763/** For debug logging. */
8764# ifndef NDEBUG
8765static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
8766{
8767 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
8768 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
8769 kHlpAssert(cbMemInfo == sizeof(MemInfo));
8770 if (cbMemInfo != 0)
8771 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
8772 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
8773 MemInfo.BaseAddress,
8774 MemInfo.AllocationBase,
8775 MemInfo.RegionSize,
8776 MemInfo.State,
8777 MemInfo.Protect,
8778 MemInfo.Type));
8779}
8780# else
8781# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
8782# endif
8783
8784/**
8785 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
8786 *
8787 * @param idxFixed The fixed allocation index to "free".
8788 */
8789static void kwSandboxResetFixedAllocation(KU32 idxFixed)
8790{
8791 BOOL fRc;
8792 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
8793 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
8794 kHlpAssert(fRc); K_NOREF(fRc);
8795 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
8796 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
8797}
8798
8799#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
8800
8801
8802/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
8803 * location (~78MB in 32-bit 2010 compiler). */
8804static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
8805{
8806 PVOID pvMem;
8807 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8808 {
8809 KU32 idxPreAllocated = KU32_MAX;
8810
8811#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8812 /*
8813 * Look for a pre-reserved CL.exe heap allocation.
8814 */
8815 pvMem = NULL;
8816 if ( pvAddr != 0
8817 && (fAllocType & MEM_RESERVE))
8818 {
8819 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8820 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
8821 while (idxFixed-- > 0)
8822 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
8823 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
8824 {
8825 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
8826 {
8827 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
8828 {
8829 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
8830 pvMem = pvAddr;
8831 idxPreAllocated = idxFixed;
8832 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
8833 pvAddr, cb, fAllocType, fProt, pvMem));
8834 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
8835 SetLastError(NO_ERROR);
8836 break;
8837 }
8838 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
8839 }
8840 else
8841 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
8842 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
8843 }
8844 }
8845 if (!pvMem)
8846#endif
8847 {
8848 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8849 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8850 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8851 if (pvAddr && pvAddr != pvMem)
8852 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
8853 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
8854 }
8855
8856 if (pvMem)
8857 {
8858 /*
8859 * Track it.
8860 */
8861 PKWVIRTALLOC pTracker;
8862 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8863
8864 pTracker = g_Sandbox.pVirtualAllocHead;
8865 while ( pTracker
8866 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
8867 pTracker = pTracker->pNext;
8868 if (!pTracker)
8869 {
8870 DWORD dwErr = GetLastError();
8871 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
8872 if (pTracker)
8873 {
8874 pTracker->pvAlloc = pvMem;
8875 pTracker->cbAlloc = cb;
8876 pTracker->idxPreAllocated = idxPreAllocated;
8877 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8878 g_Sandbox.pVirtualAllocHead = pTracker;
8879 }
8880 SetLastError(dwErr);
8881 }
8882 }
8883 }
8884 else
8885 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8886 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8887 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8888 return pvMem;
8889}
8890
8891
8892/** Kernel32 - VirtualFree. */
8893static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
8894{
8895 BOOL fRc;
8896 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8897 {
8898 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8899 if (dwFreeType & MEM_RELEASE)
8900 {
8901 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
8902 if (pTracker)
8903 {
8904 if (pTracker->pvAlloc == pvAddr)
8905 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
8906 else
8907 {
8908 PKWVIRTALLOC pPrev;
8909 do
8910 {
8911 pPrev = pTracker;
8912 pTracker = pTracker->pNext;
8913 } while (pTracker && pTracker->pvAlloc != pvAddr);
8914 if (pTracker)
8915 pPrev->pNext = pTracker->pNext;
8916 }
8917 if (pTracker)
8918 {
8919#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8920 if (pTracker->idxPreAllocated != KU32_MAX)
8921 {
8922 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
8923 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
8924 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
8925 kHlpFree(pTracker);
8926 return TRUE;
8927 }
8928#endif
8929
8930 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8931 if (fRc)
8932 kHlpFree(pTracker);
8933 else
8934 {
8935 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8936 g_Sandbox.pVirtualAllocHead = pTracker;
8937 }
8938 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8939 return fRc;
8940 }
8941
8942 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
8943 }
8944 }
8945 }
8946
8947#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8948 /*
8949 * Protect our fixed allocations (this isn't just paranoia, btw.).
8950 */
8951 if (dwFreeType & MEM_RELEASE)
8952 {
8953 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8954 while (idxFixed-- > 0)
8955 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
8956 {
8957 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
8958 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
8959 return TRUE;
8960 }
8961 }
8962#endif
8963
8964 /*
8965 * Not tracker or not actually free the virtual range.
8966 */
8967 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8968 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8969 return fRc;
8970}
8971
8972
8973/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
8974HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
8975{
8976 HANDLE hHeap;
8977 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8978
8979 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
8980 if (hHeap != NULL)
8981 {
8982 DWORD dwErr = GetLastError();
8983 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
8984 if (pTracker)
8985 {
8986 pTracker->hHeap = hHeap;
8987 pTracker->pNext = g_Sandbox.pHeapHead;
8988 g_Sandbox.pHeapHead = pTracker;
8989 }
8990
8991 SetLastError(dwErr);
8992 }
8993 return hHeap;
8994
8995}
8996
8997
8998/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
8999BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9000{
9001 BOOL fRc = HeapDestroy(hHeap);
9002 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9003 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9004 if (fRc)
9005 {
9006 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9007 if (pTracker)
9008 {
9009 if (pTracker->hHeap == hHeap)
9010 g_Sandbox.pHeapHead = pTracker->pNext;
9011 else
9012 {
9013 PKWHEAP pPrev;
9014 do
9015 {
9016 pPrev = pTracker;
9017 pTracker = pTracker->pNext;
9018 } while (pTracker && pTracker->hHeap == hHeap);
9019 if (pTracker)
9020 pPrev->pNext = pTracker->pNext;
9021 }
9022 if (pTracker)
9023 kHlpFree(pTracker);
9024 else
9025 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9026 }
9027 }
9028
9029 return fRc;
9030}
9031
9032
9033
9034/*
9035 *
9036 * Thread/Fiber local storage leak prevention.
9037 * Thread/Fiber local storage leak prevention.
9038 * Thread/Fiber local storage leak prevention.
9039 *
9040 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9041 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9042 * we're leaking these indexes, but more importantely we crash during
9043 * worker exit since the callback is triggered multiple times.
9044 */
9045
9046
9047/** Kernel32 - FlsAlloc */
9048DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9049{
9050 DWORD idxFls = FlsAlloc(pfnCallback);
9051 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9052 if (idxFls != FLS_OUT_OF_INDEXES)
9053 {
9054 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9055 if (pTracker)
9056 {
9057 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9058 pTracker->idx = idxFls;
9059 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9060 g_Sandbox.pFlsAllocHead = pTracker;
9061 }
9062 }
9063
9064 return idxFls;
9065}
9066
9067/** Kernel32 - FlsFree */
9068BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9069{
9070 BOOL fRc = FlsFree(idxFls);
9071 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9072 if (fRc)
9073 {
9074 PKWLOCALSTORAGE pTracker;
9075 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9076
9077 pTracker = g_Sandbox.pFlsAllocHead;
9078 if (pTracker)
9079 {
9080 if (pTracker->idx == idxFls)
9081 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9082 else
9083 {
9084 PKWLOCALSTORAGE pPrev;
9085 do
9086 {
9087 pPrev = pTracker;
9088 pTracker = pTracker->pNext;
9089 } while (pTracker && pTracker->idx != idxFls);
9090 if (pTracker)
9091 pPrev->pNext = pTracker->pNext;
9092 }
9093 if (pTracker)
9094 {
9095 pTracker->idx = FLS_OUT_OF_INDEXES;
9096 pTracker->pNext = NULL;
9097 kHlpFree(pTracker);
9098 }
9099 }
9100 }
9101 return fRc;
9102}
9103
9104
9105/** Kernel32 - TlsAlloc */
9106DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9107{
9108 DWORD idxTls = TlsAlloc();
9109 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9110 if (idxTls != TLS_OUT_OF_INDEXES)
9111 {
9112 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9113 if (pTracker)
9114 {
9115 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9116 pTracker->idx = idxTls;
9117 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9118 g_Sandbox.pTlsAllocHead = pTracker;
9119 }
9120 }
9121
9122 return idxTls;
9123}
9124
9125/** Kernel32 - TlsFree */
9126BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9127{
9128 BOOL fRc = TlsFree(idxTls);
9129 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9130 if (fRc)
9131 {
9132 PKWLOCALSTORAGE pTracker;
9133 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9134
9135 pTracker = g_Sandbox.pTlsAllocHead;
9136 if (pTracker)
9137 {
9138 if (pTracker->idx == idxTls)
9139 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9140 else
9141 {
9142 PKWLOCALSTORAGE pPrev;
9143 do
9144 {
9145 pPrev = pTracker;
9146 pTracker = pTracker->pNext;
9147 } while (pTracker && pTracker->idx != idxTls);
9148 if (pTracker)
9149 pPrev->pNext = pTracker->pNext;
9150 }
9151 if (pTracker)
9152 {
9153 pTracker->idx = TLS_OUT_OF_INDEXES;
9154 pTracker->pNext = NULL;
9155 kHlpFree(pTracker);
9156 }
9157 }
9158 }
9159 return fRc;
9160}
9161
9162
9163
9164/*
9165 *
9166 * Header file hashing.
9167 * Header file hashing.
9168 * Header file hashing.
9169 *
9170 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9171 * indicated that ~12% of the time was spent doing MD5 caluclation when
9172 * rebuiling openssl. The hashing it done right after reading the source
9173 * via ReadFile, same buffers and sizes.
9174 */
9175
9176#ifdef WITH_HASH_MD5_CACHE
9177
9178/** AdvApi32 - CryptCreateHash */
9179static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9180 HCRYPTHASH *phHash)
9181{
9182 BOOL fRc;
9183
9184 /*
9185 * Only do this for cl.exe when it request normal MD5.
9186 */
9187 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9188 {
9189 if (idAlg == CALG_MD5)
9190 {
9191 if (hKey == 0)
9192 {
9193 if (dwFlags == 0)
9194 {
9195 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9196 if (pHash)
9197 {
9198 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9199 pHash->uMagic = KWHASHMD5_MAGIC;
9200 pHash->cbHashed = 0;
9201 pHash->fGoneBad = K_FALSE;
9202 pHash->fFallbackMode = K_FALSE;
9203 pHash->fFinal = K_FALSE;
9204
9205 /* link it. */
9206 pHash->pNext = g_Sandbox.pHashHead;
9207 g_Sandbox.pHashHead = pHash;
9208
9209 *phHash = (KUPTR)pHash;
9210 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9211 hProv, *phHash, TRUE));
9212 return TRUE;
9213 }
9214
9215 kwErrPrintf("CryptCreateHash: out of memory!\n");
9216 }
9217 else
9218 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9219 }
9220 else
9221 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9222 }
9223 else
9224 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9225 }
9226
9227 /*
9228 * Fallback.
9229 */
9230 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9231 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9232 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9233 return fRc;
9234}
9235
9236
9237/** AdvApi32 - CryptHashData */
9238static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9239{
9240 BOOL fRc;
9241 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9242 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9243 while (pHash && (KUPTR)pHash != hHash)
9244 pHash = pHash->pNext;
9245 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9246 hHash, pHash, pbData, cbData, dwFlags));
9247 if (pHash)
9248 {
9249 /*
9250 * Validate the state.
9251 */
9252 if ( pHash->uMagic == KWHASHMD5_MAGIC
9253 && !pHash->fFinal)
9254 {
9255 if (!pHash->fFallbackMode)
9256 {
9257 /*
9258 * Does this match the previous ReadFile call to a cached file?
9259 * If it doesn't, try falling back.
9260 */
9261 if ( g_Sandbox.LastHashRead.cbRead == cbData
9262 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9263 {
9264 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9265 if ( pCachedFile
9266 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9267 {
9268
9269 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9270 {
9271 if ( pHash->pCachedFile == NULL
9272 && pHash->cbHashed == 0)
9273 pHash->pCachedFile = pCachedFile;
9274 if (pHash->pCachedFile == pCachedFile)
9275 {
9276 pHash->cbHashed += cbData;
9277 g_Sandbox.LastHashRead.pCachedFile = NULL;
9278 g_Sandbox.LastHashRead.pvRead = NULL;
9279 g_Sandbox.LastHashRead.cbRead = 0;
9280 g_Sandbox.LastHashRead.offRead = 0;
9281 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9282 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9283 return TRUE;
9284 }
9285
9286 /* Note! it's possible to fall back here too, if necessary. */
9287 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
9288 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
9289 }
9290 else
9291 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
9292 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
9293 }
9294 else if (!pCachedFile)
9295 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
9296 else
9297 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
9298 }
9299 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
9300 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
9301 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
9302 if (pHash->cbHashed == 0)
9303 pHash->fFallbackMode = K_TRUE;
9304 if (pHash->fFallbackMode)
9305 {
9306 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
9307 pHash->fFallbackMode = K_TRUE;
9308 MD5Init(&pHash->Md5Ctx);
9309 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9310 pHash->cbHashed = cbData;
9311 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
9312 hHash, pbData, cbData, dwFlags));
9313 return TRUE;
9314 }
9315 pHash->fGoneBad = K_TRUE;
9316 SetLastError(ERROR_INVALID_PARAMETER);
9317 fRc = FALSE;
9318 }
9319 else
9320 {
9321 /* fallback. */
9322 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9323 pHash->cbHashed += cbData;
9324 fRc = TRUE;
9325 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
9326 hHash, pbData, cbData, dwFlags));
9327 }
9328 }
9329 /*
9330 * Bad handle state.
9331 */
9332 else
9333 {
9334 if (pHash->uMagic != KWHASHMD5_MAGIC)
9335 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
9336 else
9337 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
9338 SetLastError(NTE_BAD_HASH);
9339 fRc = FALSE;
9340 }
9341 }
9342 else
9343 {
9344
9345 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
9346 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
9347 }
9348 return fRc;
9349}
9350
9351
9352/** AdvApi32 - CryptGetHashParam */
9353static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
9354 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
9355{
9356 BOOL fRc;
9357 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9358 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9359 while (pHash && (KUPTR)pHash != hHash)
9360 pHash = pHash->pNext;
9361 if (pHash)
9362 {
9363 if (pHash->uMagic == KWHASHMD5_MAGIC)
9364 {
9365 if (dwFlags == 0)
9366 {
9367 DWORD cbRet;
9368 void *pvRet;
9369 union
9370 {
9371 DWORD dw;
9372 } uBuf;
9373
9374 switch (dwParam)
9375 {
9376 case HP_HASHVAL:
9377 {
9378 /* Check the hash progress. */
9379 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
9380 if (pCachedFile)
9381 {
9382 if ( pCachedFile->cbCached == pHash->cbHashed
9383 && !pHash->fGoneBad)
9384 {
9385 if (pCachedFile->fValidMd5)
9386 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
9387 else
9388 {
9389 MD5Init(&pHash->Md5Ctx);
9390 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
9391 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
9392 pCachedFile->fValidMd5 = K_TRUE;
9393 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
9394 }
9395 pvRet = pCachedFile->abMd5Digest;
9396 }
9397 else
9398 {
9399 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
9400 from what I can tell, so just deal with it. */
9401 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
9402 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
9403 pHash, pCachedFile, pCachedFile->szPath));
9404 pHash->fFallbackMode = K_TRUE;
9405 pHash->pCachedFile = NULL;
9406 MD5Init(&pHash->Md5Ctx);
9407 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
9408 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9409 pvRet = pHash->abDigest;
9410 }
9411 pHash->fFinal = K_TRUE;
9412 cbRet = 16;
9413 break;
9414 }
9415 else if (pHash->fFallbackMode)
9416 {
9417 if (!pHash->fFinal)
9418 {
9419 pHash->fFinal = K_TRUE;
9420 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9421 }
9422 pvRet = pHash->abDigest;
9423 cbRet = 16;
9424 break;
9425 }
9426 else
9427 {
9428 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
9429 SetLastError(ERROR_INVALID_SERVER_STATE);
9430 }
9431 return FALSE;
9432 }
9433
9434 case HP_HASHSIZE:
9435 uBuf.dw = 16;
9436 pvRet = &uBuf;
9437 cbRet = sizeof(DWORD);
9438 break;
9439
9440 case HP_ALGID:
9441 uBuf.dw = CALG_MD5;
9442 pvRet = &uBuf;
9443 cbRet = sizeof(DWORD);
9444 break;
9445
9446 default:
9447 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
9448 SetLastError(NTE_BAD_TYPE);
9449 return FALSE;
9450 }
9451
9452 /*
9453 * Copy out cbRet from pvRet.
9454 */
9455 if (pbData)
9456 {
9457 if (*pcbData >= cbRet)
9458 {
9459 *pcbData = cbRet;
9460 kHlpMemCopy(pbData, pvRet, cbRet);
9461 if (cbRet == 4)
9462 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
9463 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
9464 else if (cbRet == 16)
9465 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",
9466 dwParam, pHash, pHash->pCachedFile, cbRet,
9467 pbData[0], pbData[1], pbData[2], pbData[3],
9468 pbData[4], pbData[5], pbData[6], pbData[7],
9469 pbData[8], pbData[9], pbData[10], pbData[11],
9470 pbData[12], pbData[13], pbData[14], pbData[15]));
9471 else
9472 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
9473 dwParam, pHash, pHash->pCachedFile, cbRet));
9474 return TRUE;
9475 }
9476
9477 kHlpMemCopy(pbData, pvRet, *pcbData);
9478 }
9479 SetLastError(ERROR_MORE_DATA);
9480 *pcbData = cbRet;
9481 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
9482 }
9483 else
9484 {
9485 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
9486 SetLastError(NTE_BAD_FLAGS);
9487 }
9488 }
9489 else
9490 {
9491 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
9492 SetLastError(NTE_BAD_HASH);
9493 }
9494 fRc = FALSE;
9495 }
9496 /*
9497 * Regular handle.
9498 */
9499 else
9500 {
9501 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
9502 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
9503 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
9504 }
9505
9506 return fRc;
9507}
9508
9509
9510/** AdvApi32 - CryptDestroyHash */
9511static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
9512{
9513 BOOL fRc;
9514 PKWHASHMD5 pPrev = NULL;
9515 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9516 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9517 while (pHash && (KUPTR)pHash != hHash)
9518 {
9519 pPrev = pHash;
9520 pHash = pHash->pNext;
9521 }
9522 if (pHash)
9523 {
9524 if (pHash->uMagic == KWHASHMD5_MAGIC)
9525 {
9526 pHash->uMagic = 0;
9527 if (!pPrev)
9528 g_Sandbox.pHashHead = pHash->pNext;
9529 else
9530 pPrev->pNext = pHash->pNext;
9531 kHlpFree(pHash);
9532 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
9533 fRc = TRUE;
9534 }
9535 else
9536 {
9537 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
9538 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
9539 SetLastError(ERROR_INVALID_HANDLE);
9540 fRc = FALSE;
9541 }
9542 }
9543 /*
9544 * Regular handle.
9545 */
9546 else
9547 {
9548 fRc = CryptDestroyHash(hHash);
9549 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
9550 }
9551 return fRc;
9552}
9553
9554#endif /* WITH_HASH_MD5_CACHE */
9555
9556
9557/*
9558 *
9559 * Reuse crypt context.
9560 * Reuse crypt context.
9561 * Reuse crypt context.
9562 *
9563 *
9564 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
9565 *
9566 */
9567
9568#ifdef WITH_CRYPT_CTX_REUSE
9569
9570/** AdvApi32 - CryptAcquireContextW. */
9571static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
9572 DWORD dwProvType, DWORD dwFlags)
9573{
9574 BOOL fRet;
9575
9576 /*
9577 * Lookup reusable context based on the input.
9578 */
9579 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
9580 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
9581 KU32 iCtx = g_Sandbox.cCryptCtxs;
9582 while (iCtx-- > 0)
9583 {
9584 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
9585 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
9586 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
9587 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
9588 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
9589 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
9590 {
9591 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
9592 {
9593 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
9594 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
9595 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9596 return TRUE;
9597 }
9598 }
9599 }
9600
9601 /*
9602 * Create it and enter it into the reused array if possible.
9603 */
9604 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
9605 if (fRet)
9606 {
9607 iCtx = g_Sandbox.cCryptCtxs;
9608 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
9609 {
9610 /* Try duplicate the input strings. */
9611 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
9612 (cwcContainer + 1) * sizeof(wchar_t));
9613 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
9614 {
9615 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
9616 (cwcProvider + 1) * sizeof(wchar_t));
9617 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
9618 {
9619 /* Add a couple of references just to be on the safe side and all that. */
9620 HCRYPTPROV hProv = *phProv;
9621 if (CryptContextAddRef(hProv, NULL, 0))
9622 {
9623 if (CryptContextAddRef(hProv, NULL, 0))
9624 {
9625 /* Okay, finish the entry and return success */
9626 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
9627 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
9628 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
9629 g_Sandbox.cCryptCtxs = iCtx + 1;
9630
9631 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
9632 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9633 return TRUE;
9634 }
9635 CryptReleaseContext(hProv, 0);
9636 }
9637 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
9638
9639 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
9640 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
9641 }
9642 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
9643 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
9644 }
9645 }
9646 else
9647 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
9648 }
9649
9650 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
9651 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9652 return fRet;
9653}
9654
9655
9656/** AdvApi32 - CryptReleaseContext */
9657static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
9658{
9659 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
9660 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
9661 return fRet;
9662}
9663
9664
9665/** AdvApi32 - CryptContextAddRef */
9666static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
9667{
9668 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
9669 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
9670 return fRet;
9671}
9672
9673#endif /* WITH_CRYPT_CTX_REUSE */
9674
9675/*
9676 *
9677 * Structured exception handling.
9678 * Structured exception handling.
9679 * Structured exception handling.
9680 *
9681 */
9682#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9683
9684# define EH_NONCONTINUABLE KU32_C(0x00000001)
9685# define EH_UNWINDING KU32_C(0x00000002)
9686# define EH_EXIT_UNWIND KU32_C(0x00000004)
9687# define EH_STACK_INVALID KU32_C(0x00000008)
9688# define EH_NESTED_CALL KU32_C(0x00000010)
9689
9690typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
9691 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
9692typedef struct _EXCEPTION_REGISTRATION_RECORD
9693{
9694 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
9695 PFNXCPTHANDLER pfnXcptHandler;
9696};
9697
9698
9699/**
9700 * Calls @a pfnHandler.
9701 */
9702static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
9703 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
9704 PFNXCPTHANDLER pfnHandler)
9705{
9706# if 1
9707 /* This is a more robust version that isn't subject to calling
9708 convension cleanup disputes and such. */
9709 KU32 uSavedEdi;
9710 KU32 uSavedEsi;
9711 KU32 uSavedEbx;
9712 KU32 rcHandler;
9713
9714 __asm
9715 {
9716 mov [uSavedEdi], edi
9717 mov [uSavedEsi], esi
9718 mov [uSavedEbx], ebx
9719 mov esi, esp
9720 mov edi, esp
9721 mov edi, [pXcptRec]
9722 mov edx, [pRegRec]
9723 mov eax, [pXcptCtx]
9724 mov ebx, [ppRegRec]
9725 mov ecx, [pfnHandler]
9726 sub esp, 16
9727 and esp, 0fffffff0h
9728 mov [esp ], edi
9729 mov [esp + 4], edx
9730 mov [esp + 8], eax
9731 mov [esp + 12], ebx
9732 mov edi, esi
9733 call ecx
9734 mov esp, esi
9735 cmp esp, edi
9736 je stack_ok
9737 int 3
9738 stack_ok:
9739 mov edi, [uSavedEdi]
9740 mov esi, [uSavedEsi]
9741 mov ebx, [uSavedEbx]
9742 mov [rcHandler], eax
9743 }
9744 return rcHandler;
9745# else
9746 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
9747# endif
9748}
9749
9750
9751/**
9752 * Vectored exception handler that emulates x86 chained exception handler.
9753 *
9754 * This is necessary because the RtlIsValidHandler check fails for self loaded
9755 * code and prevents cl.exe from working. (On AMD64 we can register function
9756 * tables, but on X86 cooking your own handling seems to be the only viabke
9757 * alternative.)
9758 *
9759 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
9760 * @param pXcptPtrs The exception details.
9761 */
9762static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
9763{
9764 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9765 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
9766 if (g_Sandbox.fRunning)
9767 {
9768 HANDLE const hCurProc = GetCurrentProcess();
9769 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
9770 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
9771 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9772 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
9773 {
9774 /* Read the exception record in a safe manner. */
9775 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9776 DWORD cbActuallyRead = 0;
9777 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9778 && cbActuallyRead == sizeof(RegRec))
9779 {
9780 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9781 KU32 rcHandler;
9782 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9783 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9784 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9785 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9786 if (rcHandler == ExceptionContinueExecution)
9787 {
9788 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
9789 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
9790 return EXCEPTION_CONTINUE_EXECUTION;
9791 }
9792
9793 if (rcHandler == ExceptionContinueSearch)
9794 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9795 else if (rcHandler == ExceptionNestedException)
9796 kHlpAssertMsgFailed(("Nested exceptions.\n"));
9797 else
9798 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9799 }
9800 else
9801 {
9802 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9803 break;
9804 }
9805
9806 /*
9807 * Next.
9808 */
9809 pRegRec = RegRec.pPrevRegRec;
9810 }
9811 }
9812 return EXCEPTION_CONTINUE_SEARCH;
9813}
9814
9815
9816/** NtDll,Kernel32 - RtlUnwind */
9817static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
9818 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
9819{
9820 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9821 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
9822 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
9823 if (g_Sandbox.fRunning)
9824 {
9825 HANDLE const hCurProc = GetCurrentProcess();
9826 PCONTEXT pXcptCtx = NULL;
9827 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9828
9829 /*
9830 * Update / create an exception record.
9831 */
9832 if (pXcptRec)
9833 pXcptRec->ExceptionFlags |= EH_UNWINDING;
9834 else
9835 {
9836 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
9837 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
9838 pXcptRec->ExceptionCode = STATUS_UNWIND;
9839 pXcptRec->ExceptionFlags = EH_UNWINDING;
9840 }
9841 if (!pStopXcptRec)
9842 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
9843
9844 /*
9845 * Walk the chain till we find pStopXctpRec.
9846 */
9847 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
9848 && pRegRec != NULL
9849 && pRegRec != pStopXcptRec)
9850 {
9851 /* Read the exception record in a safe manner. */
9852 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9853 DWORD cbActuallyRead = 0;
9854 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9855 && cbActuallyRead == sizeof(RegRec))
9856 {
9857 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9858 KU32 rcHandler;
9859 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9860 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9861 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9862 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9863
9864 if (rcHandler == ExceptionContinueSearch)
9865 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9866 else if (rcHandler == ExceptionCollidedUnwind)
9867 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
9868 else
9869 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9870 }
9871 else
9872 {
9873 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9874 break;
9875 }
9876
9877 /*
9878 * Pop next.
9879 */
9880 pTib->ExceptionList = RegRec.pPrevRegRec;
9881 pRegRec = RegRec.pPrevRegRec;
9882 }
9883 return;
9884 }
9885
9886 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
9887}
9888
9889#endif /* WINDOWS + X86 */
9890
9891
9892/*
9893 *
9894 * Misc function only intercepted while debugging.
9895 * Misc function only intercepted while debugging.
9896 * Misc function only intercepted while debugging.
9897 *
9898 */
9899
9900#ifndef NDEBUG
9901
9902/** CRT - memcpy */
9903static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
9904{
9905 KU8 const *pbSrc = (KU8 const *)pvSrc;
9906 KU8 *pbDst = (KU8 *)pvDst;
9907 KSIZE cbLeft = cb;
9908 while (cbLeft-- > 0)
9909 *pbDst++ = *pbSrc++;
9910 return pvDst;
9911}
9912
9913
9914/** CRT - memset */
9915static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
9916{
9917 KU8 *pbDst = (KU8 *)pvDst;
9918 KSIZE cbLeft = cb;
9919 while (cbLeft-- > 0)
9920 *pbDst++ = bFiller;
9921 return pvDst;
9922}
9923
9924#endif /* NDEBUG */
9925
9926
9927
9928/**
9929 * Functions that needs replacing for sandboxed execution.
9930 */
9931KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
9932{
9933 /*
9934 * Kernel32.dll and friends.
9935 */
9936 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
9937 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9938
9939 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
9940 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
9941 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
9942 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
9943 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
9944 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
9945 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
9946 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
9947 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
9948 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
9949 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9950
9951 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
9952 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
9953 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
9954 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
9955
9956 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
9957
9958 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
9959 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
9960 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
9961 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
9962 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
9963 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
9964 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
9965 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
9966 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
9967 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
9968 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
9969
9970 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
9971 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
9972 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
9973 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9974#ifdef WITH_TEMP_MEMORY_FILES
9975 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
9976 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9977 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9978 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
9979 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
9980 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9981 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9982 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9983 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9984 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9985#endif
9986 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9987 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9988 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9989 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
9990 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9991 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9992 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
9993 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
9994 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9995#ifdef WITH_TEMP_MEMORY_FILES
9996 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9997#endif
9998
9999 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10000 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10001
10002 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10003 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10004
10005 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10006 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10007
10008 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10009 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10010 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10011 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10012
10013 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10014
10015#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10016 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10017#endif
10018
10019#ifdef WITH_HASH_MD5_CACHE
10020 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10021 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10022 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10023 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10024#endif
10025
10026#ifdef WITH_CRYPT_CTX_REUSE
10027 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10028 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10029 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10030#endif
10031
10032 /*
10033 * MS Visual C++ CRTs.
10034 */
10035 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10036 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10037 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10038 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10039 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10040 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10041
10042 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10043 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10044 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10045
10046 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10047 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10048 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
10049
10050 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
10051 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
10052 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
10053 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
10054 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
10055 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
10056 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
10057 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
10058 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
10059 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
10060 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
10061 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
10062 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
10063 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
10064 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
10065 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
10066 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
10067 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
10068 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
10069 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
10070
10071 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
10072 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
10073 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
10074 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
10075 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
10076 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
10077 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
10078 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
10079 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
10080 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
10081 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
10082 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
10083 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
10084 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
10085
10086#ifndef NDEBUG
10087 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
10088 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
10089#endif
10090};
10091/** Number of entries in g_aReplacements. */
10092KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
10093
10094
10095/**
10096 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
10097 * execution.
10098 */
10099KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
10100{
10101 /*
10102 * Kernel32.dll and friends.
10103 */
10104 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10105 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10106
10107#if 0
10108 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10109#endif
10110
10111 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10112 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10113 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10114 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10115#ifdef WITH_TEMP_MEMORY_FILES
10116 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10117 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10118 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10119 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10120 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10121 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10122 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10123 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10124 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10125 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10126#endif
10127 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10128 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10129 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10130 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10131 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10132 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10133 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10134 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10135 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10136#ifdef WITH_TEMP_MEMORY_FILES
10137 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10138#endif
10139 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10140 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10141
10142 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10143 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10144
10145#ifdef WITH_HASH_MD5_CACHE
10146 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10147 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10148 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10149 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10150#endif
10151
10152 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10153
10154#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10155 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10156#endif
10157
10158 /*
10159 * MS Visual C++ CRTs.
10160 */
10161 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10162 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10163 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10164 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10165 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10166 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10167 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10168
10169#if 0 /* used by mspdbXXX.dll */
10170 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10171 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10172#endif
10173};
10174/** Number of entries in g_aSandboxNativeReplacements. */
10175KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10176
10177
10178/**
10179 * Functions that needs replacing when queried by GetProcAddress.
10180 */
10181KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10182{
10183 /*
10184 * Kernel32.dll and friends.
10185 */
10186 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10187 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10188 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10189 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10190};
10191/** Number of entries in g_aSandboxGetProcReplacements. */
10192KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10193
10194
10195/**
10196 * Control handler.
10197 *
10198 * @returns TRUE if handled, FALSE if not.
10199 * @param dwCtrlType The signal.
10200 */
10201static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10202{
10203 DWORD cbIgn;
10204 int volatile rc; /* volatile for debugging */
10205 int volatile rcPrev;
10206 const char *pszMsg;
10207 switch (dwCtrlType)
10208 {
10209 case CTRL_C_EVENT:
10210 rc = 9;
10211 pszMsg = "kWorker: Ctrl-C\r\n";
10212 break;
10213
10214 case CTRL_BREAK_EVENT:
10215 rc = 10;
10216 pszMsg = "kWorker: Ctrl-Break\r\n";
10217 break;
10218
10219 case CTRL_CLOSE_EVENT:
10220 rc = 11;
10221 pszMsg = "kWorker: console closed\r\n";
10222 break;
10223
10224 case CTRL_LOGOFF_EVENT:
10225 rc = 11;
10226 pszMsg = "kWorker: logoff event\r\n";
10227 break;
10228
10229 case CTRL_SHUTDOWN_EVENT:
10230 rc = 11;
10231 pszMsg = "kWorker: shutdown event\r\n";
10232 break;
10233
10234 default:
10235 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10236 return TRUE;
10237 }
10238
10239 /*
10240 * Terminate the process after 5 seconds.
10241 * If we get here a second time we just terminate the process ourselves.
10242 *
10243 * Note! We do no try call exit() here as it turned out to deadlock a lot
10244 * flusing file descriptors (stderr back when we first wrote to it).
10245 */
10246 rcPrev = g_rcCtrlC;
10247 g_rcCtrlC = rc;
10248 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10249 if (rcPrev == 0)
10250 {
10251 int i;
10252 for (i = 0; i < 10; i++)
10253 {
10254 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10255 Sleep(500);
10256 }
10257 }
10258 TerminateProcess(GetCurrentProcess(), rc);
10259 return TRUE;
10260}
10261
10262
10263/**
10264 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10265 */
10266static void kwSandboxResetModuleVisited(void)
10267{
10268 PKWMODULE pMod = g_pModuleHead;
10269 while (pMod)
10270 {
10271 pMod->fVisited = K_FALSE;
10272 pMod = pMod->pNextList;
10273 }
10274}
10275
10276
10277/**
10278 * Used by kwSandboxExec to reset the state of the module tree.
10279 *
10280 * This is done recursively.
10281 *
10282 * @param pMod The root of the tree to consider.
10283 */
10284static void kwSandboxResetModuleState(PKWMODULE pMod)
10285{
10286 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
10287 if (!pMod->fNative)
10288 {
10289 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
10290 if (!pMod->fVisited) /* Avoid loops. */
10291 {
10292 KSIZE iImp;
10293 pMod->fVisited = K_TRUE;
10294 iImp = pMod->u.Manual.cImpMods;
10295 while (iImp-- > 0)
10296 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
10297 }
10298 }
10299 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10300 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
10301 {
10302 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10303 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10304 {
10305 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10306 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10307 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10308 pMod->pszPath, pszValue ? pszValue : "<null>"));
10309 }
10310 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10311 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10312 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10313 pMod->pszPath, pszValue ? pszValue : "<null>"));
10314 else
10315 {
10316 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10317 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10318 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10319 if (pszValue != NULL)
10320 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10321 else
10322 pMod->pszMsPdbSrvEndpoint = NULL;
10323 pMod->fNeedReInit = K_TRUE;
10324 }
10325 }
10326}
10327
10328static PPEB kwSandboxGetProcessEnvironmentBlock(void)
10329{
10330#if K_ARCH == K_ARCH_X86_32
10331 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
10332#elif K_ARCH == K_ARCH_AMD64
10333 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
10334#else
10335# error "Port me!"
10336#endif
10337}
10338
10339
10340/**
10341 * Enters the given handle into the handle table.
10342 *
10343 * @returns K_TRUE on success, K_FALSE on failure.
10344 * @param pSandbox The sandbox.
10345 * @param pHandle The handle.
10346 * @param hHandle The handle value to enter it under (for the
10347 * duplicate handle API).
10348 */
10349static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
10350{
10351 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
10352 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
10353
10354 /*
10355 * Grow handle table.
10356 */
10357 if (idxHandle >= pSandbox->cHandles)
10358 {
10359 void *pvNew;
10360 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
10361 while (cHandles <= idxHandle)
10362 cHandles *= 2;
10363 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
10364 if (!pvNew)
10365 {
10366 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
10367 return K_FALSE;
10368 }
10369 pSandbox->papHandles = (PKWHANDLE *)pvNew;
10370 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
10371 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
10372 pSandbox->cHandles = cHandles;
10373 }
10374
10375 /*
10376 * Check that the entry is unused then insert it.
10377 */
10378 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
10379 pSandbox->papHandles[idxHandle] = pHandle;
10380 pSandbox->cActiveHandles++;
10381 return K_TRUE;
10382}
10383
10384
10385/**
10386 * Creates a correctly quoted ANSI command line string from the given argv.
10387 *
10388 * @returns Pointer to the command line.
10389 * @param cArgs Number of arguments.
10390 * @param papszArgs The argument vector.
10391 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10392 * @param pcbCmdLine Where to return the command line length,
10393 * including one terminator.
10394 */
10395static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
10396{
10397 KU32 i;
10398 KSIZE cbCmdLine;
10399 char *pszCmdLine;
10400
10401 /* Make a copy of the argument vector that we'll be quoting. */
10402 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
10403 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
10404
10405 /* Quote the arguments that need it. */
10406 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
10407
10408 /* figure out cmd line length. */
10409 cbCmdLine = 0;
10410 for (i = 0; i < cArgs; i++)
10411 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
10412 *pcbCmdLine = cbCmdLine;
10413
10414 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
10415 if (pszCmdLine)
10416 {
10417 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
10418 if (papszQuotedArgs[0] != papszArgs[0])
10419 free(papszQuotedArgs[0]);
10420
10421 for (i = 1; i < cArgs; i++)
10422 {
10423 *psz++ = ' ';
10424 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
10425 if (papszQuotedArgs[i] != papszArgs[i])
10426 free(papszQuotedArgs[i]);
10427 }
10428 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
10429
10430 *psz++ = '\0';
10431 *psz++ = '\0';
10432 }
10433
10434 return pszCmdLine;
10435}
10436
10437
10438
10439static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
10440 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10441 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10442{
10443 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10444 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10445 wchar_t *pwcPool;
10446 KSIZE cbStrings;
10447 KSIZE cwc;
10448 KSIZE cbCmdLine;
10449 KU32 i;
10450
10451 /* Simple stuff. */
10452 pSandbox->rcExitCode = 256;
10453 pSandbox->pTool = pTool;
10454 pSandbox->idMainThread = GetCurrentThreadId();
10455 pSandbox->pgmptr = (char *)pTool->pszPath;
10456 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
10457#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10458 if (pSandbox->StdOut.fIsConsole)
10459 pSandbox->StdOut.u.Con.cwcBuf = 0;
10460 else
10461 pSandbox->StdOut.u.Fully.cchBuf = 0;
10462 if (pSandbox->StdErr.fIsConsole)
10463 pSandbox->StdErr.u.Con.cwcBuf = 0;
10464 else
10465 pSandbox->StdErr.u.Fully.cchBuf = 0;
10466 pSandbox->Combined.cwcBuf = 0;
10467 pSandbox->Combined.cFlushes = 0;
10468#endif
10469 pSandbox->fNoPchCaching = fNoPchCaching;
10470 pSandbox->cArgs = cArgs;
10471 pSandbox->papszArgs = (char **)papszArgs;
10472 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
10473 if (!pSandbox->pszCmdLine)
10474 return KERR_NO_MEMORY;
10475
10476 /*
10477 * Convert command line and argv to UTF-16.
10478 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
10479 */
10480 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
10481 if (!pSandbox->papwszArgs)
10482 return KERR_NO_MEMORY;
10483 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
10484 for (i = 0; i < cArgs; i++)
10485 {
10486 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
10487 pSandbox->papwszArgs[i] = pwcPool;
10488 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
10489 pwcPool++;
10490 }
10491 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
10492 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
10493
10494 /*
10495 * Convert the commandline string to UTF-16, same pessimistic approach as above.
10496 */
10497 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
10498 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
10499 if (!pSandbox->pwszCmdLine)
10500 return KERR_NO_MEMORY;
10501 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
10502
10503 pSandbox->SavedCommandLine = pProcParams->CommandLine;
10504 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
10505 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
10506
10507 /*
10508 * Setup the environment.
10509 */
10510 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
10511 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
10512 {
10513 KU32 iDst = 0;
10514 for (i = 0; i < cEnvVars; i++)
10515 {
10516 const char *pszVar = papszEnvVars[i];
10517 KSIZE cchVar = kHlpStrLen(pszVar);
10518 const char *pszEqual;
10519 if ( cchVar > 0
10520 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
10521 {
10522 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
10523 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
10524 if (pszCopy && pwszCopy)
10525 {
10526 pSandbox->papszEnvVars[iDst] = pszCopy;
10527 pSandbox->environ[iDst] = pszCopy;
10528 pSandbox->papwszEnvVars[iDst] = pwszCopy;
10529 pSandbox->wenviron[iDst] = pwszCopy;
10530
10531 /* When we see the path, we must tell the system or native exec and module loading won't work . */
10532 if ( (pszEqual - pszVar) == 4
10533 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
10534 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
10535 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
10536 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
10537 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
10538 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
10539
10540 iDst++;
10541 }
10542 else
10543 {
10544 kHlpFree(pszCopy);
10545 kHlpFree(pwszCopy);
10546 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
10547 }
10548 }
10549 else
10550 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
10551 }
10552 pSandbox->papszEnvVars[iDst] = NULL;
10553 pSandbox->environ[iDst] = NULL;
10554 pSandbox->papwszEnvVars[iDst] = NULL;
10555 pSandbox->wenviron[iDst] = NULL;
10556 }
10557 else
10558 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
10559
10560 /*
10561 * Invalidate the volatile parts of cache (kBuild output directory,
10562 * temporary directory, whatever).
10563 */
10564 kFsCacheInvalidateCustomBoth(g_pFsCache);
10565
10566#ifdef WITH_HISTORY
10567 /*
10568 * Record command line in debug history.
10569 */
10570 kHlpFree(g_apszHistory[g_iHistoryNext]);
10571 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
10572 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
10573#endif
10574
10575 return 0;
10576}
10577
10578
10579/**
10580 * Does sandbox cleanup between jobs.
10581 *
10582 * We postpone whatever isn't externally visible (i.e. files) and doesn't
10583 * influence the result, so that kmk can get on with things ASAP.
10584 *
10585 * @param pSandbox The sandbox.
10586 */
10587static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
10588{
10589 PROCESS_MEMORY_COUNTERS MemInfo;
10590 PKWVIRTALLOC pTracker;
10591 PKWHEAP pHeap;
10592 PKWLOCALSTORAGE pLocalStorage;
10593#ifdef WITH_HASH_MD5_CACHE
10594 PKWHASHMD5 pHash;
10595#endif
10596#ifdef WITH_TEMP_MEMORY_FILES
10597 PKWFSTEMPFILE pTempFile;
10598#endif
10599 PKWEXITCALLACK pExitCallback;
10600
10601 /*
10602 * First stuff that may cause code to run.
10603 */
10604
10605 /* Do exit callback first. */
10606 pExitCallback = g_Sandbox.pExitCallbackHead;
10607 g_Sandbox.pExitCallbackHead = NULL;
10608 while (pExitCallback)
10609 {
10610 PKWEXITCALLACK pNext = pExitCallback->pNext;
10611 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
10612 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
10613 __try
10614 {
10615 pExitCallback->pfnCallback();
10616 }
10617 __except (EXCEPTION_EXECUTE_HANDLER)
10618 {
10619 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
10620 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
10621 kHlpAssertFailed();
10622 }
10623 kHlpFree(pExitCallback);
10624 pExitCallback = pNext;
10625 }
10626
10627 /* Free left behind FlsAlloc leaks. */
10628 pLocalStorage = g_Sandbox.pFlsAllocHead;
10629 g_Sandbox.pFlsAllocHead = NULL;
10630 while (pLocalStorage)
10631 {
10632 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10633 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
10634 FlsFree(pLocalStorage->idx);
10635 kHlpFree(pLocalStorage);
10636 pLocalStorage = pNext;
10637 }
10638
10639 /* Free left behind TlsAlloc leaks. */
10640 pLocalStorage = g_Sandbox.pTlsAllocHead;
10641 g_Sandbox.pTlsAllocHead = NULL;
10642 while (pLocalStorage)
10643 {
10644 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10645 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
10646 TlsFree(pLocalStorage->idx);
10647 kHlpFree(pLocalStorage);
10648 pLocalStorage = pNext;
10649 }
10650
10651
10652 /*
10653 * Then free resources associated with the sandbox run.
10654 */
10655
10656 /* Open handles, except fixed handles (stdout and stderr). */
10657 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
10658 {
10659 KU32 idxHandle = pSandbox->cHandles;
10660 while (idxHandle-- > 0)
10661 if (pSandbox->papHandles[idxHandle] == NULL)
10662 { /* likely */ }
10663 else
10664 {
10665 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
10666 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
10667 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
10668 {
10669 pSandbox->papHandles[idxHandle] = NULL;
10670 pSandbox->cLeakedHandles++;
10671
10672 switch (pHandle->enmType)
10673 {
10674 case KWHANDLETYPE_FSOBJ_READ_CACHE:
10675 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
10676 idxHandle, pHandle->hHandle, pHandle->cRefs));
10677 break;
10678 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
10679 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
10680 idxHandle, pHandle->hHandle, pHandle->cRefs));
10681 break;
10682 case KWHANDLETYPE_OUTPUT_BUF:
10683 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
10684 idxHandle, pHandle->hHandle, pHandle->cRefs));
10685 break;
10686 case KWHANDLETYPE_TEMP_FILE:
10687 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
10688 idxHandle, pHandle->hHandle, pHandle->cRefs));
10689 pHandle->u.pTempFile->cActiveHandles--;
10690 break;
10691 case KWHANDLETYPE_TEMP_FILE_MAPPING:
10692 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
10693 idxHandle, pHandle->hHandle, pHandle->cRefs));
10694 pHandle->u.pTempFile->cActiveHandles--;
10695 break;
10696 default:
10697 kHlpAssertFailed();
10698 }
10699 if (--pHandle->cRefs == 0)
10700 kHlpFree(pHandle);
10701 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
10702 break;
10703 }
10704 }
10705 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
10706 }
10707
10708 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
10709 g_Sandbox.cMemMappings = 0;
10710
10711#ifdef WITH_TEMP_MEMORY_FILES
10712 /* The temporary files aren't externally visible, they're all in memory. */
10713 pTempFile = pSandbox->pTempFileHead;
10714 pSandbox->pTempFileHead = NULL;
10715 while (pTempFile)
10716 {
10717 PKWFSTEMPFILE pNext = pTempFile->pNext;
10718 KU32 iSeg = pTempFile->cSegs;
10719 while (iSeg-- > 0)
10720 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
10721 kHlpFree(pTempFile->paSegs);
10722 pTempFile->pNext = NULL;
10723 kHlpFree(pTempFile);
10724
10725 pTempFile = pNext;
10726 }
10727#endif
10728
10729 /* Free left behind HeapCreate leaks. */
10730 pHeap = g_Sandbox.pHeapHead;
10731 g_Sandbox.pHeapHead = NULL;
10732 while (pHeap != NULL)
10733 {
10734 PKWHEAP pNext = pHeap->pNext;
10735 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
10736 HeapDestroy(pHeap->hHeap);
10737 pHeap = pNext;
10738 }
10739
10740 /* Free left behind VirtualAlloc leaks. */
10741 pTracker = g_Sandbox.pVirtualAllocHead;
10742 g_Sandbox.pVirtualAllocHead = NULL;
10743 while (pTracker)
10744 {
10745 PKWVIRTALLOC pNext = pTracker->pNext;
10746 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
10747
10748#ifdef WITH_FIXED_VIRTUAL_ALLOCS
10749 if (pTracker->idxPreAllocated != KU32_MAX)
10750 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
10751 else
10752#endif
10753 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
10754 kHlpFree(pTracker);
10755 pTracker = pNext;
10756 }
10757
10758 /* Free the environment. */
10759 if (pSandbox->papszEnvVars)
10760 {
10761 KU32 i;
10762 for (i = 0; pSandbox->papszEnvVars[i]; i++)
10763 kHlpFree(pSandbox->papszEnvVars[i]);
10764 pSandbox->environ[0] = NULL;
10765 pSandbox->papszEnvVars[0] = NULL;
10766
10767 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
10768 kHlpFree(pSandbox->papwszEnvVars[i]);
10769 pSandbox->wenviron[0] = NULL;
10770 pSandbox->papwszEnvVars[0] = NULL;
10771 }
10772
10773#ifdef WITH_HASH_MD5_CACHE
10774 /*
10775 * Hash handles.
10776 */
10777 pHash = pSandbox->pHashHead;
10778 pSandbox->pHashHead = NULL;
10779 while (pHash)
10780 {
10781 PKWHASHMD5 pNext = pHash->pNext;
10782 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
10783 kHlpFree(pHash);
10784 pHash = pNext;
10785 }
10786#endif
10787
10788 /*
10789 * Check the memory usage. If it's getting high, trigger a respawn
10790 * after the next job.
10791 */
10792 MemInfo.WorkingSetSize = 0;
10793 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
10794 {
10795 /* The first time thru, we figure out approximately when to restart
10796 based on installed RAM and CPU threads. */
10797 static KU64 s_cbMaxWorkingSet = 0;
10798 if (s_cbMaxWorkingSet != 0)
10799 { /* likely */ }
10800 else
10801 {
10802 SYSTEM_INFO SysInfo;
10803 MEMORYSTATUSEX GlobalMemInfo;
10804 const char *pszValue;
10805
10806 /* Calculate a reasonable estimate. */
10807 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
10808 GetNativeSystemInfo(&SysInfo);
10809
10810 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
10811 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
10812 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
10813#if K_ARCH_BITS >= 64
10814 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
10815#else
10816 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
10817#endif
10818 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
10819 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10820
10821 /* User limit. */
10822 pszValue = getenv("KWORKER_MEMORY_LIMIT");
10823 if (pszValue != NULL)
10824 {
10825 char *pszNext;
10826 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
10827 if (*pszNext == '\0' || *pszNext == 'M')
10828 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
10829 else if (*pszNext == 'K')
10830 s_cbMaxWorkingSet = ulValue * (KU64)1024;
10831 else if (*pszNext == 'G')
10832 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
10833 else
10834 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
10835 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10836 }
10837
10838 /* Clamp it a little. */
10839 if (s_cbMaxWorkingSet < 168*1024*1024)
10840 s_cbMaxWorkingSet = 168*1024*1024;
10841#if K_ARCH_BITS < 64
10842 else
10843 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
10844 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
10845 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
10846 : 1536*1024*1024 /* got 4GB VA */);
10847#endif
10848 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
10849 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
10850 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10851 }
10852
10853 /* Finally the check. */
10854 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
10855 {
10856 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
10857 g_fRestart = K_TRUE;
10858 }
10859 }
10860
10861 /*
10862 * The CRT has a max of 8192 handles, so we better restart after a while if
10863 * someone is leaking handles or we risk running out of descriptors.
10864 *
10865 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
10866 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
10867 */
10868 if (pSandbox->cLeakedHandles > 6000)
10869 {
10870 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
10871 g_fRestart = K_TRUE;
10872 }
10873}
10874
10875
10876/**
10877 * Does essential cleanups and restoring, anything externally visible.
10878 *
10879 * All cleanups that aren't externally visible are postponed till after we've
10880 * informed kmk of the result, so it can be done in the dead time between jobs.
10881 *
10882 * @param pSandbox The sandbox.
10883 */
10884static void kwSandboxCleanup(PKWSANDBOX pSandbox)
10885{
10886 /*
10887 * Restore the parent command line string.
10888 */
10889 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10890 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10891 pProcParams->CommandLine = pSandbox->SavedCommandLine;
10892 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
10893 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
10894}
10895
10896
10897static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10898 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10899{
10900 int rcExit = 42;
10901 int rc;
10902
10903 /*
10904 * Initialize the sandbox environment.
10905 */
10906 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
10907 if (rc == 0)
10908 {
10909 /*
10910 * Do module initialization.
10911 */
10912 kwSandboxResetModuleVisited();
10913 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
10914 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
10915 if (rc == 0)
10916 {
10917 /*
10918 * Call the main function.
10919 */
10920#if K_ARCH == K_ARCH_AMD64
10921 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
10922#elif K_ARCH == K_ARCH_X86_32
10923 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
10924#else
10925# error "Port me!"
10926#endif
10927
10928 /* Save the NT TIB first (should do that here, not in some other function). */
10929 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10930 pSandbox->TibMainThread = *pTib;
10931
10932 /* Make the call in a guarded fashion. */
10933#if K_ARCH == K_ARCH_AMD64
10934 /* AMD64 */
10935 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
10936 __try
10937 {
10938 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10939 if (setjmp(pSandbox->JmpBuf) == 0)
10940 {
10941 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10942 pSandbox->fRunning = K_TRUE;
10943 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
10944 pSandbox->fRunning = K_FALSE;
10945 }
10946 else
10947 rcExit = pSandbox->rcExitCode;
10948 }
10949#elif K_ARCH == K_ARCH_X86_32
10950 /* x86 (see _tmainCRTStartup) */
10951 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
10952 __try
10953 {
10954 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10955 if (setjmp(pSandbox->JmpBuf) == 0)
10956 {
10957 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10958 pSandbox->fRunning = K_TRUE;
10959 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
10960 pSandbox->fRunning = K_FALSE;
10961 }
10962 else
10963 rcExit = pSandbox->rcExitCode;
10964 }
10965#endif
10966 __except (EXCEPTION_EXECUTE_HANDLER)
10967 {
10968 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
10969#ifdef WITH_HISTORY
10970 {
10971 KU32 cPrinted = 0;
10972 while (cPrinted++ < 5)
10973 {
10974 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
10975 if (g_apszHistory[idx])
10976 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
10977 }
10978 }
10979#endif
10980 rcExit = 512;
10981 }
10982 pSandbox->fRunning = K_FALSE;
10983
10984 /* Now, restore the NT TIB. */
10985 *pTib = pSandbox->TibMainThread;
10986 }
10987 else
10988 rcExit = 42 + 4;
10989
10990 /*
10991 * Flush and clean up the essential bits only, postpone whatever we
10992 * can till after we've replied to kmk.
10993 */
10994#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10995 kwSandboxConsoleFlushAll(&g_Sandbox);
10996#endif
10997 kwSandboxCleanup(&g_Sandbox);
10998 /** @todo Flush sandboxed native CRTs too. */
10999 }
11000 else
11001 rcExit = 42 + 3;
11002
11003 return rcExit;
11004}
11005
11006
11007/**
11008 * Does the post command part of a job (optional).
11009 *
11010 * @returns The exit code of the job.
11011 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11012 * @param papszPostCmdArgs The post command and its argument.
11013 */
11014static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11015{
11016 const char *pszCmd = papszPostCmdArgs[0];
11017
11018 /* Allow the kmk builtin prefix. */
11019 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
11020 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
11021 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
11022
11023 /* Command switch. */
11024 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
11025 {
11026 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
11027 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
11028 }
11029
11030 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
11031}
11032
11033
11034/**
11035 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11036 */
11037static unsigned kwGetCurrentProcessorGroup(void)
11038{
11039 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
11040 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
11041 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
11042 if (pfnGetter)
11043 {
11044 GROUP_AFFINITY GroupAffinity;
11045 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
11046 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
11047 return GroupAffinity.Group;
11048 }
11049 return 0;
11050}
11051
11052
11053/**
11054 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11055 */
11056static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
11057{
11058 KSIZE cchRet = 0;
11059 HANDLE hToken;
11060 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
11061 {
11062 DWORD cbRet;
11063 TOKEN_STATISTICS TokenStats;
11064 memset(&TokenStats, 0, sizeof(TokenStats));
11065 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
11066 cchRet = sprintf(pszValue, "%" KX64_PRI,
11067 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
11068 else
11069 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
11070 CloseHandle(hToken);
11071 }
11072 else
11073 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
11074 return cchRet;
11075}
11076
11077
11078/**
11079 * Look for and expand the special environment variable.
11080 *
11081 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
11082 * couldn't accuratly determine. Currently the following variables are
11083 * implemented:
11084 * - "@@PROCESSOR_GROUP@@" - The processor group number.
11085 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
11086 * - "@@PID@@" - The kWorker process ID.
11087 * - "@@@@" - Escaped "@@".
11088 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
11089 */
11090static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
11091{
11092 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
11093 KU32 i = cEnvVars;
11094 while (i-- > 0)
11095 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
11096 && papszEnvVars[i][cchSpecialEnv] == '=')
11097 {
11098 /* We will expand stuff like @@NAME@@ */
11099 const char *pszValue = papszEnvVars[i];
11100 KSIZE offDst = 0;
11101 char szTmp[1024];
11102 for (;;)
11103 {
11104 const char *pszAt = kHlpStrChr(pszValue, '@');
11105 while (pszAt && pszAt[1] != '@')
11106 pszAt = kHlpStrChr(pszAt + 1, '@');
11107 if (pszAt)
11108 {
11109 KSIZE cchSrc = pszAt - pszValue;
11110 if (offDst + cchSrc < sizeof(szTmp))
11111 {
11112 char szSrc[64];
11113
11114 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11115 offDst += cchSrc;
11116 pszValue = pszAt + 2;
11117
11118 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11119 {
11120 pszValue += 15;
11121 if (g_iProcessGroup == -1)
11122 g_iProcessGroup = kwGetCurrentProcessorGroup();
11123 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11124 }
11125 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11126 {
11127 pszValue += 19;
11128 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11129 }
11130 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11131 {
11132 pszValue += 5;
11133 cchSrc = sprintf(szSrc, "%d", getpid());
11134 }
11135 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
11136 {
11137 pszValue += 2;
11138 szSrc[0] = '@';
11139 szSrc[1] = '@';
11140 szSrc[2] = '\0';
11141 cchSrc = 2;
11142 }
11143 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
11144 {
11145 static unsigned int s_iCounter = 0;
11146 pszValue += 15;
11147 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
11148 }
11149 else
11150 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
11151 pszValue - 2);
11152 if (offDst + cchSrc < sizeof(szTmp))
11153 {
11154 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
11155 offDst += cchSrc;
11156 continue;
11157 }
11158 }
11159 }
11160 else
11161 {
11162 KSIZE cchSrc = kHlpStrLen(pszValue);
11163 if (offDst + cchSrc < sizeof(szTmp))
11164 {
11165 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11166 offDst += cchSrc;
11167 break;
11168 }
11169 }
11170 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
11171 }
11172 szTmp[offDst] = '\0';
11173
11174 /* Return a copy of it: */
11175 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
11176 if (papszEnvVars[i])
11177 {
11178 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
11179 return 0;
11180 }
11181 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
11182 }
11183
11184 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
11185}
11186
11187
11188/**
11189 * Part 2 of the "JOB" command handler.
11190 *
11191 * @returns The exit code of the job.
11192 * @param pszExecutable The executable to execute.
11193 * @param pszCwd The current working directory of the job.
11194 * @param cArgs The number of arguments.
11195 * @param papszArgs The argument vector.
11196 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11197 * @param cEnvVars The number of environment variables.
11198 * @param papszEnvVars The environment vector.
11199 * @param pszSpecialEnv Name of special environment variable that
11200 * requires selective expansion here.
11201 * @param fNoPchCaching Whether to disable precompiled header file
11202 * caching. Avoid trouble when creating them.
11203 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11204 * @param papszPostCmdArgs The post command and its argument.
11205 */
11206static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
11207 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11208 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
11209 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11210{
11211 int rcExit;
11212 PKWTOOL pTool;
11213 char *pszSpecialEnvFree = NULL;
11214
11215 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
11216 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
11217#ifdef KW_LOG_ENABLED
11218 {
11219 KU32 i;
11220 for (i = 0; i < cArgs; i++)
11221 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
11222 for (i = 0; i < cPostCmdArgs; i++)
11223 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
11224 }
11225#endif
11226 g_cJobs++;
11227
11228 /*
11229 * Expand pszSpecialEnv if present.
11230 */
11231 if (*pszSpecialEnv)
11232 {
11233 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
11234 if (!rcExit)
11235 { /* likely */ }
11236 else
11237 return rcExit;
11238 }
11239
11240 /*
11241 * Lookup the tool.
11242 */
11243 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
11244 if (pTool)
11245 {
11246 /*
11247 * Change the directory if we're going to execute the job inside
11248 * this process. Then invoke the tool type specific handler.
11249 */
11250 switch (pTool->enmType)
11251 {
11252 case KWTOOLTYPE_SANDBOXED:
11253 case KWTOOLTYPE_WATCOM:
11254 {
11255 /* Change dir. */
11256 KFSLOOKUPERROR enmError;
11257 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
11258 if ( pNewCurDir == g_pCurDirObj
11259 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
11260 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11261 else if (SetCurrentDirectoryA(pszCwd))
11262 {
11263 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
11264 g_pCurDirObj = pNewCurDir;
11265 }
11266 else
11267 {
11268 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
11269 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11270 rcExit = 42 + 1;
11271 break;
11272 }
11273
11274 /* Call specific handler. */
11275 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
11276 {
11277 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
11278 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
11279 cEnvVars, papszEnvVars, fNoPchCaching);
11280 }
11281 else
11282 {
11283 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
11284 rcExit = 42 + 2;
11285 }
11286 break;
11287 }
11288
11289 case KWTOOLTYPE_EXEC:
11290 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
11291 rcExit = 42 + 2;
11292 break;
11293
11294 default:
11295 kHlpAssertFailed();
11296 kwErrPrintf("Internal tool type corruption!!\n");
11297 rcExit = 42 + 2;
11298 g_fRestart = K_TRUE;
11299 break;
11300 }
11301
11302 /*
11303 * Do the post command, if present.
11304 */
11305 if (cPostCmdArgs && rcExit == 0)
11306 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
11307 }
11308 else
11309 rcExit = 42 + 1;
11310 if (pszSpecialEnvFree)
11311 {
11312 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
11313 kHlpFree(pszSpecialEnvFree);
11314 }
11315 return rcExit;
11316}
11317
11318
11319/**
11320 * Handles a "JOB" command.
11321 *
11322 * @returns The exit code of the job.
11323 * @param pszMsg Points to the "JOB" command part of the message.
11324 * @param cbMsg Number of message bytes at @a pszMsg. There are
11325 * 4 more zero bytes after the message body to
11326 * simplify parsing.
11327 */
11328static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
11329{
11330 int rcExit = 42;
11331
11332 /*
11333 * Unpack the message.
11334 */
11335 const char *pszExecutable;
11336 KSIZE cbTmp;
11337
11338 pszMsg += sizeof("JOB");
11339 cbMsg -= sizeof("JOB");
11340
11341 /* Executable name. */
11342 pszExecutable = pszMsg;
11343 cbTmp = kHlpStrLen(pszMsg) + 1;
11344 pszMsg += cbTmp;
11345 if ( cbTmp < cbMsg
11346 && cbTmp > 2)
11347 {
11348 const char *pszCwd;
11349 cbMsg -= cbTmp;
11350
11351 /* Current working directory. */
11352 pszCwd = pszMsg;
11353 cbTmp = kHlpStrLen(pszMsg) + 1;
11354 pszMsg += cbTmp;
11355 if ( cbTmp + sizeof(KU32) < cbMsg
11356 && cbTmp >= 2)
11357 {
11358 KU32 cArgs;
11359 cbMsg -= cbTmp;
11360
11361 /* Argument count. */
11362 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
11363 pszMsg += sizeof(cArgs);
11364 cbMsg -= sizeof(cArgs);
11365
11366 if (cArgs > 0 && cArgs < 4096)
11367 {
11368 /* The argument vector. */
11369 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
11370 if (papszArgs)
11371 {
11372 KU32 i;
11373 for (i = 0; i < cArgs; i++)
11374 {
11375 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
11376 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
11377 pszMsg += cbTmp;
11378 if (cbTmp < cbMsg)
11379 cbMsg -= cbTmp;
11380 else
11381 {
11382 cbMsg = 0;
11383 break;
11384 }
11385
11386 }
11387 papszArgs[cArgs] = 0;
11388
11389 /* Environment variable count. */
11390 if (cbMsg > sizeof(KU32))
11391 {
11392 KU32 cEnvVars;
11393 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
11394 pszMsg += sizeof(cEnvVars);
11395 cbMsg -= sizeof(cEnvVars);
11396
11397 if (cEnvVars >= 0 && cEnvVars < 4096)
11398 {
11399 /* The argument vector. */
11400 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
11401 if (papszEnvVars)
11402 {
11403 for (i = 0; i < cEnvVars; i++)
11404 {
11405 papszEnvVars[i] = pszMsg;
11406 cbTmp = kHlpStrLen(pszMsg) + 1;
11407 pszMsg += cbTmp;
11408 if (cbTmp < cbMsg)
11409 cbMsg -= cbTmp;
11410 else
11411 {
11412 cbMsg = 0;
11413 break;
11414 }
11415 }
11416 papszEnvVars[cEnvVars] = 0;
11417
11418 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
11419 if (cbMsg >= sizeof(KU8) * 2)
11420 {
11421 KBOOL fWatcomBrainDamange = *pszMsg++;
11422 KBOOL fNoPchCaching = *pszMsg++;
11423 cbMsg -= 2;
11424
11425 /* Name of special enviornment variable requiring selective expansion. */
11426 if (cbMsg >= 1)
11427 {
11428 const char *pszSpecialEnv = pszMsg;
11429 cbTmp = kHlpStrLen(pszMsg);
11430 pszMsg += cbTmp + 1;
11431 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
11432
11433 /* Post command argument count (can be zero). */
11434 if (cbMsg >= sizeof(KU32))
11435 {
11436 KU32 cPostCmdArgs;
11437 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
11438 pszMsg += sizeof(cPostCmdArgs);
11439 cbMsg -= sizeof(cPostCmdArgs);
11440
11441 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
11442 {
11443 char const *apszPostCmdArgs[32+1];
11444 for (i = 0; i < cPostCmdArgs; i++)
11445 {
11446 apszPostCmdArgs[i] = pszMsg;
11447 cbTmp = kHlpStrLen(pszMsg) + 1;
11448 pszMsg += cbTmp;
11449 if ( cbTmp < cbMsg
11450 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
11451 cbMsg -= cbTmp;
11452 else
11453 {
11454 cbMsg = KSIZE_MAX;
11455 break;
11456 }
11457 }
11458 if (cbMsg == 0)
11459 {
11460 apszPostCmdArgs[cPostCmdArgs] = NULL;
11461
11462 /*
11463 * The next step.
11464 */
11465 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
11466 cArgs, papszArgs, fWatcomBrainDamange,
11467 cEnvVars, papszEnvVars, pszSpecialEnv,
11468 fNoPchCaching,
11469 cPostCmdArgs, apszPostCmdArgs);
11470 }
11471 else if (cbMsg == KSIZE_MAX)
11472 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
11473 else
11474 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
11475 }
11476 else
11477 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
11478 }
11479 else
11480 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
11481 }
11482 else
11483 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
11484 }
11485 else
11486 kwErrPrintf("Detected bogus message unpacking flags!\n");
11487 kHlpFree((void *)papszEnvVars);
11488 }
11489 else
11490 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
11491 }
11492 else
11493 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
11494 }
11495 else
11496 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
11497 kHlpFree((void *)papszArgs);
11498 }
11499 else
11500 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
11501 }
11502 else
11503 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
11504 }
11505 else
11506 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
11507 }
11508 else
11509 kwErrPrintf("Detected bogus message unpacking executable path!\n");
11510 return rcExit;
11511}
11512
11513
11514/**
11515 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
11516 *
11517 * @retval 0 on success.
11518 * @retval -1 on error (fully bitched).
11519 *
11520 * @param hPipe The pipe handle.
11521 * @param pvBuf The buffer to write out out.
11522 * @param cbToWrite The number of bytes to write.
11523 */
11524static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
11525{
11526 KU8 const *pbBuf = (KU8 const *)pvBuf;
11527 KU32 cbLeft = cbToWrite;
11528 while (g_rcCtrlC == 0)
11529 {
11530 DWORD cbActuallyWritten = 0;
11531 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
11532 {
11533 cbLeft -= cbActuallyWritten;
11534 if (!cbLeft)
11535 return 0;
11536 pbBuf += cbActuallyWritten;
11537 }
11538 else
11539 {
11540 DWORD dwErr = GetLastError();
11541 if (cbLeft == cbToWrite)
11542 kwErrPrintf("WriteFile failed: %u\n", dwErr);
11543 else
11544 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
11545 return -1;
11546 }
11547 }
11548 return -1;
11549}
11550
11551
11552/**
11553 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
11554 *
11555 * @retval 0 on success.
11556 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
11557 * @retval -1 on error (fully bitched).
11558 * @param hPipe The pipe handle.
11559 * @param pvBuf The buffer to read into.
11560 * @param cbToRead The number of bytes to read.
11561 * @param fShutdownOkay Whether connection shutdown while reading the
11562 * first byte is okay or not.
11563 */
11564static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
11565{
11566 KU8 *pbBuf = (KU8 *)pvBuf;
11567 KU32 cbLeft = cbToRead;
11568 while (g_rcCtrlC == 0)
11569 {
11570 DWORD cbActuallyRead = 0;
11571 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
11572 {
11573 cbLeft -= cbActuallyRead;
11574 if (!cbLeft)
11575 return 0;
11576 pbBuf += cbActuallyRead;
11577 }
11578 else
11579 {
11580 DWORD dwErr = GetLastError();
11581 if (cbLeft == cbToRead)
11582 {
11583 if ( fMayShutdown
11584 && dwErr == ERROR_BROKEN_PIPE)
11585 return 1;
11586 kwErrPrintf("ReadFile failed: %u\n", dwErr);
11587 }
11588 else
11589 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
11590 return -1;
11591 }
11592 }
11593 return -1;
11594}
11595
11596
11597/**
11598 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
11599 *
11600 * @returns pszBuf
11601 * @param pszBuf The buffer (sufficiently large).
11602 * @param uValue The value.
11603 */
11604static const char *kwFmtU64(char *pszBuf, KU64 uValue)
11605{
11606 char szTmp[64];
11607 char *psz = &szTmp[63];
11608 int cch = 4;
11609
11610 *psz-- = '\0';
11611 do
11612 {
11613 if (--cch == 0)
11614 {
11615 *psz-- = ' ';
11616 cch = 3;
11617 }
11618 *psz-- = (uValue % 10) + '0';
11619 uValue /= 10;
11620 } while (uValue != 0);
11621
11622 return strcpy(pszBuf, psz + 1);
11623}
11624
11625
11626/**
11627 * Prints statistics.
11628 */
11629static void kwPrintStats(void)
11630{
11631 PROCESS_MEMORY_COUNTERS_EX MemInfo;
11632 MEMORYSTATUSEX MemStatus;
11633 IO_COUNTERS IoCounters;
11634 DWORD cHandles;
11635 KSIZE cMisses;
11636 char szBuf[16*1024];
11637 int off = 0;
11638 char szPrf[24];
11639 char sz1[64];
11640 char sz2[64];
11641 char sz3[64];
11642 char sz4[64];
11643
11644 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
11645
11646 szBuf[off++] = '\n';
11647
11648 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
11649 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
11650 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
11651 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
11652 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
11653
11654 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
11655 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
11656
11657 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
11658 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
11659 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
11660
11661 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
11662 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
11663 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
11664
11665 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
11666 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
11667 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
11668 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
11669 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
11670 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
11671 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
11672
11673 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
11674 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
11675 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
11676 kwFmtU64(sz1, g_pFsCache->cObjects),
11677 kwFmtU64(sz2, g_pFsCache->cbObjects),
11678 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
11679 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
11680 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
11681 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
11682 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
11683 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
11684#ifdef KFSCACHE_CFG_UTF16
11685 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
11686 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
11687 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
11688 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
11689 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
11690#endif
11691 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
11692 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
11693 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
11694 kwFmtU64(sz3, g_pFsCache->cChildHashed),
11695 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
11696
11697 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
11698 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
11699 kwFmtU64(sz1, g_pFsCache->cLookups),
11700 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
11701 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11702 kwFmtU64(sz3, g_pFsCache->cWalkHits),
11703 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11704 kwFmtU64(sz4, cMisses),
11705 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
11706
11707 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
11708 kwFmtU64(sz1, g_pFsCache->cChildSearches),
11709 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
11710 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
11711 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
11712 kwFmtU64(sz1, g_pFsCache->cNameChanges),
11713 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
11714 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
11715
11716
11717 /*
11718 * Process & Memory details.
11719 */
11720 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
11721 cHandles = 0;
11722 MemInfo.cb = sizeof(MemInfo);
11723 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
11724 memset(&MemInfo, 0, sizeof(MemInfo));
11725 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
11726 kwFmtU64(sz1, cHandles),
11727 kwFmtU64(sz2, MemInfo.PageFaultCount),
11728 kwFmtU64(sz3, MemInfo.PagefileUsage),
11729 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
11730 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
11731 kwFmtU64(sz1, MemInfo.WorkingSetSize),
11732 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
11733 kwFmtU64(sz3, MemInfo.PrivateUsage));
11734 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
11735 szPrf,
11736 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
11737 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
11738 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
11739 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
11740
11741 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
11742 memset(&IoCounters, 0, sizeof(IoCounters));
11743 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
11744 kwFmtU64(sz1, IoCounters.ReadTransferCount),
11745 kwFmtU64(sz2, IoCounters.ReadOperationCount));
11746 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
11747 kwFmtU64(sz1, IoCounters.WriteTransferCount),
11748 kwFmtU64(sz2, IoCounters.WriteOperationCount));
11749 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
11750 kwFmtU64(sz1, IoCounters.OtherTransferCount),
11751 kwFmtU64(sz2, IoCounters.OtherOperationCount));
11752
11753 MemStatus.dwLength = sizeof(MemStatus);
11754 if (!GlobalMemoryStatusEx(&MemStatus))
11755 memset(&MemStatus, 0, sizeof(MemStatus));
11756 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
11757 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
11758 MemStatus.ullAvailVirtual);
11759 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
11760
11761 maybe_con_fwrite(szBuf, off, 1, stdout);
11762 fflush(stdout);
11763}
11764
11765
11766/**
11767 * Handles what comes after --test.
11768 *
11769 * @returns Exit code.
11770 * @param argc Number of arguments after --test.
11771 * @param argv Arguments after --test.
11772 */
11773static int kwTestRun(int argc, char **argv)
11774{
11775 int i;
11776 int j;
11777 int rcExit;
11778 int cRepeats;
11779 char szCwd[MAX_PATH];
11780 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
11781 KU32 cEnvVars;
11782 char **papszEnvVars;
11783 const char *pszSpecialEnv = "";
11784 const char *pszSpecialEnvFull = NULL;
11785 KBOOL fWatcomBrainDamange = K_FALSE;
11786 KBOOL fNoPchCaching = K_FALSE;
11787
11788 /*
11789 * Parse arguments.
11790 */
11791 /* Repeat count. */
11792 i = 0;
11793 if (i >= argc)
11794 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
11795 if (strcmp(argv[i], "--") != 0)
11796 {
11797 cRepeats = atoi(argv[i]);
11798 if (cRepeats <= 0)
11799 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
11800 i++;
11801
11802 /* Optional directory change. */
11803 if ( i < argc
11804 && ( strcmp(argv[i], "--chdir") == 0
11805 || strcmp(argv[i], "-C") == 0 ) )
11806 {
11807 i++;
11808 if (i >= argc)
11809 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
11810 pszCwd = argv[i++];
11811 }
11812
11813 /* Optional watcom flag directory change. */
11814 if ( i < argc
11815 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
11816 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
11817 {
11818 fWatcomBrainDamange = K_TRUE;
11819 i++;
11820 }
11821
11822 /* Optional watcom flag directory change. */
11823 if ( i < argc
11824 && strcmp(argv[i], "--no-pch-caching") == 0)
11825 {
11826 fNoPchCaching = K_TRUE;
11827 i++;
11828 }
11829
11830 /* Optional directory change. */
11831 if ( i < argc
11832 && ( strcmp(argv[i], "--set-special") == 0
11833 || strcmp(argv[i], "-s") == 0 ) )
11834 {
11835 i++;
11836 if (i >= argc)
11837 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
11838 pszSpecialEnvFull = argv[i++];
11839 putenv(pszSpecialEnvFull);
11840 pszSpecialEnv = strdup(pszSpecialEnvFull);
11841 *strchr(pszSpecialEnv, '=') = '\0';
11842 }
11843
11844 /* Trigger breakpoint */
11845 if ( i < argc
11846 && strcmp(argv[i], "--breakpoint") == 0)
11847 {
11848 __debugbreak();
11849 i++;
11850 }
11851
11852 /* Check for '--'. */
11853 if (i >= argc)
11854 return kwErrPrintfRc(2, "Missing '--'\n");
11855 if (strcmp(argv[i], "--") != 0)
11856 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
11857 i++;
11858 }
11859 else
11860 {
11861 cRepeats = 1;
11862 i++;
11863 }
11864 if (i >= argc)
11865 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
11866
11867 /*
11868 * Duplicate the environment.
11869 */
11870 cEnvVars = 0;
11871 while (environ[cEnvVars] != NULL)
11872 cEnvVars++;
11873 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
11874
11875 /*
11876 * Do the job.
11877 */
11878 for (j = 0; j < cRepeats; j++)
11879 {
11880 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
11881 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
11882 argc - i, &argv[i], fWatcomBrainDamange,
11883 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
11884 0, NULL);
11885 KW_LOG(("rcExit=%d\n", rcExit));
11886 kwSandboxCleanupLate(&g_Sandbox);
11887 }
11888
11889 if (getenv("KWORKER_STATS") != NULL)
11890 kwPrintStats();
11891
11892# ifdef WITH_LOG_FILE
11893 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
11894 CloseHandle(g_hLogFile);
11895# endif
11896 return rcExit;
11897}
11898
11899
11900/**
11901 * Reads @a pszFile into memory and chops it up into an argument vector.
11902 *
11903 * @returns Pointer to the argument vector on success, NULL on failure.
11904 * @param pszFile The file to load.
11905 * @param pcArgs Where to return the number of arguments.
11906 * @param ppszFileContent Where to return the allocation.
11907 */
11908static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
11909{
11910 char **papszArgs = NULL;
11911 FILE *pFile = fopen(pszFile, "r");
11912 if (pFile)
11913 {
11914 long cbFile;
11915 if ( fseek(pFile, 0, SEEK_END) == 0
11916 && (cbFile = ftell(pFile)) >= 0
11917 && fseek(pFile, 0, SEEK_SET) == 0)
11918 {
11919 char *pszFile = kHlpAllocZ(cbFile + 3);
11920 if (pszFile)
11921 {
11922 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
11923 if ( feof(pFile)
11924 && !ferror(pFile))
11925 {
11926 size_t off = 0;
11927 int cArgs = 0;
11928 int cAllocated = 0;
11929 char ch;
11930
11931 pszFile[cbRead] = '\0';
11932 pszFile[cbRead + 1] = '\0';
11933 pszFile[cbRead + 2] = '\0';
11934
11935 while ((ch = pszFile[off]) != '\0')
11936 {
11937 char *pszArg;
11938 switch (ch)
11939 {
11940 case ' ':
11941 case '\t':
11942 case '\n':
11943 case '\r':
11944 off++;
11945 continue;
11946
11947 case '\\':
11948 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
11949 {
11950 off += 2;
11951 continue;
11952 }
11953 /* fall thru */
11954 default:
11955 pszArg = &pszFile[off];
11956 do
11957 ch = pszFile[++off];
11958 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
11959 pszFile[off++] = '\0';
11960 break;
11961
11962 case '\'':
11963 pszArg = &pszFile[++off];
11964 while ((ch = pszFile[off]) != '\0' && ch != '\'')
11965 off++;
11966 pszFile[off++] = '\0';
11967 break;
11968
11969 case '\"': /** @todo escape sequences */
11970 pszArg = &pszFile[++off];
11971 while ((ch = pszFile[off]) != '\0' && ch != '"')
11972 off++;
11973 pszFile[off++] = '\0';
11974 break;
11975 }
11976 if (cArgs + 1 >= cAllocated)
11977 {
11978 void *pvNew;
11979 cAllocated = cAllocated ? cAllocated * 2 : 16;
11980 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
11981 if (pvNew)
11982 papszArgs = (char **)pvNew;
11983 else
11984 {
11985 kHlpFree(papszArgs);
11986 papszArgs = NULL;
11987 break;
11988 }
11989 }
11990 papszArgs[cArgs] = pszArg;
11991 papszArgs[++cArgs] = NULL;
11992 }
11993 *pcArgs = cArgs;
11994 }
11995 else
11996 kwErrPrintf("Error reading '%s'!\n", pszFile);
11997 }
11998 else
11999 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
12000 }
12001 else
12002 kwErrPrintf("Error seeking '%s'!\n", pszFile);
12003 fclose(pFile);
12004 }
12005 else
12006 kwErrPrintf("Error opening '%s'!\n", pszFile);
12007 return papszArgs;
12008}
12009
12010/**
12011 * Appends a string to an string vector (arguments or enviornment).
12012 *
12013 * @returns 0 on success, non-zero on failure (exit code).
12014 * @param ppapszVector Pointer to the string pointer array.
12015 * @param pcEntries Pointer to the array size.
12016 * @param pszAppend The string to append.
12017 */
12018static int kwFullTestVectorAppend(const char ***ppapszVector, int *pcEntries, char const *pszAppend)
12019{
12020 unsigned cEntries = *pcEntries;
12021 if (!(cEntries & 15))
12022 {
12023 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
12024 if (pvNew)
12025 *ppapszVector = (const char **)pvNew;
12026 else
12027 return kwErrPrintfRc(2, "Out of memory!\n");
12028 }
12029 (*ppapszVector)[cEntries] = pszAppend;
12030 (*ppapszVector)[++cEntries] = NULL;
12031 *pcEntries = cEntries;
12032 return 0;
12033}
12034
12035
12036/**
12037 * Parses arguments for --full-test.
12038 *
12039 * @returns 0 on success, non-zero on failure (exit code).
12040 */
12041static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
12042 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
12043{
12044 PKWONETEST pCur = *ppHead;
12045 int i;
12046 for (i = 0; i < argc; i++)
12047 {
12048 int rc = 0;
12049 const char *pszArg = argv[i];
12050 if (*pszArg == 'k' && kHlpStrComp(pszArg, "kSubmit") == 0)
12051 {
12052 if (*piState != 0)
12053 {
12054 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
12055 if (!pCur)
12056 return kwErrPrintfRc(2, "Out of memory!\n");
12057 pCur->fVirgin = K_TRUE;
12058 pCur->pszCwd = pszDefaultCwd;
12059 pCur->cRuns = 1;
12060 pCur->pNext = *ppHead;
12061 *ppHead = pCur;
12062 *piState = 0;
12063 }
12064 else if (!pCur->fVirgin)
12065 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
12066 pCur->pszJobSrc = pszJobSrc;
12067 pCur->iJobSrc = i;
12068 continue; /* (to stay virgin) */
12069 }
12070 else if (*pszArg == '-' && *piState == 0)
12071 {
12072 const char *pszValue = NULL;
12073 char ch = *++pszArg;
12074 pszArg++;
12075 if (ch == '-')
12076 {
12077 ch = '\0';
12078 if (*pszArg == '\0') /* -- */
12079 *piState = 2;
12080 /* Translate or handle long options: */
12081 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
12082 ch = 'E';
12083 else if (kHlpStrComp(pszArg, "special-env") == 0)
12084 ch = 's';
12085 else if (kHlpStrComp(pszArg, "default-env") == 0)
12086 {
12087 unsigned i;
12088 pCur->cEnvVars = 0;
12089 for (i = 0; environ[i] && rc == 0; i++)
12090 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
12091 }
12092 else if (kHlpStrComp(pszArg, "chdir") == 0)
12093 ch = 'C';
12094 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
12095 ch = 'P';
12096 else if (kHlpStrComp(pszArg, "response-file") == 0)
12097 ch = '@';
12098 else if (kHlpStrComp(pszArg, "runs") == 0)
12099 ch = 'R';
12100 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
12101 pCur->fWatcomBrainDamange = K_TRUE;
12102 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
12103 pCur->fNoPchCaching = K_TRUE;
12104 else if (kHlpStrComp(pszArg, "executable") == 0)
12105 ch = 'e';
12106 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12107 {
12108 __debugbreak();
12109 continue; /* (to stay virgin) */
12110 }
12111 else
12112 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12113 pszArg = "";
12114 }
12115
12116 while (ch != '\0' && rc == 0)
12117 {
12118 /* Fetch value if needed: */
12119 switch (ch)
12120 {
12121 case '@':
12122 case 'e':
12123 case 'E':
12124 case 's':
12125 case 'C':
12126 case 'R':
12127 if (*pszArg == ':' || *pszArg == '=')
12128 pszValue = &pszArg[1];
12129 else if (*pszArg)
12130 pszValue = pszArg;
12131 else if (i + 1 < argc)
12132 pszValue = argv[++i];
12133 else
12134 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
12135 pszArg = "";
12136 break;
12137 }
12138
12139 /* Handle the option: */
12140 switch (ch)
12141 {
12142 case 'E':
12143 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
12144 break;
12145 case 'C':
12146 pCur->pszCwd = pszValue;
12147 break;
12148 case 's':
12149 pCur->pszSpecialEnv = pszValue;
12150 break;
12151 case 'e':
12152 pCur->pszExecutable = pszValue;
12153 break;
12154 case 'P':
12155 *piState = 1;
12156 if (*pszArg)
12157 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
12158 break;
12159 case 'R':
12160 pCur->cRuns = atoi(pszValue);
12161 if ((int)pCur->cRuns < 0)
12162 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
12163 break;
12164 case '@':
12165 if (cRecursions < 5)
12166 {
12167 char *pszLeaked = NULL;
12168 int cArgs = 0;
12169 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
12170 if (papszArgsLeaked)
12171 {
12172 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
12173 cRecursions + 1, pszValue);
12174 pCur = *ppHead;
12175 }
12176 else
12177 return 2;
12178 }
12179 else
12180 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
12181 break;
12182 }
12183
12184 /* next */
12185 ch = *pszArg++;
12186 }
12187 }
12188 else if (*piState == 2)
12189 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
12190 else if (*piState == 1)
12191 {
12192 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
12193 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
12194 else
12195 *piState = 2;
12196 }
12197 else
12198 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
12199 if (rc)
12200 return rc;
12201 pCur->fVirgin = K_FALSE;
12202 }
12203 return 0;
12204}
12205
12206
12207/**
12208 * Handles what comes after --full-test.
12209 *
12210 * @returns Exit code.
12211 * @param argc Number of arguments after --full-test.
12212 * @param argv Arguments after --full-test.
12213 */
12214static int kwFullTestRun(int argc, char **argv)
12215{
12216 char szDefaultCwd[MAX_PATH];
12217 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
12218 KWONETEST FirstTest;
12219 PKWONETEST pHead = &FirstTest;
12220 PKWONETEST pCur;
12221 int iState = 0;
12222 int rcExit;
12223
12224 /*
12225 * Parse arguments.
12226 */
12227 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
12228 FirstTest.pszJobSrc = "command-line";
12229 FirstTest.iJobSrc = 1;
12230 FirstTest.fVirgin = K_TRUE;
12231 FirstTest.pszCwd = pszDefaultCwd;
12232 FirstTest.cRuns = 1;
12233
12234 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
12235 if (rcExit)
12236 return rcExit;
12237
12238 /*
12239 * Do the job. LIFO ordering (see kSubmit).
12240 */
12241 for (pCur = pHead; pCur; pCur = pCur->pNext)
12242 {
12243 if (!pCur->pszExecutable && pCur->papszArgs)
12244 pCur->pszExecutable = pCur->papszArgs[0];
12245 if ( pCur->pszExecutable
12246 && pCur->cArgs > 0
12247 && pCur->cEnvVars > 0)
12248 {
12249 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
12250 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
12251 unsigned iRun;
12252
12253 for (iRun = 0; iRun < pCur->cRuns; iRun++)
12254 {
12255 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
12256 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
12257 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
12258 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
12259
12260 KW_LOG(("rcExit=%d\n", rcExit));
12261 kwSandboxCleanupLate(&g_Sandbox);
12262
12263 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
12264 }
12265 kHlpFree(papszEnvVarsCopy);
12266 }
12267 else
12268 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
12269 pCur->pszExecutable ? "" : " No executable!",
12270 pCur->cArgs < 1 ? " No arguments!" : "",
12271 pCur->cEnvVars < 1 ? " No environment!" : "",
12272 pCur->iJobSrc, pCur->pszJobSrc);
12273 }
12274
12275 if (getenv("KWORKER_STATS") != NULL)
12276 kwPrintStats();
12277
12278# ifdef WITH_LOG_FILE
12279 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12280 CloseHandle(g_hLogFile);
12281# endif
12282 return rcExit;
12283}
12284
12285
12286int main(int argc, char **argv)
12287{
12288 KSIZE cbMsgBuf = 0;
12289 KU8 *pbMsgBuf = NULL;
12290 int i;
12291 HANDLE hPipe = INVALID_HANDLE_VALUE;
12292 const char *pszTmp;
12293 KFSLOOKUPERROR enmIgnored;
12294#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
12295 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
12296 kwSandboxVecXcptEmulateChained);
12297#endif
12298#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12299 HANDLE hCurProc = GetCurrentProcess();
12300 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12301 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12302 DWORD dwType;
12303#endif
12304
12305
12306#ifdef WITH_FIXED_VIRTUAL_ALLOCS
12307 /*
12308 * Reserve memory for cl.exe
12309 */
12310 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
12311 {
12312 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
12313 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
12314 g_aFixedVirtualAllocs[i].cbFixed,
12315 MEM_RESERVE, PAGE_READWRITE);
12316 if ( !g_aFixedVirtualAllocs[i].pvReserved
12317 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
12318 {
12319 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
12320 GetLastError());
12321 if (g_aFixedVirtualAllocs[i].pvReserved)
12322 {
12323 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
12324 g_aFixedVirtualAllocs[i].pvReserved = NULL;
12325 }
12326 }
12327 }
12328#endif
12329
12330 /*
12331 * Register our Control-C and Control-Break handlers.
12332 */
12333 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
12334 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
12335
12336 /*
12337 * Create the cache and mark the temporary directory as using the custom revision.
12338 */
12339 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
12340 if (!g_pFsCache)
12341 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
12342
12343 pszTmp = getenv("TEMP");
12344 if (pszTmp && *pszTmp != '\0')
12345 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12346 pszTmp = getenv("TMP");
12347 if (pszTmp && *pszTmp != '\0')
12348 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12349 pszTmp = getenv("TMPDIR");
12350 if (pszTmp && *pszTmp != '\0')
12351 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12352
12353 /*
12354 * Make g_abDefLdBuf executable.
12355 */
12356 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
12357 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
12358 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
12359
12360#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12361 /*
12362 * Get and duplicate the console handles.
12363 */
12364 /* Standard output. */
12365 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
12366 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
12367 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12368 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
12369 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
12370 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
12371 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12372 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12373 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
12374 g_Sandbox.HandleStdOut.cRefs = 0x10001;
12375 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
12376 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
12377 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
12378 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
12379 {
12380 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
12381 g_Sandbox.cFixedHandles++;
12382 else
12383 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
12384 }
12385 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12386 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
12387
12388 /* Standard error. */
12389 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
12390 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
12391 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12392 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
12393 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
12394 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
12395 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12396 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12397 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
12398 g_Sandbox.HandleStdErr.cRefs = 0x10001;
12399 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
12400 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
12401 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
12402 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
12403 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
12404 {
12405 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
12406 g_Sandbox.cFixedHandles++;
12407 else
12408 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
12409 }
12410 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12411 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
12412
12413 /* Combined console buffer. */
12414 if (g_Sandbox.StdErr.fIsConsole)
12415 {
12416 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
12417 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12418 }
12419 else if (g_Sandbox.StdOut.fIsConsole)
12420 {
12421 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
12422 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12423 }
12424 else
12425 {
12426 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
12427 g_Sandbox.Combined.uCodepage = CP_ACP;
12428 }
12429 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
12430#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
12431
12432
12433 /*
12434 * Parse arguments.
12435 */
12436 for (i = 1; i < argc; i++)
12437 {
12438 if (strcmp(argv[i], "--pipe") == 0)
12439 {
12440 i++;
12441 if (i < argc)
12442 {
12443 char *pszEnd = NULL;
12444 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
12445 if ( *argv[i]
12446 && pszEnd != NULL
12447 && *pszEnd == '\0'
12448 && u64Value != 0
12449 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
12450 && (uintptr_t)u64Value == u64Value)
12451 hPipe = (HANDLE)(uintptr_t)u64Value;
12452 else
12453 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
12454 }
12455 else
12456 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
12457 }
12458 else if (strcmp(argv[i], "--volatile") == 0)
12459 {
12460 i++;
12461 if (i < argc)
12462 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
12463 else
12464 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
12465 }
12466 else if (strcmp(argv[i], "--test") == 0)
12467 return kwTestRun(argc - i - 1, &argv[i + 1]);
12468 else if (strcmp(argv[i], "--full-test") == 0)
12469 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
12470 else if (strcmp(argv[i], "--priority") == 0)
12471 {
12472 i++;
12473 if (i < argc)
12474 {
12475 char *pszEnd = NULL;
12476 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12477 if ( *argv[i]
12478 && pszEnd != NULL
12479 && *pszEnd == '\0'
12480 && uValue >= 1
12481 && uValue <= 5)
12482 {
12483 DWORD dwClass, dwPriority;
12484 switch (uValue)
12485 {
12486 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
12487 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
12488 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
12489 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12490 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12491 }
12492 SetPriorityClass(GetCurrentProcess(), dwClass);
12493 if (dwPriority != 0xffffffff)
12494 SetThreadPriority(GetCurrentThread(), dwPriority);
12495 }
12496 else
12497 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12498 }
12499 else
12500 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12501 }
12502 else if (strcmp(argv[i], "--group") == 0)
12503 {
12504 i++;
12505 if (i < argc)
12506 {
12507 char *pszEnd = NULL;
12508 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12509 if ( *argv[i]
12510 && pszEnd != NULL
12511 && *pszEnd == '\0'
12512 && uValue == (WORD)uValue)
12513 {
12514 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
12515 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
12516 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(GetModuleHandleW(L"KERNEL32.DLL"),
12517 "SetThreadGroupAffinity");
12518 if (pfnSetThreadGroupAffinity)
12519 {
12520 GROUP_AFFINITY NewAff = { ~(uintptr_t)0, (WORD)uValue, 0, 0, 0 };
12521 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
12522 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
12523 kwErrPrintf("Failed to set processor group to %lu: %u\n", uValue, GetLastError());
12524 }
12525 else
12526 kwErrPrintf("Cannot set processor group to %lu because SetThreadGroupAffinity was not found\n", uValue);
12527 }
12528 else
12529 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12530 }
12531 else
12532 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12533 }
12534 else if ( strcmp(argv[i], "--help") == 0
12535 || strcmp(argv[i], "-h") == 0
12536 || strcmp(argv[i], "-?") == 0)
12537 {
12538 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
12539 "usage: kWorker <--help|-h>\n"
12540 "usage: kWorker <--version|-V>\n"
12541 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
12542 "\n"
12543 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
12544 return 0;
12545 }
12546 else if ( strcmp(argv[i], "--version") == 0
12547 || strcmp(argv[i], "-V") == 0)
12548 return kbuild_version(argv[0]);
12549 else
12550 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
12551 }
12552
12553 /*
12554 * If no --pipe argument, then assume its standard input.
12555 * We need to carefully replace the CRT stdin with a handle to "nul".
12556 */
12557 if (hPipe == INVALID_HANDLE_VALUE)
12558 {
12559 hPipe = GetStdHandle(STD_INPUT_HANDLE);
12560 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
12561 {
12562 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
12563 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
12564 {
12565 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
12566 if (fdNul >= 0)
12567 {
12568 if (_dup2(fdNul, 0) >= 0)
12569 {
12570 close(fdNul);
12571 hPipe = hDuplicate;
12572 }
12573 else
12574 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12575 }
12576 else
12577 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12578 }
12579 else
12580 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12581 }
12582 else
12583 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
12584 GetFileType(hPipe), GetLastError());
12585 }
12586 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
12587 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
12588 GetFileType(hPipe), GetLastError());
12589 g_hPipe = hPipe;
12590
12591 /*
12592 * Serve the pipe.
12593 */
12594 for (;;)
12595 {
12596 KU32 cbMsg = 0;
12597 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
12598 if (rc == 0)
12599 {
12600 /* Make sure the message length is within sane bounds. */
12601 if ( cbMsg > 4
12602 && cbMsg <= 256*1024*1024)
12603 {
12604 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
12605 if (cbMsg + 4 <= cbMsgBuf)
12606 { /* likely */ }
12607 else
12608 {
12609 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
12610 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
12611 if (!pbMsgBuf)
12612 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
12613 }
12614
12615 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
12616 *(KU32 *)pbMsgBuf = cbMsg;
12617 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
12618 if (rc == 0)
12619 {
12620 const char *psz;
12621
12622 pbMsgBuf[cbMsg] = '\0';
12623 pbMsgBuf[cbMsg + 1] = '\0';
12624 pbMsgBuf[cbMsg + 2] = '\0';
12625 pbMsgBuf[cbMsg + 3] = '\0';
12626
12627 /* The first string after the header is the command. */
12628 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
12629 if ( strcmp(psz, "JOB") == 0
12630 && g_rcCtrlC == 0)
12631 {
12632 struct
12633 {
12634 KI32 rcExitCode;
12635 KU8 bExiting;
12636 KU8 abZero[3];
12637 } Reply;
12638 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
12639 Reply.bExiting = g_fRestart;
12640 Reply.abZero[0] = 0;
12641 Reply.abZero[1] = 0;
12642 Reply.abZero[2] = 0;
12643 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
12644 if ( rc == 0
12645 && !g_fRestart)
12646 {
12647 kwSandboxCleanupLate(&g_Sandbox);
12648 if (g_rcCtrlC == 0)
12649 continue;
12650 }
12651 }
12652 else
12653 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
12654 }
12655 }
12656 else
12657 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
12658 }
12659
12660 /*
12661 * If we're exitting because we're restarting, we need to delay till
12662 * kmk/kSubmit has read the result. Windows documentation says it
12663 * immediately discards pipe buffers once the pipe is broken by the
12664 * server (us). So, We flush the buffer and queues a 1 byte read
12665 * waiting for kSubmit to close the pipe when it receives the
12666 * bExiting = K_TRUE result.
12667 */
12668 if (g_fRestart)
12669 {
12670 KU8 b;
12671 FlushFileBuffers(hPipe);
12672 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
12673 }
12674
12675 CloseHandle(hPipe);
12676#ifdef WITH_LOG_FILE
12677 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12678 CloseHandle(g_hLogFile);
12679#endif
12680 if (getenv("KWORKER_STATS") != NULL)
12681 kwPrintStats();
12682 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
12683 }
12684}
12685
12686
12687/** @page pg_kWorker kSubmit / kWorker
12688 *
12689 * @section sec_kWorker_Motivation Motivation / Inspiration
12690 *
12691 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
12692 * builds on machines "infested" by Anti Virus protection and disk encryption
12693 * software. Build times jumping from 35-40 min to 77-82 min after the machine
12694 * got "infected".
12695 *
12696 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
12697 * mainly a bunch of tiny assembly and C files being compiler a million times.
12698 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
12699 * own toolchain from within the same process, saving a lot of process creation
12700 * and teardown overhead.
12701 *
12702 *
12703 * @section sec_kWorker_kSubmit About kSubmit
12704 *
12705 * When wanting to execute a job in a kWorker instance, it must be submitted
12706 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
12707 * built into kmk and does not exist as an external program. The reason for
12708 * this is that it keep track of the kWorker instances.
12709 *
12710 * The kSubmit command has the --32-bit and --64-bit options for selecting
12711 * between 32-bit and 64-bit worker instance. We generally assume the user of
12712 * the command knows which bit count the executable has, so kSubmit is spared
12713 * the extra work of finding out.
12714 *
12715 * The kSubmit command shares a environment and current directory manipulation
12716 * with the kRedirect command, but not the file redirection. So long no file
12717 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
12718 * hand for tools like OpenWatcom, NASM and YASM which all require environment
12719 * and/or current directory changes to work.
12720 *
12721 * Unlike the kRedirect command, the kSubmit command can also specify an
12722 * internall post command to be executed after the main command succeeds.
12723 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
12724 * information from Microsoft COFF object files and Watcom OMF object files and
12725 * is scheduled to replace kDepIDB.
12726 *
12727 *
12728 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
12729 *
12730 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
12731 * A job request is written by kSubmit and kWorker read, unpacks it and executes
12732 * it. When the job is completed, kWorker writes a short reply with the exit
12733 * code and an internal status indicating whether it is going to restart.
12734 *
12735 * The kWorker intance will reply to kSubmit before completing all the internal
12736 * cleanup work, so as not to delay the next job execution unnecessarily. This
12737 * includes checking its own memory consumption and checking whether it needs
12738 * restarting. So, a decision to restart unfortunately have to wait till after
12739 * the next job has completed. This is a little bit unfortunate if the next job
12740 * requires a lot of memory and kWorker has already leaked/used a lot.
12741 *
12742 *
12743 * @section sec_kWorker_How_Works How kWorker Works
12744 *
12745 * kWorker will load the executable specified by kSubmit into memory and call
12746 * it's entrypoint in a lightly sandbox'ed environment.
12747 *
12748 *
12749 * @subsection ssec_kWorker_Loaing Image loading
12750 *
12751 * kWorker will manually load all the executable images into memory, fix them
12752 * up, and make a copy of the virgin image so it can be restored using memcpy
12753 * the next time it is used.
12754 *
12755 * Imported functions are monitored and replacements used for a few of them.
12756 * These replacements are serve the following purposes:
12757 * - Provide a different command line.
12758 * - Provide a different environment.
12759 * - Intercept process termination.
12760 * - Intercept thread creation (only linker is allowed to create threads).
12761 * - Intercept file reading for caching (header files, ++) as file system
12762 * access is made even slower by anti-virus software.
12763 * - Intercept crypto hash APIs to cache MD5 digests of header files
12764 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
12765 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
12766 * in memory as writing files grows expensive with encryption and
12767 * anti-virus software active.
12768 * - Intercept some file system queries to use the kFsCache instead of
12769 * going to the kernel and slowly worm thru the AV filter driver.
12770 * - Intercept standard output/error and console writes to aggressivly
12771 * buffer the output. The MS CRT does not buffer either when it goes to
12772 * the console, resulting in terrible performance and mixing up output
12773 * with other compile jobs.
12774 * This also allows us to filter out the annoying source file announcements
12775 * by cl.exe.
12776 * - Intercept VirtualAlloc and VirtualFree to prevent
12777 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
12778 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
12779 * the callbacks run after each job.
12780 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
12781 * executables and tools using custom heaps (like the microsoft linker).
12782 * [exectuable images only]
12783 * - Intercept atexit and _onexit registration to be able run them after
12784 * each job instead of crashing as kWorker exits. This also helps avoid
12785 * some leaks. [executable image only]
12786 *
12787 * DLLs falls into two categories, system DLLs which we always load using the
12788 * native loader, and tool DLLs which can be handled like the executable or
12789 * optionally using the native loader. We maintain a hardcoded white listing of
12790 * tool DLLs we trust to load using the native loader.
12791 *
12792 * Imports of natively loaded DLLs are processed too, but we only replace a
12793 * subset of the functions compared to natively loaded excutable and DLL images.
12794 *
12795 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
12796 * This is to speed up job execution.
12797 *
12798 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
12799 * for each job run, but so far this hasn't been necessary.
12800 *
12801 *
12802 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
12803 *
12804 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
12805 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
12806 * intermediate representation between the first (c1/c1xx.dll) and second pass
12807 * (c2.dll).
12808 *
12809 * kWorker helps the compiler as best as it can. Given a little knowledge about
12810 * stable and volatile file system areas, it can do a lot of caching that a
12811 * normal compiler driver cannot easily do when given a single file.
12812 *
12813 *
12814 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
12815 *
12816 * The preprocessor part will open and process header files exactly as they are
12817 * encountered in the source files. If string.h is included by the main source
12818 * and five other header files, it will be searched for (include path), opened,
12819 * read, MD5-summed, and pre-processed six times. The last five times is just a
12820 * waste of time because of the guards or \#pragma once. A smart compiler would
12821 * make a little extra effort and realize this.
12822 *
12823 * kWorker will cache help the preprocessor by remembering places where the
12824 * header was not found with help of kFsCache, and cache the file in memory when
12825 * found. The first part is taken care of by intercepting GetFileAttributesW,
12826 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
12827 * cached, the file is kept open and the CreateFileW call returns a duplicate of
12828 * that handle. An internal handle table is used by ReadFile and CloseFile to
12829 * keep track of intercepted handles (also used for temporary file, temporary
12830 * file mappings, console buffering, and standard out/err buffering).
12831 *
12832 * PS. The header search optimization also comes in handy when cl.exe goes on
12833 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
12834 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
12835 * optionally compile the three pass DLLs as executables during development
12836 * and problem analysis.
12837 *
12838 *
12839 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
12840 *
12841 * The issues of the temporary files is pretty severe on the Dell machine used
12842 * for benchmarking with full AV and encryption. The synthetic benchmark
12843 * improved by 30% when kWorker implemented measures to keep them entirely in
12844 * memory.
12845 *
12846 * kWorker implement these by recognizing the filename pattern in CreateFileW
12847 * and creating/opening the given file as needed. The handle returned is a
12848 * duplicate of the current process, thus giving us a good chance of catching
12849 * API calls we're not intercepting.
12850 *
12851 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
12852 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
12853 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
12854 * UnmapViewOfFile.
12855 *
12856 *
12857 * @section sec_kWorker_Numbers Some measurements.
12858 *
12859 * - r2881 building src/VBox/Runtime:
12860 * - without: 2m01.016388s = 120.016388 s
12861 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
12862 * - r2884 building vbox/debug (r110512):
12863 * - without: 11m14.446609s = 674.446609 s
12864 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
12865 * - r2896 building vbox/debug (r110577):
12866 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
12867 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
12868 * MS Defender as AV):
12869 * - without: 10m24.990389s = 624.990389s
12870 * - with: 08m04.738184s = 484.738184s
12871 * - delta: 624.99s - 484.74s = 140.25s
12872 * - saved: 140.25/624.99 = 22% faster
12873 *
12874 *
12875 * @subsection subsec_kWorker_Early_Numbers Early Experiments
12876 *
12877 * These are some early experiments doing 1024 compilations of
12878 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
12879 * main function:
12880 *
12881 * Skylake (W10/amd64, only stdandard MS defender):
12882 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
12883 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
12884 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
12885 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
12886 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
12887 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
12888 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
12889 *
12890 * Dell (W7/amd64, infected by mcafee):
12891 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
12892 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
12893 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
12894 *
12895 * The command line:
12896 * @code{.cpp}
12897 "\"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"
12898 * @endcode
12899 */
12900
Note: See TracBrowser for help on using the repository browser.

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