VirtualBox

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

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

kWorker: Fixed bad paMemMappings realloc call. (Caused trouble with moc.exe as it is doing lots of memory mappings.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 483.2 KB
Line 
1/* $Id: kWorker.c 3338 2020-04-22 18:40:19Z 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);
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 if (!pImportMod)
2037 {
2038 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2039 pszImport, pMod->pszPath);
2040 break;
2041 }
2042 }
2043 paThunks[iThunk].u1.AddressOfData
2044 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2045 }
2046 break;
2047 }
2048 }
2049 }
2050 }
2051
2052
2053 /* Next import descriptor. */
2054 pImpDesc++;
2055 }
2056
2057
2058 if (fOldProt)
2059 {
2060 DWORD fIgnore = 0;
2061 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2062 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2063 }
2064 }
2065
2066}
2067
2068
2069/**
2070 * Creates a module from a native kLdr module handle.
2071 *
2072 * @returns Module w/ 1 reference on success, NULL on failure.
2073 * @param pLdrMod The native kLdr module.
2074 * @param pszPath The normalized path to the module.
2075 * @param cbPath The module path length with terminator.
2076 * @param uHashPath The module path hash.
2077 * @param fDoReplacements Whether to do import replacements on this
2078 * module.
2079 */
2080static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2081 KBOOL fDoReplacements)
2082{
2083 /*
2084 * Create the entry.
2085 */
2086 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2087 if (pMod)
2088 {
2089 pMod->pwszPath = (wchar_t *)(pMod + 1);
2090 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2091 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2092 pMod->uHashPath = uHashPath;
2093 pMod->cRefs = 1;
2094 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2095 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2096 pMod->fExe = K_FALSE;
2097 pMod->fNative = K_TRUE;
2098 pMod->pLdrMod = pLdrMod;
2099 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2100 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2101 pMod->iCrtSlot = KU8_MAX;
2102 pMod->fNeedReInit = K_FALSE;
2103 pMod->pszMsPdbSrvEndpoint = NULL;
2104 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2105
2106 if (fDoReplacements)
2107 {
2108 DWORD const dwSavedErr = GetLastError();
2109 kwLdrModuleDoNativeImportReplacements(pMod);
2110 SetLastError(dwSavedErr);
2111 }
2112
2113 KW_LOG(("New module: %p LB %#010x %s (native)\n",
2114 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath));
2115 g_cModules++;
2116 return kwLdrModuleLink(pMod);
2117 }
2118 return NULL;
2119}
2120
2121
2122
2123/**
2124 * Creates a module using the native loader.
2125 *
2126 * @returns Module w/ 1 reference on success, NULL on failure.
2127 * @param pszPath The normalized path to the module.
2128 * @param uHashPath The module path hash.
2129 * @param fDoReplacements Whether to do import replacements on this
2130 * module.
2131 */
2132static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2133{
2134 /*
2135 * Open the module and check the type.
2136 */
2137 PKLDRMOD pLdrMod;
2138 int rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2139 if (rc == 0)
2140 {
2141 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, kHlpStrLen(pszPath) + 1,
2142 uHashPath, fDoReplacements);
2143 if (pMod)
2144 return pMod;
2145 kLdrModClose(pLdrMod);
2146 }
2147 return NULL;
2148}
2149
2150
2151/**
2152 * Sets up the quick zero & copy tables for the non-native module.
2153 *
2154 * This is a worker for kwLdrModuleCreateNonNative.
2155 *
2156 * @param pMod The module.
2157 */
2158static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2159{
2160 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2161 KU32 cSegs = pMod->pLdrMod->cSegments;
2162 KU32 iSeg;
2163
2164 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2165 pMod->u.Manual.cQuickCopyChunks = 0;
2166 pMod->u.Manual.cQuickZeroChunks = 0;
2167
2168 for (iSeg = 0; iSeg < cSegs; iSeg++)
2169 switch (paSegs[iSeg].enmProt)
2170 {
2171 case KPROT_READWRITE:
2172 case KPROT_WRITECOPY:
2173 case KPROT_EXECUTE_READWRITE:
2174 case KPROT_EXECUTE_WRITECOPY:
2175 if (paSegs[iSeg].cbMapped)
2176 {
2177 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2178 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2179 {
2180 /*
2181 * Check for trailing zero words.
2182 */
2183 KSIZE cbTrailingZeros;
2184 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2185 && (paSegs[iSeg].cbMapped & 7) == 0
2186 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2187 {
2188 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2189 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2190 KSIZE idxFirstZero = cNatural;
2191 while (idxFirstZero > 0)
2192 if (pauNatural[--idxFirstZero] == 0)
2193 { /* likely */ }
2194 else
2195 {
2196 idxFirstZero++;
2197 break;
2198 }
2199 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2200 if (cbTrailingZeros < 128)
2201 cbTrailingZeros = 0;
2202 }
2203 else
2204 cbTrailingZeros = 0;
2205
2206 /*
2207 * Add quick copy entry.
2208 */
2209 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2210 {
2211 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2212 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2213 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2214 pMod->u.Manual.cQuickCopyChunks = iChunk + 1;
2215 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2216 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2217 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2218 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2219 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2220 }
2221
2222 /*
2223 * Add quick zero entry.
2224 */
2225 if (cbTrailingZeros)
2226 {
2227 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2228 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2229 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2230 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2231 pMod->u.Manual.cQuickZeroChunks = iZero + 1;
2232 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2233 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2234 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2235 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2236 }
2237 }
2238 else
2239 {
2240 /*
2241 * We're out of quick copy table entries, so just copy the whole darn thing.
2242 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2243 */
2244 kHlpAssertFailed();
2245 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2246 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2247 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2248 pMod->u.Manual.cQuickCopyChunks = 1;
2249 KWLDR_LOG(("Quick copy not possible!\n"));
2250 return;
2251 }
2252 }
2253 break;
2254
2255 default:
2256 break;
2257 }
2258}
2259
2260
2261/**
2262 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2263 *
2264 * @param hDll The DLL handle.
2265 * @param idxTls The allocated TLS index.
2266 * @param ppfnTlsCallback Pointer to the TLS callback table entry.
2267 */
2268__declspec(dllexport) void kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, PIMAGE_TLS_CALLBACK *ppfnTlsCallback)
2269{
2270 /*
2271 * Do the module initialization thing first.
2272 */
2273 PKWMODULE pMod = g_pModPendingTlsAlloc;
2274 if (pMod)
2275 {
2276 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2277 LIST_ENTRY *pHead;
2278 LIST_ENTRY *pCur;
2279
2280 pMod->u.Manual.idxTls = idxTls;
2281 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2282
2283 /*
2284 * Try sabotage the DLL name so we can load this module again.
2285 */
2286 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2287 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2288 {
2289 LDR_DATA_TABLE_ENTRY *pMte;
2290 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2291 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2292 {
2293 PUNICODE_STRING pStr = &pMte->FullDllName;
2294 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2295 pStr->Buffer[--off]++;
2296 pStr->Buffer[--off]++;
2297 pStr->Buffer[--off]++;
2298 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2299 break;
2300 }
2301 }
2302 }
2303}
2304
2305
2306/**
2307 * Allocates and initializes TLS variables.
2308 *
2309 * @returns 0 on success, non-zero failure.
2310 * @param pMod The module.
2311 */
2312static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2313{
2314 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2315 IMAGE_NT_HEADERS const *pNtHdrs;
2316 IMAGE_DATA_DIRECTORY const *pTlsDir;
2317
2318 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2319 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2320 else
2321 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2322 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2323
2324 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2325 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2326 {
2327 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2328 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2329 KU32 iEntry;
2330 KUPTR offIndex;
2331 KUPTR offCallbacks;
2332 KUPTR const *puCallbacks;
2333 KSIZE cbData;
2334 const wchar_t *pwszTlsDll;
2335 HMODULE hmodTlsDll;
2336
2337 /*
2338 * Check and log.
2339 */
2340 for (iEntry = 0; iEntry < cEntries; iEntry++)
2341 {
2342 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2343 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2344 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2345 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2346 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2347 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2348 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2349
2350 if (offIndex >= pMod->cbImage)
2351 {
2352 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2353 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2354 return -1;
2355 }
2356 if (offCallbacks >= pMod->cbImage)
2357 {
2358 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2359 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2360 return -1;
2361 }
2362 while (*puCallbacks != 0)
2363 {
2364 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2365 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2366 puCallbacks++;
2367 }
2368 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2369 {
2370 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2371 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2372 return -1;
2373 }
2374 }
2375
2376 if (cEntries > 1)
2377 {
2378 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2379 return -1;
2380 }
2381
2382 /*
2383 * Make the allocation by loading a new instance of one of the TLS dlls.
2384 * The DLL will make a call to
2385 */
2386 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2387 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2388 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2389 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2390 if (cbData <= 1024)
2391 pwszTlsDll = L"kWorkerTls1K.dll";
2392 else if (cbData <= 65536)
2393 pwszTlsDll = L"kWorkerTls64K.dll";
2394 else if (cbData <= 524288)
2395 pwszTlsDll = L"kWorkerTls512K.dll";
2396 else
2397 {
2398 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2399 return -1;
2400 }
2401
2402 pMod->u.Manual.idxTls = KU32_MAX;
2403 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2404 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2405 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2406 pMod->u.Manual.cTlsCallbacks = 0;
2407 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2408 pMod->u.Manual.cTlsCallbacks++;
2409 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2410
2411 g_pModPendingTlsAlloc = pMod;
2412 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2413 g_pModPendingTlsAlloc = NULL;
2414 if (hmodTlsDll == NULL)
2415 {
2416 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2417 return -1;
2418 }
2419 if (pMod->u.Manual.idxTls == KU32_MAX)
2420 {
2421 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2422 return -1;
2423 }
2424
2425 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2426 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x\n",
2427 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData));
2428 }
2429 return 0;
2430}
2431
2432
2433/**
2434 * Creates a module using the our own loader.
2435 *
2436 * @returns Module w/ 1 reference on success, NULL on failure.
2437 * @param pszPath The normalized path to the module.
2438 * @param uHashPath The module path hash.
2439 * @param fExe K_TRUE if this is an executable image, K_FALSE
2440 * if not. Executable images does not get entered
2441 * into the global module table.
2442 * @param pExeMod The executable module of the process (for
2443 * resolving imports). NULL if fExe is set.
2444 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2445 */
2446static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2447 PKWMODULE pExeMod, const char *pszSearchPath)
2448{
2449 /*
2450 * Open the module and check the type.
2451 */
2452 PKLDRMOD pLdrMod;
2453 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2454 if (rc == 0)
2455 {
2456 switch (pLdrMod->enmType)
2457 {
2458 case KLDRTYPE_EXECUTABLE_FIXED:
2459 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2460 case KLDRTYPE_EXECUTABLE_PIC:
2461 if (!fExe)
2462 rc = KERR_GENERAL_FAILURE;
2463 break;
2464
2465 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2466 case KLDRTYPE_SHARED_LIBRARY_PIC:
2467 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2468 if (fExe)
2469 rc = KERR_GENERAL_FAILURE;
2470 break;
2471
2472 default:
2473 rc = KERR_GENERAL_FAILURE;
2474 break;
2475 }
2476 if (rc == 0)
2477 {
2478 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2479 if (cImports >= 0)
2480 {
2481 /*
2482 * Create the entry.
2483 */
2484 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2485 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2486 + sizeof(pMod) * cImports
2487 + cbPath
2488 + cbPath * 2 * sizeof(wchar_t));
2489 if (pMod)
2490 {
2491 KBOOL fFixed;
2492
2493 pMod->cRefs = 1;
2494 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2495 pMod->uHashPath = uHashPath;
2496 pMod->fExe = fExe;
2497 pMod->fNative = K_FALSE;
2498 pMod->pLdrMod = pLdrMod;
2499 pMod->iCrtSlot = KU8_MAX;
2500 pMod->fNeedReInit = K_FALSE;
2501 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2502 pMod->pszMsPdbSrvEndpoint = NULL;
2503 pMod->u.Manual.cImpMods = (KU32)cImports;
2504#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2505 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2506#endif
2507 pMod->u.Manual.fUseLdBuf = K_FALSE;
2508 pMod->u.Manual.fCanDoQuick = K_FALSE;
2509 pMod->u.Manual.cQuickZeroChunks = 0;
2510 pMod->u.Manual.cQuickCopyChunks = 0;
2511 pMod->u.Manual.idxTls = KU32_MAX;
2512 pMod->u.Manual.offTlsInitData = KU32_MAX;
2513 pMod->u.Manual.cbTlsInitData = 0;
2514 pMod->u.Manual.cbTlsAlloc = 0;
2515 pMod->u.Manual.cTlsCallbacks = 0;
2516 pMod->u.Manual.offTlsCallbacks = 0;
2517 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2518 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2519 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2520 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2521
2522 /*
2523 * Figure out where to load it and get memory there.
2524 */
2525 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2526 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2527 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2528 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2529 if ( !fFixed
2530 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2531 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2532 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2533 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2534 else
2535 pMod->u.Manual.fUseLdBuf = K_TRUE;
2536 if (rc == 0)
2537 {
2538 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2539 if (rc == 0)
2540 {
2541 KI32 iImp;
2542
2543 /*
2544 * Link the module (unless it's an executable image) and process the imports.
2545 */
2546 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2547 kwLdrModuleLink(pMod);
2548 KW_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2549 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2550 KW_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2551 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2552
2553 for (iImp = 0; iImp < cImports; iImp++)
2554 {
2555 char szName[1024];
2556 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2557 if (rc == 0)
2558 {
2559 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2560 &pMod->u.Manual.apImpMods[iImp]);
2561 if (rc == 0)
2562 continue;
2563 }
2564 break;
2565 }
2566
2567 if (rc == 0)
2568 {
2569 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2570 kwLdrModuleGetImportCallback, pMod);
2571 if (rc == 0)
2572 {
2573#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2574 /*
2575 * Find the function table. No validation here because the
2576 * loader did that already, right...
2577 */
2578 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2579 IMAGE_NT_HEADERS const *pNtHdrs;
2580 IMAGE_DATA_DIRECTORY const *pXcptDir;
2581 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2582 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2583 else
2584 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2585 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2586 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2587 if (pXcptDir->Size > 0)
2588 {
2589 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2590 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2591 == pXcptDir->Size);
2592 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2593 }
2594 else
2595 {
2596 pMod->u.Manual.cFunctions = 0;
2597 pMod->u.Manual.paFunctions = NULL;
2598 }
2599#endif
2600
2601 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2602
2603 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2604 if (rc == 0)
2605 {
2606 /*
2607 * Final finish.
2608 */
2609 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2610 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2611 g_cModules++;
2612 g_cNonNativeModules++;
2613 return pMod;
2614 }
2615 }
2616 else
2617 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
2618 }
2619
2620 kwLdrModuleRelease(pMod);
2621 return NULL;
2622 }
2623
2624 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2625 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2626 }
2627 else if (fFixed)
2628 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
2629 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
2630 else
2631 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2632 }
2633 }
2634 }
2635 kLdrModClose(pLdrMod);
2636 }
2637 else
2638 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
2639 return NULL;
2640}
2641
2642
2643/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
2644static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
2645 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
2646{
2647 PKWMODULE pCurMod = (PKWMODULE)pvUser;
2648 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
2649 int rc;
2650 K_NOREF(pMod);
2651
2652 if (pImpMod->fNative)
2653 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
2654 iSymbol, pchSymbol, cchSymbol, pszVersion,
2655 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2656 puValue, pfKind);
2657 else
2658 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
2659 iSymbol, pchSymbol, cchSymbol, pszVersion,
2660 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2661 puValue, pfKind);
2662 if (rc == 0)
2663 {
2664 KU32 i = g_cSandboxReplacements;
2665 while (i-- > 0)
2666 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
2667 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
2668 {
2669 if ( !g_aSandboxReplacements[i].pszModule
2670 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
2671 {
2672 if ( pCurMod->fExe
2673 || !g_aSandboxReplacements[i].fOnlyExe)
2674 {
2675 KW_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
2676 *puValue = g_aSandboxReplacements[i].pfnReplacement;
2677 }
2678 break;
2679 }
2680 }
2681 }
2682
2683 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
2684 KW_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
2685 return rc;
2686
2687}
2688
2689
2690/**
2691 * Gets the main entrypoint for a module.
2692 *
2693 * @returns 0 on success, KERR on failure
2694 * @param pMod The module.
2695 * @param puAddrMain Where to return the address.
2696 */
2697static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
2698{
2699 KLDRADDR uLdrAddrMain;
2700 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
2701 if (rc == 0)
2702 {
2703 *puAddrMain = (KUPTR)uLdrAddrMain;
2704 return 0;
2705 }
2706 return rc;
2707}
2708
2709
2710/**
2711 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
2712 *
2713 * @returns K_TRUE/K_FALSE.
2714 * @param pszFilename The filename (no path).
2715 * @param enmLocation The location.
2716 */
2717static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
2718{
2719 if (enmLocation != KWLOCATION_SYSTEM32)
2720 return K_TRUE;
2721 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2722 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2723 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2724}
2725
2726
2727/**
2728 * Lazily initializes the g_pWinSys32 variable.
2729 */
2730static PKFSDIR kwLdrResolveWinSys32(void)
2731{
2732 KFSLOOKUPERROR enmError;
2733 PKFSDIR pWinSys32;
2734
2735 /* Get the path first. */
2736 char szSystem32[MAX_PATH];
2737 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
2738 {
2739 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
2740 strcpy(szSystem32, "C:\\Windows\\System32");
2741 }
2742
2743 /* Look it up and verify it. */
2744 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
2745 if (pWinSys32)
2746 {
2747 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
2748 {
2749 g_pWinSys32 = pWinSys32;
2750 return pWinSys32;
2751 }
2752
2753 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
2754 }
2755 else
2756 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
2757 return NULL;
2758}
2759
2760
2761/**
2762 * Whether we can load this DLL natively or not.
2763 *
2764 * @returns K_TRUE/K_FALSE.
2765 * @param pszFilename The filename (no path).
2766 * @param enmLocation The location.
2767 * @param pszFullPath The full filename and path.
2768 */
2769static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
2770{
2771 if (enmLocation == KWLOCATION_SYSTEM32)
2772 return K_TRUE;
2773 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
2774 return K_TRUE;
2775
2776 /* If the location is unknown, we must check if it's some dynamic loading
2777 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
2778 if (enmLocation == KWLOCATION_UNKNOWN)
2779 {
2780 PKFSDIR pWinSys32 = g_pWinSys32;
2781 if (!pWinSys32)
2782 pWinSys32 = kwLdrResolveWinSys32();
2783 if (pWinSys32)
2784 {
2785 KFSLOOKUPERROR enmError;
2786 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
2787 if (pFsObj)
2788 {
2789 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
2790 kFsCacheObjRelease(g_pFsCache, pFsObj);
2791 if (fInWinSys32)
2792 return K_TRUE;
2793 }
2794 }
2795 }
2796
2797 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2798 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2799 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2800}
2801
2802
2803/**
2804 * Check if the path leads to a regular file (that exists).
2805 *
2806 * @returns K_TRUE / K_FALSE
2807 * @param pszPath Path to the file to check out.
2808 */
2809static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
2810{
2811 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
2812 KSIZE cchPath = kHlpStrLen(pszPath);
2813 if ( cchPath > 3
2814 && pszPath[cchPath - 4] == '.'
2815 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
2816 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
2817 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
2818 {
2819 KFSLOOKUPERROR enmError;
2820 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
2821 if (pFsObj)
2822 {
2823 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
2824 kFsCacheObjRelease(g_pFsCache, pFsObj);
2825 return fRc;
2826 }
2827 }
2828 else
2829 {
2830 BirdStat_T Stat;
2831 int rc = birdStatFollowLink(pszPath, &Stat);
2832 if (rc == 0)
2833 {
2834 if (S_ISREG(Stat.st_mode))
2835 return K_TRUE;
2836 }
2837 }
2838 return K_FALSE;
2839}
2840
2841
2842/**
2843 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
2844 *
2845 * If the file exists, we consult the module hash table before trying to load it
2846 * off the disk.
2847 *
2848 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
2849 * failure.
2850 * @param pszPath The name of the import module.
2851 * @param enmLocation The location we're searching. This is used in
2852 * the heuristics for determining if we can use the
2853 * native loader or need to sandbox the DLL.
2854 * @param pExe The executable (optional).
2855 * @param pszSearchPath The PATH to search (optional).
2856 */
2857static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
2858{
2859 /*
2860 * Does the file exists and is it a regular file?
2861 */
2862 if (kwLdrModuleIsRegularFile(pszPath))
2863 {
2864 /*
2865 * Yes! Normalize it and look it up in the hash table.
2866 */
2867 char szNormPath[1024];
2868 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
2869 if (rc == 0)
2870 {
2871 const char *pszName;
2872 KU32 const uHashPath = kwStrHash(szNormPath);
2873 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2874 PKWMODULE pMod = g_apModules[idxHash];
2875 if (pMod)
2876 {
2877 do
2878 {
2879 if ( pMod->uHashPath == uHashPath
2880 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
2881 return kwLdrModuleRetain(pMod);
2882 pMod = pMod->pNextHash;
2883 } while (pMod);
2884 }
2885
2886 /*
2887 * Not in the hash table, so we have to load it from scratch.
2888 */
2889 pszName = kHlpGetFilename(szNormPath);
2890 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
2891 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
2892 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
2893 else
2894 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
2895 if (pMod)
2896 return pMod;
2897 return (PKWMODULE)~(KUPTR)0;
2898 }
2899 }
2900 return NULL;
2901}
2902
2903
2904/**
2905 * Gets a reference to the module by the given name.
2906 *
2907 * We must do the search path thing, as our hash table may multiple DLLs with
2908 * the same base name due to different tools version and similar. We'll use a
2909 * modified search sequence, though. No point in searching the current
2910 * directory for instance.
2911 *
2912 * @returns 0 on success, KERR on failure.
2913 * @param pszName The name of the import module.
2914 * @param pExe The executable (optional).
2915 * @param pImporter The module doing the importing (optional).
2916 * @param pszSearchPath The PATH to search (optional).
2917 * @param ppMod Where to return the module pointer w/ reference.
2918 */
2919static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
2920 const char *pszSearchPath, PKWMODULE *ppMod)
2921{
2922 KSIZE const cchName = kHlpStrLen(pszName);
2923 char szPath[1024];
2924 char *psz;
2925 PKWMODULE pMod = NULL;
2926 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
2927 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
2928
2929
2930 /* The import path. */
2931 if (pMod == NULL && pImporter != NULL)
2932 {
2933 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
2934 return KERR_BUFFER_OVERFLOW;
2935
2936 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
2937 if (fNeedSuffix)
2938 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2939 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
2940 }
2941
2942 /* Application directory first. */
2943 if (pMod == NULL && pExe != NULL && pExe != pImporter)
2944 {
2945 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
2946 return KERR_BUFFER_OVERFLOW;
2947 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
2948 if (fNeedSuffix)
2949 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2950 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
2951 }
2952
2953 /* The windows directory. */
2954 if (pMod == NULL)
2955 {
2956 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
2957 if ( cchDir <= 2
2958 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
2959 return KERR_BUFFER_OVERFLOW;
2960 szPath[cchDir++] = '\\';
2961 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
2962 if (fNeedSuffix)
2963 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2964 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2965 }
2966
2967 /* The path. */
2968 if ( pMod == NULL
2969 && pszSearchPath)
2970 {
2971 const char *pszCur = pszSearchPath;
2972 while (*pszCur != '\0')
2973 {
2974 /* Find the end of the component */
2975 KSIZE cch = 0;
2976 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
2977 cch++;
2978
2979 if ( cch > 0 /* wrong, but whatever */
2980 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
2981 {
2982 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
2983 if ( szPath[cch - 1] != ':'
2984 && szPath[cch - 1] != '/'
2985 && szPath[cch - 1] != '\\')
2986 *pszDst++ = '\\';
2987 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
2988 if (fNeedSuffix)
2989 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
2990 *pszDst = '\0';
2991
2992 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2993 if (pMod)
2994 break;
2995 }
2996
2997 /* Advance */
2998 pszCur += cch;
2999 while (*pszCur == ';')
3000 pszCur++;
3001 }
3002 }
3003
3004 /* Return. */
3005 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3006 {
3007 *ppMod = pMod;
3008 return 0;
3009 }
3010 *ppMod = NULL;
3011 return KERR_GENERAL_FAILURE;
3012}
3013
3014
3015/**
3016 * Creates a CRT slot for the given module.
3017 *
3018 * @returns 0 on success, non-zero on failure.
3019 * @param pModule The module.
3020 */
3021static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3022{
3023 KSIZE iSlot;
3024 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3025 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3026 if (g_aCrtSlots[iSlot].pModule == NULL)
3027 {
3028 KLDRADDR uAddr;
3029 int rc;
3030
3031 /* Do the linking: */
3032 g_aCrtSlots[iSlot].pModule = pModule;
3033 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3034 pModule->iCrtSlot = (KU8)iSlot;
3035
3036 /* resolve symbols: */
3037 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3038 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3039 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3040 if (rc != 0)
3041 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3042
3043 return 0;
3044 }
3045 kwErrPrintf("Out of CRT slots!\n");
3046 return KERR_NO_MEMORY;
3047}
3048
3049
3050/**
3051 * Locates the module structure for an already loaded native module.
3052 *
3053 * This will create a module structure if needed.
3054 *
3055 * @returns Pointer to the module structure on success, NULL on failure.
3056 * @param pszName The name of the module.
3057 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3058 */
3059static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot)
3060{
3061 /*
3062 * Locate the module and get a normalized path for it.
3063 */
3064 HANDLE hModule = GetModuleHandleA(pszName);
3065 if (hModule)
3066 {
3067 char szModPath[1024];
3068 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3069 {
3070 char szNormPath[1024];
3071 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3072 if (rc == 0)
3073 {
3074 /*
3075 * Hash the path and look it up.
3076 */
3077 KU32 uHashPath;
3078 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3079 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3080 PKWMODULE pMod = g_apModules[idxHash];
3081 if (pMod)
3082 {
3083 do
3084 {
3085 if ( pMod->uHashPath == uHashPath
3086 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3087 {
3088 kwLdrModuleRetain(pMod);
3089 break;
3090 }
3091 pMod = pMod->pNextHash;
3092 } while (pMod);
3093 }
3094
3095 /*
3096 * If not in the hash table, so create a module entry.
3097 */
3098 if (!pMod)
3099 {
3100 PKLDRMOD pLdrMod;
3101 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3102 if (rc == 0)
3103 {
3104 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3105 K_FALSE /*fDoReplacements*/);
3106 if (!pMod)
3107 {
3108 kLdrModClose(pLdrMod);
3109 kwErrPrintf("out of memory\n");
3110 }
3111 }
3112 else
3113 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
3114 }
3115 if (pMod)
3116 {
3117 /*
3118 * Create a CRT slot for the module if necessary.
3119 */
3120 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3121 return pMod;
3122 rc = kwLdrModuleCreateCrtSlot(pMod);
3123 if (rc == 0)
3124 return pMod;
3125 kwLdrModuleRelease(pMod);
3126 }
3127 }
3128 else
3129 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszName, GetLastError());
3130 }
3131 else
3132 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszName, GetLastError());
3133 }
3134 else
3135 kwErrPrintf("Module '%s' was not found by GetModuleHandleA!\n", pszName);
3136 return NULL;
3137}
3138
3139
3140/**
3141 * Does the TLS memory initialization for a module on the current thread.
3142 *
3143 * @returns 0 on success, error on failure.
3144 * @param pMod The module.
3145 */
3146static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3147{
3148 if (pMod->u.Manual.idxTls != KU32_MAX)
3149 {
3150 PTEB pTeb = NtCurrentTeb();
3151 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3152 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3153 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3154 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3155 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3156 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3157 if (pMod->u.Manual.cbTlsInitData)
3158 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3159 }
3160 return 0;
3161}
3162
3163
3164/**
3165 * Does the TLS callbacks for a module.
3166 *
3167 * @param pMod The module.
3168 * @param dwReason The callback reason.
3169 */
3170static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3171{
3172 if (pMod->u.Manual.cTlsCallbacks)
3173 {
3174 PIMAGE_TLS_CALLBACK *pCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3175 do
3176 {
3177 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *pCallback, pMod->hOurMod, dwReason));
3178 (*pCallback)(pMod->hOurMod, dwReason, 0);
3179 } while (*++pCallback);
3180 }
3181}
3182
3183
3184/**
3185 * Does module initialization starting at @a pMod.
3186 *
3187 * This is initially used on the executable. Later it is used by the
3188 * LoadLibrary interceptor.
3189 *
3190 * @returns 0 on success, error on failure.
3191 * @param pMod The module to initialize.
3192 */
3193static int kwLdrModuleInitTree(PKWMODULE pMod)
3194{
3195 int rc = 0;
3196 if (!pMod->fNative)
3197 {
3198 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3199 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3200
3201 /*
3202 * Need to copy bits?
3203 */
3204 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3205 {
3206 if (pMod->u.Manual.fUseLdBuf)
3207 {
3208#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3209 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3210 {
3211 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3212 kHlpAssert(fRc); K_NOREF(fRc);
3213 }
3214#endif
3215 g_pModPrevInLdBuf = g_pModInLdBuf;
3216 g_pModInLdBuf = pMod;
3217 }
3218
3219 /* Do quick zeroing and copying when we can. */
3220 pMod->u.Manual.fCanDoQuick = K_FALSE;
3221 if ( pMod->u.Manual.fCanDoQuick
3222 && ( !pMod->u.Manual.fUseLdBuf
3223 || g_pModPrevInLdBuf == pMod))
3224 {
3225 /* Zero first. */
3226 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3227 switch (pMod->u.Manual.cQuickZeroChunks)
3228 {
3229 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3230 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3231 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3232 case 0: break;
3233 }
3234
3235 /* Then copy. */
3236 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3237 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3238 switch (pMod->u.Manual.cQuickCopyChunks)
3239 {
3240 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3241 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3242 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3243 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3244 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3245 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3246 case 0: break;
3247 }
3248 }
3249 /* Must copy the whole image. */
3250 else
3251 {
3252 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3253 pMod->u.Manual.fCanDoQuick = K_TRUE;
3254 }
3255 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3256 }
3257
3258#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3259 /*
3260 * Need to register function table?
3261 */
3262 if ( !pMod->u.Manual.fRegisteredFunctionTable
3263 && pMod->u.Manual.cFunctions > 0)
3264 {
3265 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3266 pMod->u.Manual.cFunctions,
3267 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3268 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3269 }
3270#endif
3271
3272
3273 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3274 {
3275 /*
3276 * Must do imports first, but mark our module as being initialized to avoid
3277 * endless recursion should there be a dependency loop.
3278 */
3279 KSIZE iImp;
3280 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3281
3282 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3283 {
3284 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3285 if (rc != 0)
3286 return rc;
3287 }
3288
3289 /* Do TLS allocations for module init? */
3290 rc = kwLdrCallTlsAllocateAndInit(pMod);
3291 if (rc != 0)
3292 return rc;
3293 if (pMod->u.Manual.cTlsCallbacks > 0)
3294 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3295
3296 /* Finally call the entry point. */
3297 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3298 if (rc == 0)
3299 pMod->u.Manual.enmState = KWMODSTATE_READY;
3300 else
3301 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3302 }
3303 }
3304 /*
3305 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3306 * _MSPDBSRV_ENDPOINT_ changes value.
3307 */
3308 else if (pMod->fNeedReInit)
3309 {
3310 int rc2;
3311 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3312 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3313 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3314 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3315 if (!rc && !rc2)
3316 { /* likely */ }
3317 else
3318 {
3319 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3320 if (rc2 && !rc)
3321 rc = rc2;
3322 }
3323 pMod->fNeedReInit = K_FALSE;
3324 }
3325 return rc;
3326}
3327
3328
3329/**
3330 * Looks up a module handle for a tool.
3331 *
3332 * @returns Referenced loader module on success, NULL on if not found.
3333 * @param pTool The tool.
3334 * @param hmod The module handle.
3335 */
3336static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3337{
3338 KUPTR const uHMod = (KUPTR)hmod;
3339 PKWMODULE *papMods;
3340 KU32 iEnd;
3341 KU32 i;
3342 PKWDYNLOAD pDynLoad;
3343
3344 /* The executable. */
3345 if ( hmod == NULL
3346 || pTool->u.Sandboxed.pExe->hOurMod == hmod)
3347 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3348
3349 /*
3350 * Binary lookup using the module table.
3351 */
3352 papMods = pTool->u.Sandboxed.papModules;
3353 iEnd = pTool->u.Sandboxed.cModules;
3354 if (iEnd)
3355 {
3356 KU32 iStart = 0;
3357 i = iEnd / 2;
3358 for (;;)
3359 {
3360 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3361 if (uHMod < uHModThis)
3362 {
3363 iEnd = i--;
3364 if (iStart <= i)
3365 { }
3366 else
3367 break;
3368 }
3369 else if (uHMod != uHModThis)
3370 {
3371 iStart = ++i;
3372 if (i < iEnd)
3373 { }
3374 else
3375 break;
3376 }
3377 else
3378 return kwLdrModuleRetain(papMods[i]);
3379
3380 i = iStart + (iEnd - iStart) / 2;
3381 }
3382
3383#ifndef NDEBUG
3384 iStart = pTool->u.Sandboxed.cModules;
3385 while (--iStart > 0)
3386 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3387 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3388#endif
3389 }
3390
3391 /*
3392 * Dynamically loaded images.
3393 */
3394 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3395 if (pDynLoad->hmod == hmod)
3396 {
3397 if (pDynLoad->pMod)
3398 return kwLdrModuleRetain(pDynLoad->pMod);
3399 KWFS_TODO();
3400 return NULL;
3401 }
3402
3403 return NULL;
3404}
3405
3406/**
3407 * Adds the given module to the tool import table.
3408 *
3409 * @returns 0 on success, non-zero on failure.
3410 * @param pTool The tool.
3411 * @param pMod The module.
3412 */
3413static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3414{
3415 /*
3416 * Binary lookup. Locating the right slot for it, return if already there.
3417 */
3418 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3419 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3420 KU32 iEnd = pTool->u.Sandboxed.cModules;
3421 KU32 i;
3422 if (iEnd)
3423 {
3424 KU32 iStart = 0;
3425 i = iEnd / 2;
3426 for (;;)
3427 {
3428 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3429 if (uHMod < uHModThis)
3430 {
3431 iEnd = i;
3432 if (iStart < i)
3433 { }
3434 else
3435 break;
3436 }
3437 else if (uHMod != uHModThis)
3438 {
3439 iStart = ++i;
3440 if (i < iEnd)
3441 { }
3442 else
3443 break;
3444 }
3445 else
3446 {
3447 /* Already there in the table. */
3448 return 0;
3449 }
3450
3451 i = iStart + (iEnd - iStart) / 2;
3452 }
3453#ifndef NDEBUG
3454 iStart = pTool->u.Sandboxed.cModules;
3455 while (--iStart > 0)
3456 {
3457 kHlpAssert(papMods[iStart] != pMod);
3458 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3459 }
3460 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3461 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod > uHMod);
3462#endif
3463 }
3464 else
3465 i = 0;
3466
3467 /*
3468 * Grow the table?
3469 */
3470 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3471 {
3472 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3473 if (!pvNew)
3474 return KERR_NO_MEMORY;
3475 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3476 }
3477
3478 /* Insert it. */
3479 if (i != pTool->u.Sandboxed.cModules)
3480 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3481 papMods[i] = kwLdrModuleRetain(pMod);
3482 pTool->u.Sandboxed.cModules++;
3483 KW_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3484 return 0;
3485}
3486
3487
3488/**
3489 * Adds the given module and all its imports to the
3490 *
3491 * @returns 0 on success, non-zero on failure.
3492 * @param pTool The tool.
3493 * @param pMod The module.
3494 */
3495static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
3496{
3497 int rc = kwToolAddModule(pTool, pMod);
3498 if (!pMod->fNative && rc == 0)
3499 {
3500 KSIZE iImp = pMod->u.Manual.cImpMods;
3501 while (iImp-- > 0)
3502 {
3503 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
3504 if (rc == 0)
3505 { }
3506 else
3507 break;
3508 }
3509 }
3510 return 0;
3511}
3512
3513
3514/**
3515 * Creates a tool entry and inserts it.
3516 *
3517 * @returns Pointer to the tool entry. NULL on failure.
3518 * @param pToolFsObj The file object of the tool. The created tool
3519 * will be associated with it.
3520 *
3521 * A reference is donated by the caller and must be
3522 * released.
3523 * @param pszSearchPath The PATH environment variable value, or NULL.
3524 */
3525static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
3526{
3527 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
3528 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
3529 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
3530 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
3531 if (pTool)
3532 {
3533 KBOOL fRc;
3534 pTool->pwszPath = (wchar_t const *)(pTool + 1);
3535 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
3536 kHlpAssert(fRc); K_NOREF(fRc);
3537
3538 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
3539 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
3540 kHlpAssert(fRc);
3541
3542 pTool->enmType = KWTOOLTYPE_SANDBOXED;
3543 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
3544 NULL /*pEexeMod*/, pszSearchPath);
3545 if (pTool->u.Sandboxed.pExe)
3546 {
3547 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
3548 if (rc == 0)
3549 {
3550 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
3551 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
3552 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
3553 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
3554 else
3555 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
3556 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
3557 }
3558 else
3559 {
3560 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
3561 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
3562 pTool->u.Sandboxed.pExe = NULL;
3563 pTool->enmType = KWTOOLTYPE_EXEC;
3564 }
3565 }
3566 else
3567 pTool->enmType = KWTOOLTYPE_EXEC;
3568
3569 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3570 g_cTools++;
3571 return pTool;
3572 }
3573 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3574 return NULL;
3575}
3576
3577
3578/**
3579 * Looks up the given tool, creating a new tool table entry if necessary.
3580 *
3581 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
3582 * @param pszExe The executable for the tool (not normalized).
3583 * @param cEnvVars Number of environment varibles.
3584 * @param papszEnvVars Environment variables. For getting the PATH.
3585 */
3586static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
3587{
3588 /*
3589 * We associate the tools instances with the file system objects.
3590 *
3591 * We'd like to do the lookup without invaliding the volatile parts of the
3592 * cache, thus the double lookup here. The cache gets invalidate later on.
3593 */
3594 KFSLOOKUPERROR enmError;
3595 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3596 if ( !pToolFsObj
3597 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
3598 {
3599 kFsCacheInvalidateCustomBoth(g_pFsCache);
3600 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3601 }
3602 if (pToolFsObj)
3603 {
3604 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
3605 {
3606 const char *pszSearchPath;
3607 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
3608 if (pTool)
3609 {
3610 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3611 return pTool;
3612 }
3613
3614 /*
3615 * Need to create a new tool.
3616 */
3617 pszSearchPath = NULL;
3618 while (cEnvVars-- > 0)
3619 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
3620 {
3621 pszSearchPath = &papszEnvVars[cEnvVars][5];
3622 break;
3623 }
3624
3625 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
3626 if (pTool)
3627 return pTool;
3628
3629 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
3630 }
3631 else
3632 {
3633 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3634 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
3635 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
3636 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
3637 }
3638 }
3639 else
3640 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
3641 return NULL;
3642}
3643
3644
3645
3646/*
3647 *
3648 * File system cache.
3649 * File system cache.
3650 * File system cache.
3651 *
3652 */
3653
3654
3655/**
3656 * This is for kDep.
3657 */
3658int kwFsPathExists(const char *pszPath)
3659{
3660 BirdTimeSpec_T TsIgnored;
3661 KFSLOOKUPERROR enmError;
3662 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3663 if (pFsObj)
3664 {
3665 kFsCacheObjRelease(g_pFsCache, pFsObj);
3666 return 1;
3667 }
3668 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
3669}
3670
3671
3672/* duplicated in dir-nt-bird.c */
3673void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
3674{
3675 KFSLOOKUPERROR enmError;
3676 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
3677 if (pPathObj)
3678 {
3679 KSIZE off = pPathObj->cchParent;
3680 if (off > 0)
3681 {
3682 KSIZE offEnd = off + pPathObj->cchName;
3683 if (offEnd < cbFull)
3684 {
3685 PKFSDIR pAncestor;
3686
3687 pszFull[off + pPathObj->cchName] = '\0';
3688 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
3689
3690 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3691 {
3692 kHlpAssert(off > 1);
3693 kHlpAssert(pAncestor != NULL);
3694 kHlpAssert(pAncestor->Obj.cchName > 0);
3695 pszFull[--off] = '/';
3696 off -= pAncestor->Obj.cchName;
3697 kHlpAssert(pAncestor->Obj.cchParent == off);
3698 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3699 }
3700 kFsCacheObjRelease(g_pFsCache, pPathObj);
3701 return;
3702 }
3703 }
3704 else
3705 {
3706 if ((size_t)pPathObj->cchName + 1 < cbFull)
3707 {
3708 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
3709 pszFull[pPathObj->cchName] = '/';
3710 pszFull[pPathObj->cchName + 1] = '\0';
3711
3712 kFsCacheObjRelease(g_pFsCache, pPathObj);
3713 return;
3714 }
3715 }
3716
3717 /* do fallback. */
3718 kHlpAssertFailed();
3719 kFsCacheObjRelease(g_pFsCache, pPathObj);
3720 }
3721
3722 nt_fullpath(pszPath, pszFull, cbFull);
3723}
3724
3725
3726/**
3727 * Helper for getting the extension of a UTF-16 path.
3728 *
3729 * @returns Pointer to the extension or the terminator.
3730 * @param pwszPath The path.
3731 * @param pcwcExt Where to return the length of the extension.
3732 */
3733static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
3734{
3735 wchar_t const *pwszName = pwszPath;
3736 wchar_t const *pwszExt = NULL;
3737 for (;;)
3738 {
3739 wchar_t const wc = *pwszPath++;
3740 if (wc == '.')
3741 pwszExt = pwszPath;
3742 else if (wc == '/' || wc == '\\' || wc == ':')
3743 {
3744 pwszName = pwszPath;
3745 pwszExt = NULL;
3746 }
3747 else if (wc == '\0')
3748 {
3749 if (pwszExt)
3750 {
3751 *pcwcExt = pwszPath - pwszExt - 1;
3752 return pwszExt;
3753 }
3754 *pcwcExt = 0;
3755 return pwszPath - 1;
3756 }
3757 }
3758}
3759
3760
3761
3762/**
3763 * Parses the argument string passed in as pszSrc.
3764 *
3765 * @returns size of the processed arguments.
3766 * @param pszSrc Pointer to the commandline that's to be parsed.
3767 * @param pcArgs Where to return the number of arguments.
3768 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
3769 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
3770 *
3771 * @remarks Lifted from startuphacks-win.c
3772 */
3773static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
3774{
3775 int bs;
3776 char chQuote;
3777 char *pfFlags;
3778 int cbArgs;
3779 int cArgs;
3780
3781#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
3782#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
3783#define WHITE(c) ((c) == ' ' || (c) == '\t')
3784
3785#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
3786#define _ARG_RESPONSE 0x02 /* Argument read from response file */
3787#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
3788#define _ARG_ENV 0x08 /* Argument from environment */
3789#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
3790
3791 cArgs = 0;
3792 cbArgs = 0;
3793
3794#if 0
3795 /* argv[0] */
3796 PUTC((char)_ARG_NONZERO);
3797 PUTV;
3798 for (;;)
3799 {
3800 PUTC(*pszSrc);
3801 if (*pszSrc == 0)
3802 break;
3803 ++pszSrc;
3804 }
3805 ++pszSrc;
3806#endif
3807
3808 for (;;)
3809 {
3810 while (WHITE(*pszSrc))
3811 ++pszSrc;
3812 if (*pszSrc == 0)
3813 break;
3814 pfFlags = pchPool;
3815 PUTC((char)_ARG_NONZERO);
3816 PUTV;
3817 bs = 0; chQuote = 0;
3818 for (;;)
3819 {
3820 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
3821 {
3822 while (bs >= 2)
3823 {
3824 PUTC('\\');
3825 bs -= 2;
3826 }
3827 if (bs & 1)
3828 PUTC(*pszSrc);
3829 else
3830 {
3831 chQuote = chQuote ? 0 : *pszSrc;
3832 if (pfFlags != NULL)
3833 *pfFlags |= _ARG_DQUOTE;
3834 }
3835 bs = 0;
3836 }
3837 else if (*pszSrc == '\\')
3838 ++bs;
3839 else
3840 {
3841 while (bs != 0)
3842 {
3843 PUTC('\\');
3844 --bs;
3845 }
3846 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
3847 break;
3848 PUTC(*pszSrc);
3849 }
3850 ++pszSrc;
3851 }
3852 PUTC(0);
3853 }
3854
3855 *pcArgs = cArgs;
3856 return cbArgs;
3857}
3858
3859
3860
3861
3862/*
3863 *
3864 * Process and thread related APIs.
3865 * Process and thread related APIs.
3866 * Process and thread related APIs.
3867 *
3868 */
3869
3870/** Common worker for ExitProcess(), exit() and friends. */
3871static void WINAPI kwSandboxDoExit(int uExitCode)
3872{
3873 if (g_Sandbox.idMainThread == GetCurrentThreadId())
3874 {
3875 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
3876
3877 g_Sandbox.rcExitCode = (int)uExitCode;
3878
3879 /* Before we jump, restore the TIB as we're not interested in any
3880 exception chain stuff installed by the sandboxed executable. */
3881 *pTib = g_Sandbox.TibMainThread;
3882 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
3883
3884 longjmp(g_Sandbox.JmpBuf, 1);
3885 }
3886 KWFS_TODO();
3887}
3888
3889
3890/** ExitProcess replacement. */
3891static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
3892{
3893 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
3894 kwSandboxDoExit((int)uExitCode);
3895}
3896
3897
3898/** ExitProcess replacement. */
3899static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
3900{
3901 if (hProcess == GetCurrentProcess())
3902 kwSandboxDoExit(uExitCode);
3903 KWFS_TODO();
3904 return TerminateProcess(hProcess, uExitCode);
3905}
3906
3907
3908/** Normal CRT exit(). */
3909static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
3910{
3911 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
3912 kwSandboxDoExit(rcExitCode);
3913}
3914
3915
3916/** Quick CRT _exit(). */
3917static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
3918{
3919 /* Quick. */
3920 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
3921 kwSandboxDoExit(rcExitCode);
3922}
3923
3924
3925/** Return to caller CRT _cexit(). */
3926static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
3927{
3928 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
3929 kwSandboxDoExit(rcExitCode);
3930}
3931
3932
3933/** Quick return to caller CRT _c_exit(). */
3934static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
3935{
3936 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
3937 kwSandboxDoExit(rcExitCode);
3938}
3939
3940
3941/** Runtime error and exit _amsg_exit(). */
3942static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
3943{
3944 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
3945 kwSandboxDoExit(255);
3946}
3947
3948
3949/** CRT - terminate(). */
3950static void __cdecl kwSandbox_msvcrt_terminate(void)
3951{
3952 KW_LOG(("\nRuntime - terminate!\n"));
3953 kwSandboxDoExit(254);
3954}
3955
3956
3957/** CRT - _onexit */
3958static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
3959{
3960 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3961 {
3962 PKWEXITCALLACK pCallback;
3963 KW_LOG(("_onexit(%p)\n", pfnFunc));
3964 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3965
3966 pCallback = kHlpAlloc(sizeof(*pCallback));
3967 if (pCallback)
3968 {
3969 pCallback->pfnCallback = pfnFunc;
3970 pCallback->fAtExit = K_FALSE;
3971 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3972 g_Sandbox.pExitCallbackHead = pCallback;
3973 return pfnFunc;
3974 }
3975 return NULL;
3976 }
3977 KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
3978 return pfnFunc;
3979}
3980
3981
3982/** CRT - atexit */
3983static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
3984{
3985 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3986 {
3987 PKWEXITCALLACK pCallback;
3988 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3989 KW_LOG(("atexit(%p)\n", pfnFunc));
3990
3991 pCallback = kHlpAlloc(sizeof(*pCallback));
3992 if (pCallback)
3993 {
3994 pCallback->pfnCallback = (_onexit_t)pfnFunc;
3995 pCallback->fAtExit = K_TRUE;
3996 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3997 g_Sandbox.pExitCallbackHead = pCallback;
3998 return 0;
3999 }
4000 return -1;
4001 }
4002 KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4003 return 0;
4004}
4005
4006
4007/** Kernel32 - SetConsoleCtrlHandler(). */
4008static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4009{
4010 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4011 return TRUE;
4012}
4013
4014
4015/** The CRT internal __getmainargs() API. */
4016static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4017 int dowildcard, int const *piNewMode)
4018{
4019 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4020 *pargc = g_Sandbox.cArgs;
4021 *pargv = g_Sandbox.papszArgs;
4022 *penvp = g_Sandbox.environ;
4023
4024 /** @todo startinfo points at a newmode (setmode) value. */
4025 return 0;
4026}
4027
4028
4029/** The CRT internal __wgetmainargs() API. */
4030static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4031 int dowildcard, int const *piNewMode)
4032{
4033 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4034 *pargc = g_Sandbox.cArgs;
4035 *pargv = g_Sandbox.papwszArgs;
4036 *penvp = g_Sandbox.wenviron;
4037
4038 /** @todo startinfo points at a newmode (setmode) value. */
4039 return 0;
4040}
4041
4042
4043
4044/** Kernel32 - GetCommandLineA() */
4045static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4046{
4047 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4048 return g_Sandbox.pszCmdLine;
4049}
4050
4051
4052/** Kernel32 - GetCommandLineW() */
4053static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4054{
4055 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4056 return g_Sandbox.pwszCmdLine;
4057}
4058
4059
4060/** Kernel32 - GetStartupInfoA() */
4061static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4062{
4063 KW_LOG(("GetStartupInfoA\n"));
4064 GetStartupInfoA(pStartupInfo);
4065 pStartupInfo->lpReserved = NULL;
4066 pStartupInfo->lpTitle = NULL;
4067 pStartupInfo->lpReserved2 = NULL;
4068 pStartupInfo->cbReserved2 = 0;
4069}
4070
4071
4072/** Kernel32 - GetStartupInfoW() */
4073static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4074{
4075 KW_LOG(("GetStartupInfoW\n"));
4076 GetStartupInfoW(pStartupInfo);
4077 pStartupInfo->lpReserved = NULL;
4078 pStartupInfo->lpTitle = NULL;
4079 pStartupInfo->lpReserved2 = NULL;
4080 pStartupInfo->cbReserved2 = 0;
4081}
4082
4083
4084/** CRT - __p___argc(). */
4085static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4086{
4087 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4088 return &g_Sandbox.cArgs;
4089}
4090
4091
4092/** CRT - __p___argv(). */
4093static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4094{
4095 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4096 return &g_Sandbox.papszArgs;
4097}
4098
4099
4100/** CRT - __p___sargv(). */
4101static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4102{
4103 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4104 return &g_Sandbox.papwszArgs;
4105}
4106
4107
4108/** CRT - __p__acmdln(). */
4109static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4110{
4111 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4112 return (char **)&g_Sandbox.pszCmdLine;
4113}
4114
4115
4116/** CRT - __p__acmdln(). */
4117static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4118{
4119 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4120 return &g_Sandbox.pwszCmdLine;
4121}
4122
4123
4124/** CRT - __p__pgmptr(). */
4125static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4126{
4127 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4128 return &g_Sandbox.pgmptr;
4129}
4130
4131
4132/** CRT - __p__wpgmptr(). */
4133static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4134{
4135 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4136 return &g_Sandbox.wpgmptr;
4137}
4138
4139
4140/** CRT - _get_pgmptr(). */
4141static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4142{
4143 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4144 *ppszValue = g_Sandbox.pgmptr;
4145 return 0;
4146}
4147
4148
4149/** CRT - _get_wpgmptr(). */
4150static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4151{
4152 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4153 *ppwszValue = g_Sandbox.wpgmptr;
4154 return 0;
4155}
4156
4157/** Just in case. */
4158static void kwSandbox_msvcrt__wincmdln(void)
4159{
4160 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4161 KWFS_TODO();
4162}
4163
4164
4165/** Just in case. */
4166static void kwSandbox_msvcrt__wwincmdln(void)
4167{
4168 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4169 KWFS_TODO();
4170}
4171
4172/** CreateThread interceptor. */
4173static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4174 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4175 DWORD fFlags, PDWORD pidThread)
4176{
4177 HANDLE hThread = NULL;
4178 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4179 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4180 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4181 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4182 {
4183 /* Allow link::DbgThread. */
4184 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4185 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4186 }
4187 else
4188 KWFS_TODO();
4189 return hThread;
4190}
4191
4192
4193/** _beginthread - create a new thread. */
4194static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4195{
4196 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4197 KWFS_TODO();
4198 return 0;
4199}
4200
4201
4202/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4203static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4204 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4205 unsigned fCreate, unsigned *pidThread)
4206{
4207 /*
4208 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4209 * whatever it needs to.
4210 */
4211 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4212 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4213 pfnThreadProc, pvUser, fCreate, pidThread));
4214 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4215 {
4216 uintptr_t rcRet;
4217 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4218 if (!s_pfnReal)
4219 {
4220 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4221 if (!s_pfnReal)
4222 {
4223 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4224 __debugbreak();
4225 }
4226 }
4227 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4228 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4229 return rcRet;
4230 }
4231
4232 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4233 KWFS_TODO();
4234 return 0;
4235}
4236
4237
4238/** _beginthreadex - create a new thread. */
4239static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex(void *pvSecAttr, unsigned cbStack,
4240 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4241 unsigned fCreate, unsigned *pidThread)
4242{
4243 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4244 KWFS_TODO();
4245 return 0;
4246}
4247
4248
4249/*
4250 *
4251 * Environment related APIs.
4252 * Environment related APIs.
4253 * Environment related APIs.
4254 *
4255 */
4256
4257/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4258static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4259{
4260 char *pszzEnv;
4261 char *pszCur;
4262 KSIZE cbNeeded = 1;
4263 KSIZE iVar = 0;
4264
4265 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4266
4267 /* Figure how space much we need first. */
4268 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4269 cbNeeded += kHlpStrLen(pszCur) + 1;
4270
4271 /* Allocate it. */
4272 pszzEnv = kHlpAlloc(cbNeeded);
4273 if (pszzEnv)
4274 {
4275 char *psz = pszzEnv;
4276 iVar = 0;
4277 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4278 {
4279 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4280 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4281 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4282 }
4283 *psz++ = '\0';
4284 kHlpAssert(psz - pszzEnv == cbNeeded);
4285 }
4286
4287 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4288#if 0
4289 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4290 pszCur = pszzEnv;
4291 iVar = 0;
4292 while (*pszCur)
4293 {
4294 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4295 iVar++;
4296 pszCur += kHlpStrLen(pszCur) + 1;
4297 }
4298 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4299 pszCur++;
4300 fprintf(stderr, "ended at %p, after %u bytes (exepcted %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4301#endif
4302 return pszzEnv;
4303}
4304
4305
4306/** Kernel32 - GetEnvironmentStrings */
4307static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4308{
4309 KW_LOG(("GetEnvironmentStrings!\n"));
4310 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4311}
4312
4313
4314/** Kernel32 - GetEnvironmentStringsW */
4315static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4316{
4317 wchar_t *pwszzEnv;
4318 wchar_t *pwszCur;
4319 KSIZE cwcNeeded = 1;
4320 KSIZE iVar = 0;
4321
4322 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4323
4324 /* Figure how space much we need first. */
4325 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4326 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4327
4328 /* Allocate it. */
4329 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4330 if (pwszzEnv)
4331 {
4332 wchar_t *pwsz = pwszzEnv;
4333 iVar = 0;
4334 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4335 {
4336 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4337 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4338 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4339 }
4340 *pwsz++ = '\0';
4341 kHlpAssert(pwsz - pwszzEnv == cwcNeeded);
4342 }
4343
4344 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4345 return pwszzEnv;
4346}
4347
4348
4349/** Kernel32 - FreeEnvironmentStringsA */
4350static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4351{
4352 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4353 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4354 kHlpFree(pszzEnv);
4355 return TRUE;
4356}
4357
4358
4359/** Kernel32 - FreeEnvironmentStringsW */
4360static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4361{
4362 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4363 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4364 kHlpFree(pwszzEnv);
4365 return TRUE;
4366}
4367
4368
4369/**
4370 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4371 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4372 *
4373 * @returns 0 on success, non-zero on failure.
4374 * @param pSandbox The sandbox.
4375 * @param cMin Minimum size, including terminator.
4376 */
4377static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4378{
4379 void *pvNew;
4380 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4381 KSIZE cNew = cOld + 256;
4382 while (cNew < cMin)
4383 cNew += 256;
4384
4385 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4386 if (pvNew)
4387 {
4388 pSandbox->environ = (char **)pvNew;
4389 pSandbox->environ[cOld] = NULL;
4390
4391 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4392 if (pvNew)
4393 {
4394 pSandbox->papszEnvVars = (char **)pvNew;
4395 pSandbox->papszEnvVars[cOld] = NULL;
4396
4397 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4398 if (pvNew)
4399 {
4400 pSandbox->wenviron = (wchar_t **)pvNew;
4401 pSandbox->wenviron[cOld] = NULL;
4402
4403 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4404 if (pvNew)
4405 {
4406 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4407 pSandbox->papwszEnvVars[cOld] = NULL;
4408
4409 pSandbox->cEnvVarsAllocated = cNew;
4410 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4411 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4412 return 0;
4413 }
4414 }
4415 }
4416 }
4417 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4418 return KERR_NO_MEMORY;
4419}
4420
4421
4422/**
4423 * Sets an environment variable, ANSI style.
4424 *
4425 * @returns 0 on success, non-zero on failure.
4426 * @param pSandbox The sandbox.
4427 * @param pchVar The variable name.
4428 * @param cchVar The length of the name.
4429 * @param pszValue The value.
4430 */
4431static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4432{
4433 /* Allocate and construct the new strings. */
4434 KSIZE cchTmp = kHlpStrLen(pszValue);
4435 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4436 if (pszNew)
4437 {
4438 wchar_t *pwszNew;
4439 kHlpMemCopy(pszNew, pchVar, cchVar);
4440 pszNew[cchVar] = '=';
4441 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4442 cchTmp += cchVar + 1;
4443 pszNew[cchTmp] = '\0';
4444
4445 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4446 if (pwszNew)
4447 {
4448 /* Look it up. */
4449 KSIZE iVar = 0;
4450 char *pszEnv;
4451 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4452 {
4453 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4454 && pszEnv[cchVar] == '=')
4455 {
4456 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4457 " iVar=%d: %p='%s' and %p='%ls'\n",
4458 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4459 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4460 iVar, pszNew, pszNew, pwszNew, pwszNew));
4461
4462 kHlpFree(pSandbox->papszEnvVars[iVar]);
4463 pSandbox->papszEnvVars[iVar] = pszNew;
4464 pSandbox->environ[iVar] = pszNew;
4465
4466 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4467 pSandbox->papwszEnvVars[iVar] = pwszNew;
4468 pSandbox->wenviron[iVar] = pwszNew;
4469 return 0;
4470 }
4471 iVar++;
4472 }
4473
4474 /* Not found, do we need to grow the table first? */
4475 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4476 kwSandboxGrowEnv(pSandbox, iVar + 2);
4477 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4478 {
4479 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4480
4481 pSandbox->papszEnvVars[iVar + 1] = NULL;
4482 pSandbox->papszEnvVars[iVar] = pszNew;
4483 pSandbox->environ[iVar + 1] = NULL;
4484 pSandbox->environ[iVar] = pszNew;
4485
4486 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4487 pSandbox->papwszEnvVars[iVar] = pwszNew;
4488 pSandbox->wenviron[iVar + 1] = NULL;
4489 pSandbox->wenviron[iVar] = pwszNew;
4490 return 0;
4491 }
4492
4493 kHlpFree(pwszNew);
4494 }
4495 kHlpFree(pszNew);
4496 }
4497 KW_LOG(("Out of memory!\n"));
4498 return 0;
4499}
4500
4501
4502/**
4503 * Sets an environment variable, UTF-16 style.
4504 *
4505 * @returns 0 on success, non-zero on failure.
4506 * @param pSandbox The sandbox.
4507 * @param pwcVar The variable name.
4508 * @param cwcVar The length of the name.
4509 * @param pwszValue The value.
4510 */
4511static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4512{
4513 /* Allocate and construct the new strings. */
4514 KSIZE cwcTmp = kwUtf16Len(pwszValue);
4515 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4516 if (pwszNew)
4517 {
4518 char *pszNew;
4519 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4520 pwszNew[cwcVar] = '=';
4521 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4522 cwcTmp += cwcVar + 1;
4523 pwszNew[cwcVar] = '\0';
4524
4525 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4526 if (pszNew)
4527 {
4528 /* Look it up. */
4529 KSIZE iVar = 0;
4530 wchar_t *pwszEnv;
4531 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4532 {
4533 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4534 && pwszEnv[cwcVar] == '=')
4535 {
4536 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4537 " iVar=%d: %p='%s' and %p='%ls'\n",
4538 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4539 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4540 iVar, pszNew, pszNew, pwszNew, pwszNew));
4541
4542 kHlpFree(pSandbox->papszEnvVars[iVar]);
4543 pSandbox->papszEnvVars[iVar] = pszNew;
4544 pSandbox->environ[iVar] = pszNew;
4545
4546 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4547 pSandbox->papwszEnvVars[iVar] = pwszNew;
4548 pSandbox->wenviron[iVar] = pwszNew;
4549 return 0;
4550 }
4551 iVar++;
4552 }
4553
4554 /* Not found, do we need to grow the table first? */
4555 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4556 kwSandboxGrowEnv(pSandbox, iVar + 2);
4557 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4558 {
4559 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4560
4561 pSandbox->papszEnvVars[iVar + 1] = NULL;
4562 pSandbox->papszEnvVars[iVar] = pszNew;
4563 pSandbox->environ[iVar + 1] = NULL;
4564 pSandbox->environ[iVar] = pszNew;
4565
4566 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4567 pSandbox->papwszEnvVars[iVar] = pwszNew;
4568 pSandbox->wenviron[iVar + 1] = NULL;
4569 pSandbox->wenviron[iVar] = pwszNew;
4570 return 0;
4571 }
4572
4573 kHlpFree(pwszNew);
4574 }
4575 kHlpFree(pszNew);
4576 }
4577 KW_LOG(("Out of memory!\n"));
4578 return 0;
4579}
4580
4581
4582/** ANSI unsetenv worker. */
4583static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4584{
4585 KSIZE iVar = 0;
4586 char *pszEnv;
4587 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4588 {
4589 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4590 && pszEnv[cchVar] == '=')
4591 {
4592 KSIZE cVars = iVar;
4593 while (pSandbox->papszEnvVars[cVars])
4594 cVars++;
4595 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4596 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4597
4598 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4599 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4600 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4601
4602 kHlpFree(pSandbox->papszEnvVars[iVar]);
4603 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4604 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4605 pSandbox->papszEnvVars[cVars] = NULL;
4606 pSandbox->environ[cVars] = NULL;
4607
4608 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4609 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4610 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4611 pSandbox->papwszEnvVars[cVars] = NULL;
4612 pSandbox->wenviron[cVars] = NULL;
4613 return 0;
4614 }
4615 iVar++;
4616 }
4617 return KERR_ENVVAR_NOT_FOUND;
4618}
4619
4620
4621/** UTF-16 unsetenv worker. */
4622static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4623{
4624 KSIZE iVar = 0;
4625 wchar_t *pwszEnv;
4626 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4627 {
4628 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4629 && pwszEnv[cwcVar] == '=')
4630 {
4631 KSIZE cVars = iVar;
4632 while (pSandbox->papwszEnvVars[cVars])
4633 cVars++;
4634 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
4635 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
4636
4637 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4638 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4639 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4640
4641 kHlpFree(pSandbox->papszEnvVars[iVar]);
4642 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4643 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4644 pSandbox->papszEnvVars[cVars] = NULL;
4645 pSandbox->environ[cVars] = NULL;
4646
4647 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4648 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4649 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4650 pSandbox->papwszEnvVars[cVars] = NULL;
4651 pSandbox->wenviron[cVars] = NULL;
4652 return 0;
4653 }
4654 iVar++;
4655 }
4656 return KERR_ENVVAR_NOT_FOUND;
4657}
4658
4659
4660
4661/** ANSI getenv worker. */
4662static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4663{
4664 KSIZE iVar = 0;
4665 char *pszEnv;
4666 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
4667 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4668 && pszEnv[cchVar] == '=')
4669 return &pszEnv[cchVar + 1];
4670 return NULL;
4671}
4672
4673
4674/** UTF-16 getenv worker. */
4675static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4676{
4677 KSIZE iVar = 0;
4678 wchar_t *pwszEnv;
4679 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
4680 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4681 && pwszEnv[cwcVar] == '=')
4682 return &pwszEnv[cwcVar + 1];
4683 return NULL;
4684}
4685
4686
4687/** Kernel32 - GetEnvironmentVariableA() */
4688static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
4689{
4690 char *pszFoundValue;
4691 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4692
4693 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4694 if (pszFoundValue)
4695 {
4696 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
4697 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
4698 return cchRet;
4699 }
4700 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
4701 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4702 return 0;
4703}
4704
4705
4706/** Kernel32 - GetEnvironmentVariableW() */
4707static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
4708{
4709 wchar_t *pwszFoundValue;
4710 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4711
4712 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4713 if (pwszFoundValue)
4714 {
4715 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
4716 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
4717 return cchRet;
4718 }
4719 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
4720 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4721 return 0;
4722}
4723
4724
4725/** Kernel32 - SetEnvironmentVariableA() */
4726static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
4727{
4728 int rc;
4729 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4730
4731 if (pszValue)
4732 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4733 else
4734 {
4735 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4736 rc = 0; //??
4737 }
4738 if (rc == 0)
4739 {
4740 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
4741 return TRUE;
4742 }
4743 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4744 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
4745 return FALSE;
4746}
4747
4748
4749/** Kernel32 - SetEnvironmentVariableW() */
4750static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
4751{
4752 int rc;
4753 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4754
4755 if (pwszValue)
4756 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4757 else
4758 {
4759 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4760 rc = 0; //??
4761 }
4762 if (rc == 0)
4763 {
4764 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
4765 return TRUE;
4766 }
4767 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4768 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
4769 return FALSE;
4770}
4771
4772
4773/** Kernel32 - ExpandEnvironmentStringsA() */
4774static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
4775{
4776 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4777 KWFS_TODO();
4778 return 0;
4779}
4780
4781
4782/** Kernel32 - ExpandEnvironmentStringsW() */
4783static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
4784{
4785 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4786 KWFS_TODO();
4787 return 0;
4788}
4789
4790
4791/** CRT - _putenv(). */
4792static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
4793{
4794 int rc;
4795 char const *pszEqual;
4796 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4797
4798 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
4799 if (pszEqual)
4800 {
4801 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
4802 if (rc == 0)
4803 { }
4804 else
4805 rc = -1;
4806 }
4807 else
4808 {
4809 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
4810 rc = 0;
4811 }
4812 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
4813 return rc;
4814}
4815
4816
4817/** CRT - _wputenv(). */
4818static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
4819{
4820 int rc;
4821 wchar_t const *pwszEqual;
4822 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4823
4824 pwszEqual = wcschr(pwszVarEqualValue, '=');
4825 if (pwszEqual)
4826 {
4827 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
4828 if (rc == 0)
4829 { }
4830 else
4831 rc = -1;
4832 }
4833 else
4834 {
4835 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
4836 rc = 0;
4837 }
4838 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
4839 return rc;
4840}
4841
4842
4843/** CRT - _putenv_s(). */
4844static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
4845{
4846 char const *pszEqual;
4847 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4848
4849 pszEqual = kHlpStrChr(pszVar, '=');
4850 if (pszEqual == NULL)
4851 {
4852 if (pszValue)
4853 {
4854 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4855 if (rc == 0)
4856 {
4857 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
4858 return 0;
4859 }
4860 }
4861 else
4862 {
4863 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4864 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
4865 return 0;
4866 }
4867 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
4868 return ENOMEM;
4869 }
4870 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
4871 return EINVAL;
4872}
4873
4874
4875/** CRT - _wputenv_s(). */
4876static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
4877{
4878 wchar_t const *pwszEqual;
4879 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4880
4881 pwszEqual = wcschr(pwszVar, '=');
4882 if (pwszEqual == NULL)
4883 {
4884 if (pwszValue)
4885 {
4886 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4887 if (rc == 0)
4888 {
4889 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
4890 return 0;
4891 }
4892 }
4893 else
4894 {
4895 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4896 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
4897 return 0;
4898 }
4899 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
4900 return ENOMEM;
4901 }
4902 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
4903 return EINVAL;
4904}
4905
4906
4907/** CRT - get pointer to the __initenv variable (initial environment). */
4908static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
4909{
4910 KW_LOG(("__p___initenv\n"));
4911 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4912 KWFS_TODO();
4913 return &g_Sandbox.initenv;
4914}
4915
4916
4917/** CRT - get pointer to the __winitenv variable (initial environment). */
4918static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
4919{
4920 KW_LOG(("__p___winitenv\n"));
4921 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4922 KWFS_TODO();
4923 return &g_Sandbox.winitenv;
4924}
4925
4926
4927/** CRT - get pointer to the _environ variable (current environment). */
4928static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
4929{
4930 KW_LOG(("__p__environ\n"));
4931 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4932 return &g_Sandbox.environ;
4933}
4934
4935
4936/** CRT - get pointer to the _wenviron variable (current environment). */
4937static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
4938{
4939 KW_LOG(("__p__wenviron\n"));
4940 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4941 return &g_Sandbox.wenviron;
4942}
4943
4944
4945/** CRT - get the _environ variable (current environment).
4946 * @remarks Not documented or prototyped? */
4947static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
4948{
4949 KWFS_TODO(); /** @todo check the callers expectations! */
4950 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4951 *ppapszEnviron = g_Sandbox.environ;
4952 return 0;
4953}
4954
4955
4956/** CRT - get the _wenviron variable (current environment).
4957 * @remarks Not documented or prototyped? */
4958static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
4959{
4960 KWFS_TODO(); /** @todo check the callers expectations! */
4961 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4962 *ppapwszEnviron = g_Sandbox.wenviron;
4963 return 0;
4964}
4965
4966
4967/** CRT - _wdupenv_s() (see _tdupenv_s(). */
4968static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
4969 PKWCRTSLOT pSlot)
4970{
4971 errno_t rc;
4972 wchar_t *pwszValue;
4973 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4974
4975 if (ppwszValue)
4976 {
4977 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
4978 if (pwszValue)
4979 {
4980 size_t cwcValue = wcslen(pwszValue);
4981 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
4982 if (pwszDst)
4983 {
4984 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
4985 pwszDst[cwcValue] = '\0';
4986 *ppwszValue = pwszDst;
4987 if (pcwcValue)
4988 *pcwcValue = cwcValue;
4989 rc = 0;
4990 }
4991 else
4992 {
4993 *ppwszValue = NULL;
4994 if (pcwcValue)
4995 *pcwcValue = 0;
4996 rc = ENOMEM;
4997 }
4998 }
4999 else
5000 {
5001 *ppwszValue = NULL;
5002 if (pcwcValue)
5003 *pcwcValue = 0;
5004 rc = 0;
5005 }
5006 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5007 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5008 }
5009 else
5010 {
5011 /*
5012 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5013 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5014 * was set to a value.
5015 */
5016 if (pcwcValue)
5017 *pcwcValue = 0;
5018 rc = EINVAL;
5019 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5020 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5021 }
5022 return rc;
5023}
5024CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5025 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5026 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5027
5028
5029
5030/*
5031 *
5032 * Loader related APIs
5033 * Loader related APIs
5034 * Loader related APIs
5035 *
5036 */
5037
5038/**
5039 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5040 */
5041static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5042{
5043 /* Load it first. */
5044 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5045 if (hmod)
5046 {
5047 pDynLoad->hmod = hmod;
5048 pDynLoad->pMod = NULL; /* indicates special */
5049
5050 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5051 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5052 KW_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5053 }
5054 else
5055 kHlpFree(pDynLoad);
5056 return hmod;
5057}
5058
5059
5060/**
5061 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5062 */
5063static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5064{
5065 HMODULE hmod;
5066 PKWMODULE pMod;
5067 KU32 uHashPath;
5068 KSIZE idxHash;
5069 char szNormPath[256];
5070 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5071
5072 /*
5073 * Lower case it and make it ends with .dll.
5074 */
5075 if (cbFilename <= sizeof(szNormPath))
5076 {
5077 static const char s_szDll[] = ".dll";
5078 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5079 _strlwr(szNormPath);
5080 if ( ( cbFilename <= 4
5081 || strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5082 && cbFilename + sizeof(s_szDll) - 1 <= sizeof(szNormPath))
5083 {
5084 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5085 cbFilename += sizeof(s_szDll) - 1;
5086 }
5087 }
5088 else
5089 {
5090 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5091 return NULL;
5092 }
5093
5094 /*
5095 * Check if it has already been loaded so we don't create an unnecessary
5096 * loader module for it.
5097 */
5098 uHashPath = kwStrHash(szNormPath);
5099 idxHash = uHashPath % K_ELEMENTS(g_apModules);
5100 pMod = g_apModules[idxHash];
5101 if (pMod)
5102 {
5103 do
5104 {
5105 if ( pMod->uHashPath == uHashPath
5106 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
5107 {
5108 pDynLoad->pMod = kwLdrModuleRetain(pMod);
5109 pDynLoad->hmod = pMod->hOurMod;
5110
5111 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5112 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5113 KW_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - already loaded]\n", pDynLoad->szRequest, pDynLoad->hmod));
5114 return pDynLoad->hmod;
5115 }
5116 pMod = pMod->pNextHash;
5117 } while (pMod);
5118 }
5119
5120
5121 /*
5122 * Try load it and make a kLdr module for it.
5123 */
5124 hmod = LoadLibraryExA(szNormPath, NULL /*hFile*/, fFlags);
5125 if (hmod)
5126 {
5127 PKLDRMOD pLdrMod;
5128 int rc = kLdrModOpenNativeByHandle((KUPTR)hmod, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
5129 if (rc == 0)
5130 {
5131 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cbFilename, uHashPath,
5132 K_FALSE /*fDoReplacements*/);
5133 if (pMod)
5134 {
5135 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5136
5137 pDynLoad->pMod = pMod;
5138 pDynLoad->hmod = hmod;
5139
5140 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5141 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5142 KW_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5143 return hmod;
5144 }
5145 KWFS_TODO();
5146 }
5147 else
5148 KWFS_TODO();
5149 }
5150 kHlpFree(pDynLoad);
5151 return hmod;
5152}
5153
5154
5155/** Kernel32 - LoadLibraryExA() */
5156static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5157{
5158 KSIZE cchFilename = kHlpStrLen(pszFilename);
5159 const char *pszSearchPath;
5160 PKWDYNLOAD pDynLoad;
5161 PKWMODULE pMod;
5162 int rc;
5163 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5164
5165 /*
5166 * Deal with a couple of extremely unlikely special cases right away.
5167 */
5168 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5169 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5170 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5171 { /* likely */ }
5172 else
5173 {
5174 KWFS_TODO();
5175 return LoadLibraryExA(pszFilename, hFile, fFlags);
5176 }
5177
5178 /*
5179 * Check if we've already got a dynload entry for this one.
5180 */
5181 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5182 if ( pDynLoad->cchRequest == cchFilename
5183 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5184 {
5185 if (pDynLoad->pMod)
5186 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5187 else
5188 rc = 0;
5189 if (rc == 0)
5190 {
5191 KW_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5192 return pDynLoad->hmod;
5193 }
5194 SetLastError(ERROR_DLL_INIT_FAILED);
5195 return NULL;
5196 }
5197
5198 /*
5199 * Allocate a dynload entry for the request.
5200 */
5201 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5202 if (pDynLoad)
5203 {
5204 pDynLoad->cchRequest = cchFilename;
5205 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5206 }
5207 else
5208 {
5209 KW_LOG(("LoadLibraryExA: Out of memory!\n"));
5210 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5211 return NULL;
5212 }
5213
5214 /*
5215 * Deal with resource / data DLLs.
5216 */
5217 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5218 | LOAD_LIBRARY_AS_DATAFILE
5219 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5220 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5221 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5222
5223 /*
5224 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5225 */
5226 if ( strnicmp(pszFilename, TUPLE("api-ms-")) == 0
5227 && kHlpIsFilenameOnly(pszFilename))
5228 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5229
5230 /*
5231 * Normal library loading.
5232 * We start by being very lazy and reusing the code for resolving imports.
5233 */
5234 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5235 if (!kHlpIsFilenameOnly(pszFilename))
5236 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5237 else
5238 {
5239 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5240 if (rc != 0)
5241 pMod = NULL;
5242 }
5243 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5244 {
5245 /* Enter it into the tool module table and dynamic link request cache. */
5246 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5247
5248 pDynLoad->pMod = pMod;
5249 pDynLoad->hmod = pMod->hOurMod;
5250
5251 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5252 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5253
5254 /*
5255 * Make sure it's initialized (need to link it first since DllMain may
5256 * use loader APIs).
5257 */
5258 rc = kwLdrModuleInitTree(pMod);
5259 if (rc == 0)
5260 {
5261 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5262 return pDynLoad->hmod;
5263 }
5264
5265 SetLastError(ERROR_DLL_INIT_FAILED);
5266 }
5267 else
5268 {
5269 KWFS_TODO();
5270 kHlpFree(pDynLoad);
5271 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5272 }
5273 return NULL;
5274}
5275
5276
5277/** Kernel32 - LoadLibraryExA() for native overloads */
5278static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5279{
5280 char szPath[1024];
5281 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5282
5283 /*
5284 * We may have to help resolved unqualified DLLs living in the executable directory.
5285 */
5286 if (kHlpIsFilenameOnly(pszFilename))
5287 {
5288 KSIZE cchFilename = kHlpStrLen(pszFilename);
5289 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5290 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5291 {
5292 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5293 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5294 if (kwFsPathExists(szPath))
5295 {
5296 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5297 pszFilename = szPath;
5298 }
5299 }
5300
5301 if (pszFilename != szPath)
5302 {
5303 KSIZE cchSuffix = 0;
5304 KBOOL fNeedSuffix = K_FALSE;
5305 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5306 while (*pszCur != '\0')
5307 {
5308 /* Find the end of the component */
5309 KSIZE cch = 0;
5310 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5311 cch++;
5312
5313 if ( cch > 0 /* wrong, but whatever */
5314 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5315 {
5316 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5317 if ( szPath[cch - 1] != ':'
5318 && szPath[cch - 1] != '/'
5319 && szPath[cch - 1] != '\\')
5320 *pszDst++ = '\\';
5321 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5322 if (fNeedSuffix)
5323 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5324 *pszDst = '\0';
5325
5326 if (kwFsPathExists(szPath))
5327 {
5328 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5329 pszFilename = szPath;
5330 break;
5331 }
5332 }
5333
5334 /* Advance */
5335 pszCur += cch;
5336 while (*pszCur == ';')
5337 pszCur++;
5338 }
5339 }
5340 }
5341
5342 return LoadLibraryExA(pszFilename, hFile, fFlags);
5343}
5344
5345
5346/** Kernel32 - LoadLibraryExW() */
5347static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5348{
5349 char szTmp[4096];
5350 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5351 if (cchTmp < sizeof(szTmp))
5352 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5353
5354 KWFS_TODO();
5355 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5356 return NULL;
5357}
5358
5359/** Kernel32 - LoadLibraryA() */
5360static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5361{
5362 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5363}
5364
5365
5366/** Kernel32 - LoadLibraryW() */
5367static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5368{
5369 char szTmp[4096];
5370 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5371 if (cchTmp < sizeof(szTmp))
5372 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5373 KWFS_TODO();
5374 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5375 return NULL;
5376}
5377
5378
5379/** Kernel32 - FreeLibrary() */
5380static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5381{
5382 /* Ignored, we like to keep everything loaded. */
5383 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5384 return TRUE;
5385}
5386
5387
5388/** Worker for GetModuleHandleA/W for handling cached modules. */
5389static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5390{
5391 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5392 if (hmod)
5393 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5394 hmod, g_aGetModuleHandleCache[i].pszName));
5395 else
5396 {
5397 /*
5398 * The first time around we have to make sure we have a module table
5399 * entry for it, if not we add one. We need to add it to the tools
5400 * module list to for it to work.
5401 */
5402 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE);
5403 if (pMod)
5404 {
5405 hmod = pMod->hOurMod;
5406 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5407 {
5408 kwToolAddModule(g_Sandbox.pTool, pMod);
5409 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5410 hmod, g_aGetModuleHandleCache[i].pszName));
5411 }
5412 else
5413 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5414 hmod, g_aGetModuleHandleCache[i].pszName));
5415
5416 }
5417 }
5418 return hmod;
5419}
5420
5421
5422/** Kernel32 - GetModuleHandleA() */
5423static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5424{
5425 KSIZE i;
5426 KSIZE cchModule;
5427 PKWDYNLOAD pDynLoad;
5428 KSIZE cchSuffix;
5429 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5430 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5431
5432 /*
5433 * The executable.
5434 */
5435 if (pszModule == NULL)
5436 {
5437 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5438 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5439 }
5440
5441 /*
5442 * If no path of suffix, pretend it ends with .DLL.
5443 */
5444 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5445
5446 /*
5447 * Cache of system modules we've seen queried.
5448 */
5449 cchModule = kHlpStrLen(pszModule);
5450 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5451 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5452 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5453 || ( cchSuffix > 0
5454 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5455 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5456 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5457 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5458
5459 /*
5460 * Modules we've dynamically loaded.
5461 */
5462 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5463 if (pDynLoad->pMod)
5464 {
5465 const char *pszPath = pDynLoad->pMod->pszPath;
5466 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
5467 if ( stricmp(pszPath, pszModule) == 0
5468 || stricmp(pszName, pszModule) == 0
5469 || ( cchSuffix > 0
5470 && strnicmp(pszName, pszModule, cchModule) == 0
5471 && stricmp(&pszName[cchModule], ".dll") == 0))
5472 {
5473 if ( pDynLoad->pMod->fNative
5474 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5475 {
5476 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
5477 return pDynLoad->hmod;
5478 }
5479 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
5480 SetLastError(ERROR_MOD_NOT_FOUND);
5481 return NULL;
5482 }
5483 }
5484
5485 /*
5486 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5487 * to and go via the g_aGetModuleHandleCache cache.
5488 */
5489 if (strnicmp(pszModule, "api-ms-win-", 11) == 0)
5490 {
5491 HMODULE hmod = GetModuleHandleA(pszModule);
5492 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
5493 if (hmod)
5494 {
5495 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5496 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
5497 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5498 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
5499 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5500 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
5501 }
5502 else
5503 dwErr = GetLastError();
5504 }
5505
5506 kwErrPrintf("pszModule=%s\n", pszModule);
5507 KWFS_TODO();
5508 SetLastError(ERROR_MOD_NOT_FOUND);
5509 return NULL;
5510}
5511
5512
5513/** Kernel32 - GetModuleHandleW() */
5514static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
5515{
5516 KSIZE i;
5517 KSIZE cwcModule;
5518 PKWDYNLOAD pDynLoad;
5519 KSIZE cwcSuffix;
5520 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5521 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5522
5523 /*
5524 * The executable.
5525 */
5526 if (pwszModule == NULL)
5527 {
5528 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5529 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5530 }
5531
5532 /*
5533 * If no path of suffix, pretend it ends with .DLL.
5534 */
5535 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
5536
5537 /*
5538 * Cache of system modules we've seen queried.
5539 */
5540 cwcModule = kwUtf16Len(pwszModule);
5541 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5542 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
5543 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5544 || ( cwcSuffix > 0
5545 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
5546 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
5547 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
5548 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5549
5550 /*
5551 * Modules we've dynamically loaded.
5552 */
5553 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5554 if (pDynLoad->pMod)
5555 {
5556 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
5557 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
5558 if ( _wcsicmp(pwszPath, pwszModule) == 0
5559 || _wcsicmp(pwszName, pwszModule) == 0
5560 || ( cwcSuffix
5561 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
5562 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
5563 {
5564 if ( pDynLoad->pMod->fNative
5565 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5566 {
5567 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5568 return pDynLoad->hmod;
5569 }
5570 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
5571 SetLastError(ERROR_MOD_NOT_FOUND);
5572 return NULL;
5573 }
5574 }
5575
5576 /*
5577 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5578 * to and go via the g_aGetModuleHandleCache cache.
5579 */
5580 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
5581 {
5582 HMODULE hmod = GetModuleHandleW(pwszModule);
5583 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
5584 if (hmod)
5585 {
5586 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5587 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
5588 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5589 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
5590 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5591 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
5592 }
5593 else
5594 dwErr = GetLastError();
5595 }
5596
5597 kwErrPrintf("pwszModule=%ls\n", pwszModule);
5598 KWFS_TODO();
5599 SetLastError(dwErr);
5600 return NULL;
5601}
5602
5603
5604/** Used to debug dynamically resolved procedures. */
5605static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5606{
5607#ifdef _MSC_VER
5608 __debugbreak();
5609#else
5610 KWFS_TODO();
5611#endif
5612 return -1;
5613}
5614
5615
5616#ifndef NDEBUG
5617/*
5618 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5619 */
5620# if K_ARCH == K_ARCH_X86_32
5621static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5622# else
5623static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5624# endif
5625typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5626typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5627typedef struct KWCXINTERCEPTORENTRY
5628{
5629 PFNINVOKECOMPILERPASSW pfnOrg;
5630 PKWMODULE pModule;
5631 PFNINVOKECOMPILERPASSW pfnWrap;
5632} KWCXINTERCEPTORENTRY;
5633
5634static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5635 KWCXINTERCEPTORENTRY *pEntry)
5636{
5637 int i;
5638 KIPTR rcExit;
5639 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5640 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5641 for (i = 0; i < cArgs; i++)
5642 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5643
5644 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5645
5646 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5647 return rcExit;
5648}
5649
5650static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
5651static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
5652static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
5653
5654static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
5655{
5656 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
5657 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
5658 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
5659};
5660
5661static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5662{
5663 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
5664}
5665
5666static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5667{
5668 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
5669}
5670
5671static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5672{
5673 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
5674}
5675
5676#endif /* !NDEBUG */
5677
5678
5679/** Kernel32 - GetProcAddress() */
5680static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
5681{
5682 KSIZE i;
5683 PKWMODULE pMod;
5684 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5685
5686 /*
5687 * Try locate the module.
5688 */
5689 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5690 if (pMod)
5691 {
5692 KLDRADDR uValue;
5693 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
5694 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
5695 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
5696 KU32_MAX /*iSymbol*/,
5697 pszProc,
5698 kHlpStrLen(pszProc),
5699 NULL /*pszVersion*/,
5700 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
5701 &uValue,
5702 NULL /*pfKind*/);
5703 if (rc == 0)
5704 {
5705 //static int s_cDbgGets = 0;
5706 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
5707 KU32 i = g_cSandboxGetProcReplacements;
5708 while (i-- > 0)
5709 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
5710 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
5711 {
5712 if ( !g_aSandboxGetProcReplacements[i].pszModule
5713 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
5714 {
5715 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
5716 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
5717 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
5718 {
5719 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
5720 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5721 }
5722 kwLdrModuleRelease(pMod);
5723 return (FARPROC)(KUPTR)uValue;
5724 }
5725 }
5726
5727#ifndef NDEBUG
5728 /* Intercept the compiler pass method, dumping arguments. */
5729 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
5730 {
5731 KU32 i;
5732 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
5733 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
5734 {
5735 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5736 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
5737 break;
5738 }
5739 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
5740 while (i-- > 0)
5741 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
5742 {
5743 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
5744 g_aCxInterceptorEntries[i].pModule = pMod;
5745 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5746 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
5747 break;
5748 }
5749 }
5750#endif
5751 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5752 kwLdrModuleRelease(pMod);
5753 //s_cDbgGets++;
5754 //if (s_cGets >= 3)
5755 // return (FARPROC)kwSandbox_BreakIntoDebugger;
5756 return (FARPROC)(KUPTR)uValue;
5757 }
5758
5759 KWFS_TODO();
5760 SetLastError(ERROR_PROC_NOT_FOUND);
5761 kwLdrModuleRelease(pMod);
5762 return NULL;
5763 }
5764
5765 /*
5766 * Hmm... could be a cached module-by-name.
5767 */
5768 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5769 if (g_aGetModuleHandleCache[i].hmod == hmod)
5770 return GetProcAddress(hmod, pszProc);
5771
5772 KWFS_TODO();
5773 return GetProcAddress(hmod, pszProc);
5774}
5775
5776
5777/** Kernel32 - GetModuleFileNameA() */
5778static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
5779{
5780 PKWMODULE pMod;
5781 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5782
5783 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5784 if (pMod != NULL)
5785 {
5786 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
5787 kwLdrModuleRelease(pMod);
5788 return cbRet;
5789 }
5790 KWFS_TODO();
5791 return 0;
5792}
5793
5794
5795/** Kernel32 - GetModuleFileNameW() */
5796static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
5797{
5798 PKWMODULE pMod;
5799 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5800
5801 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5802 if (pMod)
5803 {
5804 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
5805 kwLdrModuleRelease(pMod);
5806 return cwcRet;
5807 }
5808
5809 KWFS_TODO();
5810 return 0;
5811}
5812
5813
5814/** NtDll - RtlPcToFileHeader
5815 * This is necessary for msvcr100.dll!CxxThrowException. */
5816static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
5817{
5818 PVOID pvRet;
5819
5820 /*
5821 * Do a binary lookup of the module table for the current tool.
5822 * This will give us a
5823 */
5824 if (g_Sandbox.fRunning)
5825 {
5826 KUPTR const uPC = (KUPTR)pvPC;
5827 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
5828 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
5829 KU32 i;
5830 if (iEnd)
5831 {
5832 KU32 iStart = 0;
5833 i = iEnd / 2;
5834 for (;;)
5835 {
5836 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
5837 if (uPC < uHModThis)
5838 {
5839 iEnd = i;
5840 if (iStart < i)
5841 { }
5842 else
5843 break;
5844 }
5845 else if (uPC != uHModThis)
5846 {
5847 iStart = ++i;
5848 if (i < iEnd)
5849 { }
5850 else
5851 break;
5852 }
5853 else
5854 {
5855 /* This isn't supposed to happen. */
5856 break;
5857 }
5858
5859 i = iStart + (iEnd - iStart) / 2;
5860 }
5861
5862 /* For reasons of simplicity (= copy & paste), we end up with the
5863 module after the one we're interested in here. */
5864 i--;
5865 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
5866 && papMods[i]->pLdrMod)
5867 {
5868 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
5869 if (uRvaPC < papMods[i]->cbImage)
5870 {
5871 *ppvImageBase = papMods[i]->hOurMod;
5872 pvRet = papMods[i]->hOurMod;
5873 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
5874 return pvRet;
5875 }
5876 }
5877 }
5878 else
5879 i = 0;
5880 }
5881
5882 /*
5883 * Call the regular API.
5884 */
5885 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
5886 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
5887 return pvRet;
5888}
5889
5890
5891/*
5892 *
5893 * File access APIs (for speeding them up).
5894 * File access APIs (for speeding them up).
5895 * File access APIs (for speeding them up).
5896 *
5897 */
5898
5899
5900/**
5901 * Converts a lookup error to a windows error code.
5902 *
5903 * @returns The windows error code.
5904 * @param enmError The lookup error.
5905 */
5906static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
5907{
5908 switch (enmError)
5909 {
5910 case KFSLOOKUPERROR_NOT_FOUND:
5911 case KFSLOOKUPERROR_NOT_DIR:
5912 return ERROR_FILE_NOT_FOUND;
5913
5914 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
5915 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
5916 return ERROR_PATH_NOT_FOUND;
5917
5918 case KFSLOOKUPERROR_PATH_TOO_LONG:
5919 return ERROR_FILENAME_EXCED_RANGE;
5920
5921 case KFSLOOKUPERROR_OUT_OF_MEMORY:
5922 return ERROR_NOT_ENOUGH_MEMORY;
5923
5924 default:
5925 return ERROR_PATH_NOT_FOUND;
5926 }
5927}
5928
5929#ifdef WITH_TEMP_MEMORY_FILES
5930
5931/**
5932 * Checks for a cl.exe temporary file.
5933 *
5934 * There are quite a bunch of these. They seems to be passing data between the
5935 * first and second compiler pass. Since they're on disk, they get subjected to
5936 * AV software screening and normal file consistency rules. So, not necessarily
5937 * a very efficient way of handling reasonably small amounts of data.
5938 *
5939 * We make the files live in virtual memory by intercepting their opening,
5940 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
5941 *
5942 * @returns K_TRUE / K_FALSE
5943 * @param pwszFilename The file name being accessed.
5944 */
5945static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
5946{
5947 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
5948 if (pwszName)
5949 {
5950 /* The name starts with _CL_... */
5951 if ( pwszName[0] == '_'
5952 && pwszName[1] == 'C'
5953 && pwszName[2] == 'L'
5954 && pwszName[3] == '_' )
5955 {
5956 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
5957 this check by just checking that it's alpha numerical ascii from here on. */
5958 wchar_t wc;
5959 pwszName += 4;
5960 while ((wc = *pwszName++) != '\0')
5961 {
5962 if (wc < 127 && iswalnum(wc))
5963 { /* likely */ }
5964 else
5965 return K_FALSE;
5966 }
5967 return K_TRUE;
5968 }
5969 }
5970 return K_FALSE;
5971}
5972
5973
5974/**
5975 * Creates a handle to a temporary file.
5976 *
5977 * @returns The handle on success.
5978 * INVALID_HANDLE_VALUE and SetLastError on failure.
5979 * @param pTempFile The temporary file.
5980 * @param dwDesiredAccess The desired access to the handle.
5981 * @param fMapping Whether this is a mapping (K_TRUE) or file
5982 * (K_FALSE) handle type.
5983 */
5984static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
5985{
5986 /*
5987 * Create a handle to the temporary file.
5988 */
5989 HANDLE hFile = INVALID_HANDLE_VALUE;
5990 HANDLE hProcSelf = GetCurrentProcess();
5991 if (DuplicateHandle(hProcSelf, hProcSelf,
5992 hProcSelf, &hFile,
5993 SYNCHRONIZE, FALSE,
5994 0 /*dwOptions*/))
5995 {
5996 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
5997 if (pHandle)
5998 {
5999 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6000 pHandle->cRefs = 1;
6001 pHandle->offFile = 0;
6002 pHandle->hHandle = hFile;
6003 pHandle->dwDesiredAccess = dwDesiredAccess;
6004 pHandle->u.pTempFile = pTempFile;
6005 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6006 {
6007 pTempFile->cActiveHandles++;
6008 kHlpAssert(pTempFile->cActiveHandles >= 1);
6009 kHlpAssert(pTempFile->cActiveHandles <= 2);
6010 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6011 return hFile;
6012 }
6013
6014 kHlpFree(pHandle);
6015 }
6016 else
6017 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6018 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6019 }
6020 else
6021 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6022 return INVALID_HANDLE_VALUE;
6023}
6024
6025
6026static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
6027{
6028 HANDLE hFile;
6029 DWORD dwErr;
6030
6031 /*
6032 * Check if we've got an existing temp file.
6033 * ASSUME exact same path for now.
6034 */
6035 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6036 PKWFSTEMPFILE pTempFile;
6037 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6038 {
6039 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6040 if ( pTempFile->cwcPath == cwcFilename
6041 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6042 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6043 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6044 break;
6045 }
6046
6047 /*
6048 * Create a new temporary file instance if not found.
6049 */
6050 if (pTempFile == NULL)
6051 {
6052 KSIZE cbFilename;
6053
6054 switch (dwCreationDisposition)
6055 {
6056 case CREATE_ALWAYS:
6057 case OPEN_ALWAYS:
6058 dwErr = NO_ERROR;
6059 break;
6060
6061 case CREATE_NEW:
6062 kHlpAssertFailed();
6063 SetLastError(ERROR_ALREADY_EXISTS);
6064 return INVALID_HANDLE_VALUE;
6065
6066 case OPEN_EXISTING:
6067 case TRUNCATE_EXISTING:
6068 kHlpAssertFailed();
6069 SetLastError(ERROR_FILE_NOT_FOUND);
6070 return INVALID_HANDLE_VALUE;
6071
6072 default:
6073 kHlpAssertFailed();
6074 SetLastError(ERROR_INVALID_PARAMETER);
6075 return INVALID_HANDLE_VALUE;
6076 }
6077
6078 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6079 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6080 if (pTempFile)
6081 {
6082 pTempFile->cwcPath = (KU16)cwcFilename;
6083 pTempFile->cbFile = 0;
6084 pTempFile->cbFileAllocated = 0;
6085 pTempFile->cActiveHandles = 0;
6086 pTempFile->cMappings = 0;
6087 pTempFile->cSegs = 0;
6088 pTempFile->paSegs = NULL;
6089 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6090
6091 pTempFile->pNext = g_Sandbox.pTempFileHead;
6092 g_Sandbox.pTempFileHead = pTempFile;
6093 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6094 }
6095 else
6096 {
6097 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6098 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6099 return INVALID_HANDLE_VALUE;
6100 }
6101 }
6102 else
6103 {
6104 switch (dwCreationDisposition)
6105 {
6106 case OPEN_EXISTING:
6107 dwErr = NO_ERROR;
6108 break;
6109 case OPEN_ALWAYS:
6110 dwErr = ERROR_ALREADY_EXISTS ;
6111 break;
6112
6113 case TRUNCATE_EXISTING:
6114 case CREATE_ALWAYS:
6115 kHlpAssertFailed();
6116 pTempFile->cbFile = 0;
6117 dwErr = ERROR_ALREADY_EXISTS;
6118 break;
6119
6120 case CREATE_NEW:
6121 kHlpAssertFailed();
6122 SetLastError(ERROR_FILE_EXISTS);
6123 return INVALID_HANDLE_VALUE;
6124
6125 default:
6126 kHlpAssertFailed();
6127 SetLastError(ERROR_INVALID_PARAMETER);
6128 return INVALID_HANDLE_VALUE;
6129 }
6130 }
6131
6132 /*
6133 * Create a handle to the temporary file.
6134 */
6135 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6136 if (hFile != INVALID_HANDLE_VALUE)
6137 SetLastError(dwErr);
6138 return hFile;
6139}
6140
6141#endif /* WITH_TEMP_MEMORY_FILES */
6142
6143/**
6144 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6145 *
6146 * @returns K_TRUE if cacheable, K_FALSE if not.
6147 * @param wcFirst The first extension character.
6148 * @param wcSecond The second extension character.
6149 * @param wcThird The third extension character.
6150 * @param fAttrQuery Set if it's for an attribute query, clear if for
6151 * file creation.
6152 */
6153static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6154{
6155 /* C++ header without an extension or a directory. */
6156 if (wcFirst == '\0')
6157 {
6158 /** @todo exclude temporary files... */
6159 return K_TRUE;
6160 }
6161
6162 /* C Header: .h */
6163 if (wcFirst == 'h' || wcFirst == 'H')
6164 {
6165 if (wcSecond == '\0')
6166 return K_TRUE;
6167
6168 /* C++ Header: .hpp, .hxx */
6169 if ( (wcSecond == 'p' || wcSecond == 'P')
6170 && (wcThird == 'p' || wcThird == 'P'))
6171 return K_TRUE;
6172 if ( (wcSecond == 'x' || wcSecond == 'X')
6173 && (wcThird == 'x' || wcThird == 'X'))
6174 return K_TRUE;
6175 }
6176 /* Misc starting with i. */
6177 else if (wcFirst == 'i' || wcFirst == 'I')
6178 {
6179 if (wcSecond != '\0')
6180 {
6181 if (wcSecond == 'n' || wcSecond == 'N')
6182 {
6183 /* C++ inline header: .inl */
6184 if (wcThird == 'l' || wcThird == 'L')
6185 return K_TRUE;
6186
6187 /* Assembly include file: .inc */
6188 if (wcThird == 'c' || wcThird == 'C')
6189 return K_TRUE;
6190 }
6191 }
6192 }
6193 /* Assembly header: .mac */
6194 else if (wcFirst == 'm' || wcFirst == 'M')
6195 {
6196 if (wcSecond == 'a' || wcSecond == 'A')
6197 {
6198 if (wcThird == 'c' || wcThird == 'C')
6199 return K_TRUE;
6200 }
6201 }
6202#ifdef WITH_PCH_CACHING
6203 /* Precompiled header: .pch */
6204 else if (wcFirst == 'p' || wcFirst == 'P')
6205 {
6206 if (wcSecond == 'c' || wcSecond == 'C')
6207 {
6208 if (wcThird == 'h' || wcThird == 'H')
6209 return !g_Sandbox.fNoPchCaching;
6210 }
6211 }
6212#endif
6213#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6214 /* Linker - Object file: .obj */
6215 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6216 {
6217 if (wcSecond == 'b' || wcSecond == 'B')
6218 {
6219 if (wcThird == 'j' || wcThird == 'J')
6220 return K_TRUE;
6221 }
6222 }
6223#endif
6224 else if (fAttrQuery)
6225 {
6226 /* Dynamic link library: .dll */
6227 if (wcFirst == 'd' || wcFirst == 'D')
6228 {
6229 if (wcSecond == 'l' || wcSecond == 'L')
6230 {
6231 if (wcThird == 'l' || wcThird == 'L')
6232 return K_TRUE;
6233 }
6234 }
6235 /* Executable file: .exe */
6236 else if (wcFirst == 'e' || wcFirst == 'E')
6237 {
6238 if (wcSecond == 'x' || wcSecond == 'X')
6239 {
6240 if (wcThird == 'e' || wcThird == 'E')
6241 return K_TRUE;
6242 }
6243 }
6244 /* Response file: .rsp */
6245 else if (wcFirst == 'r' || wcFirst == 'R')
6246 {
6247 if (wcSecond == 's' || wcSecond == 'S')
6248 {
6249 if (wcThird == 'p' || wcThird == 'P')
6250 return !g_Sandbox.fNoPchCaching;
6251 }
6252 }
6253 /* Linker: */
6254 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6255 {
6256 /* Object file: .obj */
6257 if (wcFirst == 'o' || wcFirst == 'O')
6258 {
6259 if (wcSecond == 'b' || wcSecond == 'B')
6260 {
6261 if (wcThird == 'j' || wcThird == 'J')
6262 return K_TRUE;
6263 }
6264 }
6265 /* Library file: .lib */
6266 else if (wcFirst == 'l' || wcFirst == 'L')
6267 {
6268 if (wcSecond == 'i' || wcSecond == 'I')
6269 {
6270 if (wcThird == 'b' || wcThird == 'B')
6271 return K_TRUE;
6272 }
6273 }
6274 /* Linker definition file: .def */
6275 else if (wcFirst == 'd' || wcFirst == 'D')
6276 {
6277 if (wcSecond == 'e' || wcSecond == 'E')
6278 {
6279 if (wcThird == 'f' || wcThird == 'F')
6280 return K_TRUE;
6281 }
6282 }
6283 }
6284 }
6285
6286 return K_FALSE;
6287}
6288
6289
6290/**
6291 * Checks if the file extension indicates that the file/dir is something we
6292 * ought to cache.
6293 *
6294 * @returns K_TRUE if cachable, K_FALSE if not.
6295 * @param pszExt The kHlpGetExt result.
6296 * @param fAttrQuery Set if it's for an attribute query, clear if for
6297 * file creation.
6298 */
6299static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6300{
6301 wchar_t const wcFirst = *pszExt;
6302 if (wcFirst)
6303 {
6304 wchar_t const wcSecond = pszExt[1];
6305 if (wcSecond)
6306 {
6307 wchar_t const wcThird = pszExt[2];
6308 if (pszExt[3] == '\0')
6309 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6310 return K_FALSE;
6311 }
6312 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6313 }
6314 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6315}
6316
6317
6318/**
6319 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6320 * should be cached.
6321 *
6322 * @returns K_TRUE if cachable, K_FALSE if not.
6323 * @param pwszPath The UTF-16 path to examine.
6324 * @param fAttrQuery Set if it's for an attribute query, clear if for
6325 * file creation.
6326 */
6327static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6328{
6329 KSIZE cwcExt;
6330 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6331 switch (cwcExt)
6332 {
6333 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6334 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6335 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6336 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6337 }
6338 return K_FALSE;
6339}
6340
6341
6342
6343/**
6344 * Creates a new
6345 *
6346 * @returns
6347 * @param pFsObj .
6348 * @param pwszFilename .
6349 */
6350static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6351{
6352 HANDLE hFile;
6353 MY_IO_STATUS_BLOCK Ios;
6354 MY_OBJECT_ATTRIBUTES ObjAttr;
6355 MY_UNICODE_STRING UniStr;
6356 MY_NTSTATUS rcNt;
6357 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6358
6359 /*
6360 * Open the file relative to the parent directory.
6361 */
6362 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6363 kHlpAssert(pFsObj->pParent);
6364 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6365
6366 Ios.Information = -1;
6367 Ios.u.Status = -1;
6368
6369 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6370 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6371 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6372
6373 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6374
6375 rcNt = g_pfnNtCreateFile(&hFile,
6376 GENERIC_READ | SYNCHRONIZE,
6377 &ObjAttr,
6378 &Ios,
6379 NULL, /*cbFileInitialAlloc */
6380 FILE_ATTRIBUTE_NORMAL,
6381 FILE_SHARE_READ,
6382 FILE_OPEN,
6383 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6384 NULL, /*pEaBuffer*/
6385 0); /*cbEaBuffer*/
6386 if (MY_NT_SUCCESS(rcNt))
6387 {
6388 /*
6389 * Read the whole file into memory.
6390 */
6391 LARGE_INTEGER cbFile;
6392 if (GetFileSizeEx(hFile, &cbFile))
6393 {
6394 if ( cbFile.QuadPart >= 0
6395#ifdef WITH_PCH_CACHING
6396 && ( cbFile.QuadPart < 16*1024*1024
6397 || ( cbFile.QuadPart < 96*1024*1024
6398 && pFsObj->cchName > 4
6399 && !g_Sandbox.fNoPchCaching
6400 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
6401#endif
6402 )
6403 {
6404 KU32 cbCache = (KU32)cbFile.QuadPart;
6405 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
6406 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
6407 if (hMapping != NULL)
6408 {
6409 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
6410 if (pbCache)
6411 {
6412 /*
6413 * Create the cached file object.
6414 */
6415 PKFSWCACHEDFILE pCachedFile;
6416 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
6417 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
6418 sizeof(*pCachedFile) + cbPath);
6419 if (pCachedFile)
6420 {
6421 pCachedFile->hCached = hFile;
6422 pCachedFile->hSection = hMapping;
6423 pCachedFile->cbCached = cbCache;
6424 pCachedFile->pbCached = pbCache;
6425 pCachedFile->pFsObj = pFsObj;
6426 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
6427 kFsCacheObjRetain(pFsObj);
6428
6429 g_cReadCachedFiles++;
6430 g_cbReadCachedFiles += cbCache;
6431
6432 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
6433 return pCachedFile;
6434 }
6435
6436 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
6437 }
6438 else
6439 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
6440 CloseHandle(hMapping);
6441 }
6442 else
6443 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
6444 }
6445 else
6446 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
6447 }
6448 else
6449 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
6450 g_pfnNtClose(hFile);
6451 }
6452 else
6453 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
6454 return NULL;
6455}
6456
6457
6458/**
6459 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
6460 */
6461static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
6462 KBOOL fIsFileHandle, HANDLE *phFile)
6463{
6464 HANDLE hProcSelf = GetCurrentProcess();
6465 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
6466 hProcSelf, phFile,
6467 dwDesiredAccess, fInheritHandle,
6468 0 /*dwOptions*/))
6469 {
6470 /*
6471 * Create handle table entry for the duplicate handle.
6472 */
6473 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6474 if (pHandle)
6475 {
6476 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
6477 pHandle->cRefs = 1;
6478 pHandle->offFile = 0;
6479 pHandle->hHandle = *phFile;
6480 pHandle->dwDesiredAccess = dwDesiredAccess;
6481 pHandle->u.pCachedFile = pCachedFile;
6482 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
6483 return K_TRUE;
6484
6485 kHlpFree(pHandle);
6486 }
6487 else
6488 KWFS_LOG(("Out of memory for handle!\n"));
6489
6490 CloseHandle(*phFile);
6491 *phFile = INVALID_HANDLE_VALUE;
6492 }
6493 else
6494 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
6495 return K_FALSE;
6496}
6497
6498
6499/**
6500 * Kernel32 - Common code for CreateFileW and CreateFileA.
6501 */
6502static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
6503{
6504 *phFile = INVALID_HANDLE_VALUE;
6505
6506 /*
6507 * At the moment we only handle existing files.
6508 */
6509 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
6510 {
6511 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
6512 kHlpAssert(pFsObj->fHaveStats);
6513 if ( pCachedFile != NULL
6514 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
6515 {
6516 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
6517 return K_TRUE;
6518 }
6519 }
6520 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
6521
6522 /* Do fallback, please. */
6523 return K_FALSE;
6524}
6525
6526
6527/** Kernel32 - CreateFileA */
6528static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6529 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6530 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6531{
6532 HANDLE hFile;
6533
6534 /*
6535 * Check for include files and similar that we do read-only caching of.
6536 */
6537 if (dwCreationDisposition == FILE_OPEN_IF)
6538 {
6539 if ( dwDesiredAccess == GENERIC_READ
6540 || dwDesiredAccess == FILE_GENERIC_READ)
6541 {
6542 if (dwShareMode & FILE_SHARE_READ)
6543 {
6544 if ( !pSecAttrs
6545 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6546 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6547 {
6548 const char *pszExt = kHlpGetExt(pszFilename);
6549 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
6550 {
6551 KFSLOOKUPERROR enmError;
6552 PKFSOBJ pFsObj;
6553 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6554
6555 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
6556 if (pFsObj)
6557 {
6558 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6559 &hFile);
6560 kFsCacheObjRelease(g_pFsCache, pFsObj);
6561 if (fRc)
6562 {
6563 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6564 return hFile;
6565 }
6566 }
6567 /* These are for nasm and yasm header searching. Cache will already
6568 have checked the directories for the file, no need to call
6569 CreateFile to do it again. */
6570 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6571 {
6572 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6573 return INVALID_HANDLE_VALUE;
6574 }
6575 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6576 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6577 {
6578 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6579 return INVALID_HANDLE_VALUE;
6580 }
6581
6582 /* fallback */
6583 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6584 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6585 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
6586 return hFile;
6587 }
6588 }
6589 }
6590 }
6591 }
6592
6593 /*
6594 * Okay, normal.
6595 */
6596 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6597 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6598 if (hFile != INVALID_HANDLE_VALUE)
6599 {
6600 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6601 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6602 }
6603 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
6604 return hFile;
6605}
6606
6607
6608/** Kernel32 - CreateFileW */
6609static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6610 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6611 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6612{
6613 HANDLE hFile;
6614
6615#ifdef WITH_TEMP_MEMORY_FILES
6616 /*
6617 * Check for temporary files (cl.exe only).
6618 */
6619 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6620 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
6621 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
6622 && kwFsIsClTempFileW(pwszFilename))
6623 {
6624 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition);
6625 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
6626 return hFile;
6627 }
6628#endif
6629
6630 /*
6631 * Check for include files and similar that we do read-only caching of.
6632 */
6633 if (dwCreationDisposition == FILE_OPEN_IF)
6634 {
6635 if ( dwDesiredAccess == GENERIC_READ
6636 || dwDesiredAccess == FILE_GENERIC_READ)
6637 {
6638 if (dwShareMode & FILE_SHARE_READ)
6639 {
6640 if ( !pSecAttrs
6641 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6642 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6643 {
6644 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
6645 {
6646 KFSLOOKUPERROR enmError;
6647 PKFSOBJ pFsObj;
6648 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6649
6650 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6651 if (pFsObj)
6652 {
6653 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6654 &hFile);
6655 kFsCacheObjRelease(g_pFsCache, pFsObj);
6656 if (fRc)
6657 {
6658 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
6659 return hFile;
6660 }
6661 }
6662 /* These are for nasm and yasm style header searching. Cache will
6663 already have checked the directories for the file, no need to call
6664 CreateFile to do it again. */
6665 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6666 {
6667 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
6668 return INVALID_HANDLE_VALUE;
6669 }
6670 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6671 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6672 {
6673 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
6674 return INVALID_HANDLE_VALUE;
6675 }
6676
6677 /* fallback */
6678 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6679 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6680 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
6681 return hFile;
6682 }
6683 }
6684 else
6685 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
6686 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
6687 }
6688 else
6689 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
6690 }
6691 else
6692 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
6693 }
6694 else
6695 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
6696
6697 /*
6698 * Okay, normal.
6699 */
6700 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6701 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6702 if (hFile != INVALID_HANDLE_VALUE)
6703 {
6704 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6705 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6706 }
6707 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
6708 return hFile;
6709}
6710
6711
6712/** Kernel32 - SetFilePointer */
6713static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
6714{
6715 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6716 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6717 if (idxHandle < g_Sandbox.cHandles)
6718 {
6719 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6720 if (pHandle != NULL)
6721 {
6722 KU32 cbFile;
6723 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
6724 switch (pHandle->enmType)
6725 {
6726 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6727 cbFile = pHandle->u.pCachedFile->cbCached;
6728 break;
6729#ifdef WITH_TEMP_MEMORY_FILES
6730 case KWHANDLETYPE_TEMP_FILE:
6731 cbFile = pHandle->u.pTempFile->cbFile;
6732 break;
6733#endif
6734 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6735 case KWHANDLETYPE_OUTPUT_BUF:
6736 default:
6737 kHlpAssertFailed();
6738 SetLastError(ERROR_INVALID_FUNCTION);
6739 return INVALID_SET_FILE_POINTER;
6740 }
6741
6742 switch (dwMoveMethod)
6743 {
6744 case FILE_BEGIN:
6745 break;
6746 case FILE_CURRENT:
6747 offMove += pHandle->offFile;
6748 break;
6749 case FILE_END:
6750 offMove += cbFile;
6751 break;
6752 default:
6753 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6754 SetLastError(ERROR_INVALID_PARAMETER);
6755 return INVALID_SET_FILE_POINTER;
6756 }
6757 if (offMove >= 0)
6758 {
6759 if (offMove >= (KSSIZE)cbFile)
6760 {
6761 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6762 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6763 offMove = (KSSIZE)cbFile;
6764 /* For writable files, seeking beyond the end is fine, but check that we've got
6765 the type range for the request. */
6766 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
6767 {
6768 kHlpAssertMsgFailed(("%#llx\n", offMove));
6769 SetLastError(ERROR_SEEK);
6770 return INVALID_SET_FILE_POINTER;
6771 }
6772 }
6773 pHandle->offFile = (KU32)offMove;
6774 }
6775 else
6776 {
6777 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
6778 SetLastError(ERROR_NEGATIVE_SEEK);
6779 return INVALID_SET_FILE_POINTER;
6780 }
6781 if (pcbMoveHi)
6782 *pcbMoveHi = (KU64)offMove >> 32;
6783 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
6784 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6785 SetLastError(NO_ERROR);
6786 return (KU32)offMove;
6787 }
6788 }
6789 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
6790 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
6791}
6792
6793
6794/** Kernel32 - SetFilePointerEx */
6795static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
6796 DWORD dwMoveMethod)
6797{
6798 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6799 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6800 if (idxHandle < g_Sandbox.cHandles)
6801 {
6802 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6803 if (pHandle != NULL)
6804 {
6805 KI64 offMyMove = offMove.QuadPart;
6806 KU32 cbFile;
6807 switch (pHandle->enmType)
6808 {
6809 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6810 cbFile = pHandle->u.pCachedFile->cbCached;
6811 break;
6812#ifdef WITH_TEMP_MEMORY_FILES
6813 case KWHANDLETYPE_TEMP_FILE:
6814 cbFile = pHandle->u.pTempFile->cbFile;
6815 break;
6816#endif
6817 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6818 case KWHANDLETYPE_OUTPUT_BUF:
6819 default:
6820 kHlpAssertFailed();
6821 SetLastError(ERROR_INVALID_FUNCTION);
6822 return INVALID_SET_FILE_POINTER;
6823 }
6824
6825 switch (dwMoveMethod)
6826 {
6827 case FILE_BEGIN:
6828 break;
6829 case FILE_CURRENT:
6830 offMyMove += pHandle->offFile;
6831 break;
6832 case FILE_END:
6833 offMyMove += cbFile;
6834 break;
6835 default:
6836 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6837 SetLastError(ERROR_INVALID_PARAMETER);
6838 return INVALID_SET_FILE_POINTER;
6839 }
6840 if (offMyMove >= 0)
6841 {
6842 if (offMyMove >= (KSSIZE)cbFile)
6843 {
6844 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6845 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6846 offMyMove = (KSSIZE)cbFile;
6847 /* For writable files, seeking beyond the end is fine, but check that we've got
6848 the type range for the request. */
6849 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
6850 {
6851 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
6852 SetLastError(ERROR_SEEK);
6853 return INVALID_SET_FILE_POINTER;
6854 }
6855 }
6856 pHandle->offFile = (KU32)offMyMove;
6857 }
6858 else
6859 {
6860 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
6861 SetLastError(ERROR_NEGATIVE_SEEK);
6862 return INVALID_SET_FILE_POINTER;
6863 }
6864 if (poffNew)
6865 poffNew->QuadPart = offMyMove;
6866 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
6867 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6868 return TRUE;
6869 }
6870 }
6871 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
6872 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
6873}
6874
6875
6876/** Kernel32 - ReadFile */
6877static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
6878 LPOVERLAPPED pOverlapped)
6879{
6880 BOOL fRet;
6881 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6882 g_cReadFileCalls++;
6883 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6884 if (idxHandle < g_Sandbox.cHandles)
6885 {
6886 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6887 if (pHandle != NULL)
6888 {
6889 switch (pHandle->enmType)
6890 {
6891 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6892 {
6893 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
6894 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
6895 if (cbActually > cbToRead)
6896 cbActually = cbToRead;
6897
6898#ifdef WITH_HASH_MD5_CACHE
6899 if (g_Sandbox.pHashHead)
6900 {
6901 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
6902 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
6903 g_Sandbox.LastHashRead.cbRead = cbActually;
6904 g_Sandbox.LastHashRead.pvRead = pvBuffer;
6905 }
6906#endif
6907
6908 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
6909 pHandle->offFile += cbActually;
6910
6911 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6912 *pcbActuallyRead = cbActually;
6913
6914 g_cbReadFileFromReadCached += cbActually;
6915 g_cbReadFileTotal += cbActually;
6916 g_cReadFileFromReadCached++;
6917
6918 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
6919 return TRUE;
6920 }
6921
6922#ifdef WITH_TEMP_MEMORY_FILES
6923 case KWHANDLETYPE_TEMP_FILE:
6924 {
6925 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6926 KU32 cbActually;
6927 if (pHandle->offFile < pTempFile->cbFile)
6928 {
6929 cbActually = pTempFile->cbFile - pHandle->offFile;
6930 if (cbActually > cbToRead)
6931 cbActually = cbToRead;
6932
6933 /* Copy the data. */
6934 if (cbActually > 0)
6935 {
6936 KU32 cbLeft;
6937 KU32 offSeg;
6938 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
6939
6940 /* Locate the segment containing the byte at offFile. */
6941 KU32 iSeg = pTempFile->cSegs - 1;
6942 kHlpAssert(pTempFile->cSegs > 0);
6943 while (paSegs[iSeg].offData > pHandle->offFile)
6944 iSeg--;
6945
6946 /* Copy out the data. */
6947 cbLeft = cbActually;
6948 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6949 for (;;)
6950 {
6951 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6952 if (cbAvail >= cbLeft)
6953 {
6954 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
6955 break;
6956 }
6957
6958 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
6959 cbLeft -= cbAvail;
6960 offSeg = 0;
6961 iSeg++;
6962 kHlpAssert(iSeg < pTempFile->cSegs);
6963 }
6964
6965 /* Update the file offset. */
6966 pHandle->offFile += cbActually;
6967 }
6968 }
6969 /* Read does not commit file space, so return zero bytes. */
6970 else
6971 cbActually = 0;
6972
6973 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6974 *pcbActuallyRead = cbActually;
6975
6976 g_cbReadFileTotal += cbActually;
6977 g_cbReadFileFromInMemTemp += cbActually;
6978 g_cReadFileFromInMemTemp++;
6979
6980 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
6981 return TRUE;
6982 }
6983#endif /* WITH_TEMP_MEMORY_FILES */
6984
6985 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6986 case KWHANDLETYPE_OUTPUT_BUF:
6987 default:
6988 kHlpAssertFailed();
6989 SetLastError(ERROR_INVALID_FUNCTION);
6990 *pcbActuallyRead = 0;
6991 return FALSE;
6992 }
6993 }
6994 }
6995
6996 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
6997 if (fRet && pcbActuallyRead)
6998 g_cbReadFileTotal += *pcbActuallyRead;
6999 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7000 return fRet;
7001}
7002
7003
7004/** Kernel32 - ReadFileEx */
7005static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7006 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7007{
7008 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7009 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7010 if (idxHandle < g_Sandbox.cHandles)
7011 {
7012 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7013 if (pHandle != NULL)
7014 {
7015 kHlpAssertFailed();
7016 }
7017 }
7018
7019 KWFS_LOG(("ReadFile(%p)\n", hFile));
7020 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7021}
7022
7023#ifdef WITH_STD_OUT_ERR_BUFFERING
7024
7025/**
7026 * Write something to a handle, making sure everything is actually written.
7027 *
7028 * @param hHandle Where to write it to.
7029 * @param pchBuf What to write
7030 * @param cchToWrite How much to write.
7031 */
7032static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7033{
7034 if (cchToWrite > 0)
7035 {
7036 DWORD cchWritten = 0;
7037 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7038 {
7039 if (cchWritten == cchToWrite)
7040 { /* likely */ }
7041 else
7042 {
7043 do
7044 {
7045 pchBuf += cchWritten;
7046 cchToWrite -= cchWritten;
7047 cchWritten = 0;
7048 } while ( cchToWrite > 0
7049 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7050 }
7051 }
7052 else
7053 kHlpAssertFailed();
7054 }
7055}
7056
7057
7058/**
7059 * Worker for WriteFile when the output isn't going to the console.
7060 *
7061 * @param pSandbox The sandbox.
7062 * @param pOutBuf The output buffer.
7063 * @param pchBuffer What to write.
7064 * @param cchToWrite How much to write.
7065 */
7066static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7067{
7068 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7069 { /* likely */ }
7070 else
7071 {
7072 /* No realloc, max size is 64KB. */
7073 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7074 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7075 if (!pOutBuf->u.Fully.pchBuf)
7076 {
7077 while ( !pOutBuf->u.Fully.pchBuf
7078 && pOutBuf->u.Fully.cchBufAlloc > 64)
7079 {
7080 pOutBuf->u.Fully.cchBufAlloc /= 2;
7081 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7082 }
7083 if (!pOutBuf->u.Fully.pchBuf)
7084 {
7085 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7086 pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
7087 }
7088 }
7089 }
7090
7091 /*
7092 * Special case: Output ends with newline and fits in the buffer.
7093 */
7094 if ( cchToWrite > 1
7095 && pchBuffer[cchToWrite - 1] == '\n'
7096 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7097 {
7098 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7099 pOutBuf->u.Fully.cchBuf += cchToWrite;
7100 }
7101 else
7102 {
7103 /*
7104 * Work thru the text line by line, flushing the buffer when
7105 * appropriate. The buffer is not a line buffer here, it's a
7106 * full buffer.
7107 */
7108 while (cchToWrite > 0)
7109 {
7110 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7111 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7112 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7113 {
7114 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7115 pOutBuf->u.Fully.cchBuf += cchLine;
7116 }
7117 /*
7118 * Option one: Flush the buffer and the current line.
7119 *
7120 * We choose this one when the line won't ever fit, or when we have
7121 * an incomplete line in the buffer.
7122 */
7123 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7124 || pOutBuf->u.Fully.cchBuf == 0
7125 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7126 {
7127 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7128 if (pOutBuf->u.Fully.cchBuf > 0)
7129 {
7130 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7131 pOutBuf->u.Fully.cchBuf = 0;
7132 }
7133 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7134 }
7135 /*
7136 * Option two: Only flush the lines in the buffer.
7137 */
7138 else
7139 {
7140 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7141 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7142 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7143 pOutBuf->u.Fully.cchBuf = cchLine;
7144 }
7145
7146 /* advance */
7147 pchBuffer += cchLine;
7148 cchToWrite -= cchLine;
7149 }
7150 }
7151}
7152
7153#endif /* WITH_STD_OUT_ERR_BUFFERING */
7154
7155#ifdef WITH_TEMP_MEMORY_FILES
7156static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7157{
7158 KU32 cbMinFile = offFile + cbNeeded;
7159 if (cbMinFile >= offFile)
7160 {
7161 /* Calc how much space we've already allocated and */
7162 if (cbMinFile <= pTempFile->cbFileAllocated)
7163 return K_TRUE;
7164
7165 /* Grow the file. */
7166 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7167 {
7168 int rc;
7169 KU32 cSegs = pTempFile->cSegs;
7170 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7171 do
7172 {
7173 /* grow the segment array? */
7174 if ((cSegs % 16) == 0)
7175 {
7176 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7177 if (!pvNew)
7178 return K_FALSE;
7179 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7180 }
7181
7182 /* Use page alloc here to simplify mapping later. */
7183 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7184 if (rc == 0)
7185 { /* likely */ }
7186 else
7187 {
7188 cbNewSeg = 64*1024;
7189 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7190 if (rc != 0)
7191 {
7192 kHlpAssertFailed();
7193 return K_FALSE;
7194 }
7195 }
7196 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7197 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7198 pTempFile->cbFileAllocated += cbNewSeg;
7199 pTempFile->cSegs = ++cSegs;
7200
7201 } while (pTempFile->cbFileAllocated < cbMinFile);
7202
7203 return K_TRUE;
7204 }
7205 }
7206
7207 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7208 return K_FALSE;
7209}
7210#endif /* WITH_TEMP_MEMORY_FILES */
7211
7212
7213#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7214/** Kernel32 - WriteFile */
7215static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7216 LPOVERLAPPED pOverlapped)
7217{
7218 BOOL fRet;
7219 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7220 g_cWriteFileCalls++;
7221 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7222 if (idxHandle < g_Sandbox.cHandles)
7223 {
7224 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7225 if (pHandle != NULL)
7226 {
7227 switch (pHandle->enmType)
7228 {
7229# ifdef WITH_TEMP_MEMORY_FILES
7230 case KWHANDLETYPE_TEMP_FILE:
7231 {
7232 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7233
7234 kHlpAssert(!pOverlapped);
7235 kHlpAssert(pcbActuallyWritten);
7236
7237 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7238 {
7239 KU32 cbLeft;
7240 KU32 offSeg;
7241
7242 /* Locate the segment containing the byte at offFile. */
7243 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7244 KU32 iSeg = pTempFile->cSegs - 1;
7245 kHlpAssert(pTempFile->cSegs > 0);
7246 while (paSegs[iSeg].offData > pHandle->offFile)
7247 iSeg--;
7248
7249 /* Copy in the data. */
7250 cbLeft = cbToWrite;
7251 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7252 for (;;)
7253 {
7254 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7255 if (cbAvail >= cbLeft)
7256 {
7257 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7258 break;
7259 }
7260
7261 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7262 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7263 cbLeft -= cbAvail;
7264 offSeg = 0;
7265 iSeg++;
7266 kHlpAssert(iSeg < pTempFile->cSegs);
7267 }
7268
7269 /* Update the file offset. */
7270 pHandle->offFile += cbToWrite;
7271 if (pHandle->offFile > pTempFile->cbFile)
7272 pTempFile->cbFile = pHandle->offFile;
7273
7274 *pcbActuallyWritten = cbToWrite;
7275
7276 g_cbWriteFileTotal += cbToWrite;
7277 g_cbWriteFileToInMemTemp += cbToWrite;
7278 g_cWriteFileToInMemTemp++;
7279
7280 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7281 return TRUE;
7282 }
7283
7284 kHlpAssertFailed();
7285 *pcbActuallyWritten = 0;
7286 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7287 return FALSE;
7288 }
7289# endif
7290
7291 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7292 kHlpAssertFailed();
7293 SetLastError(ERROR_ACCESS_DENIED);
7294 *pcbActuallyWritten = 0;
7295 return FALSE;
7296
7297# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7298 /*
7299 * Standard output & error.
7300 */
7301 case KWHANDLETYPE_OUTPUT_BUF:
7302 {
7303 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7304 if (pOutBuf->fIsConsole)
7305 {
7306 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7307 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7308 }
7309 else
7310 {
7311# ifdef WITH_STD_OUT_ERR_BUFFERING
7312 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7313 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7314 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7315# else
7316 kHlpAssertFailed();
7317# endif
7318 }
7319 if (pcbActuallyWritten)
7320 *pcbActuallyWritten = cbToWrite;
7321 g_cbWriteFileTotal += cbToWrite;
7322 return TRUE;
7323 }
7324# endif
7325
7326 default:
7327 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7328 kHlpAssertFailed();
7329 SetLastError(ERROR_INVALID_FUNCTION);
7330 *pcbActuallyWritten = 0;
7331 return FALSE;
7332 }
7333 }
7334 }
7335
7336 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7337 if (fRet && pcbActuallyWritten)
7338 g_cbWriteFileTotal += *pcbActuallyWritten;
7339 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7340 return fRet;
7341}
7342
7343
7344/** Kernel32 - WriteFileEx */
7345static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7346 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7347{
7348 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7349 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7350 if (idxHandle < g_Sandbox.cHandles)
7351 {
7352 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7353 if (pHandle != NULL)
7354 {
7355 kHlpAssertFailed();
7356 }
7357 }
7358
7359 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
7360 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
7361}
7362
7363#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
7364
7365#ifdef WITH_TEMP_MEMORY_FILES
7366
7367/** Kernel32 - SetEndOfFile; */
7368static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
7369{
7370 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7371 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7372 if (idxHandle < g_Sandbox.cHandles)
7373 {
7374 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7375 if (pHandle != NULL)
7376 {
7377 switch (pHandle->enmType)
7378 {
7379 case KWHANDLETYPE_TEMP_FILE:
7380 {
7381 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7382 if ( pHandle->offFile > pTempFile->cbFile
7383 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
7384 {
7385 kHlpAssertFailed();
7386 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7387 return FALSE;
7388 }
7389
7390 pTempFile->cbFile = pHandle->offFile;
7391 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
7392 return TRUE;
7393 }
7394
7395 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7396 kHlpAssertFailed();
7397 SetLastError(ERROR_ACCESS_DENIED);
7398 return FALSE;
7399
7400 case KWHANDLETYPE_OUTPUT_BUF:
7401 kHlpAssertFailed();
7402 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
7403 return FALSE;
7404
7405 default:
7406 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7407 kHlpAssertFailed();
7408 SetLastError(ERROR_INVALID_FUNCTION);
7409 return FALSE;
7410 }
7411 }
7412 }
7413
7414 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
7415 return SetEndOfFile(hFile);
7416}
7417
7418
7419/** Kernel32 - GetFileType */
7420static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
7421{
7422 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7423 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7424 if (idxHandle < g_Sandbox.cHandles)
7425 {
7426 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7427 if (pHandle != NULL)
7428 {
7429 switch (pHandle->enmType)
7430 {
7431 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7432 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
7433 return FILE_TYPE_DISK;
7434
7435 case KWHANDLETYPE_TEMP_FILE:
7436 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
7437 return FILE_TYPE_DISK;
7438
7439 case KWHANDLETYPE_OUTPUT_BUF:
7440 {
7441 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7442 DWORD fRet;
7443 if (pOutBuf->fFileType != KU8_MAX)
7444 {
7445 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
7446 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
7447 }
7448 else
7449 {
7450 fRet = GetFileType(hFile);
7451 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
7452 }
7453 return fRet;
7454 }
7455
7456 }
7457 }
7458 }
7459
7460 KWFS_LOG(("GetFileType(%p)\n", hFile));
7461 return GetFileType(hFile);
7462}
7463
7464
7465/** Kernel32 - GetFileSize */
7466static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
7467{
7468 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7469 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7470 if (idxHandle < g_Sandbox.cHandles)
7471 {
7472 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7473 if (pHandle != NULL)
7474 {
7475 if (pcbHighDword)
7476 *pcbHighDword = 0;
7477 SetLastError(NO_ERROR);
7478 switch (pHandle->enmType)
7479 {
7480 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7481 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7482 return pHandle->u.pCachedFile->cbCached;
7483
7484 case KWHANDLETYPE_TEMP_FILE:
7485 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7486 return pHandle->u.pTempFile->cbFile;
7487
7488 case KWHANDLETYPE_OUTPUT_BUF:
7489 /* do default */
7490 break;
7491
7492 default:
7493 kHlpAssertFailed();
7494 SetLastError(ERROR_INVALID_FUNCTION);
7495 return INVALID_FILE_SIZE;
7496 }
7497 }
7498 }
7499
7500 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
7501 return GetFileSize(hFile, pcbHighDword);
7502}
7503
7504
7505/** Kernel32 - GetFileSizeEx */
7506static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
7507{
7508 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7509 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7510 if (idxHandle < g_Sandbox.cHandles)
7511 {
7512 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7513 if (pHandle != NULL)
7514 {
7515 switch (pHandle->enmType)
7516 {
7517 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7518 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7519 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
7520 return TRUE;
7521
7522 case KWHANDLETYPE_TEMP_FILE:
7523 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7524 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
7525 return TRUE;
7526
7527 case KWHANDLETYPE_OUTPUT_BUF:
7528 /* do default */
7529 break;
7530
7531 default:
7532 kHlpAssertFailed();
7533 SetLastError(ERROR_INVALID_FUNCTION);
7534 return INVALID_FILE_SIZE;
7535 }
7536 }
7537 }
7538
7539 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
7540 return GetFileSizeEx(hFile, pcbFile);
7541}
7542
7543
7544/** Kernel32 - CreateFileMappingW */
7545static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
7546 DWORD fProtect, DWORD dwMaximumSizeHigh,
7547 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
7548{
7549 HANDLE hMapping;
7550 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7551 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7552 if (idxHandle < g_Sandbox.cHandles)
7553 {
7554 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7555 if (pHandle != NULL)
7556 {
7557 switch (pHandle->enmType)
7558 {
7559 case KWHANDLETYPE_TEMP_FILE:
7560 {
7561 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7562 if ( ( fProtect == PAGE_READONLY
7563 || fProtect == PAGE_EXECUTE_READ)
7564 && dwMaximumSizeHigh == 0
7565 && ( dwMaximumSizeLow == 0
7566 || dwMaximumSizeLow == pTempFile->cbFile)
7567 && pwszName == NULL)
7568 {
7569 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
7570 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
7571 return hMapping;
7572 }
7573 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
7574 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7575 SetLastError(ERROR_ACCESS_DENIED);
7576 return INVALID_HANDLE_VALUE;
7577 }
7578
7579 /* moc.exe benefits from this. */
7580 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7581 {
7582 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7583 if ( ( fProtect == PAGE_READONLY
7584 || fProtect == PAGE_EXECUTE_READ)
7585 && dwMaximumSizeHigh == 0
7586 && ( dwMaximumSizeLow == 0
7587 || dwMaximumSizeLow == pCachedFile->cbCached)
7588 && pwszName == NULL)
7589 {
7590 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
7591 K_FALSE /*fIsFileHandle*/, &hMapping))
7592 { /* likely */ }
7593 else
7594 hMapping = NULL;
7595 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
7596 return hMapping;
7597 }
7598
7599 /* Do fallback (for .pch). */
7600 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
7601 ("fProtect=%#x cb=%#x'%08x name=%p\n",
7602 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7603
7604 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7605 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
7606 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7607 return hMapping;
7608 }
7609
7610 /** @todo read cached memory mapped files too for moc. */
7611 }
7612 }
7613 }
7614
7615 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7616 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
7617 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7618 return hMapping;
7619}
7620
7621
7622/** Kernel32 - MapViewOfFile */
7623static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
7624 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
7625{
7626 PVOID pvRet;
7627 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7628 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7629 if (idxHandle < g_Sandbox.cHandles)
7630 {
7631 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7632 if (pHandle != NULL)
7633 {
7634 KU32 idxMapping;
7635
7636 /*
7637 * Ensure one free entry in the mapping tracking table first,
7638 * since this is common to both temporary and cached files.
7639 */
7640 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
7641 { /* likely */ }
7642 else
7643 {
7644 void *pvNew;
7645 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
7646 if (cNew)
7647 cNew *= 2;
7648 else
7649 cNew = 32;
7650 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
7651 if (pvNew)
7652 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
7653 else
7654 {
7655 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
7656 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7657 return NULL;
7658 }
7659 g_Sandbox.cMemMappingsAlloc = cNew;
7660 }
7661
7662 /*
7663 * Type specific work.
7664 */
7665 switch (pHandle->enmType)
7666 {
7667 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7668 case KWHANDLETYPE_TEMP_FILE:
7669 case KWHANDLETYPE_OUTPUT_BUF:
7670 default:
7671 kHlpAssertFailed();
7672 SetLastError(ERROR_INVALID_OPERATION);
7673 return NULL;
7674
7675 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7676 {
7677 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7678 if ( dwDesiredAccess == FILE_MAP_READ
7679 && offFileHigh == 0
7680 && offFileLow == 0
7681 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
7682 {
7683 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
7684 if (pTempFile->cSegs != 1)
7685 {
7686 KU32 iSeg;
7687 KU32 cbLeft;
7688 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
7689 KU8 *pbAll = NULL;
7690 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
7691 if (rc != 0)
7692 {
7693 kHlpAssertFailed();
7694 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7695 return NULL;
7696 }
7697
7698 cbLeft = pTempFile->cbFile;
7699 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
7700 {
7701 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
7702 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
7703 cbLeft -= cbToCopy;
7704 }
7705
7706 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
7707 {
7708 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
7709 pTempFile->paSegs[iSeg].pbData = NULL;
7710 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
7711 }
7712
7713 pTempFile->cSegs = 1;
7714 pTempFile->cbFileAllocated = cbAll;
7715 pTempFile->paSegs[0].cbDataAlloc = cbAll;
7716 pTempFile->paSegs[0].pbData = pbAll;
7717 pTempFile->paSegs[0].offData = 0;
7718 }
7719
7720 pTempFile->cMappings++;
7721 kHlpAssert(pTempFile->cMappings == 1);
7722
7723 pvRet = pTempFile->paSegs[0].pbData;
7724 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
7725 break;
7726 }
7727
7728 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7729 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
7730 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7731 return NULL;
7732 }
7733
7734 /*
7735 * This is simple in comparison to the above temporary file code.
7736 */
7737 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7738 {
7739 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7740 if ( dwDesiredAccess == FILE_MAP_READ
7741 && offFileHigh == 0
7742 && offFileLow == 0
7743 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
7744 {
7745 pvRet = pCachedFile->pbCached;
7746 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
7747 break;
7748 }
7749 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7750 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
7751 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7752 return NULL;
7753 }
7754 }
7755
7756 /*
7757 * Insert into the mapping tracking table. This is common
7758 * and we should only get here with a non-NULL pvRet.
7759 *
7760 * Note! We could look for duplicates and do ref counting, but it's
7761 * easier to just append for now.
7762 */
7763 kHlpAssert(pvRet != NULL);
7764 idxMapping = g_Sandbox.cMemMappings;
7765 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
7766
7767 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
7768 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
7769 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
7770 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
7771 g_Sandbox.cMemMappings++;
7772
7773 return pvRet;
7774 }
7775 }
7776
7777 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7778 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
7779 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
7780 return pvRet;
7781}
7782
7783
7784/** Kernel32 - MapViewOfFileEx */
7785static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
7786 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
7787{
7788 PVOID pvRet;
7789 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7790 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7791 if (idxHandle < g_Sandbox.cHandles)
7792 {
7793 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7794 if (pHandle != NULL)
7795 {
7796 switch (pHandle->enmType)
7797 {
7798 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7799 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
7800 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7801 if (!pvMapAddr)
7802 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7803 kHlpAssertFailed();
7804 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7805 return NULL;
7806
7807 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7808 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
7809 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7810 if (!pvMapAddr)
7811 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7812 /* We can use fallback here as the handle is an actual section handle. */
7813 break;
7814
7815 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7816 case KWHANDLETYPE_TEMP_FILE:
7817 case KWHANDLETYPE_OUTPUT_BUF:
7818 default:
7819 kHlpAssertFailed();
7820 SetLastError(ERROR_INVALID_OPERATION);
7821 return NULL;
7822 }
7823 }
7824 }
7825
7826 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
7827 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
7828 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
7829 return pvRet;
7830
7831}
7832
7833/** Kernel32 - UnmapViewOfFile */
7834static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
7835{
7836 /*
7837 * Consult the memory mapping tracker.
7838 */
7839 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
7840 KU32 idxMapping = g_Sandbox.cMemMappings;
7841 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7842 while (idxMapping-- > 0)
7843 if (paMemMappings[idxMapping].pvMapping == pvBase)
7844 {
7845 /* Type specific stuff. */
7846 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
7847 {
7848 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
7849 paMemMappings[idxMapping].u.pTempFile->cMappings--;
7850 }
7851 else
7852 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
7853
7854 /* Deref and probably free it. */
7855 if (--paMemMappings[idxMapping].cRefs == 0)
7856 {
7857 g_Sandbox.cMemMappings--;
7858 if (idxMapping != g_Sandbox.cMemMappings)
7859 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
7860 }
7861 return TRUE;
7862 }
7863
7864 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
7865 return UnmapViewOfFile(pvBase);
7866}
7867
7868/** @todo UnmapViewOfFileEx */
7869
7870#endif /* WITH_TEMP_MEMORY_FILES */
7871
7872
7873/** Kernel32 - DuplicateHandle */
7874static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
7875 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
7876{
7877 BOOL fRet;
7878
7879 /*
7880 * We must catch our handles being duplicated.
7881 */
7882 if (hSrcProc == GetCurrentProcess())
7883 {
7884 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
7885 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7886 if (idxHandle < g_Sandbox.cHandles)
7887 {
7888 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7889 if (pHandle)
7890 {
7891 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7892 if (fRet)
7893 {
7894 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
7895 {
7896 pHandle->cRefs++;
7897 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
7898 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
7899 pHandle->enmType, pHandle->cRefs));
7900 }
7901 else
7902 {
7903 fRet = FALSE;
7904 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7905 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
7906 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
7907 }
7908 }
7909 else
7910 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
7911 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
7912 return fRet;
7913 }
7914 }
7915 }
7916
7917 /*
7918 * Not one of ours, just do what the caller asks and log it.
7919 */
7920 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7921 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
7922 fInheritHandle, dwOptions, fRet, *phNew));
7923 return fRet;
7924}
7925
7926
7927/** Kernel32 - CloseHandle */
7928static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
7929{
7930 BOOL fRet;
7931 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
7932 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7933 if (idxHandle < g_Sandbox.cHandles)
7934 {
7935 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7936 if (pHandle)
7937 {
7938 /* Prevent the closing of the standard output and error handles. */
7939 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
7940 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
7941 {
7942 fRet = CloseHandle(hObject);
7943 if (fRet)
7944 {
7945 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7946 g_Sandbox.papHandles[idxHandle] = NULL;
7947 g_Sandbox.cActiveHandles--;
7948 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
7949 if (--pHandle->cRefs == 0)
7950 {
7951#ifdef WITH_TEMP_MEMORY_FILES
7952 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
7953 {
7954 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
7955 pHandle->u.pTempFile->cActiveHandles--;
7956 }
7957#endif
7958 kHlpFree(pHandle);
7959 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
7960 }
7961 else
7962 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
7963 }
7964 else
7965 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
7966 }
7967 else
7968 {
7969 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
7970 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
7971 fRet = TRUE;
7972 }
7973 return fRet;
7974 }
7975 }
7976
7977 fRet = CloseHandle(hObject);
7978 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
7979 return fRet;
7980}
7981
7982
7983/** Kernel32 - GetFileAttributesA. */
7984static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
7985{
7986 DWORD fRet;
7987 const char *pszExt = kHlpGetExt(pszFilename);
7988 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
7989 {
7990 KFSLOOKUPERROR enmError;
7991 PKFSOBJ pFsObj;
7992 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7993
7994 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
7995 if (pFsObj)
7996 {
7997 kHlpAssert(pFsObj->fHaveStats);
7998 fRet = pFsObj->Stats.st_attribs;
7999 kFsCacheObjRelease(g_pFsCache, pFsObj);
8000 }
8001 else
8002 {
8003 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8004 fRet = INVALID_FILE_ATTRIBUTES;
8005 }
8006
8007 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8008 return fRet;
8009 }
8010
8011 fRet = GetFileAttributesA(pszFilename);
8012 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8013 return fRet;
8014}
8015
8016
8017/** Kernel32 - GetFileAttributesW. */
8018static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8019{
8020 DWORD fRet;
8021 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8022 {
8023 KFSLOOKUPERROR enmError;
8024 PKFSOBJ pFsObj;
8025 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8026
8027 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8028 if (pFsObj)
8029 {
8030 kHlpAssert(pFsObj->fHaveStats);
8031 fRet = pFsObj->Stats.st_attribs;
8032 kFsCacheObjRelease(g_pFsCache, pFsObj);
8033 }
8034 else
8035 {
8036 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8037 fRet = INVALID_FILE_ATTRIBUTES;
8038 }
8039
8040 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8041 return fRet;
8042 }
8043
8044 fRet = GetFileAttributesW(pwszFilename);
8045 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8046 return fRet;
8047}
8048
8049
8050/** Kernel32 - GetFileAttributesExA. */
8051static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8052 WIN32_FILE_ATTRIBUTE_DATA *pData)
8053{
8054 BOOL fRet;
8055 const char *pszExt = kHlpGetExt(pszFilename);
8056 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8057 {
8058 KFSLOOKUPERROR enmError;
8059 PKFSOBJ pFsObj;
8060 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8061
8062 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8063 if (pFsObj)
8064 {
8065 kHlpAssert(pFsObj->fHaveStats);
8066 if (enmLevel == GetFileExInfoStandard)
8067 {
8068 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8069 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8070 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8071 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8072 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8073 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8074 kFsCacheObjRelease(g_pFsCache, pFsObj);
8075 }
8076 else
8077 {
8078 kFsCacheObjRelease(g_pFsCache, pFsObj);
8079 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8080 }
8081 }
8082 else
8083 {
8084 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8085 fRet = FALSE;
8086 }
8087
8088 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8089 return fRet;
8090 }
8091
8092 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8093 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8094 return fRet;
8095}
8096
8097
8098/** Kernel32 - GetFileAttributesExW. */
8099static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8100 WIN32_FILE_ATTRIBUTE_DATA *pData)
8101{
8102 BOOL fRet;
8103 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8104 {
8105 KFSLOOKUPERROR enmError;
8106 PKFSOBJ pFsObj;
8107 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8108
8109 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8110 if (pFsObj)
8111 {
8112 kHlpAssert(pFsObj->fHaveStats);
8113 if (enmLevel == GetFileExInfoStandard)
8114 {
8115 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8116 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8117 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8118 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8119 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8120 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8121 kFsCacheObjRelease(g_pFsCache, pFsObj);
8122 }
8123 else
8124 {
8125 kFsCacheObjRelease(g_pFsCache, pFsObj);
8126 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8127 }
8128 }
8129 else
8130 {
8131 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8132 fRet = FALSE;
8133 }
8134
8135 KWFS_LOG(("GetFileAttributesW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8136 return fRet;
8137 }
8138
8139 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8140 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8141 return fRet;
8142}
8143
8144
8145/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8146 * directory containing each include file. We cache the result to speed
8147 * things up a little. */
8148static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8149{
8150 DWORD cwcRet;
8151 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8152 {
8153 KFSLOOKUPERROR enmError;
8154 PKFSOBJ pObj;
8155 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8156
8157 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8158 if (pObj)
8159 {
8160 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8161 {
8162 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8163 {
8164 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8165
8166 /* Should preserve trailing slash on directory paths. */
8167 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8168 {
8169 if ( cwcRet + 1 < cwcShortPath
8170 && pwszShortPath[cwcRet - 1] != '\\')
8171 {
8172 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8173 if ( cwcIn > 0
8174 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8175 {
8176 pwszShortPath[cwcRet++] = '\\';
8177 pwszShortPath[cwcRet] = '\0';
8178 }
8179 }
8180 }
8181
8182 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8183 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8184 kFsCacheObjRelease(g_pFsCache, pObj);
8185 return cwcRet;
8186 }
8187
8188 /* fall back for complicated cases. */
8189 }
8190 kFsCacheObjRelease(g_pFsCache, pObj);
8191 }
8192 }
8193 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8194 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8195 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8196 return cwcRet;
8197}
8198
8199
8200#ifdef WITH_TEMP_MEMORY_FILES
8201/** Kernel32 - DeleteFileW
8202 * Skip deleting the in-memory files. */
8203static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8204{
8205 BOOL fRc;
8206 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8207 && kwFsIsClTempFileW(pwszFilename))
8208 {
8209 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8210 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8211 fRc = TRUE;
8212 }
8213 else
8214 {
8215 fRc = DeleteFileW(pwszFilename);
8216 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8217 }
8218 return fRc;
8219}
8220#endif /* WITH_TEMP_MEMORY_FILES */
8221
8222
8223
8224#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8225
8226/*
8227 *
8228 * Console output buffering.
8229 * Console output buffering.
8230 * Console output buffering.
8231 *
8232 */
8233
8234
8235/**
8236 * Write a wide char string to the console.
8237 *
8238 * @param pSandbox The sandbox which output buffer to flush.
8239 */
8240static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8241{
8242 if (cwcToWrite > 0)
8243 {
8244 DWORD cwcWritten = 0;
8245 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8246 {
8247 if (cwcWritten == cwcToWrite)
8248 { /* likely */ }
8249 else
8250 {
8251 DWORD off = 0;
8252 do
8253 {
8254 off += cwcWritten;
8255 cwcWritten = 0;
8256 } while ( off < cwcToWrite
8257 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8258 kHlpAssert(off == cwcWritten);
8259 }
8260 }
8261 else
8262 kHlpAssertFailed();
8263 pSandbox->Combined.cFlushes++;
8264 }
8265}
8266
8267
8268/**
8269 * Flushes the combined console output buffer.
8270 *
8271 * @param pSandbox The sandbox which output buffer to flush.
8272 */
8273static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8274{
8275 if (pSandbox->Combined.cwcBuf > 0)
8276 {
8277 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8278 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8279 pSandbox->Combined.cwcBuf = 0;
8280 }
8281}
8282
8283
8284/**
8285 * For handling combined buffer overflow cases line by line.
8286 *
8287 * @param pSandbox The sandbox.
8288 * @param pwcBuf What to add to the combined buffer. Usually a
8289 * line, unless we're really low on buffer space.
8290 * @param cwcBuf The length of what to add.
8291 * @param fBrokenLine Whether this is a broken line.
8292 */
8293static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8294{
8295 if (fBrokenLine)
8296 kwSandboxConsoleFlushCombined(pSandbox);
8297 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8298 {
8299 kwSandboxConsoleFlushCombined(pSandbox);
8300 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
8301 }
8302 else
8303 {
8304 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
8305 pSandbox->Combined.cwcBuf += cwcBuf;
8306 }
8307}
8308
8309
8310/**
8311 * Called to final flush a line buffer via the combined buffer (if applicable).
8312 *
8313 * @param pSandbox The sandbox.
8314 * @param pLineBuf The line buffer.
8315 * @param pszName The line buffer name (for logging)
8316 */
8317static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
8318{
8319 if (pLineBuf->fIsConsole)
8320 {
8321 if (pLineBuf->u.Con.cwcBuf > 0)
8322 {
8323 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
8324
8325 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
8326 {
8327 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
8328 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
8329 }
8330 else
8331 {
8332 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
8333 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
8334 }
8335 pLineBuf->u.Con.cwcBuf = 0;
8336 }
8337 }
8338#ifdef WITH_STD_OUT_ERR_BUFFERING
8339 else if (pLineBuf->u.Fully.cchBuf > 0)
8340 {
8341 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
8342
8343 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
8344 pLineBuf->u.Fully.cchBuf = 0;
8345 }
8346#endif
8347}
8348
8349
8350/**
8351 * Called at the end of sandboxed execution to flush both stream buffers.
8352 *
8353 * @param pSandbox The sandbox.
8354 */
8355static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
8356{
8357 /*
8358 * First do the cl.exe source file supression trick, if applicable.
8359 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
8360 * handle.
8361 */
8362 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8363 && pSandbox->Combined.cFlushes == 0)
8364 {
8365 if ( pSandbox->StdOut.fIsConsole
8366 || pSandbox->StdErr.fIsConsole)
8367 {
8368 if ( pSandbox->Combined.cwcBuf >= 3
8369 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
8370 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
8371 {
8372 KI32 off = pSandbox->Combined.cwcBuf - 1;
8373 if (pSandbox->Combined.wszBuf[off] == '\n')
8374 {
8375 KBOOL fOk = K_TRUE;
8376 while (off-- > 0)
8377 {
8378 wchar_t const wc = pSandbox->Combined.wszBuf[off];
8379 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
8380 { /* likely */ }
8381 else
8382 {
8383 fOk = K_FALSE;
8384 break;
8385 }
8386 }
8387 if (fOk)
8388 {
8389 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
8390 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8391 pSandbox->Combined.cwcBuf = 0;
8392 return;
8393 }
8394 }
8395 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
8396 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8397 }
8398 }
8399#ifdef WITH_STD_OUT_ERR_BUFFERING
8400 /*
8401 * Otherwise, it goes to standard output (redirected).
8402 */
8403 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
8404 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
8405 {
8406 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
8407 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
8408 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
8409
8410 if (pchBuf[off] == '\n')
8411 {
8412 KBOOL fOk = K_TRUE;
8413 if (pchBuf[off - 1] == '\r')
8414 off--;
8415 while (off-- > 0)
8416 {
8417 char const ch = pchBuf[off];
8418 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
8419 { /* likely */ }
8420 else
8421 {
8422 fOk = K_FALSE;
8423 break;
8424 }
8425 }
8426 if (fOk)
8427 {
8428 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
8429 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8430 pSandbox->StdOut.u.Fully.cchBuf = 0;
8431 return;
8432 }
8433 }
8434 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
8435 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8436 }
8437#endif
8438 }
8439
8440 /*
8441 * Flush the two line buffer, then the combined buffer.
8442 */
8443 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
8444 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
8445 kwSandboxConsoleFlushCombined(pSandbox);
8446}
8447
8448
8449/**
8450 * Writes a string to the given output stream.
8451 *
8452 * @param pSandbox The sandbox.
8453 * @param pLineBuf The line buffer for the output stream.
8454 * @param pwcBuffer The buffer to write.
8455 * @param cwcToWrite The number of wchar_t's in the buffer.
8456 */
8457static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
8458{
8459 kHlpAssert(pLineBuf->fIsConsole);
8460 if (cwcToWrite > 0)
8461 {
8462 /*
8463 * First, find the start of the last incomplete line so we can figure
8464 * out how much line buffering we need to do.
8465 */
8466 KU32 cchLastIncompleteLine;
8467 KU32 offLastIncompleteLine = cwcToWrite;
8468 while ( offLastIncompleteLine > 0
8469 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
8470 offLastIncompleteLine--;
8471 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
8472
8473 /* Was there anything to line buffer? */
8474 if (offLastIncompleteLine < cwcToWrite)
8475 {
8476 /* Need to grow the line buffer? */
8477 KU32 cwcNeeded = offLastIncompleteLine == 0
8478 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
8479 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
8480 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
8481 {
8482 void *pvNew;
8483 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
8484 while (cwcNew < cwcNeeded)
8485 cwcNew *= 2;
8486 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
8487 if (pvNew)
8488 {
8489 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8490 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
8491 }
8492 else
8493 {
8494 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
8495 if (pvNew)
8496 {
8497 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8498 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
8499 }
8500 else
8501 {
8502 /* This isn't perfect, but it will have to do for now. */
8503 if (pLineBuf->u.Con.cwcBuf > 0)
8504 {
8505 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8506 K_TRUE /*fBrokenLine*/);
8507 pLineBuf->u.Con.cwcBuf = 0;
8508 }
8509 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
8510 return;
8511 }
8512 }
8513 }
8514
8515 /*
8516 * Handle the case where we only add to the line buffer.
8517 */
8518 if (offLastIncompleteLine == 0)
8519 {
8520 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
8521 pLineBuf->u.Con.cwcBuf += cwcToWrite;
8522 return;
8523 }
8524 }
8525
8526 /*
8527 * If there is sufficient combined buffer to handle this request, this is rather simple.
8528 */
8529 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
8530 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8531 {
8532 if (pLineBuf->u.Con.cwcBuf > 0)
8533 {
8534 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8535 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8536 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8537 pLineBuf->u.Con.cwcBuf = 0;
8538 }
8539
8540 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8541 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
8542 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
8543 }
8544 else
8545 {
8546 /*
8547 * Do line-by-line processing of the input, flusing the combined buffer
8548 * when it becomes necessary. We may have to write lines
8549 */
8550 KU32 off = 0;
8551 KU32 offNextLine = 0;
8552
8553 /* If there are buffered chars, we handle the first line outside the
8554 main loop. We must try our best outputting it as a complete line. */
8555 if (pLineBuf->u.Con.cwcBuf > 0)
8556 {
8557 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
8558 offNextLine++;
8559 offNextLine++;
8560 kHlpAssert(offNextLine <= offLastIncompleteLine);
8561
8562 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8563 {
8564 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8565 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8566 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8567 pLineBuf->u.Con.cwcBuf = 0;
8568
8569 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
8570 pSandbox->Combined.cwcBuf += offNextLine;
8571 }
8572 else
8573 {
8574 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
8575 if (cwcLeft > 0)
8576 {
8577 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
8578 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
8579 pLineBuf->u.Con.cwcBuf += cwcCopy;
8580 off += cwcCopy;
8581 }
8582 if (pLineBuf->u.Con.cwcBuf > 0)
8583 {
8584 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8585 K_TRUE /*fBrokenLine*/);
8586 pLineBuf->u.Con.cwcBuf = 0;
8587 }
8588 if (off < offNextLine)
8589 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
8590 }
8591 off = offNextLine;
8592 }
8593
8594 /* Deal with the remaining lines */
8595 while (off < offLastIncompleteLine)
8596 {
8597 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
8598 offNextLine++;
8599 offNextLine++;
8600 kHlpAssert(offNextLine <= offLastIncompleteLine);
8601 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
8602 off = offNextLine;
8603 }
8604 }
8605
8606 /*
8607 * Buffer any remaining incomplete line chars.
8608 */
8609 if (cchLastIncompleteLine)
8610 {
8611 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
8612 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
8613 }
8614 }
8615}
8616
8617
8618/**
8619 * Worker for WriteConsoleA and WriteFile.
8620 *
8621 * @param pSandbox The sandbox.
8622 * @param pLineBuf The line buffer.
8623 * @param pchBuffer What to write.
8624 * @param cchToWrite How much to write.
8625 */
8626static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
8627{
8628 /*
8629 * Convert it to wide char and use the 'W' to do the work.
8630 */
8631 int cwcRet;
8632 KU32 cwcBuf = cchToWrite * 2 + 1;
8633 wchar_t *pwcBufFree = NULL;
8634 wchar_t *pwcBuf;
8635 kHlpAssert(pLineBuf->fIsConsole);
8636
8637 if (cwcBuf <= 4096)
8638 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
8639 else
8640 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
8641
8642 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
8643 if (cwcRet > 0)
8644 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
8645 else
8646 {
8647 DWORD cchWritten;
8648 kHlpAssertFailed();
8649
8650 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
8651 if (pLineBuf->u.Con.cwcBuf > 0)
8652 {
8653 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
8654 pLineBuf->u.Con.cwcBuf = 0;
8655 }
8656 kwSandboxConsoleFlushCombined(pSandbox);
8657
8658 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
8659 {
8660 if (cchWritten >= cchToWrite)
8661 { /* likely */ }
8662 else
8663 {
8664 KU32 off = 0;
8665 do
8666 {
8667 off += cchWritten;
8668 cchWritten = 0;
8669 } while ( off < cchToWrite
8670 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
8671 }
8672 }
8673 }
8674
8675 if (pwcBufFree)
8676 kHlpFree(pwcBufFree);
8677}
8678
8679
8680/** Kernel32 - WriteConsoleA */
8681BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
8682 PVOID pvReserved)
8683{
8684 BOOL fRc;
8685 PKWOUTPUTSTREAMBUF pLineBuf;
8686 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8687
8688 if (hConOutput == g_Sandbox.StdErr.hOutput)
8689 pLineBuf = &g_Sandbox.StdErr;
8690 else
8691 pLineBuf = &g_Sandbox.StdOut;
8692 if (pLineBuf->fIsConsole)
8693 {
8694 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
8695
8696 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
8697 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
8698 if (pcbWritten)
8699 *pcbWritten = cbToWrite;
8700 fRc = TRUE;
8701 }
8702 else
8703 {
8704 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
8705 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
8706 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
8707 }
8708 return fRc;
8709}
8710
8711
8712/** Kernel32 - WriteConsoleW */
8713BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
8714 PVOID pvReserved)
8715{
8716 BOOL fRc;
8717 PKWOUTPUTSTREAMBUF pLineBuf;
8718 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8719
8720 if (hConOutput == g_Sandbox.StdErr.hOutput)
8721 pLineBuf = &g_Sandbox.StdErr;
8722 else if (hConOutput == g_Sandbox.StdOut.hOutput)
8723 pLineBuf = &g_Sandbox.StdOut;
8724 else
8725 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
8726 if (pLineBuf->fIsConsole)
8727 {
8728 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
8729
8730 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
8731 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
8732 if (pcwcWritten)
8733 *pcwcWritten = cwcToWrite;
8734 fRc = TRUE;
8735 }
8736 else
8737 {
8738 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
8739 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
8740 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
8741 }
8742 return fRc;
8743}
8744
8745#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8746
8747
8748
8749/*
8750 *
8751 * Virtual memory leak prevension.
8752 * Virtual memory leak prevension.
8753 * Virtual memory leak prevension.
8754 *
8755 */
8756
8757#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8758
8759/** For debug logging. */
8760# ifndef NDEBUG
8761static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
8762{
8763 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
8764 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
8765 kHlpAssert(cbMemInfo == sizeof(MemInfo));
8766 if (cbMemInfo != 0)
8767 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
8768 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
8769 MemInfo.BaseAddress,
8770 MemInfo.AllocationBase,
8771 MemInfo.RegionSize,
8772 MemInfo.State,
8773 MemInfo.Protect,
8774 MemInfo.Type));
8775}
8776# else
8777# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
8778# endif
8779
8780/**
8781 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
8782 *
8783 * @param idxFixed The fixed allocation index to "free".
8784 */
8785static void kwSandboxResetFixedAllocation(KU32 idxFixed)
8786{
8787 BOOL fRc;
8788 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
8789 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
8790 kHlpAssert(fRc); K_NOREF(fRc);
8791 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
8792 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
8793}
8794
8795#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
8796
8797
8798/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
8799 * location (~78MB in 32-bit 2010 compiler). */
8800static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
8801{
8802 PVOID pvMem;
8803 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8804 {
8805 KU32 idxPreAllocated = KU32_MAX;
8806
8807#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8808 /*
8809 * Look for a pre-reserved CL.exe heap allocation.
8810 */
8811 pvMem = NULL;
8812 if ( pvAddr != 0
8813 && (fAllocType & MEM_RESERVE))
8814 {
8815 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8816 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
8817 while (idxFixed-- > 0)
8818 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
8819 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
8820 {
8821 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
8822 {
8823 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
8824 {
8825 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
8826 pvMem = pvAddr;
8827 idxPreAllocated = idxFixed;
8828 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
8829 pvAddr, cb, fAllocType, fProt, pvMem));
8830 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
8831 SetLastError(NO_ERROR);
8832 break;
8833 }
8834 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
8835 }
8836 else
8837 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
8838 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
8839 }
8840 }
8841 if (!pvMem)
8842#endif
8843 {
8844 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8845 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8846 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8847 if (pvAddr && pvAddr != pvMem)
8848 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
8849 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
8850 }
8851
8852 if (pvMem)
8853 {
8854 /*
8855 * Track it.
8856 */
8857 PKWVIRTALLOC pTracker;
8858 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8859
8860 pTracker = g_Sandbox.pVirtualAllocHead;
8861 while ( pTracker
8862 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
8863 pTracker = pTracker->pNext;
8864 if (!pTracker)
8865 {
8866 DWORD dwErr = GetLastError();
8867 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
8868 if (pTracker)
8869 {
8870 pTracker->pvAlloc = pvMem;
8871 pTracker->cbAlloc = cb;
8872 pTracker->idxPreAllocated = idxPreAllocated;
8873 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8874 g_Sandbox.pVirtualAllocHead = pTracker;
8875 }
8876 SetLastError(dwErr);
8877 }
8878 }
8879 }
8880 else
8881 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8882 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8883 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8884 return pvMem;
8885}
8886
8887
8888/** Kernel32 - VirtualFree. */
8889static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
8890{
8891 BOOL fRc;
8892 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8893 {
8894 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8895 if (dwFreeType & MEM_RELEASE)
8896 {
8897 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
8898 if (pTracker)
8899 {
8900 if (pTracker->pvAlloc == pvAddr)
8901 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
8902 else
8903 {
8904 PKWVIRTALLOC pPrev;
8905 do
8906 {
8907 pPrev = pTracker;
8908 pTracker = pTracker->pNext;
8909 } while (pTracker && pTracker->pvAlloc != pvAddr);
8910 if (pTracker)
8911 pPrev->pNext = pTracker->pNext;
8912 }
8913 if (pTracker)
8914 {
8915#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8916 if (pTracker->idxPreAllocated != KU32_MAX)
8917 {
8918 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
8919 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
8920 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
8921 kHlpFree(pTracker);
8922 return TRUE;
8923 }
8924#endif
8925
8926 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8927 if (fRc)
8928 kHlpFree(pTracker);
8929 else
8930 {
8931 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8932 g_Sandbox.pVirtualAllocHead = pTracker;
8933 }
8934 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8935 return fRc;
8936 }
8937
8938 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
8939 }
8940 }
8941 }
8942
8943#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8944 /*
8945 * Protect our fixed allocations (this isn't just paranoia, btw.).
8946 */
8947 if (dwFreeType & MEM_RELEASE)
8948 {
8949 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8950 while (idxFixed-- > 0)
8951 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
8952 {
8953 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
8954 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
8955 return TRUE;
8956 }
8957 }
8958#endif
8959
8960 /*
8961 * Not tracker or not actually free the virtual range.
8962 */
8963 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8964 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8965 return fRc;
8966}
8967
8968
8969/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
8970HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
8971{
8972 HANDLE hHeap;
8973 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8974
8975 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
8976 if (hHeap != NULL)
8977 {
8978 DWORD dwErr = GetLastError();
8979 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
8980 if (pTracker)
8981 {
8982 pTracker->hHeap = hHeap;
8983 pTracker->pNext = g_Sandbox.pHeapHead;
8984 g_Sandbox.pHeapHead = pTracker;
8985 }
8986
8987 SetLastError(dwErr);
8988 }
8989 return hHeap;
8990
8991}
8992
8993
8994/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
8995BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
8996{
8997 BOOL fRc = HeapDestroy(hHeap);
8998 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
8999 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9000 if (fRc)
9001 {
9002 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9003 if (pTracker)
9004 {
9005 if (pTracker->hHeap == hHeap)
9006 g_Sandbox.pHeapHead = pTracker->pNext;
9007 else
9008 {
9009 PKWHEAP pPrev;
9010 do
9011 {
9012 pPrev = pTracker;
9013 pTracker = pTracker->pNext;
9014 } while (pTracker && pTracker->hHeap == hHeap);
9015 if (pTracker)
9016 pPrev->pNext = pTracker->pNext;
9017 }
9018 if (pTracker)
9019 kHlpFree(pTracker);
9020 else
9021 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9022 }
9023 }
9024
9025 return fRc;
9026}
9027
9028
9029
9030/*
9031 *
9032 * Thread/Fiber local storage leak prevention.
9033 * Thread/Fiber local storage leak prevention.
9034 * Thread/Fiber local storage leak prevention.
9035 *
9036 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9037 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9038 * we're leaking these indexes, but more importantely we crash during
9039 * worker exit since the callback is triggered multiple times.
9040 */
9041
9042
9043/** Kernel32 - FlsAlloc */
9044DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9045{
9046 DWORD idxFls = FlsAlloc(pfnCallback);
9047 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9048 if (idxFls != FLS_OUT_OF_INDEXES)
9049 {
9050 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9051 if (pTracker)
9052 {
9053 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9054 pTracker->idx = idxFls;
9055 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9056 g_Sandbox.pFlsAllocHead = pTracker;
9057 }
9058 }
9059
9060 return idxFls;
9061}
9062
9063/** Kernel32 - FlsFree */
9064BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9065{
9066 BOOL fRc = FlsFree(idxFls);
9067 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9068 if (fRc)
9069 {
9070 PKWLOCALSTORAGE pTracker;
9071 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9072
9073 pTracker = g_Sandbox.pFlsAllocHead;
9074 if (pTracker)
9075 {
9076 if (pTracker->idx == idxFls)
9077 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9078 else
9079 {
9080 PKWLOCALSTORAGE pPrev;
9081 do
9082 {
9083 pPrev = pTracker;
9084 pTracker = pTracker->pNext;
9085 } while (pTracker && pTracker->idx != idxFls);
9086 if (pTracker)
9087 pPrev->pNext = pTracker->pNext;
9088 }
9089 if (pTracker)
9090 {
9091 pTracker->idx = FLS_OUT_OF_INDEXES;
9092 pTracker->pNext = NULL;
9093 kHlpFree(pTracker);
9094 }
9095 }
9096 }
9097 return fRc;
9098}
9099
9100
9101/** Kernel32 - TlsAlloc */
9102DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9103{
9104 DWORD idxTls = TlsAlloc();
9105 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9106 if (idxTls != TLS_OUT_OF_INDEXES)
9107 {
9108 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9109 if (pTracker)
9110 {
9111 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9112 pTracker->idx = idxTls;
9113 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9114 g_Sandbox.pTlsAllocHead = pTracker;
9115 }
9116 }
9117
9118 return idxTls;
9119}
9120
9121/** Kernel32 - TlsFree */
9122BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9123{
9124 BOOL fRc = TlsFree(idxTls);
9125 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9126 if (fRc)
9127 {
9128 PKWLOCALSTORAGE pTracker;
9129 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9130
9131 pTracker = g_Sandbox.pTlsAllocHead;
9132 if (pTracker)
9133 {
9134 if (pTracker->idx == idxTls)
9135 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9136 else
9137 {
9138 PKWLOCALSTORAGE pPrev;
9139 do
9140 {
9141 pPrev = pTracker;
9142 pTracker = pTracker->pNext;
9143 } while (pTracker && pTracker->idx != idxTls);
9144 if (pTracker)
9145 pPrev->pNext = pTracker->pNext;
9146 }
9147 if (pTracker)
9148 {
9149 pTracker->idx = TLS_OUT_OF_INDEXES;
9150 pTracker->pNext = NULL;
9151 kHlpFree(pTracker);
9152 }
9153 }
9154 }
9155 return fRc;
9156}
9157
9158
9159
9160/*
9161 *
9162 * Header file hashing.
9163 * Header file hashing.
9164 * Header file hashing.
9165 *
9166 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9167 * indicated that ~12% of the time was spent doing MD5 caluclation when
9168 * rebuiling openssl. The hashing it done right after reading the source
9169 * via ReadFile, same buffers and sizes.
9170 */
9171
9172#ifdef WITH_HASH_MD5_CACHE
9173
9174/** AdvApi32 - CryptCreateHash */
9175static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9176 HCRYPTHASH *phHash)
9177{
9178 BOOL fRc;
9179
9180 /*
9181 * Only do this for cl.exe when it request normal MD5.
9182 */
9183 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9184 {
9185 if (idAlg == CALG_MD5)
9186 {
9187 if (hKey == 0)
9188 {
9189 if (dwFlags == 0)
9190 {
9191 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9192 if (pHash)
9193 {
9194 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9195 pHash->uMagic = KWHASHMD5_MAGIC;
9196 pHash->cbHashed = 0;
9197 pHash->fGoneBad = K_FALSE;
9198 pHash->fFallbackMode = K_FALSE;
9199 pHash->fFinal = K_FALSE;
9200
9201 /* link it. */
9202 pHash->pNext = g_Sandbox.pHashHead;
9203 g_Sandbox.pHashHead = pHash;
9204
9205 *phHash = (KUPTR)pHash;
9206 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9207 hProv, *phHash, TRUE));
9208 return TRUE;
9209 }
9210
9211 kwErrPrintf("CryptCreateHash: out of memory!\n");
9212 }
9213 else
9214 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9215 }
9216 else
9217 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9218 }
9219 else
9220 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9221 }
9222
9223 /*
9224 * Fallback.
9225 */
9226 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9227 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9228 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9229 return fRc;
9230}
9231
9232
9233/** AdvApi32 - CryptHashData */
9234static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9235{
9236 BOOL fRc;
9237 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9238 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9239 while (pHash && (KUPTR)pHash != hHash)
9240 pHash = pHash->pNext;
9241 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9242 hHash, pHash, pbData, cbData, dwFlags));
9243 if (pHash)
9244 {
9245 /*
9246 * Validate the state.
9247 */
9248 if ( pHash->uMagic == KWHASHMD5_MAGIC
9249 && !pHash->fFinal)
9250 {
9251 if (!pHash->fFallbackMode)
9252 {
9253 /*
9254 * Does this match the previous ReadFile call to a cached file?
9255 * If it doesn't, try falling back.
9256 */
9257 if ( g_Sandbox.LastHashRead.cbRead == cbData
9258 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9259 {
9260 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9261 if ( pCachedFile
9262 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9263 {
9264
9265 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9266 {
9267 if ( pHash->pCachedFile == NULL
9268 && pHash->cbHashed == 0)
9269 pHash->pCachedFile = pCachedFile;
9270 if (pHash->pCachedFile == pCachedFile)
9271 {
9272 pHash->cbHashed += cbData;
9273 g_Sandbox.LastHashRead.pCachedFile = NULL;
9274 g_Sandbox.LastHashRead.pvRead = NULL;
9275 g_Sandbox.LastHashRead.cbRead = 0;
9276 g_Sandbox.LastHashRead.offRead = 0;
9277 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9278 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9279 return TRUE;
9280 }
9281
9282 /* Note! it's possible to fall back here too, if necessary. */
9283 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
9284 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
9285 }
9286 else
9287 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
9288 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
9289 }
9290 else if (!pCachedFile)
9291 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
9292 else
9293 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
9294 }
9295 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
9296 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
9297 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
9298 if (pHash->cbHashed == 0)
9299 pHash->fFallbackMode = K_TRUE;
9300 if (pHash->fFallbackMode)
9301 {
9302 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
9303 pHash->fFallbackMode = K_TRUE;
9304 MD5Init(&pHash->Md5Ctx);
9305 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9306 pHash->cbHashed = cbData;
9307 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
9308 hHash, pbData, cbData, dwFlags));
9309 return TRUE;
9310 }
9311 pHash->fGoneBad = K_TRUE;
9312 SetLastError(ERROR_INVALID_PARAMETER);
9313 fRc = FALSE;
9314 }
9315 else
9316 {
9317 /* fallback. */
9318 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9319 pHash->cbHashed += cbData;
9320 fRc = TRUE;
9321 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
9322 hHash, pbData, cbData, dwFlags));
9323 }
9324 }
9325 /*
9326 * Bad handle state.
9327 */
9328 else
9329 {
9330 if (pHash->uMagic != KWHASHMD5_MAGIC)
9331 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
9332 else
9333 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
9334 SetLastError(NTE_BAD_HASH);
9335 fRc = FALSE;
9336 }
9337 }
9338 else
9339 {
9340
9341 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
9342 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
9343 }
9344 return fRc;
9345}
9346
9347
9348/** AdvApi32 - CryptGetHashParam */
9349static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
9350 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
9351{
9352 BOOL fRc;
9353 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9354 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9355 while (pHash && (KUPTR)pHash != hHash)
9356 pHash = pHash->pNext;
9357 if (pHash)
9358 {
9359 if (pHash->uMagic == KWHASHMD5_MAGIC)
9360 {
9361 if (dwFlags == 0)
9362 {
9363 DWORD cbRet;
9364 void *pvRet;
9365 union
9366 {
9367 DWORD dw;
9368 } uBuf;
9369
9370 switch (dwParam)
9371 {
9372 case HP_HASHVAL:
9373 {
9374 /* Check the hash progress. */
9375 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
9376 if (pCachedFile)
9377 {
9378 if ( pCachedFile->cbCached == pHash->cbHashed
9379 && !pHash->fGoneBad)
9380 {
9381 if (pCachedFile->fValidMd5)
9382 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
9383 else
9384 {
9385 MD5Init(&pHash->Md5Ctx);
9386 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
9387 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
9388 pCachedFile->fValidMd5 = K_TRUE;
9389 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
9390 }
9391 pvRet = pCachedFile->abMd5Digest;
9392 }
9393 else
9394 {
9395 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
9396 from what I can tell, so just deal with it. */
9397 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
9398 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
9399 pHash, pCachedFile, pCachedFile->szPath));
9400 pHash->fFallbackMode = K_TRUE;
9401 pHash->pCachedFile = NULL;
9402 MD5Init(&pHash->Md5Ctx);
9403 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
9404 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9405 pvRet = pHash->abDigest;
9406 }
9407 pHash->fFinal = K_TRUE;
9408 cbRet = 16;
9409 break;
9410 }
9411 else if (pHash->fFallbackMode)
9412 {
9413 if (!pHash->fFinal)
9414 {
9415 pHash->fFinal = K_TRUE;
9416 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9417 }
9418 pvRet = pHash->abDigest;
9419 cbRet = 16;
9420 break;
9421 }
9422 else
9423 {
9424 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
9425 SetLastError(ERROR_INVALID_SERVER_STATE);
9426 }
9427 return FALSE;
9428 }
9429
9430 case HP_HASHSIZE:
9431 uBuf.dw = 16;
9432 pvRet = &uBuf;
9433 cbRet = sizeof(DWORD);
9434 break;
9435
9436 case HP_ALGID:
9437 uBuf.dw = CALG_MD5;
9438 pvRet = &uBuf;
9439 cbRet = sizeof(DWORD);
9440 break;
9441
9442 default:
9443 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
9444 SetLastError(NTE_BAD_TYPE);
9445 return FALSE;
9446 }
9447
9448 /*
9449 * Copy out cbRet from pvRet.
9450 */
9451 if (pbData)
9452 {
9453 if (*pcbData >= cbRet)
9454 {
9455 *pcbData = cbRet;
9456 kHlpMemCopy(pbData, pvRet, cbRet);
9457 if (cbRet == 4)
9458 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
9459 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
9460 else if (cbRet == 16)
9461 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",
9462 dwParam, pHash, pHash->pCachedFile, cbRet,
9463 pbData[0], pbData[1], pbData[2], pbData[3],
9464 pbData[4], pbData[5], pbData[6], pbData[7],
9465 pbData[8], pbData[9], pbData[10], pbData[11],
9466 pbData[12], pbData[13], pbData[14], pbData[15]));
9467 else
9468 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
9469 dwParam, pHash, pHash->pCachedFile, cbRet));
9470 return TRUE;
9471 }
9472
9473 kHlpMemCopy(pbData, pvRet, *pcbData);
9474 }
9475 SetLastError(ERROR_MORE_DATA);
9476 *pcbData = cbRet;
9477 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
9478 }
9479 else
9480 {
9481 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
9482 SetLastError(NTE_BAD_FLAGS);
9483 }
9484 }
9485 else
9486 {
9487 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
9488 SetLastError(NTE_BAD_HASH);
9489 }
9490 fRc = FALSE;
9491 }
9492 /*
9493 * Regular handle.
9494 */
9495 else
9496 {
9497 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
9498 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
9499 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
9500 }
9501
9502 return fRc;
9503}
9504
9505
9506/** AdvApi32 - CryptDestroyHash */
9507static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
9508{
9509 BOOL fRc;
9510 PKWHASHMD5 pPrev = NULL;
9511 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9512 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9513 while (pHash && (KUPTR)pHash != hHash)
9514 {
9515 pPrev = pHash;
9516 pHash = pHash->pNext;
9517 }
9518 if (pHash)
9519 {
9520 if (pHash->uMagic == KWHASHMD5_MAGIC)
9521 {
9522 pHash->uMagic = 0;
9523 if (!pPrev)
9524 g_Sandbox.pHashHead = pHash->pNext;
9525 else
9526 pPrev->pNext = pHash->pNext;
9527 kHlpFree(pHash);
9528 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
9529 fRc = TRUE;
9530 }
9531 else
9532 {
9533 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
9534 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
9535 SetLastError(ERROR_INVALID_HANDLE);
9536 fRc = FALSE;
9537 }
9538 }
9539 /*
9540 * Regular handle.
9541 */
9542 else
9543 {
9544 fRc = CryptDestroyHash(hHash);
9545 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
9546 }
9547 return fRc;
9548}
9549
9550#endif /* WITH_HASH_MD5_CACHE */
9551
9552
9553/*
9554 *
9555 * Reuse crypt context.
9556 * Reuse crypt context.
9557 * Reuse crypt context.
9558 *
9559 *
9560 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
9561 *
9562 */
9563
9564#ifdef WITH_CRYPT_CTX_REUSE
9565
9566/** AdvApi32 - CryptAcquireContextW. */
9567static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
9568 DWORD dwProvType, DWORD dwFlags)
9569{
9570 BOOL fRet;
9571
9572 /*
9573 * Lookup reusable context based on the input.
9574 */
9575 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
9576 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
9577 KU32 iCtx = g_Sandbox.cCryptCtxs;
9578 while (iCtx-- > 0)
9579 {
9580 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
9581 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
9582 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
9583 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
9584 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
9585 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
9586 {
9587 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
9588 {
9589 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
9590 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
9591 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9592 return TRUE;
9593 }
9594 }
9595 }
9596
9597 /*
9598 * Create it and enter it into the reused array if possible.
9599 */
9600 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
9601 if (fRet)
9602 {
9603 iCtx = g_Sandbox.cCryptCtxs;
9604 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
9605 {
9606 /* Try duplicate the input strings. */
9607 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
9608 (cwcContainer + 1) * sizeof(wchar_t));
9609 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
9610 {
9611 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
9612 (cwcProvider + 1) * sizeof(wchar_t));
9613 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
9614 {
9615 /* Add a couple of references just to be on the safe side and all that. */
9616 HCRYPTPROV hProv = *phProv;
9617 if (CryptContextAddRef(hProv, NULL, 0))
9618 {
9619 if (CryptContextAddRef(hProv, NULL, 0))
9620 {
9621 /* Okay, finish the entry and return success */
9622 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
9623 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
9624 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
9625 g_Sandbox.cCryptCtxs = iCtx + 1;
9626
9627 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
9628 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9629 return TRUE;
9630 }
9631 CryptReleaseContext(hProv, 0);
9632 }
9633 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
9634
9635 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
9636 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
9637 }
9638 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
9639 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
9640 }
9641 }
9642 else
9643 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
9644 }
9645
9646 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
9647 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9648 return fRet;
9649}
9650
9651
9652/** AdvApi32 - CryptReleaseContext */
9653static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
9654{
9655 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
9656 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
9657 return fRet;
9658}
9659
9660
9661/** AdvApi32 - CryptContextAddRef */
9662static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
9663{
9664 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
9665 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
9666 return fRet;
9667}
9668
9669#endif /* WITH_CRYPT_CTX_REUSE */
9670
9671/*
9672 *
9673 * Structured exception handling.
9674 * Structured exception handling.
9675 * Structured exception handling.
9676 *
9677 */
9678#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9679
9680# define EH_NONCONTINUABLE KU32_C(0x00000001)
9681# define EH_UNWINDING KU32_C(0x00000002)
9682# define EH_EXIT_UNWIND KU32_C(0x00000004)
9683# define EH_STACK_INVALID KU32_C(0x00000008)
9684# define EH_NESTED_CALL KU32_C(0x00000010)
9685
9686typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
9687 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
9688typedef struct _EXCEPTION_REGISTRATION_RECORD
9689{
9690 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
9691 PFNXCPTHANDLER pfnXcptHandler;
9692};
9693
9694
9695/**
9696 * Calls @a pfnHandler.
9697 */
9698static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
9699 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
9700 PFNXCPTHANDLER pfnHandler)
9701{
9702# if 1
9703 /* This is a more robust version that isn't subject to calling
9704 convension cleanup disputes and such. */
9705 KU32 uSavedEdi;
9706 KU32 uSavedEsi;
9707 KU32 uSavedEbx;
9708 KU32 rcHandler;
9709
9710 __asm
9711 {
9712 mov [uSavedEdi], edi
9713 mov [uSavedEsi], esi
9714 mov [uSavedEbx], ebx
9715 mov esi, esp
9716 mov edi, esp
9717 mov edi, [pXcptRec]
9718 mov edx, [pRegRec]
9719 mov eax, [pXcptCtx]
9720 mov ebx, [ppRegRec]
9721 mov ecx, [pfnHandler]
9722 sub esp, 16
9723 and esp, 0fffffff0h
9724 mov [esp ], edi
9725 mov [esp + 4], edx
9726 mov [esp + 8], eax
9727 mov [esp + 12], ebx
9728 mov edi, esi
9729 call ecx
9730 mov esp, esi
9731 cmp esp, edi
9732 je stack_ok
9733 int 3
9734 stack_ok:
9735 mov edi, [uSavedEdi]
9736 mov esi, [uSavedEsi]
9737 mov ebx, [uSavedEbx]
9738 mov [rcHandler], eax
9739 }
9740 return rcHandler;
9741# else
9742 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
9743# endif
9744}
9745
9746
9747/**
9748 * Vectored exception handler that emulates x86 chained exception handler.
9749 *
9750 * This is necessary because the RtlIsValidHandler check fails for self loaded
9751 * code and prevents cl.exe from working. (On AMD64 we can register function
9752 * tables, but on X86 cooking your own handling seems to be the only viabke
9753 * alternative.)
9754 *
9755 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
9756 * @param pXcptPtrs The exception details.
9757 */
9758static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
9759{
9760 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9761 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
9762 if (g_Sandbox.fRunning)
9763 {
9764 HANDLE const hCurProc = GetCurrentProcess();
9765 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
9766 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
9767 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9768 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
9769 {
9770 /* Read the exception record in a safe manner. */
9771 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9772 DWORD cbActuallyRead = 0;
9773 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9774 && cbActuallyRead == sizeof(RegRec))
9775 {
9776 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9777 KU32 rcHandler;
9778 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9779 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9780 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9781 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9782 if (rcHandler == ExceptionContinueExecution)
9783 {
9784 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
9785 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
9786 return EXCEPTION_CONTINUE_EXECUTION;
9787 }
9788
9789 if (rcHandler == ExceptionContinueSearch)
9790 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9791 else if (rcHandler == ExceptionNestedException)
9792 kHlpAssertMsgFailed(("Nested exceptions.\n"));
9793 else
9794 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9795 }
9796 else
9797 {
9798 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9799 break;
9800 }
9801
9802 /*
9803 * Next.
9804 */
9805 pRegRec = RegRec.pPrevRegRec;
9806 }
9807 }
9808 return EXCEPTION_CONTINUE_SEARCH;
9809}
9810
9811
9812/** NtDll,Kernel32 - RtlUnwind */
9813static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
9814 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
9815{
9816 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9817 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
9818 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
9819 if (g_Sandbox.fRunning)
9820 {
9821 HANDLE const hCurProc = GetCurrentProcess();
9822 PCONTEXT pXcptCtx = NULL;
9823 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9824
9825 /*
9826 * Update / create an exception record.
9827 */
9828 if (pXcptRec)
9829 pXcptRec->ExceptionFlags |= EH_UNWINDING;
9830 else
9831 {
9832 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
9833 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
9834 pXcptRec->ExceptionCode = STATUS_UNWIND;
9835 pXcptRec->ExceptionFlags = EH_UNWINDING;
9836 }
9837 if (!pStopXcptRec)
9838 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
9839
9840 /*
9841 * Walk the chain till we find pStopXctpRec.
9842 */
9843 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
9844 && pRegRec != NULL
9845 && pRegRec != pStopXcptRec)
9846 {
9847 /* Read the exception record in a safe manner. */
9848 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9849 DWORD cbActuallyRead = 0;
9850 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9851 && cbActuallyRead == sizeof(RegRec))
9852 {
9853 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9854 KU32 rcHandler;
9855 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9856 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9857 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9858 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9859
9860 if (rcHandler == ExceptionContinueSearch)
9861 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9862 else if (rcHandler == ExceptionCollidedUnwind)
9863 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
9864 else
9865 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9866 }
9867 else
9868 {
9869 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9870 break;
9871 }
9872
9873 /*
9874 * Pop next.
9875 */
9876 pTib->ExceptionList = RegRec.pPrevRegRec;
9877 pRegRec = RegRec.pPrevRegRec;
9878 }
9879 return;
9880 }
9881
9882 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
9883}
9884
9885#endif /* WINDOWS + X86 */
9886
9887
9888/*
9889 *
9890 * Misc function only intercepted while debugging.
9891 * Misc function only intercepted while debugging.
9892 * Misc function only intercepted while debugging.
9893 *
9894 */
9895
9896#ifndef NDEBUG
9897
9898/** CRT - memcpy */
9899static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
9900{
9901 KU8 const *pbSrc = (KU8 const *)pvSrc;
9902 KU8 *pbDst = (KU8 *)pvDst;
9903 KSIZE cbLeft = cb;
9904 while (cbLeft-- > 0)
9905 *pbDst++ = *pbSrc++;
9906 return pvDst;
9907}
9908
9909
9910/** CRT - memset */
9911static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
9912{
9913 KU8 *pbDst = (KU8 *)pvDst;
9914 KSIZE cbLeft = cb;
9915 while (cbLeft-- > 0)
9916 *pbDst++ = bFiller;
9917 return pvDst;
9918}
9919
9920#endif /* NDEBUG */
9921
9922
9923
9924/**
9925 * Functions that needs replacing for sandboxed execution.
9926 */
9927KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
9928{
9929 /*
9930 * Kernel32.dll and friends.
9931 */
9932 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
9933 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9934
9935 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
9936 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
9937 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
9938 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
9939 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
9940 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
9941 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
9942 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
9943 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
9944 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
9945 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9946
9947 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
9948 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
9949 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
9950 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
9951
9952 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
9953
9954 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
9955 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
9956 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
9957 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
9958 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
9959 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
9960 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
9961 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
9962 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
9963 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
9964 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
9965
9966 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
9967 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
9968 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
9969 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9970#ifdef WITH_TEMP_MEMORY_FILES
9971 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
9972 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9973 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9974 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
9975 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
9976 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9977 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9978 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9979 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9980 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9981#endif
9982 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9983 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9984 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9985 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
9986 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9987 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9988 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
9989 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
9990 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9991#ifdef WITH_TEMP_MEMORY_FILES
9992 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9993#endif
9994
9995 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
9996 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
9997
9998 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
9999 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10000
10001 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10002 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10003
10004 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10005 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10006 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10007 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10008
10009 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10010
10011#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10012 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10013#endif
10014
10015#ifdef WITH_HASH_MD5_CACHE
10016 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10017 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10018 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10019 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10020#endif
10021
10022#ifdef WITH_CRYPT_CTX_REUSE
10023 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10024 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10025 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10026#endif
10027
10028 /*
10029 * MS Visual C++ CRTs.
10030 */
10031 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10032 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10033 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10034 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10035 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10036 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10037
10038 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10039 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10040 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10041
10042 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10043 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10044 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
10045
10046 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
10047 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
10048 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
10049 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
10050 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
10051 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
10052 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
10053 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
10054 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
10055 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
10056 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
10057 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
10058 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
10059 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
10060 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
10061 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
10062 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
10063 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
10064 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
10065 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
10066
10067 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
10068 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
10069 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
10070 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
10071 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
10072 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
10073 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
10074 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
10075 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
10076 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
10077 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
10078 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
10079 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
10080 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
10081
10082#ifndef NDEBUG
10083 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
10084 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
10085#endif
10086};
10087/** Number of entries in g_aReplacements. */
10088KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
10089
10090
10091/**
10092 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
10093 * execution.
10094 */
10095KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
10096{
10097 /*
10098 * Kernel32.dll and friends.
10099 */
10100 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10101 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10102
10103#if 0
10104 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10105#endif
10106
10107 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10108 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10109 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10110 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10111#ifdef WITH_TEMP_MEMORY_FILES
10112 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10113 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10114 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10115 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10116 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10117 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10118 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10119 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10120 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10121 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10122#endif
10123 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10124 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10125 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10126 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10127 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10128 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10129 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10130 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10131 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10132#ifdef WITH_TEMP_MEMORY_FILES
10133 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10134#endif
10135 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10136 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10137
10138 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10139 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10140
10141#ifdef WITH_HASH_MD5_CACHE
10142 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10143 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10144 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10145 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10146#endif
10147
10148 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10149
10150#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10151 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10152#endif
10153
10154 /*
10155 * MS Visual C++ CRTs.
10156 */
10157 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10158 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10159 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10160 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10161 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10162 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10163 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10164
10165#if 0 /* used by mspdbXXX.dll */
10166 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10167 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10168#endif
10169};
10170/** Number of entries in g_aSandboxNativeReplacements. */
10171KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10172
10173
10174/**
10175 * Functions that needs replacing when queried by GetProcAddress.
10176 */
10177KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10178{
10179 /*
10180 * Kernel32.dll and friends.
10181 */
10182 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10183 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10184 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10185 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10186};
10187/** Number of entries in g_aSandboxGetProcReplacements. */
10188KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10189
10190
10191/**
10192 * Control handler.
10193 *
10194 * @returns TRUE if handled, FALSE if not.
10195 * @param dwCtrlType The signal.
10196 */
10197static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10198{
10199 DWORD cbIgn;
10200 int volatile rc; /* volatile for debugging */
10201 int volatile rcPrev;
10202 const char *pszMsg;
10203 switch (dwCtrlType)
10204 {
10205 case CTRL_C_EVENT:
10206 rc = 9;
10207 pszMsg = "kWorker: Ctrl-C\r\n";
10208 break;
10209
10210 case CTRL_BREAK_EVENT:
10211 rc = 10;
10212 pszMsg = "kWorker: Ctrl-Break\r\n";
10213 break;
10214
10215 case CTRL_CLOSE_EVENT:
10216 rc = 11;
10217 pszMsg = "kWorker: console closed\r\n";
10218 break;
10219
10220 case CTRL_LOGOFF_EVENT:
10221 rc = 11;
10222 pszMsg = "kWorker: logoff event\r\n";
10223 break;
10224
10225 case CTRL_SHUTDOWN_EVENT:
10226 rc = 11;
10227 pszMsg = "kWorker: shutdown event\r\n";
10228 break;
10229
10230 default:
10231 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10232 return TRUE;
10233 }
10234
10235 /*
10236 * Terminate the process after 5 seconds.
10237 * If we get here a second time we just terminate the process ourselves.
10238 *
10239 * Note! We do no try call exit() here as it turned out to deadlock a lot
10240 * flusing file descriptors (stderr back when we first wrote to it).
10241 */
10242 rcPrev = g_rcCtrlC;
10243 g_rcCtrlC = rc;
10244 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10245 if (rcPrev == 0)
10246 {
10247 int i;
10248 for (i = 0; i < 10; i++)
10249 {
10250 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10251 Sleep(500);
10252 }
10253 }
10254 TerminateProcess(GetCurrentProcess(), rc);
10255 return TRUE;
10256}
10257
10258
10259/**
10260 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10261 */
10262static void kwSandboxResetModuleVisited(void)
10263{
10264 PKWMODULE pMod = g_pModuleHead;
10265 while (pMod)
10266 {
10267 pMod->fVisited = K_FALSE;
10268 pMod = pMod->pNextList;
10269 }
10270}
10271
10272
10273/**
10274 * Used by kwSandboxExec to reset the state of the module tree.
10275 *
10276 * This is done recursively.
10277 *
10278 * @param pMod The root of the tree to consider.
10279 */
10280static void kwSandboxResetModuleState(PKWMODULE pMod)
10281{
10282 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
10283 if (!pMod->fNative)
10284 {
10285 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
10286 if (!pMod->fVisited) /* Avoid loops. */
10287 {
10288 KSIZE iImp;
10289 pMod->fVisited = K_TRUE;
10290 iImp = pMod->u.Manual.cImpMods;
10291 while (iImp-- > 0)
10292 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
10293 }
10294 }
10295 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10296 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
10297 {
10298 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10299 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10300 {
10301 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10302 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10303 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10304 pMod->pszPath, pszValue ? pszValue : "<null>"));
10305 }
10306 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10307 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10308 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10309 pMod->pszPath, pszValue ? pszValue : "<null>"));
10310 else
10311 {
10312 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10313 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10314 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10315 if (pszValue != NULL)
10316 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10317 else
10318 pMod->pszMsPdbSrvEndpoint = NULL;
10319 pMod->fNeedReInit = K_TRUE;
10320 }
10321 }
10322}
10323
10324static PPEB kwSandboxGetProcessEnvironmentBlock(void)
10325{
10326#if K_ARCH == K_ARCH_X86_32
10327 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
10328#elif K_ARCH == K_ARCH_AMD64
10329 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
10330#else
10331# error "Port me!"
10332#endif
10333}
10334
10335
10336/**
10337 * Enters the given handle into the handle table.
10338 *
10339 * @returns K_TRUE on success, K_FALSE on failure.
10340 * @param pSandbox The sandbox.
10341 * @param pHandle The handle.
10342 * @param hHandle The handle value to enter it under (for the
10343 * duplicate handle API).
10344 */
10345static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
10346{
10347 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
10348 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
10349
10350 /*
10351 * Grow handle table.
10352 */
10353 if (idxHandle >= pSandbox->cHandles)
10354 {
10355 void *pvNew;
10356 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
10357 while (cHandles <= idxHandle)
10358 cHandles *= 2;
10359 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
10360 if (!pvNew)
10361 {
10362 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
10363 return K_FALSE;
10364 }
10365 pSandbox->papHandles = (PKWHANDLE *)pvNew;
10366 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
10367 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
10368 pSandbox->cHandles = cHandles;
10369 }
10370
10371 /*
10372 * Check that the entry is unused then insert it.
10373 */
10374 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
10375 pSandbox->papHandles[idxHandle] = pHandle;
10376 pSandbox->cActiveHandles++;
10377 return K_TRUE;
10378}
10379
10380
10381/**
10382 * Creates a correctly quoted ANSI command line string from the given argv.
10383 *
10384 * @returns Pointer to the command line.
10385 * @param cArgs Number of arguments.
10386 * @param papszArgs The argument vector.
10387 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10388 * @param pcbCmdLine Where to return the command line length,
10389 * including one terminator.
10390 */
10391static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
10392{
10393 KU32 i;
10394 KSIZE cbCmdLine;
10395 char *pszCmdLine;
10396
10397 /* Make a copy of the argument vector that we'll be quoting. */
10398 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
10399 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
10400
10401 /* Quote the arguments that need it. */
10402 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
10403
10404 /* figure out cmd line length. */
10405 cbCmdLine = 0;
10406 for (i = 0; i < cArgs; i++)
10407 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
10408 *pcbCmdLine = cbCmdLine;
10409
10410 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
10411 if (pszCmdLine)
10412 {
10413 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
10414 if (papszQuotedArgs[0] != papszArgs[0])
10415 free(papszQuotedArgs[0]);
10416
10417 for (i = 1; i < cArgs; i++)
10418 {
10419 *psz++ = ' ';
10420 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
10421 if (papszQuotedArgs[i] != papszArgs[i])
10422 free(papszQuotedArgs[i]);
10423 }
10424 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
10425
10426 *psz++ = '\0';
10427 *psz++ = '\0';
10428 }
10429
10430 return pszCmdLine;
10431}
10432
10433
10434
10435static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
10436 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10437 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10438{
10439 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10440 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10441 wchar_t *pwcPool;
10442 KSIZE cbStrings;
10443 KSIZE cwc;
10444 KSIZE cbCmdLine;
10445 KU32 i;
10446
10447 /* Simple stuff. */
10448 pSandbox->rcExitCode = 256;
10449 pSandbox->pTool = pTool;
10450 pSandbox->idMainThread = GetCurrentThreadId();
10451 pSandbox->pgmptr = (char *)pTool->pszPath;
10452 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
10453#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10454 if (pSandbox->StdOut.fIsConsole)
10455 pSandbox->StdOut.u.Con.cwcBuf = 0;
10456 else
10457 pSandbox->StdOut.u.Fully.cchBuf = 0;
10458 if (pSandbox->StdErr.fIsConsole)
10459 pSandbox->StdErr.u.Con.cwcBuf = 0;
10460 else
10461 pSandbox->StdErr.u.Fully.cchBuf = 0;
10462 pSandbox->Combined.cwcBuf = 0;
10463 pSandbox->Combined.cFlushes = 0;
10464#endif
10465 pSandbox->fNoPchCaching = fNoPchCaching;
10466 pSandbox->cArgs = cArgs;
10467 pSandbox->papszArgs = (char **)papszArgs;
10468 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
10469 if (!pSandbox->pszCmdLine)
10470 return KERR_NO_MEMORY;
10471
10472 /*
10473 * Convert command line and argv to UTF-16.
10474 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
10475 */
10476 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
10477 if (!pSandbox->papwszArgs)
10478 return KERR_NO_MEMORY;
10479 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
10480 for (i = 0; i < cArgs; i++)
10481 {
10482 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
10483 pSandbox->papwszArgs[i] = pwcPool;
10484 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
10485 pwcPool++;
10486 }
10487 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
10488 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
10489
10490 /*
10491 * Convert the commandline string to UTF-16, same pessimistic approach as above.
10492 */
10493 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
10494 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
10495 if (!pSandbox->pwszCmdLine)
10496 return KERR_NO_MEMORY;
10497 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
10498
10499 pSandbox->SavedCommandLine = pProcParams->CommandLine;
10500 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
10501 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
10502
10503 /*
10504 * Setup the environment.
10505 */
10506 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
10507 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
10508 {
10509 KU32 iDst = 0;
10510 for (i = 0; i < cEnvVars; i++)
10511 {
10512 const char *pszVar = papszEnvVars[i];
10513 KSIZE cchVar = kHlpStrLen(pszVar);
10514 const char *pszEqual;
10515 if ( cchVar > 0
10516 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
10517 {
10518 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
10519 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
10520 if (pszCopy && pwszCopy)
10521 {
10522 pSandbox->papszEnvVars[iDst] = pszCopy;
10523 pSandbox->environ[iDst] = pszCopy;
10524 pSandbox->papwszEnvVars[iDst] = pwszCopy;
10525 pSandbox->wenviron[iDst] = pwszCopy;
10526
10527 /* When we see the path, we must tell the system or native exec and module loading won't work . */
10528 if ( (pszEqual - pszVar) == 4
10529 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
10530 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
10531 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
10532 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
10533 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
10534 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
10535
10536 iDst++;
10537 }
10538 else
10539 {
10540 kHlpFree(pszCopy);
10541 kHlpFree(pwszCopy);
10542 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
10543 }
10544 }
10545 else
10546 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
10547 }
10548 pSandbox->papszEnvVars[iDst] = NULL;
10549 pSandbox->environ[iDst] = NULL;
10550 pSandbox->papwszEnvVars[iDst] = NULL;
10551 pSandbox->wenviron[iDst] = NULL;
10552 }
10553 else
10554 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
10555
10556 /*
10557 * Invalidate the volatile parts of cache (kBuild output directory,
10558 * temporary directory, whatever).
10559 */
10560 kFsCacheInvalidateCustomBoth(g_pFsCache);
10561
10562#ifdef WITH_HISTORY
10563 /*
10564 * Record command line in debug history.
10565 */
10566 kHlpFree(g_apszHistory[g_iHistoryNext]);
10567 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
10568 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
10569#endif
10570
10571 return 0;
10572}
10573
10574
10575/**
10576 * Does sandbox cleanup between jobs.
10577 *
10578 * We postpone whatever isn't externally visible (i.e. files) and doesn't
10579 * influence the result, so that kmk can get on with things ASAP.
10580 *
10581 * @param pSandbox The sandbox.
10582 */
10583static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
10584{
10585 PROCESS_MEMORY_COUNTERS MemInfo;
10586 PKWVIRTALLOC pTracker;
10587 PKWHEAP pHeap;
10588 PKWLOCALSTORAGE pLocalStorage;
10589#ifdef WITH_HASH_MD5_CACHE
10590 PKWHASHMD5 pHash;
10591#endif
10592#ifdef WITH_TEMP_MEMORY_FILES
10593 PKWFSTEMPFILE pTempFile;
10594#endif
10595 PKWEXITCALLACK pExitCallback;
10596
10597 /*
10598 * First stuff that may cause code to run.
10599 */
10600
10601 /* Do exit callback first. */
10602 pExitCallback = g_Sandbox.pExitCallbackHead;
10603 g_Sandbox.pExitCallbackHead = NULL;
10604 while (pExitCallback)
10605 {
10606 PKWEXITCALLACK pNext = pExitCallback->pNext;
10607 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
10608 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
10609 __try
10610 {
10611 pExitCallback->pfnCallback();
10612 }
10613 __except (EXCEPTION_EXECUTE_HANDLER)
10614 {
10615 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
10616 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
10617 kHlpAssertFailed();
10618 }
10619 kHlpFree(pExitCallback);
10620 pExitCallback = pNext;
10621 }
10622
10623 /* Free left behind FlsAlloc leaks. */
10624 pLocalStorage = g_Sandbox.pFlsAllocHead;
10625 g_Sandbox.pFlsAllocHead = NULL;
10626 while (pLocalStorage)
10627 {
10628 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10629 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
10630 FlsFree(pLocalStorage->idx);
10631 kHlpFree(pLocalStorage);
10632 pLocalStorage = pNext;
10633 }
10634
10635 /* Free left behind TlsAlloc leaks. */
10636 pLocalStorage = g_Sandbox.pTlsAllocHead;
10637 g_Sandbox.pTlsAllocHead = NULL;
10638 while (pLocalStorage)
10639 {
10640 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10641 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
10642 TlsFree(pLocalStorage->idx);
10643 kHlpFree(pLocalStorage);
10644 pLocalStorage = pNext;
10645 }
10646
10647
10648 /*
10649 * Then free resources associated with the sandbox run.
10650 */
10651
10652 /* Open handles, except fixed handles (stdout and stderr). */
10653 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
10654 {
10655 KU32 idxHandle = pSandbox->cHandles;
10656 while (idxHandle-- > 0)
10657 if (pSandbox->papHandles[idxHandle] == NULL)
10658 { /* likely */ }
10659 else
10660 {
10661 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
10662 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
10663 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
10664 {
10665 pSandbox->papHandles[idxHandle] = NULL;
10666 pSandbox->cLeakedHandles++;
10667
10668 switch (pHandle->enmType)
10669 {
10670 case KWHANDLETYPE_FSOBJ_READ_CACHE:
10671 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
10672 idxHandle, pHandle->hHandle, pHandle->cRefs));
10673 break;
10674 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
10675 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
10676 idxHandle, pHandle->hHandle, pHandle->cRefs));
10677 break;
10678 case KWHANDLETYPE_OUTPUT_BUF:
10679 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
10680 idxHandle, pHandle->hHandle, pHandle->cRefs));
10681 break;
10682 case KWHANDLETYPE_TEMP_FILE:
10683 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
10684 idxHandle, pHandle->hHandle, pHandle->cRefs));
10685 pHandle->u.pTempFile->cActiveHandles--;
10686 break;
10687 case KWHANDLETYPE_TEMP_FILE_MAPPING:
10688 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
10689 idxHandle, pHandle->hHandle, pHandle->cRefs));
10690 pHandle->u.pTempFile->cActiveHandles--;
10691 break;
10692 default:
10693 kHlpAssertFailed();
10694 }
10695 if (--pHandle->cRefs == 0)
10696 kHlpFree(pHandle);
10697 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
10698 break;
10699 }
10700 }
10701 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
10702 }
10703
10704 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
10705 g_Sandbox.cMemMappings = 0;
10706
10707#ifdef WITH_TEMP_MEMORY_FILES
10708 /* The temporary files aren't externally visible, they're all in memory. */
10709 pTempFile = pSandbox->pTempFileHead;
10710 pSandbox->pTempFileHead = NULL;
10711 while (pTempFile)
10712 {
10713 PKWFSTEMPFILE pNext = pTempFile->pNext;
10714 KU32 iSeg = pTempFile->cSegs;
10715 while (iSeg-- > 0)
10716 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
10717 kHlpFree(pTempFile->paSegs);
10718 pTempFile->pNext = NULL;
10719 kHlpFree(pTempFile);
10720
10721 pTempFile = pNext;
10722 }
10723#endif
10724
10725 /* Free left behind HeapCreate leaks. */
10726 pHeap = g_Sandbox.pHeapHead;
10727 g_Sandbox.pHeapHead = NULL;
10728 while (pHeap != NULL)
10729 {
10730 PKWHEAP pNext = pHeap->pNext;
10731 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
10732 HeapDestroy(pHeap->hHeap);
10733 pHeap = pNext;
10734 }
10735
10736 /* Free left behind VirtualAlloc leaks. */
10737 pTracker = g_Sandbox.pVirtualAllocHead;
10738 g_Sandbox.pVirtualAllocHead = NULL;
10739 while (pTracker)
10740 {
10741 PKWVIRTALLOC pNext = pTracker->pNext;
10742 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
10743
10744#ifdef WITH_FIXED_VIRTUAL_ALLOCS
10745 if (pTracker->idxPreAllocated != KU32_MAX)
10746 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
10747 else
10748#endif
10749 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
10750 kHlpFree(pTracker);
10751 pTracker = pNext;
10752 }
10753
10754 /* Free the environment. */
10755 if (pSandbox->papszEnvVars)
10756 {
10757 KU32 i;
10758 for (i = 0; pSandbox->papszEnvVars[i]; i++)
10759 kHlpFree(pSandbox->papszEnvVars[i]);
10760 pSandbox->environ[0] = NULL;
10761 pSandbox->papszEnvVars[0] = NULL;
10762
10763 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
10764 kHlpFree(pSandbox->papwszEnvVars[i]);
10765 pSandbox->wenviron[0] = NULL;
10766 pSandbox->papwszEnvVars[0] = NULL;
10767 }
10768
10769#ifdef WITH_HASH_MD5_CACHE
10770 /*
10771 * Hash handles.
10772 */
10773 pHash = pSandbox->pHashHead;
10774 pSandbox->pHashHead = NULL;
10775 while (pHash)
10776 {
10777 PKWHASHMD5 pNext = pHash->pNext;
10778 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
10779 kHlpFree(pHash);
10780 pHash = pNext;
10781 }
10782#endif
10783
10784 /*
10785 * Check the memory usage. If it's getting high, trigger a respawn
10786 * after the next job.
10787 */
10788 MemInfo.WorkingSetSize = 0;
10789 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
10790 {
10791 /* The first time thru, we figure out approximately when to restart
10792 based on installed RAM and CPU threads. */
10793 static KU64 s_cbMaxWorkingSet = 0;
10794 if (s_cbMaxWorkingSet != 0)
10795 { /* likely */ }
10796 else
10797 {
10798 SYSTEM_INFO SysInfo;
10799 MEMORYSTATUSEX GlobalMemInfo;
10800 const char *pszValue;
10801
10802 /* Calculate a reasonable estimate. */
10803 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
10804 GetNativeSystemInfo(&SysInfo);
10805
10806 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
10807 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
10808 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
10809#if K_ARCH_BITS >= 64
10810 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
10811#else
10812 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
10813#endif
10814 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
10815 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10816
10817 /* User limit. */
10818 pszValue = getenv("KWORKER_MEMORY_LIMIT");
10819 if (pszValue != NULL)
10820 {
10821 char *pszNext;
10822 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
10823 if (*pszNext == '\0' || *pszNext == 'M')
10824 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
10825 else if (*pszNext == 'K')
10826 s_cbMaxWorkingSet = ulValue * (KU64)1024;
10827 else if (*pszNext == 'G')
10828 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
10829 else
10830 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
10831 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10832 }
10833
10834 /* Clamp it a little. */
10835 if (s_cbMaxWorkingSet < 168*1024*1024)
10836 s_cbMaxWorkingSet = 168*1024*1024;
10837#if K_ARCH_BITS < 64
10838 else
10839 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
10840 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
10841 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
10842 : 1536*1024*1024 /* got 4GB VA */);
10843#endif
10844 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
10845 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
10846 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10847 }
10848
10849 /* Finally the check. */
10850 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
10851 {
10852 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
10853 g_fRestart = K_TRUE;
10854 }
10855 }
10856
10857 /*
10858 * The CRT has a max of 8192 handles, so we better restart after a while if
10859 * someone is leaking handles or we risk running out of descriptors.
10860 *
10861 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
10862 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
10863 */
10864 if (pSandbox->cLeakedHandles > 6000)
10865 {
10866 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
10867 g_fRestart = K_TRUE;
10868 }
10869}
10870
10871
10872/**
10873 * Does essential cleanups and restoring, anything externally visible.
10874 *
10875 * All cleanups that aren't externally visible are postponed till after we've
10876 * informed kmk of the result, so it can be done in the dead time between jobs.
10877 *
10878 * @param pSandbox The sandbox.
10879 */
10880static void kwSandboxCleanup(PKWSANDBOX pSandbox)
10881{
10882 /*
10883 * Restore the parent command line string.
10884 */
10885 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10886 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10887 pProcParams->CommandLine = pSandbox->SavedCommandLine;
10888 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
10889 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
10890}
10891
10892
10893static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10894 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10895{
10896 int rcExit = 42;
10897 int rc;
10898
10899 /*
10900 * Initialize the sandbox environment.
10901 */
10902 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
10903 if (rc == 0)
10904 {
10905 /*
10906 * Do module initialization.
10907 */
10908 kwSandboxResetModuleVisited();
10909 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
10910 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
10911 if (rc == 0)
10912 {
10913 /*
10914 * Call the main function.
10915 */
10916#if K_ARCH == K_ARCH_AMD64
10917 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
10918#elif K_ARCH == K_ARCH_X86_32
10919 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
10920#else
10921# error "Port me!"
10922#endif
10923
10924 /* Save the NT TIB first (should do that here, not in some other function). */
10925 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10926 pSandbox->TibMainThread = *pTib;
10927
10928 /* Make the call in a guarded fashion. */
10929#if K_ARCH == K_ARCH_AMD64
10930 /* AMD64 */
10931 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
10932 __try
10933 {
10934 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10935 if (setjmp(pSandbox->JmpBuf) == 0)
10936 {
10937 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10938 pSandbox->fRunning = K_TRUE;
10939 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
10940 pSandbox->fRunning = K_FALSE;
10941 }
10942 else
10943 rcExit = pSandbox->rcExitCode;
10944 }
10945#elif K_ARCH == K_ARCH_X86_32
10946 /* x86 (see _tmainCRTStartup) */
10947 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
10948 __try
10949 {
10950 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10951 if (setjmp(pSandbox->JmpBuf) == 0)
10952 {
10953 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10954 pSandbox->fRunning = K_TRUE;
10955 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
10956 pSandbox->fRunning = K_FALSE;
10957 }
10958 else
10959 rcExit = pSandbox->rcExitCode;
10960 }
10961#endif
10962 __except (EXCEPTION_EXECUTE_HANDLER)
10963 {
10964 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
10965#ifdef WITH_HISTORY
10966 {
10967 KU32 cPrinted = 0;
10968 while (cPrinted++ < 5)
10969 {
10970 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
10971 if (g_apszHistory[idx])
10972 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
10973 }
10974 }
10975#endif
10976 rcExit = 512;
10977 }
10978 pSandbox->fRunning = K_FALSE;
10979
10980 /* Now, restore the NT TIB. */
10981 *pTib = pSandbox->TibMainThread;
10982 }
10983 else
10984 rcExit = 42 + 4;
10985
10986 /*
10987 * Flush and clean up the essential bits only, postpone whatever we
10988 * can till after we've replied to kmk.
10989 */
10990#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10991 kwSandboxConsoleFlushAll(&g_Sandbox);
10992#endif
10993 kwSandboxCleanup(&g_Sandbox);
10994 /** @todo Flush sandboxed native CRTs too. */
10995 }
10996 else
10997 rcExit = 42 + 3;
10998
10999 return rcExit;
11000}
11001
11002
11003/**
11004 * Does the post command part of a job (optional).
11005 *
11006 * @returns The exit code of the job.
11007 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11008 * @param papszPostCmdArgs The post command and its argument.
11009 */
11010static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11011{
11012 const char *pszCmd = papszPostCmdArgs[0];
11013
11014 /* Allow the kmk builtin prefix. */
11015 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
11016 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
11017 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
11018
11019 /* Command switch. */
11020 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
11021 {
11022 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
11023 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
11024 }
11025
11026 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
11027}
11028
11029
11030/**
11031 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11032 */
11033static unsigned kwGetCurrentProcessorGroup(void)
11034{
11035 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
11036 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
11037 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
11038 if (pfnGetter)
11039 {
11040 GROUP_AFFINITY GroupAffinity;
11041 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
11042 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
11043 return GroupAffinity.Group;
11044 }
11045 return 0;
11046}
11047
11048
11049/**
11050 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11051 */
11052static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
11053{
11054 KSIZE cchRet = 0;
11055 HANDLE hToken;
11056 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
11057 {
11058 DWORD cbRet;
11059 TOKEN_STATISTICS TokenStats;
11060 memset(&TokenStats, 0, sizeof(TokenStats));
11061 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
11062 cchRet = sprintf(pszValue, "%" KX64_PRI,
11063 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
11064 else
11065 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
11066 CloseHandle(hToken);
11067 }
11068 else
11069 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
11070 return cchRet;
11071}
11072
11073
11074/**
11075 * Look for and expand the special environment variable.
11076 *
11077 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
11078 * couldn't accuratly determine. Currently the following variables are
11079 * implemented:
11080 * - "@@PROCESSOR_GROUP@@" - The processor group number.
11081 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
11082 * - "@@PID@@" - The kWorker process ID.
11083 * - "@@@@" - Escaped "@@".
11084 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
11085 */
11086static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
11087{
11088 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
11089 KU32 i = cEnvVars;
11090 while (i-- > 0)
11091 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
11092 && papszEnvVars[i][cchSpecialEnv] == '=')
11093 {
11094 /* We will expand stuff like @@NAME@@ */
11095 const char *pszValue = papszEnvVars[i];
11096 KSIZE offDst = 0;
11097 char szTmp[1024];
11098 for (;;)
11099 {
11100 const char *pszAt = kHlpStrChr(pszValue, '@');
11101 while (pszAt && pszAt[1] != '@')
11102 pszAt = kHlpStrChr(pszAt + 1, '@');
11103 if (pszAt)
11104 {
11105 KSIZE cchSrc = pszAt - pszValue;
11106 if (offDst + cchSrc < sizeof(szTmp))
11107 {
11108 char szSrc[64];
11109
11110 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11111 offDst += cchSrc;
11112 pszValue = pszAt + 2;
11113
11114 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11115 {
11116 pszValue += 15;
11117 if (g_iProcessGroup == -1)
11118 g_iProcessGroup = kwGetCurrentProcessorGroup();
11119 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11120 }
11121 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11122 {
11123 pszValue += 19;
11124 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11125 }
11126 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11127 {
11128 pszValue += 5;
11129 cchSrc = sprintf(szSrc, "%d", getpid());
11130 }
11131 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
11132 {
11133 pszValue += 2;
11134 szSrc[0] = '@';
11135 szSrc[1] = '@';
11136 szSrc[2] = '\0';
11137 cchSrc = 2;
11138 }
11139 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
11140 {
11141 static unsigned int s_iCounter = 0;
11142 pszValue += 15;
11143 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
11144 }
11145 else
11146 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
11147 pszValue - 2);
11148 if (offDst + cchSrc < sizeof(szTmp))
11149 {
11150 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
11151 offDst += cchSrc;
11152 continue;
11153 }
11154 }
11155 }
11156 else
11157 {
11158 KSIZE cchSrc = kHlpStrLen(pszValue);
11159 if (offDst + cchSrc < sizeof(szTmp))
11160 {
11161 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11162 offDst += cchSrc;
11163 break;
11164 }
11165 }
11166 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
11167 }
11168 szTmp[offDst] = '\0';
11169
11170 /* Return a copy of it: */
11171 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
11172 if (papszEnvVars[i])
11173 {
11174 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
11175 return 0;
11176 }
11177 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
11178 }
11179
11180 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
11181}
11182
11183
11184/**
11185 * Part 2 of the "JOB" command handler.
11186 *
11187 * @returns The exit code of the job.
11188 * @param pszExecutable The executable to execute.
11189 * @param pszCwd The current working directory of the job.
11190 * @param cArgs The number of arguments.
11191 * @param papszArgs The argument vector.
11192 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11193 * @param cEnvVars The number of environment variables.
11194 * @param papszEnvVars The environment vector.
11195 * @param pszSpecialEnv Name of special environment variable that
11196 * requires selective expansion here.
11197 * @param fNoPchCaching Whether to disable precompiled header file
11198 * caching. Avoid trouble when creating them.
11199 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11200 * @param papszPostCmdArgs The post command and its argument.
11201 */
11202static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
11203 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11204 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
11205 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11206{
11207 int rcExit;
11208 PKWTOOL pTool;
11209 char *pszSpecialEnvFree = NULL;
11210
11211 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
11212 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
11213#ifdef KW_LOG_ENABLED
11214 {
11215 KU32 i;
11216 for (i = 0; i < cArgs; i++)
11217 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
11218 for (i = 0; i < cPostCmdArgs; i++)
11219 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
11220 }
11221#endif
11222 g_cJobs++;
11223
11224 /*
11225 * Expand pszSpecialEnv if present.
11226 */
11227 if (*pszSpecialEnv)
11228 {
11229 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
11230 if (!rcExit)
11231 { /* likely */ }
11232 else
11233 return rcExit;
11234 }
11235
11236 /*
11237 * Lookup the tool.
11238 */
11239 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
11240 if (pTool)
11241 {
11242 /*
11243 * Change the directory if we're going to execute the job inside
11244 * this process. Then invoke the tool type specific handler.
11245 */
11246 switch (pTool->enmType)
11247 {
11248 case KWTOOLTYPE_SANDBOXED:
11249 case KWTOOLTYPE_WATCOM:
11250 {
11251 /* Change dir. */
11252 KFSLOOKUPERROR enmError;
11253 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
11254 if ( pNewCurDir == g_pCurDirObj
11255 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
11256 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11257 else if (SetCurrentDirectoryA(pszCwd))
11258 {
11259 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
11260 g_pCurDirObj = pNewCurDir;
11261 }
11262 else
11263 {
11264 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
11265 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11266 rcExit = 42 + 1;
11267 break;
11268 }
11269
11270 /* Call specific handler. */
11271 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
11272 {
11273 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
11274 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
11275 cEnvVars, papszEnvVars, fNoPchCaching);
11276 }
11277 else
11278 {
11279 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
11280 rcExit = 42 + 2;
11281 }
11282 break;
11283 }
11284
11285 case KWTOOLTYPE_EXEC:
11286 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
11287 rcExit = 42 + 2;
11288 break;
11289
11290 default:
11291 kHlpAssertFailed();
11292 kwErrPrintf("Internal tool type corruption!!\n");
11293 rcExit = 42 + 2;
11294 g_fRestart = K_TRUE;
11295 break;
11296 }
11297
11298 /*
11299 * Do the post command, if present.
11300 */
11301 if (cPostCmdArgs && rcExit == 0)
11302 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
11303 }
11304 else
11305 rcExit = 42 + 1;
11306 if (pszSpecialEnvFree)
11307 {
11308 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
11309 kHlpFree(pszSpecialEnvFree);
11310 }
11311 return rcExit;
11312}
11313
11314
11315/**
11316 * Handles a "JOB" command.
11317 *
11318 * @returns The exit code of the job.
11319 * @param pszMsg Points to the "JOB" command part of the message.
11320 * @param cbMsg Number of message bytes at @a pszMsg. There are
11321 * 4 more zero bytes after the message body to
11322 * simplify parsing.
11323 */
11324static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
11325{
11326 int rcExit = 42;
11327
11328 /*
11329 * Unpack the message.
11330 */
11331 const char *pszExecutable;
11332 KSIZE cbTmp;
11333
11334 pszMsg += sizeof("JOB");
11335 cbMsg -= sizeof("JOB");
11336
11337 /* Executable name. */
11338 pszExecutable = pszMsg;
11339 cbTmp = kHlpStrLen(pszMsg) + 1;
11340 pszMsg += cbTmp;
11341 if ( cbTmp < cbMsg
11342 && cbTmp > 2)
11343 {
11344 const char *pszCwd;
11345 cbMsg -= cbTmp;
11346
11347 /* Current working directory. */
11348 pszCwd = pszMsg;
11349 cbTmp = kHlpStrLen(pszMsg) + 1;
11350 pszMsg += cbTmp;
11351 if ( cbTmp + sizeof(KU32) < cbMsg
11352 && cbTmp >= 2)
11353 {
11354 KU32 cArgs;
11355 cbMsg -= cbTmp;
11356
11357 /* Argument count. */
11358 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
11359 pszMsg += sizeof(cArgs);
11360 cbMsg -= sizeof(cArgs);
11361
11362 if (cArgs > 0 && cArgs < 4096)
11363 {
11364 /* The argument vector. */
11365 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
11366 if (papszArgs)
11367 {
11368 KU32 i;
11369 for (i = 0; i < cArgs; i++)
11370 {
11371 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
11372 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
11373 pszMsg += cbTmp;
11374 if (cbTmp < cbMsg)
11375 cbMsg -= cbTmp;
11376 else
11377 {
11378 cbMsg = 0;
11379 break;
11380 }
11381
11382 }
11383 papszArgs[cArgs] = 0;
11384
11385 /* Environment variable count. */
11386 if (cbMsg > sizeof(KU32))
11387 {
11388 KU32 cEnvVars;
11389 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
11390 pszMsg += sizeof(cEnvVars);
11391 cbMsg -= sizeof(cEnvVars);
11392
11393 if (cEnvVars >= 0 && cEnvVars < 4096)
11394 {
11395 /* The argument vector. */
11396 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
11397 if (papszEnvVars)
11398 {
11399 for (i = 0; i < cEnvVars; i++)
11400 {
11401 papszEnvVars[i] = pszMsg;
11402 cbTmp = kHlpStrLen(pszMsg) + 1;
11403 pszMsg += cbTmp;
11404 if (cbTmp < cbMsg)
11405 cbMsg -= cbTmp;
11406 else
11407 {
11408 cbMsg = 0;
11409 break;
11410 }
11411 }
11412 papszEnvVars[cEnvVars] = 0;
11413
11414 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
11415 if (cbMsg >= sizeof(KU8) * 2)
11416 {
11417 KBOOL fWatcomBrainDamange = *pszMsg++;
11418 KBOOL fNoPchCaching = *pszMsg++;
11419 cbMsg -= 2;
11420
11421 /* Name of special enviornment variable requiring selective expansion. */
11422 if (cbMsg >= 1)
11423 {
11424 const char *pszSpecialEnv = pszMsg;
11425 cbTmp = kHlpStrLen(pszMsg);
11426 pszMsg += cbTmp + 1;
11427 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
11428
11429 /* Post command argument count (can be zero). */
11430 if (cbMsg >= sizeof(KU32))
11431 {
11432 KU32 cPostCmdArgs;
11433 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
11434 pszMsg += sizeof(cPostCmdArgs);
11435 cbMsg -= sizeof(cPostCmdArgs);
11436
11437 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
11438 {
11439 char const *apszPostCmdArgs[32+1];
11440 for (i = 0; i < cPostCmdArgs; i++)
11441 {
11442 apszPostCmdArgs[i] = pszMsg;
11443 cbTmp = kHlpStrLen(pszMsg) + 1;
11444 pszMsg += cbTmp;
11445 if ( cbTmp < cbMsg
11446 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
11447 cbMsg -= cbTmp;
11448 else
11449 {
11450 cbMsg = KSIZE_MAX;
11451 break;
11452 }
11453 }
11454 if (cbMsg == 0)
11455 {
11456 apszPostCmdArgs[cPostCmdArgs] = NULL;
11457
11458 /*
11459 * The next step.
11460 */
11461 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
11462 cArgs, papszArgs, fWatcomBrainDamange,
11463 cEnvVars, papszEnvVars, pszSpecialEnv,
11464 fNoPchCaching,
11465 cPostCmdArgs, apszPostCmdArgs);
11466 }
11467 else if (cbMsg == KSIZE_MAX)
11468 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
11469 else
11470 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
11471 }
11472 else
11473 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
11474 }
11475 else
11476 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
11477 }
11478 else
11479 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
11480 }
11481 else
11482 kwErrPrintf("Detected bogus message unpacking flags!\n");
11483 kHlpFree((void *)papszEnvVars);
11484 }
11485 else
11486 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
11487 }
11488 else
11489 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
11490 }
11491 else
11492 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
11493 kHlpFree((void *)papszArgs);
11494 }
11495 else
11496 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
11497 }
11498 else
11499 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
11500 }
11501 else
11502 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
11503 }
11504 else
11505 kwErrPrintf("Detected bogus message unpacking executable path!\n");
11506 return rcExit;
11507}
11508
11509
11510/**
11511 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
11512 *
11513 * @retval 0 on success.
11514 * @retval -1 on error (fully bitched).
11515 *
11516 * @param hPipe The pipe handle.
11517 * @param pvBuf The buffer to write out out.
11518 * @param cbToWrite The number of bytes to write.
11519 */
11520static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
11521{
11522 KU8 const *pbBuf = (KU8 const *)pvBuf;
11523 KU32 cbLeft = cbToWrite;
11524 while (g_rcCtrlC == 0)
11525 {
11526 DWORD cbActuallyWritten = 0;
11527 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
11528 {
11529 cbLeft -= cbActuallyWritten;
11530 if (!cbLeft)
11531 return 0;
11532 pbBuf += cbActuallyWritten;
11533 }
11534 else
11535 {
11536 DWORD dwErr = GetLastError();
11537 if (cbLeft == cbToWrite)
11538 kwErrPrintf("WriteFile failed: %u\n", dwErr);
11539 else
11540 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
11541 return -1;
11542 }
11543 }
11544 return -1;
11545}
11546
11547
11548/**
11549 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
11550 *
11551 * @retval 0 on success.
11552 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
11553 * @retval -1 on error (fully bitched).
11554 * @param hPipe The pipe handle.
11555 * @param pvBuf The buffer to read into.
11556 * @param cbToRead The number of bytes to read.
11557 * @param fShutdownOkay Whether connection shutdown while reading the
11558 * first byte is okay or not.
11559 */
11560static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
11561{
11562 KU8 *pbBuf = (KU8 *)pvBuf;
11563 KU32 cbLeft = cbToRead;
11564 while (g_rcCtrlC == 0)
11565 {
11566 DWORD cbActuallyRead = 0;
11567 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
11568 {
11569 cbLeft -= cbActuallyRead;
11570 if (!cbLeft)
11571 return 0;
11572 pbBuf += cbActuallyRead;
11573 }
11574 else
11575 {
11576 DWORD dwErr = GetLastError();
11577 if (cbLeft == cbToRead)
11578 {
11579 if ( fMayShutdown
11580 && dwErr == ERROR_BROKEN_PIPE)
11581 return 1;
11582 kwErrPrintf("ReadFile failed: %u\n", dwErr);
11583 }
11584 else
11585 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
11586 return -1;
11587 }
11588 }
11589 return -1;
11590}
11591
11592
11593/**
11594 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
11595 *
11596 * @returns pszBuf
11597 * @param pszBuf The buffer (sufficiently large).
11598 * @param uValue The value.
11599 */
11600static const char *kwFmtU64(char *pszBuf, KU64 uValue)
11601{
11602 char szTmp[64];
11603 char *psz = &szTmp[63];
11604 int cch = 4;
11605
11606 *psz-- = '\0';
11607 do
11608 {
11609 if (--cch == 0)
11610 {
11611 *psz-- = ' ';
11612 cch = 3;
11613 }
11614 *psz-- = (uValue % 10) + '0';
11615 uValue /= 10;
11616 } while (uValue != 0);
11617
11618 return strcpy(pszBuf, psz + 1);
11619}
11620
11621
11622/**
11623 * Prints statistics.
11624 */
11625static void kwPrintStats(void)
11626{
11627 PROCESS_MEMORY_COUNTERS_EX MemInfo;
11628 MEMORYSTATUSEX MemStatus;
11629 IO_COUNTERS IoCounters;
11630 DWORD cHandles;
11631 KSIZE cMisses;
11632 char szBuf[16*1024];
11633 int off = 0;
11634 char szPrf[24];
11635 char sz1[64];
11636 char sz2[64];
11637 char sz3[64];
11638 char sz4[64];
11639
11640 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
11641
11642 szBuf[off++] = '\n';
11643
11644 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
11645 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
11646 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
11647 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
11648 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
11649
11650 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
11651 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
11652
11653 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
11654 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
11655 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
11656
11657 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
11658 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
11659 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
11660
11661 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
11662 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
11663 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
11664 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
11665 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
11666 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
11667 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
11668
11669 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
11670 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
11671 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
11672 kwFmtU64(sz1, g_pFsCache->cObjects),
11673 kwFmtU64(sz2, g_pFsCache->cbObjects),
11674 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
11675 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
11676 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
11677 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
11678 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
11679 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
11680#ifdef KFSCACHE_CFG_UTF16
11681 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
11682 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
11683 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
11684 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
11685 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
11686#endif
11687 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
11688 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
11689 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
11690 kwFmtU64(sz3, g_pFsCache->cChildHashed),
11691 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
11692
11693 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
11694 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
11695 kwFmtU64(sz1, g_pFsCache->cLookups),
11696 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
11697 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11698 kwFmtU64(sz3, g_pFsCache->cWalkHits),
11699 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11700 kwFmtU64(sz4, cMisses),
11701 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
11702
11703 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
11704 kwFmtU64(sz1, g_pFsCache->cChildSearches),
11705 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
11706 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
11707 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
11708 kwFmtU64(sz1, g_pFsCache->cNameChanges),
11709 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
11710 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
11711
11712
11713 /*
11714 * Process & Memory details.
11715 */
11716 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
11717 cHandles = 0;
11718 MemInfo.cb = sizeof(MemInfo);
11719 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
11720 memset(&MemInfo, 0, sizeof(MemInfo));
11721 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
11722 kwFmtU64(sz1, cHandles),
11723 kwFmtU64(sz2, MemInfo.PageFaultCount),
11724 kwFmtU64(sz3, MemInfo.PagefileUsage),
11725 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
11726 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
11727 kwFmtU64(sz1, MemInfo.WorkingSetSize),
11728 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
11729 kwFmtU64(sz3, MemInfo.PrivateUsage));
11730 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
11731 szPrf,
11732 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
11733 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
11734 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
11735 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
11736
11737 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
11738 memset(&IoCounters, 0, sizeof(IoCounters));
11739 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
11740 kwFmtU64(sz1, IoCounters.ReadTransferCount),
11741 kwFmtU64(sz2, IoCounters.ReadOperationCount));
11742 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
11743 kwFmtU64(sz1, IoCounters.WriteTransferCount),
11744 kwFmtU64(sz2, IoCounters.WriteOperationCount));
11745 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
11746 kwFmtU64(sz1, IoCounters.OtherTransferCount),
11747 kwFmtU64(sz2, IoCounters.OtherOperationCount));
11748
11749 MemStatus.dwLength = sizeof(MemStatus);
11750 if (!GlobalMemoryStatusEx(&MemStatus))
11751 memset(&MemStatus, 0, sizeof(MemStatus));
11752 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
11753 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
11754 MemStatus.ullAvailVirtual);
11755 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
11756
11757 maybe_con_fwrite(szBuf, off, 1, stdout);
11758 fflush(stdout);
11759}
11760
11761
11762/**
11763 * Handles what comes after --test.
11764 *
11765 * @returns Exit code.
11766 * @param argc Number of arguments after --test.
11767 * @param argv Arguments after --test.
11768 */
11769static int kwTestRun(int argc, char **argv)
11770{
11771 int i;
11772 int j;
11773 int rcExit;
11774 int cRepeats;
11775 char szCwd[MAX_PATH];
11776 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
11777 KU32 cEnvVars;
11778 char **papszEnvVars;
11779 const char *pszSpecialEnv = "";
11780 const char *pszSpecialEnvFull = NULL;
11781 KBOOL fWatcomBrainDamange = K_FALSE;
11782 KBOOL fNoPchCaching = K_FALSE;
11783
11784 /*
11785 * Parse arguments.
11786 */
11787 /* Repeat count. */
11788 i = 0;
11789 if (i >= argc)
11790 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
11791 if (strcmp(argv[i], "--") != 0)
11792 {
11793 cRepeats = atoi(argv[i]);
11794 if (cRepeats <= 0)
11795 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
11796 i++;
11797
11798 /* Optional directory change. */
11799 if ( i < argc
11800 && ( strcmp(argv[i], "--chdir") == 0
11801 || strcmp(argv[i], "-C") == 0 ) )
11802 {
11803 i++;
11804 if (i >= argc)
11805 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
11806 pszCwd = argv[i++];
11807 }
11808
11809 /* Optional watcom flag directory change. */
11810 if ( i < argc
11811 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
11812 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
11813 {
11814 fWatcomBrainDamange = K_TRUE;
11815 i++;
11816 }
11817
11818 /* Optional watcom flag directory change. */
11819 if ( i < argc
11820 && strcmp(argv[i], "--no-pch-caching") == 0)
11821 {
11822 fNoPchCaching = K_TRUE;
11823 i++;
11824 }
11825
11826 /* Optional directory change. */
11827 if ( i < argc
11828 && ( strcmp(argv[i], "--set-special") == 0
11829 || strcmp(argv[i], "-s") == 0 ) )
11830 {
11831 i++;
11832 if (i >= argc)
11833 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
11834 pszSpecialEnvFull = argv[i++];
11835 putenv(pszSpecialEnvFull);
11836 pszSpecialEnv = strdup(pszSpecialEnvFull);
11837 *strchr(pszSpecialEnv, '=') = '\0';
11838 }
11839
11840 /* Trigger breakpoint */
11841 if ( i < argc
11842 && strcmp(argv[i], "--breakpoint") == 0)
11843 {
11844 __debugbreak();
11845 i++;
11846 }
11847
11848 /* Check for '--'. */
11849 if (i >= argc)
11850 return kwErrPrintfRc(2, "Missing '--'\n");
11851 if (strcmp(argv[i], "--") != 0)
11852 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
11853 i++;
11854 }
11855 else
11856 {
11857 cRepeats = 1;
11858 i++;
11859 }
11860 if (i >= argc)
11861 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
11862
11863 /*
11864 * Duplicate the environment.
11865 */
11866 cEnvVars = 0;
11867 while (environ[cEnvVars] != NULL)
11868 cEnvVars++;
11869 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
11870
11871 /*
11872 * Do the job.
11873 */
11874 for (j = 0; j < cRepeats; j++)
11875 {
11876 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
11877 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
11878 argc - i, &argv[i], fWatcomBrainDamange,
11879 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
11880 0, NULL);
11881 KW_LOG(("rcExit=%d\n", rcExit));
11882 kwSandboxCleanupLate(&g_Sandbox);
11883 }
11884
11885 if (getenv("KWORKER_STATS") != NULL)
11886 kwPrintStats();
11887
11888# ifdef WITH_LOG_FILE
11889 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
11890 CloseHandle(g_hLogFile);
11891# endif
11892 return rcExit;
11893}
11894
11895
11896/**
11897 * Reads @a pszFile into memory and chops it up into an argument vector.
11898 *
11899 * @returns Pointer to the argument vector on success, NULL on failure.
11900 * @param pszFile The file to load.
11901 * @param pcArgs Where to return the number of arguments.
11902 * @param ppszFileContent Where to return the allocation.
11903 */
11904static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
11905{
11906 char **papszArgs = NULL;
11907 FILE *pFile = fopen(pszFile, "r");
11908 if (pFile)
11909 {
11910 long cbFile;
11911 if ( fseek(pFile, 0, SEEK_END) == 0
11912 && (cbFile = ftell(pFile)) >= 0
11913 && fseek(pFile, 0, SEEK_SET) == 0)
11914 {
11915 char *pszFile = kHlpAllocZ(cbFile + 3);
11916 if (pszFile)
11917 {
11918 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
11919 if ( feof(pFile)
11920 && !ferror(pFile))
11921 {
11922 size_t off = 0;
11923 int cArgs = 0;
11924 int cAllocated = 0;
11925 char ch;
11926
11927 pszFile[cbRead] = '\0';
11928 pszFile[cbRead + 1] = '\0';
11929 pszFile[cbRead + 2] = '\0';
11930
11931 while ((ch = pszFile[off]) != '\0')
11932 {
11933 char *pszArg;
11934 switch (ch)
11935 {
11936 case ' ':
11937 case '\t':
11938 case '\n':
11939 case '\r':
11940 off++;
11941 continue;
11942
11943 case '\\':
11944 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
11945 {
11946 off += 2;
11947 continue;
11948 }
11949 /* fall thru */
11950 default:
11951 pszArg = &pszFile[off];
11952 do
11953 ch = pszFile[++off];
11954 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
11955 pszFile[off++] = '\0';
11956 break;
11957
11958 case '\'':
11959 pszArg = &pszFile[++off];
11960 while ((ch = pszFile[off]) != '\0' && ch != '\'')
11961 off++;
11962 pszFile[off++] = '\0';
11963 break;
11964
11965 case '\"': /** @todo escape sequences */
11966 pszArg = &pszFile[++off];
11967 while ((ch = pszFile[off]) != '\0' && ch != '"')
11968 off++;
11969 pszFile[off++] = '\0';
11970 break;
11971 }
11972 if (cArgs + 1 >= cAllocated)
11973 {
11974 void *pvNew;
11975 cAllocated = cAllocated ? cAllocated * 2 : 16;
11976 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
11977 if (pvNew)
11978 papszArgs = (char **)pvNew;
11979 else
11980 {
11981 kHlpFree(papszArgs);
11982 papszArgs = NULL;
11983 break;
11984 }
11985 }
11986 papszArgs[cArgs] = pszArg;
11987 papszArgs[++cArgs] = NULL;
11988 }
11989 *pcArgs = cArgs;
11990 }
11991 else
11992 kwErrPrintf("Error reading '%s'!\n", pszFile);
11993 }
11994 else
11995 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
11996 }
11997 else
11998 kwErrPrintf("Error seeking '%s'!\n", pszFile);
11999 fclose(pFile);
12000 }
12001 else
12002 kwErrPrintf("Error opening '%s'!\n", pszFile);
12003 return papszArgs;
12004}
12005
12006/**
12007 * Appends a string to an string vector (arguments or enviornment).
12008 *
12009 * @returns 0 on success, non-zero on failure (exit code).
12010 * @param ppapszVector Pointer to the string pointer array.
12011 * @param pcEntries Pointer to the array size.
12012 * @param pszAppend The string to append.
12013 */
12014static int kwFullTestVectorAppend(const char ***ppapszVector, int *pcEntries, char const *pszAppend)
12015{
12016 unsigned cEntries = *pcEntries;
12017 if (!(cEntries & 15))
12018 {
12019 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
12020 if (pvNew)
12021 *ppapszVector = (const char **)pvNew;
12022 else
12023 return kwErrPrintfRc(2, "Out of memory!\n");
12024 }
12025 (*ppapszVector)[cEntries] = pszAppend;
12026 (*ppapszVector)[++cEntries] = NULL;
12027 *pcEntries = cEntries;
12028 return 0;
12029}
12030
12031
12032/**
12033 * Parses arguments for --full-test.
12034 *
12035 * @returns 0 on success, non-zero on failure (exit code).
12036 */
12037static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
12038 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
12039{
12040 PKWONETEST pCur = *ppHead;
12041 int i;
12042 for (i = 0; i < argc; i++)
12043 {
12044 int rc = 0;
12045 const char *pszArg = argv[i];
12046 if (*pszArg == 'k' && kHlpStrComp(pszArg, "kSubmit") == 0)
12047 {
12048 if (*piState != 0)
12049 {
12050 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
12051 if (!pCur)
12052 return kwErrPrintfRc(2, "Out of memory!\n");
12053 pCur->fVirgin = K_TRUE;
12054 pCur->pszCwd = pszDefaultCwd;
12055 pCur->cRuns = 1;
12056 pCur->pNext = *ppHead;
12057 *ppHead = pCur;
12058 *piState = 0;
12059 }
12060 else if (!pCur->fVirgin)
12061 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
12062 pCur->pszJobSrc = pszJobSrc;
12063 pCur->iJobSrc = i;
12064 continue; /* (to stay virgin) */
12065 }
12066 else if (*pszArg == '-' && *piState == 0)
12067 {
12068 const char *pszValue = NULL;
12069 char ch = *++pszArg;
12070 pszArg++;
12071 if (ch == '-')
12072 {
12073 ch = '\0';
12074 if (*pszArg == '\0') /* -- */
12075 *piState = 2;
12076 /* Translate or handle long options: */
12077 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
12078 ch = 'E';
12079 else if (kHlpStrComp(pszArg, "special-env") == 0)
12080 ch = 's';
12081 else if (kHlpStrComp(pszArg, "default-env") == 0)
12082 {
12083 unsigned i;
12084 pCur->cEnvVars = 0;
12085 for (i = 0; environ[i] && rc == 0; i++)
12086 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
12087 }
12088 else if (kHlpStrComp(pszArg, "chdir") == 0)
12089 ch = 'C';
12090 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
12091 ch = 'P';
12092 else if (kHlpStrComp(pszArg, "response-file") == 0)
12093 ch = '@';
12094 else if (kHlpStrComp(pszArg, "runs") == 0)
12095 ch = 'R';
12096 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
12097 pCur->fWatcomBrainDamange = K_TRUE;
12098 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
12099 pCur->fNoPchCaching = K_TRUE;
12100 else if (kHlpStrComp(pszArg, "executable") == 0)
12101 ch = 'e';
12102 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12103 {
12104 __debugbreak();
12105 continue; /* (to stay virgin) */
12106 }
12107 else
12108 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12109 pszArg = "";
12110 }
12111
12112 while (ch != '\0' && rc == 0)
12113 {
12114 /* Fetch value if needed: */
12115 switch (ch)
12116 {
12117 case '@':
12118 case 'e':
12119 case 'E':
12120 case 's':
12121 case 'C':
12122 case 'R':
12123 if (*pszArg == ':' || *pszArg == '=')
12124 pszValue = &pszArg[1];
12125 else if (*pszArg)
12126 pszValue = pszArg;
12127 else if (i + 1 < argc)
12128 pszValue = argv[++i];
12129 else
12130 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
12131 pszArg = "";
12132 break;
12133 }
12134
12135 /* Handle the option: */
12136 switch (ch)
12137 {
12138 case 'E':
12139 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
12140 break;
12141 case 'C':
12142 pCur->pszCwd = pszValue;
12143 break;
12144 case 's':
12145 pCur->pszSpecialEnv = pszValue;
12146 break;
12147 case 'e':
12148 pCur->pszExecutable = pszValue;
12149 break;
12150 case 'P':
12151 *piState = 1;
12152 if (*pszArg)
12153 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
12154 break;
12155 case 'R':
12156 pCur->cRuns = atoi(pszValue);
12157 if ((int)pCur->cRuns < 0)
12158 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
12159 break;
12160 case '@':
12161 if (cRecursions < 5)
12162 {
12163 char *pszLeaked = NULL;
12164 int cArgs = 0;
12165 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
12166 if (papszArgsLeaked)
12167 {
12168 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
12169 cRecursions + 1, pszValue);
12170 pCur = *ppHead;
12171 }
12172 else
12173 return 2;
12174 }
12175 else
12176 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
12177 break;
12178 }
12179
12180 /* next */
12181 ch = *pszArg++;
12182 }
12183 }
12184 else if (*piState == 2)
12185 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
12186 else if (*piState == 1)
12187 {
12188 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
12189 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
12190 else
12191 *piState = 2;
12192 }
12193 else
12194 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
12195 if (rc)
12196 return rc;
12197 pCur->fVirgin = K_FALSE;
12198 }
12199 return 0;
12200}
12201
12202
12203/**
12204 * Handles what comes after --full-test.
12205 *
12206 * @returns Exit code.
12207 * @param argc Number of arguments after --full-test.
12208 * @param argv Arguments after --full-test.
12209 */
12210static int kwFullTestRun(int argc, char **argv)
12211{
12212 char szDefaultCwd[MAX_PATH];
12213 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
12214 KWONETEST FirstTest;
12215 PKWONETEST pHead = &FirstTest;
12216 PKWONETEST pCur;
12217 int iState = 0;
12218 int rcExit;
12219
12220 /*
12221 * Parse arguments.
12222 */
12223 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
12224 FirstTest.pszJobSrc = "command-line";
12225 FirstTest.iJobSrc = 1;
12226 FirstTest.fVirgin = K_TRUE;
12227 FirstTest.pszCwd = pszDefaultCwd;
12228 FirstTest.cRuns = 1;
12229
12230 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
12231 if (rcExit)
12232 return rcExit;
12233
12234 /*
12235 * Do the job. LIFO ordering (see kSubmit).
12236 */
12237 for (pCur = pHead; pCur; pCur = pCur->pNext)
12238 {
12239 if (!pCur->pszExecutable && pCur->papszArgs)
12240 pCur->pszExecutable = pCur->papszArgs[0];
12241 if ( pCur->pszExecutable
12242 && pCur->cArgs > 0
12243 && pCur->cEnvVars > 0)
12244 {
12245 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
12246 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
12247 unsigned iRun;
12248
12249 for (iRun = 0; iRun < pCur->cRuns; iRun++)
12250 {
12251 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
12252 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
12253 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
12254 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
12255
12256 KW_LOG(("rcExit=%d\n", rcExit));
12257 kwSandboxCleanupLate(&g_Sandbox);
12258
12259 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
12260 }
12261 kHlpFree(papszEnvVarsCopy);
12262 }
12263 else
12264 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
12265 pCur->pszExecutable ? "" : " No executable!",
12266 pCur->cArgs < 1 ? " No arguments!" : "",
12267 pCur->cEnvVars < 1 ? " No environment!" : "",
12268 pCur->iJobSrc, pCur->pszJobSrc);
12269 }
12270
12271 if (getenv("KWORKER_STATS") != NULL)
12272 kwPrintStats();
12273
12274# ifdef WITH_LOG_FILE
12275 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12276 CloseHandle(g_hLogFile);
12277# endif
12278 return rcExit;
12279}
12280
12281
12282int main(int argc, char **argv)
12283{
12284 KSIZE cbMsgBuf = 0;
12285 KU8 *pbMsgBuf = NULL;
12286 int i;
12287 HANDLE hPipe = INVALID_HANDLE_VALUE;
12288 const char *pszTmp;
12289 KFSLOOKUPERROR enmIgnored;
12290#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
12291 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
12292 kwSandboxVecXcptEmulateChained);
12293#endif
12294#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12295 HANDLE hCurProc = GetCurrentProcess();
12296 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12297 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12298 DWORD dwType;
12299#endif
12300
12301
12302#ifdef WITH_FIXED_VIRTUAL_ALLOCS
12303 /*
12304 * Reserve memory for cl.exe
12305 */
12306 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
12307 {
12308 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
12309 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
12310 g_aFixedVirtualAllocs[i].cbFixed,
12311 MEM_RESERVE, PAGE_READWRITE);
12312 if ( !g_aFixedVirtualAllocs[i].pvReserved
12313 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
12314 {
12315 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
12316 GetLastError());
12317 if (g_aFixedVirtualAllocs[i].pvReserved)
12318 {
12319 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
12320 g_aFixedVirtualAllocs[i].pvReserved = NULL;
12321 }
12322 }
12323 }
12324#endif
12325
12326 /*
12327 * Register our Control-C and Control-Break handlers.
12328 */
12329 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
12330 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
12331
12332 /*
12333 * Create the cache and mark the temporary directory as using the custom revision.
12334 */
12335 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
12336 if (!g_pFsCache)
12337 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
12338
12339 pszTmp = getenv("TEMP");
12340 if (pszTmp && *pszTmp != '\0')
12341 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12342 pszTmp = getenv("TMP");
12343 if (pszTmp && *pszTmp != '\0')
12344 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12345 pszTmp = getenv("TMPDIR");
12346 if (pszTmp && *pszTmp != '\0')
12347 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12348
12349 /*
12350 * Make g_abDefLdBuf executable.
12351 */
12352 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
12353 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
12354 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
12355
12356#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12357 /*
12358 * Get and duplicate the console handles.
12359 */
12360 /* Standard output. */
12361 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
12362 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
12363 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12364 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
12365 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
12366 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
12367 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12368 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12369 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
12370 g_Sandbox.HandleStdOut.cRefs = 0x10001;
12371 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
12372 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
12373 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
12374 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
12375 {
12376 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
12377 g_Sandbox.cFixedHandles++;
12378 else
12379 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
12380 }
12381 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12382 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
12383
12384 /* Standard error. */
12385 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
12386 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
12387 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12388 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
12389 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
12390 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
12391 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12392 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12393 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
12394 g_Sandbox.HandleStdErr.cRefs = 0x10001;
12395 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
12396 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
12397 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
12398 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
12399 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
12400 {
12401 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
12402 g_Sandbox.cFixedHandles++;
12403 else
12404 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
12405 }
12406 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12407 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
12408
12409 /* Combined console buffer. */
12410 if (g_Sandbox.StdErr.fIsConsole)
12411 {
12412 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
12413 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12414 }
12415 else if (g_Sandbox.StdOut.fIsConsole)
12416 {
12417 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
12418 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12419 }
12420 else
12421 {
12422 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
12423 g_Sandbox.Combined.uCodepage = CP_ACP;
12424 }
12425 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
12426#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
12427
12428
12429 /*
12430 * Parse arguments.
12431 */
12432 for (i = 1; i < argc; i++)
12433 {
12434 if (strcmp(argv[i], "--pipe") == 0)
12435 {
12436 i++;
12437 if (i < argc)
12438 {
12439 char *pszEnd = NULL;
12440 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
12441 if ( *argv[i]
12442 && pszEnd != NULL
12443 && *pszEnd == '\0'
12444 && u64Value != 0
12445 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
12446 && (uintptr_t)u64Value == u64Value)
12447 hPipe = (HANDLE)(uintptr_t)u64Value;
12448 else
12449 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
12450 }
12451 else
12452 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
12453 }
12454 else if (strcmp(argv[i], "--volatile") == 0)
12455 {
12456 i++;
12457 if (i < argc)
12458 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
12459 else
12460 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
12461 }
12462 else if (strcmp(argv[i], "--test") == 0)
12463 return kwTestRun(argc - i - 1, &argv[i + 1]);
12464 else if (strcmp(argv[i], "--full-test") == 0)
12465 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
12466 else if (strcmp(argv[i], "--priority") == 0)
12467 {
12468 i++;
12469 if (i < argc)
12470 {
12471 char *pszEnd = NULL;
12472 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12473 if ( *argv[i]
12474 && pszEnd != NULL
12475 && *pszEnd == '\0'
12476 && uValue >= 1
12477 && uValue <= 5)
12478 {
12479 DWORD dwClass, dwPriority;
12480 switch (uValue)
12481 {
12482 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
12483 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
12484 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
12485 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12486 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12487 }
12488 SetPriorityClass(GetCurrentProcess(), dwClass);
12489 if (dwPriority != 0xffffffff)
12490 SetThreadPriority(GetCurrentThread(), dwPriority);
12491 }
12492 else
12493 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12494 }
12495 else
12496 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12497 }
12498 else if (strcmp(argv[i], "--group") == 0)
12499 {
12500 i++;
12501 if (i < argc)
12502 {
12503 char *pszEnd = NULL;
12504 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12505 if ( *argv[i]
12506 && pszEnd != NULL
12507 && *pszEnd == '\0'
12508 && uValue == (WORD)uValue)
12509 {
12510 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
12511 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
12512 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(GetModuleHandleW(L"KERNEL32.DLL"),
12513 "SetThreadGroupAffinity");
12514 if (pfnSetThreadGroupAffinity)
12515 {
12516 GROUP_AFFINITY NewAff = { ~(uintptr_t)0, (WORD)uValue, 0, 0, 0 };
12517 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
12518 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
12519 kwErrPrintf("Failed to set processor group to %lu: %u\n", uValue, GetLastError());
12520 }
12521 else
12522 kwErrPrintf("Cannot set processor group to %lu because SetThreadGroupAffinity was not found\n", uValue);
12523 }
12524 else
12525 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12526 }
12527 else
12528 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12529 }
12530 else if ( strcmp(argv[i], "--help") == 0
12531 || strcmp(argv[i], "-h") == 0
12532 || strcmp(argv[i], "-?") == 0)
12533 {
12534 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
12535 "usage: kWorker <--help|-h>\n"
12536 "usage: kWorker <--version|-V>\n"
12537 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
12538 "\n"
12539 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
12540 return 0;
12541 }
12542 else if ( strcmp(argv[i], "--version") == 0
12543 || strcmp(argv[i], "-V") == 0)
12544 return kbuild_version(argv[0]);
12545 else
12546 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
12547 }
12548
12549 /*
12550 * If no --pipe argument, then assume its standard input.
12551 * We need to carefully replace the CRT stdin with a handle to "nul".
12552 */
12553 if (hPipe == INVALID_HANDLE_VALUE)
12554 {
12555 hPipe = GetStdHandle(STD_INPUT_HANDLE);
12556 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
12557 {
12558 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
12559 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
12560 {
12561 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
12562 if (fdNul >= 0)
12563 {
12564 if (_dup2(fdNul, 0) >= 0)
12565 {
12566 close(fdNul);
12567 hPipe = hDuplicate;
12568 }
12569 else
12570 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12571 }
12572 else
12573 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12574 }
12575 else
12576 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12577 }
12578 else
12579 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
12580 GetFileType(hPipe), GetLastError());
12581 }
12582 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
12583 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
12584 GetFileType(hPipe), GetLastError());
12585 g_hPipe = hPipe;
12586
12587 /*
12588 * Serve the pipe.
12589 */
12590 for (;;)
12591 {
12592 KU32 cbMsg = 0;
12593 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
12594 if (rc == 0)
12595 {
12596 /* Make sure the message length is within sane bounds. */
12597 if ( cbMsg > 4
12598 && cbMsg <= 256*1024*1024)
12599 {
12600 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
12601 if (cbMsg + 4 <= cbMsgBuf)
12602 { /* likely */ }
12603 else
12604 {
12605 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
12606 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
12607 if (!pbMsgBuf)
12608 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
12609 }
12610
12611 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
12612 *(KU32 *)pbMsgBuf = cbMsg;
12613 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
12614 if (rc == 0)
12615 {
12616 const char *psz;
12617
12618 pbMsgBuf[cbMsg] = '\0';
12619 pbMsgBuf[cbMsg + 1] = '\0';
12620 pbMsgBuf[cbMsg + 2] = '\0';
12621 pbMsgBuf[cbMsg + 3] = '\0';
12622
12623 /* The first string after the header is the command. */
12624 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
12625 if ( strcmp(psz, "JOB") == 0
12626 && g_rcCtrlC == 0)
12627 {
12628 struct
12629 {
12630 KI32 rcExitCode;
12631 KU8 bExiting;
12632 KU8 abZero[3];
12633 } Reply;
12634 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
12635 Reply.bExiting = g_fRestart;
12636 Reply.abZero[0] = 0;
12637 Reply.abZero[1] = 0;
12638 Reply.abZero[2] = 0;
12639 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
12640 if ( rc == 0
12641 && !g_fRestart)
12642 {
12643 kwSandboxCleanupLate(&g_Sandbox);
12644 if (g_rcCtrlC == 0)
12645 continue;
12646 }
12647 }
12648 else
12649 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
12650 }
12651 }
12652 else
12653 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
12654 }
12655
12656 /*
12657 * If we're exitting because we're restarting, we need to delay till
12658 * kmk/kSubmit has read the result. Windows documentation says it
12659 * immediately discards pipe buffers once the pipe is broken by the
12660 * server (us). So, We flush the buffer and queues a 1 byte read
12661 * waiting for kSubmit to close the pipe when it receives the
12662 * bExiting = K_TRUE result.
12663 */
12664 if (g_fRestart)
12665 {
12666 KU8 b;
12667 FlushFileBuffers(hPipe);
12668 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
12669 }
12670
12671 CloseHandle(hPipe);
12672#ifdef WITH_LOG_FILE
12673 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12674 CloseHandle(g_hLogFile);
12675#endif
12676 if (getenv("KWORKER_STATS") != NULL)
12677 kwPrintStats();
12678 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
12679 }
12680}
12681
12682
12683/** @page pg_kWorker kSubmit / kWorker
12684 *
12685 * @section sec_kWorker_Motivation Motivation / Inspiration
12686 *
12687 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
12688 * builds on machines "infested" by Anti Virus protection and disk encryption
12689 * software. Build times jumping from 35-40 min to 77-82 min after the machine
12690 * got "infected".
12691 *
12692 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
12693 * mainly a bunch of tiny assembly and C files being compiler a million times.
12694 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
12695 * own toolchain from within the same process, saving a lot of process creation
12696 * and teardown overhead.
12697 *
12698 *
12699 * @section sec_kWorker_kSubmit About kSubmit
12700 *
12701 * When wanting to execute a job in a kWorker instance, it must be submitted
12702 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
12703 * built into kmk and does not exist as an external program. The reason for
12704 * this is that it keep track of the kWorker instances.
12705 *
12706 * The kSubmit command has the --32-bit and --64-bit options for selecting
12707 * between 32-bit and 64-bit worker instance. We generally assume the user of
12708 * the command knows which bit count the executable has, so kSubmit is spared
12709 * the extra work of finding out.
12710 *
12711 * The kSubmit command shares a environment and current directory manipulation
12712 * with the kRedirect command, but not the file redirection. So long no file
12713 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
12714 * hand for tools like OpenWatcom, NASM and YASM which all require environment
12715 * and/or current directory changes to work.
12716 *
12717 * Unlike the kRedirect command, the kSubmit command can also specify an
12718 * internall post command to be executed after the main command succeeds.
12719 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
12720 * information from Microsoft COFF object files and Watcom OMF object files and
12721 * is scheduled to replace kDepIDB.
12722 *
12723 *
12724 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
12725 *
12726 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
12727 * A job request is written by kSubmit and kWorker read, unpacks it and executes
12728 * it. When the job is completed, kWorker writes a short reply with the exit
12729 * code and an internal status indicating whether it is going to restart.
12730 *
12731 * The kWorker intance will reply to kSubmit before completing all the internal
12732 * cleanup work, so as not to delay the next job execution unnecessarily. This
12733 * includes checking its own memory consumption and checking whether it needs
12734 * restarting. So, a decision to restart unfortunately have to wait till after
12735 * the next job has completed. This is a little bit unfortunate if the next job
12736 * requires a lot of memory and kWorker has already leaked/used a lot.
12737 *
12738 *
12739 * @section sec_kWorker_How_Works How kWorker Works
12740 *
12741 * kWorker will load the executable specified by kSubmit into memory and call
12742 * it's entrypoint in a lightly sandbox'ed environment.
12743 *
12744 *
12745 * @subsection ssec_kWorker_Loaing Image loading
12746 *
12747 * kWorker will manually load all the executable images into memory, fix them
12748 * up, and make a copy of the virgin image so it can be restored using memcpy
12749 * the next time it is used.
12750 *
12751 * Imported functions are monitored and replacements used for a few of them.
12752 * These replacements are serve the following purposes:
12753 * - Provide a different command line.
12754 * - Provide a different environment.
12755 * - Intercept process termination.
12756 * - Intercept thread creation (only linker is allowed to create threads).
12757 * - Intercept file reading for caching (header files, ++) as file system
12758 * access is made even slower by anti-virus software.
12759 * - Intercept crypto hash APIs to cache MD5 digests of header files
12760 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
12761 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
12762 * in memory as writing files grows expensive with encryption and
12763 * anti-virus software active.
12764 * - Intercept some file system queries to use the kFsCache instead of
12765 * going to the kernel and slowly worm thru the AV filter driver.
12766 * - Intercept standard output/error and console writes to aggressivly
12767 * buffer the output. The MS CRT does not buffer either when it goes to
12768 * the console, resulting in terrible performance and mixing up output
12769 * with other compile jobs.
12770 * This also allows us to filter out the annoying source file announcements
12771 * by cl.exe.
12772 * - Intercept VirtualAlloc and VirtualFree to prevent
12773 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
12774 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
12775 * the callbacks run after each job.
12776 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
12777 * executables and tools using custom heaps (like the microsoft linker).
12778 * [exectuable images only]
12779 * - Intercept atexit and _onexit registration to be able run them after
12780 * each job instead of crashing as kWorker exits. This also helps avoid
12781 * some leaks. [executable image only]
12782 *
12783 * DLLs falls into two categories, system DLLs which we always load using the
12784 * native loader, and tool DLLs which can be handled like the executable or
12785 * optionally using the native loader. We maintain a hardcoded white listing of
12786 * tool DLLs we trust to load using the native loader.
12787 *
12788 * Imports of natively loaded DLLs are processed too, but we only replace a
12789 * subset of the functions compared to natively loaded excutable and DLL images.
12790 *
12791 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
12792 * This is to speed up job execution.
12793 *
12794 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
12795 * for each job run, but so far this hasn't been necessary.
12796 *
12797 *
12798 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
12799 *
12800 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
12801 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
12802 * intermediate representation between the first (c1/c1xx.dll) and second pass
12803 * (c2.dll).
12804 *
12805 * kWorker helps the compiler as best as it can. Given a little knowledge about
12806 * stable and volatile file system areas, it can do a lot of caching that a
12807 * normal compiler driver cannot easily do when given a single file.
12808 *
12809 *
12810 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
12811 *
12812 * The preprocessor part will open and process header files exactly as they are
12813 * encountered in the source files. If string.h is included by the main source
12814 * and five other header files, it will be searched for (include path), opened,
12815 * read, MD5-summed, and pre-processed six times. The last five times is just a
12816 * waste of time because of the guards or \#pragma once. A smart compiler would
12817 * make a little extra effort and realize this.
12818 *
12819 * kWorker will cache help the preprocessor by remembering places where the
12820 * header was not found with help of kFsCache, and cache the file in memory when
12821 * found. The first part is taken care of by intercepting GetFileAttributesW,
12822 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
12823 * cached, the file is kept open and the CreateFileW call returns a duplicate of
12824 * that handle. An internal handle table is used by ReadFile and CloseFile to
12825 * keep track of intercepted handles (also used for temporary file, temporary
12826 * file mappings, console buffering, and standard out/err buffering).
12827 *
12828 * PS. The header search optimization also comes in handy when cl.exe goes on
12829 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
12830 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
12831 * optionally compile the three pass DLLs as executables during development
12832 * and problem analysis.
12833 *
12834 *
12835 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
12836 *
12837 * The issues of the temporary files is pretty severe on the Dell machine used
12838 * for benchmarking with full AV and encryption. The synthetic benchmark
12839 * improved by 30% when kWorker implemented measures to keep them entirely in
12840 * memory.
12841 *
12842 * kWorker implement these by recognizing the filename pattern in CreateFileW
12843 * and creating/opening the given file as needed. The handle returned is a
12844 * duplicate of the current process, thus giving us a good chance of catching
12845 * API calls we're not intercepting.
12846 *
12847 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
12848 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
12849 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
12850 * UnmapViewOfFile.
12851 *
12852 *
12853 * @section sec_kWorker_Numbers Some measurements.
12854 *
12855 * - r2881 building src/VBox/Runtime:
12856 * - without: 2m01.016388s = 120.016388 s
12857 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
12858 * - r2884 building vbox/debug (r110512):
12859 * - without: 11m14.446609s = 674.446609 s
12860 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
12861 * - r2896 building vbox/debug (r110577):
12862 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
12863 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
12864 * MS Defender as AV):
12865 * - without: 10m24.990389s = 624.990389s
12866 * - with: 08m04.738184s = 484.738184s
12867 * - delta: 624.99s - 484.74s = 140.25s
12868 * - saved: 140.25/624.99 = 22% faster
12869 *
12870 *
12871 * @subsection subsec_kWorker_Early_Numbers Early Experiments
12872 *
12873 * These are some early experiments doing 1024 compilations of
12874 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
12875 * main function:
12876 *
12877 * Skylake (W10/amd64, only stdandard MS defender):
12878 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
12879 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
12880 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
12881 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
12882 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
12883 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
12884 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
12885 *
12886 * Dell (W7/amd64, infected by mcafee):
12887 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
12888 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
12889 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
12890 *
12891 * The command line:
12892 * @code{.cpp}
12893 "\"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"
12894 * @endcode
12895 */
12896
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