VirtualBox

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

Last change on this file since 3355 was 3355, checked in by bird, 4 years ago

kWorker,kmk: Correcting a few SetThreadGroupAffinity calls.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 484.3 KB
Line 
1/* $Id: kWorker.c 3355 2020-06-05 02:06:42Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33//#undef NDEBUG
34//#define K_STRICT 1
35//#define KW_LOG_ENABLED
36
37#define PSAPI_VERSION 1
38#include <k/kHlp.h>
39#include <k/kLdr.h>
40
41#include <stdio.h>
42#include <intrin.h>
43#include <setjmp.h>
44#include <ctype.h>
45#include <errno.h>
46#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 "win_get_processor_group_active_mask.h"
58#include "quote_argv.h"
59#include "md5.h"
60#include "console.h"
61
62#include "../kmk/kmkbuiltin.h"
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68/** @def WITH_TEMP_MEMORY_FILES
69 * Enables temporary memory files for cl.exe. */
70#define WITH_TEMP_MEMORY_FILES
71
72/** @def WITH_HASH_MD5_CACHE
73 * Enables caching of MD5 sums for cl.exe.
74 * This prevents wasting time on rehashing common headers each time
75 * they are included. */
76#define WITH_HASH_MD5_CACHE
77
78/** @def WITH_CRYPT_CTX_REUSE
79 * Enables reusing crypt contexts. The Visual C++ compiler always creates a
80 * context which is only used for MD5 and maybe some random bytes (VS 2010).
81 * So, only create it once and add a reference to it instead of creating new
82 * ones. Saves registry access among other things. */
83#define WITH_CRYPT_CTX_REUSE
84
85/** @def WITH_CONSOLE_OUTPUT_BUFFERING
86 * Enables buffering of all console output as well as removal of annoying
87 * source file echo by cl.exe. */
88#define WITH_CONSOLE_OUTPUT_BUFFERING
89
90/** @def WITH_STD_OUT_ERR_BUFFERING
91 * Enables buffering of standard output and standard error buffer as well as
92 * removal of annoying source file echo by cl.exe. */
93#define WITH_STD_OUT_ERR_BUFFERING
94
95/** @def WITH_LOG_FILE
96 * Log to file instead of stderr. */
97#define WITH_LOG_FILE
98
99/** @def WITH_HISTORY
100 * Keep history of the last jobs. For debugging. */
101#define WITH_HISTORY
102
103/** @def WITH_FIXED_VIRTUAL_ALLOCS
104 * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
105 * there is only one, but an important one, from cl.exe).
106 */
107#if K_ARCH == K_ARCH_X86_32
108# define WITH_FIXED_VIRTUAL_ALLOCS
109#endif
110
111/** @def WITH_PCH_CACHING
112 * Enables read caching of precompiled header files. */
113#if K_ARCH_BITS >= 64
114# define WITH_PCH_CACHING
115#endif
116
117
118#ifndef NDEBUG
119# define KW_LOG_ENABLED
120#endif
121
122/** @def KW_LOG
123 * Generic logging.
124 * @param a Argument list for kwDbgPrintf */
125#ifdef KW_LOG_ENABLED
126# define KW_LOG(a) kwDbgPrintf a
127#else
128# define KW_LOG(a) do { } while (0)
129#endif
130
131/** @def KWLDR_LOG
132 * Loader related logging.
133 * @param a Argument list for kwDbgPrintf */
134#ifdef KW_LOG_ENABLED
135# define KWLDR_LOG(a) kwDbgPrintf a
136#else
137# define KWLDR_LOG(a) do { } while (0)
138#endif
139
140
141/** @def KWFS_LOG
142 * FS cache logging.
143 * @param a Argument list for kwDbgPrintf */
144#ifdef KW_LOG_ENABLED
145# define KWFS_LOG(a) kwDbgPrintf a
146#else
147# define KWFS_LOG(a) do { } while (0)
148#endif
149
150/** @def KWOUT_LOG
151 * Output related logging.
152 * @param a Argument list for kwDbgPrintf */
153#ifdef KW_LOG_ENABLED
154# define KWOUT_LOG(a) kwDbgPrintf a
155#else
156# define KWOUT_LOG(a) do { } while (0)
157#endif
158
159/** @def KWCRYPT_LOG
160 * FS cache logging.
161 * @param a Argument list for kwDbgPrintf */
162#ifdef KW_LOG_ENABLED
163# define KWCRYPT_LOG(a) kwDbgPrintf a
164#else
165# define KWCRYPT_LOG(a) do { } while (0)
166#endif
167
168/** Converts a windows handle to a handle table index.
169 * @note We currently just mask off the 31th bit, and do no shifting or anything
170 * else to create an index of the handle.
171 * @todo consider shifting by 2 or 3. */
172#define KW_HANDLE_TO_INDEX(a_hHandle) ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
173/** Maximum handle value we can deal with. */
174#define KW_HANDLE_MAX 0x20000
175
176/** Max temporary file size (memory backed). */
177#if K_ARCH_BITS >= 64
178# define KWFS_TEMP_FILE_MAX (256*1024*1024)
179#else
180# define KWFS_TEMP_FILE_MAX (64*1024*1024)
181#endif
182
183/** Marks unfinished code. */
184#if 1
185# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
186#else
187# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
188#endif
189
190/** User data key for tools. */
191#define KW_DATA_KEY_TOOL (~(KUPTR)16381)
192/** User data key for a cached file. */
193#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
194
195/** String constant comma length. */
196#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
197
198
199/**
200 * Generate CRT slot wrapper functions.
201 */
202#define CRT_SLOT_FUNCTION_WRAPPER(a_RetTypeAndCallConv, a_FnName, a_aArgsDecl, a_aArgCall) \
203 static a_RetTypeAndCallConv a_FnName##00 a_aArgsDecl { const unsigned iCrtSlot = 0; return a_FnName##_wrapped a_aArgCall; } \
204 static a_RetTypeAndCallConv a_FnName##01 a_aArgsDecl { const unsigned iCrtSlot = 1; return a_FnName##_wrapped a_aArgCall; } \
205 static a_RetTypeAndCallConv a_FnName##02 a_aArgsDecl { const unsigned iCrtSlot = 2; return a_FnName##_wrapped a_aArgCall; } \
206 static a_RetTypeAndCallConv a_FnName##03 a_aArgsDecl { const unsigned iCrtSlot = 3; return a_FnName##_wrapped a_aArgCall; } \
207 static a_RetTypeAndCallConv a_FnName##04 a_aArgsDecl { const unsigned iCrtSlot = 4; return a_FnName##_wrapped a_aArgCall; } \
208 static a_RetTypeAndCallConv a_FnName##05 a_aArgsDecl { const unsigned iCrtSlot = 5; return a_FnName##_wrapped a_aArgCall; } \
209 static a_RetTypeAndCallConv a_FnName##06 a_aArgsDecl { const unsigned iCrtSlot = 6; return a_FnName##_wrapped a_aArgCall; } \
210 static a_RetTypeAndCallConv a_FnName##07 a_aArgsDecl { const unsigned iCrtSlot = 7; return a_FnName##_wrapped a_aArgCall; } \
211 static a_RetTypeAndCallConv a_FnName##08 a_aArgsDecl { const unsigned iCrtSlot = 8; return a_FnName##_wrapped a_aArgCall; } \
212 static a_RetTypeAndCallConv a_FnName##09 a_aArgsDecl { const unsigned iCrtSlot = 9; return a_FnName##_wrapped a_aArgCall; } \
213 static a_RetTypeAndCallConv a_FnName##10 a_aArgsDecl { const unsigned iCrtSlot = 10; return a_FnName##_wrapped a_aArgCall; } \
214 static a_RetTypeAndCallConv a_FnName##11 a_aArgsDecl { const unsigned iCrtSlot = 11; return a_FnName##_wrapped a_aArgCall; } \
215 static a_RetTypeAndCallConv a_FnName##12 a_aArgsDecl { const unsigned iCrtSlot = 12; return a_FnName##_wrapped a_aArgCall; } \
216 static a_RetTypeAndCallConv a_FnName##13 a_aArgsDecl { const unsigned iCrtSlot = 13; return a_FnName##_wrapped a_aArgCall; } \
217 static a_RetTypeAndCallConv a_FnName##14 a_aArgsDecl { const unsigned iCrtSlot = 14; return a_FnName##_wrapped a_aArgCall; } \
218 static a_RetTypeAndCallConv a_FnName##15 a_aArgsDecl { const unsigned iCrtSlot = 15; return a_FnName##_wrapped a_aArgCall; } \
219 static a_RetTypeAndCallConv a_FnName##16 a_aArgsDecl { const unsigned iCrtSlot = 16; return a_FnName##_wrapped a_aArgCall; } \
220 static a_RetTypeAndCallConv a_FnName##17 a_aArgsDecl { const unsigned iCrtSlot = 17; return a_FnName##_wrapped a_aArgCall; } \
221 static a_RetTypeAndCallConv a_FnName##18 a_aArgsDecl { const unsigned iCrtSlot = 18; return a_FnName##_wrapped a_aArgCall; } \
222 static a_RetTypeAndCallConv a_FnName##19 a_aArgsDecl { const unsigned iCrtSlot = 19; return a_FnName##_wrapped a_aArgCall; } \
223 static a_RetTypeAndCallConv a_FnName##20 a_aArgsDecl { const unsigned iCrtSlot = 20; return a_FnName##_wrapped a_aArgCall; } \
224 static a_RetTypeAndCallConv a_FnName##21 a_aArgsDecl { const unsigned iCrtSlot = 21; return a_FnName##_wrapped a_aArgCall; } \
225 static a_RetTypeAndCallConv a_FnName##22 a_aArgsDecl { const unsigned iCrtSlot = 22; return a_FnName##_wrapped a_aArgCall; } \
226 static a_RetTypeAndCallConv a_FnName##23 a_aArgsDecl { const unsigned iCrtSlot = 23; return a_FnName##_wrapped a_aArgCall; } \
227 static a_RetTypeAndCallConv a_FnName##24 a_aArgsDecl { const unsigned iCrtSlot = 24; return a_FnName##_wrapped a_aArgCall; } \
228 static a_RetTypeAndCallConv a_FnName##25 a_aArgsDecl { const unsigned iCrtSlot = 25; return a_FnName##_wrapped a_aArgCall; } \
229 static a_RetTypeAndCallConv a_FnName##26 a_aArgsDecl { const unsigned iCrtSlot = 26; return a_FnName##_wrapped a_aArgCall; } \
230 static a_RetTypeAndCallConv a_FnName##27 a_aArgsDecl { const unsigned iCrtSlot = 27; return a_FnName##_wrapped a_aArgCall; } \
231 static a_RetTypeAndCallConv a_FnName##28 a_aArgsDecl { const unsigned iCrtSlot = 28; return a_FnName##_wrapped a_aArgCall; } \
232 static a_RetTypeAndCallConv a_FnName##29 a_aArgsDecl { const unsigned iCrtSlot = 29; return a_FnName##_wrapped a_aArgCall; } \
233 static a_RetTypeAndCallConv a_FnName##30 a_aArgsDecl { const unsigned iCrtSlot = 30; return a_FnName##_wrapped a_aArgCall; } \
234 static a_RetTypeAndCallConv a_FnName##31 a_aArgsDecl { const unsigned iCrtSlot = 31; return a_FnName##_wrapped a_aArgCall; } \
235 static const KUPTR a_FnName[] = \
236 { \
237 (KUPTR)a_FnName##00, \
238 (KUPTR)a_FnName##01, \
239 (KUPTR)a_FnName##02, \
240 (KUPTR)a_FnName##03, \
241 (KUPTR)a_FnName##04, \
242 (KUPTR)a_FnName##05, \
243 (KUPTR)a_FnName##06, \
244 (KUPTR)a_FnName##07, \
245 (KUPTR)a_FnName##08, \
246 (KUPTR)a_FnName##09, \
247 (KUPTR)a_FnName##10, \
248 (KUPTR)a_FnName##11, \
249 (KUPTR)a_FnName##12, \
250 (KUPTR)a_FnName##13, \
251 (KUPTR)a_FnName##14, \
252 (KUPTR)a_FnName##15, \
253 (KUPTR)a_FnName##16, \
254 (KUPTR)a_FnName##17, \
255 (KUPTR)a_FnName##18, \
256 (KUPTR)a_FnName##19, \
257 (KUPTR)a_FnName##20, \
258 (KUPTR)a_FnName##21, \
259 (KUPTR)a_FnName##22, \
260 (KUPTR)a_FnName##23, \
261 (KUPTR)a_FnName##24, \
262 (KUPTR)a_FnName##25, \
263 (KUPTR)a_FnName##26, \
264 (KUPTR)a_FnName##27, \
265 (KUPTR)a_FnName##28, \
266 (KUPTR)a_FnName##29, \
267 (KUPTR)a_FnName##30, \
268 (KUPTR)a_FnName##31, \
269 }
270
271
272/*********************************************************************************************************************************
273* Structures and Typedefs *
274*********************************************************************************************************************************/
275typedef enum KWLOCATION
276{
277 KWLOCATION_INVALID = 0,
278 KWLOCATION_EXE_DIR,
279 KWLOCATION_IMPORTER_DIR,
280 KWLOCATION_SYSTEM32,
281 KWLOCATION_UNKNOWN_NATIVE,
282 KWLOCATION_UNKNOWN,
283} KWLOCATION;
284
285typedef enum KWMODSTATE
286{
287 KWMODSTATE_INVALID = 0,
288 KWMODSTATE_NEEDS_BITS,
289 KWMODSTATE_NEEDS_INIT,
290 KWMODSTATE_BEING_INITED,
291 KWMODSTATE_INIT_FAILED,
292 KWMODSTATE_READY,
293} KWMODSTATE;
294
295typedef struct KWMODULE *PKWMODULE;
296typedef struct KWMODULE
297{
298 /** Pointer to the next image withe the same hash. */
299 PKWMODULE pNextHash;
300 /** Pointer to the next image in the global list. */
301 PKWMODULE pNextList;
302 /** The normalized path to the image. */
303 const char *pszPath;
304 /** The hash of the program path. */
305 KU32 uHashPath;
306 /** Number of references. */
307 KU32 cRefs;
308 /** UTF-16 version of pszPath. */
309 const wchar_t *pwszPath;
310 /** The offset of the filename in pszPath. */
311 KU16 offFilename;
312 /** The offset of the filename in pwszPath. */
313 KU16 offFilenameW;
314 /** Set if executable. */
315 KBOOL fExe;
316 /** Set if native module entry. */
317 KBOOL fNative;
318 /** Loader module handle. */
319 PKLDRMOD pLdrMod;
320 /** The windows module handle. */
321 HMODULE hOurMod;
322 /** The of the loaded image bits. */
323 KSIZE cbImage;
324 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
325 KU8 iCrtSlot;
326 /** Loop prevention when working the tree. */
327 KBOOL fVisited;
328 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
329 KBOOL fNeedReInit;
330 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
331 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
332 KU8 fReInitOnMsPdbSrvEndpointChange;
333 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
334 char *pszMsPdbSrvEndpoint;
335
336 union
337 {
338 /** Data for a manually loaded image. */
339 struct
340 {
341 /** Where we load the image. */
342 KU8 *pbLoad;
343 /** Virgin copy of the image. */
344 KU8 *pbCopy;
345 /** Ldr pvBits argument. This is NULL till we've successfully resolved
346 * the imports. */
347 void *pvBits;
348 /** The state. */
349 KWMODSTATE enmState;
350#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
351 /** The number of entries in the table. */
352 KU32 cFunctions;
353 /** The function table address (in the copy). */
354 PRUNTIME_FUNCTION paFunctions;
355 /** Set if we've already registered a function table already. */
356 KBOOL fRegisteredFunctionTable;
357#endif
358 /** Set if we share memory with other executables. */
359 KBOOL fUseLdBuf;
360 /** Set after the first whole image copy is done. */
361 KBOOL fCanDoQuick;
362 /** Number of quick copy chunks. */
363 KU8 cQuickCopyChunks;
364 /** Number of quick zero chunks. */
365 KU8 cQuickZeroChunks;
366 /** Quicker image copy instructions that skips non-writable parts when
367 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
368 * image. */
369 struct
370 {
371 /** The copy destination. */
372 KU8 *pbDst;
373 /** The copy source. */
374 KU8 const *pbSrc;
375 /** How much to copy. */
376 KSIZE cbToCopy;
377 } aQuickCopyChunks[3];
378 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
379 struct
380 {
381 /** Where to start zeroing. */
382 KU8 *pbDst;
383 /** How much to zero. */
384 KSIZE cbToZero;
385 } aQuickZeroChunks[3];
386
387 /** TLS index if one was allocated, otherwise KU32_MAX. */
388 KU32 idxTls;
389 /** Offset (RVA) of the TLS initialization data. */
390 KU32 offTlsInitData;
391 /** Number of bytes of TLS initialization data. */
392 KU32 cbTlsInitData;
393 /** Number of allocated bytes for TLS. */
394 KU32 cbTlsAlloc;
395 /** Number of TLS callbacks. */
396 KU32 cTlsCallbacks;
397 /** Offset (RVA) of the TLS callback table. */
398 KU32 offTlsCallbacks;
399
400 /** Number of imported modules. */
401 KSIZE cImpMods;
402 /** Import array (variable size). */
403 PKWMODULE apImpMods[1];
404 } Manual;
405 } u;
406} KWMODULE;
407
408
409typedef struct KWDYNLOAD *PKWDYNLOAD;
410typedef struct KWDYNLOAD
411{
412 /** Pointer to the next in the list. */
413 PKWDYNLOAD pNext;
414
415 /** The module handle we present to the application.
416 * This is the LoadLibraryEx return value for special modules and the
417 * KWMODULE.hOurMod value for the others. */
418 HMODULE hmod;
419
420 /** The module for non-special resource stuff, NULL if special. */
421 PKWMODULE pMod;
422
423 /** The length of the LoadLibary filename. */
424 KSIZE cchRequest;
425 /** The LoadLibrary filename. */
426 char szRequest[1];
427} KWDYNLOAD;
428
429
430/**
431 * GetModuleHandle cache for system modules frequently queried.
432 */
433typedef struct KWGETMODULEHANDLECACHE
434{
435 const char *pszName;
436 const wchar_t *pwszName;
437 KU8 cchName;
438 KU8 cwcName;
439 KBOOL fAlwaysPresent;
440 HANDLE hmod;
441} KWGETMODULEHANDLECACHE;
442typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
443
444
445/**
446 * A cached file.
447 */
448typedef struct KFSWCACHEDFILE
449{
450 /** The user data core. */
451 KFSUSERDATA Core;
452
453 /** Cached file handle. */
454 HANDLE hCached;
455 /** Cached file section handle. */
456 HANDLE hSection;
457 /** Cached file content. */
458 KU8 *pbCached;
459 /** The file size. */
460 KU32 cbCached;
461#ifdef WITH_HASH_MD5_CACHE
462 /** Set if we've got a valid MD5 hash in abMd5Digest. */
463 KBOOL fValidMd5;
464 /** The MD5 digest if fValidMd5 is set. */
465 KU8 abMd5Digest[16];
466#endif
467
468 /** Circular self reference. Prevents the object from ever going away and
469 * keeps it handy for debugging. */
470 PKFSOBJ pFsObj;
471 /** The file path (for debugging). */
472 char szPath[1];
473} KFSWCACHEDFILE;
474/** Pointer to a cached filed. */
475typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
476
477#ifdef WITH_HASH_MD5_CACHE
478
479/** Pointer to a MD5 hash instance. */
480typedef struct KWHASHMD5 *PKWHASHMD5;
481/**
482 * A MD5 hash instance.
483 */
484typedef struct KWHASHMD5
485{
486 /** The magic value. */
487 KUPTR uMagic;
488 /** Pointer to the next hash handle. */
489 PKWHASHMD5 pNext;
490 /** The cached file we've associated this handle with. */
491 PKFSWCACHEDFILE pCachedFile;
492 /** The number of bytes we've hashed. */
493 KU32 cbHashed;
494 /** Set if this has gone wrong. */
495 KBOOL fGoneBad;
496 /** Set if we're in fallback mode (file not cached). */
497 KBOOL fFallbackMode;
498 /** Set if we've already finalized the digest. */
499 KBOOL fFinal;
500 /** The MD5 fallback context. */
501 struct MD5Context Md5Ctx;
502 /** The finalized digest. */
503 KU8 abDigest[16];
504
505} KWHASHMD5;
506/** Magic value for KWHASHMD5::uMagic (Les McCann). */
507# define KWHASHMD5_MAGIC KUPTR_C(0x19350923)
508
509#endif /* WITH_HASH_MD5_CACHE */
510#ifdef WITH_TEMP_MEMORY_FILES
511
512typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
513typedef struct KWFSTEMPFILESEG
514{
515 /** File offset of data. */
516 KU32 offData;
517 /** The size of the buffer pbData points to. */
518 KU32 cbDataAlloc;
519 /** The segment data. */
520 KU8 *pbData;
521} KWFSTEMPFILESEG;
522
523typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
524typedef struct KWFSTEMPFILE
525{
526 /** Pointer to the next temporary file for this run. */
527 PKWFSTEMPFILE pNext;
528 /** The UTF-16 path. (Allocated after this structure.) */
529 const wchar_t *pwszPath;
530 /** The path length. */
531 KU16 cwcPath;
532 /** Number of active handles using this file/mapping (<= 2). */
533 KU8 cActiveHandles;
534 /** Number of active mappings (mapped views) (0 or 1). */
535 KU8 cMappings;
536 /** The amount of space allocated in the segments. */
537 KU32 cbFileAllocated;
538 /** The current file size. */
539 KU32 cbFile;
540 /** The number of segments. */
541 KU32 cSegs;
542 /** Segments making up the file. */
543 PKWFSTEMPFILESEG paSegs;
544} KWFSTEMPFILE;
545
546#endif /* WITH_TEMP_MEMORY_FILES */
547#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
548
549/**
550 * Console line buffer or output full buffer.
551 */
552typedef struct KWOUTPUTSTREAMBUF
553{
554 /** The main output handle. */
555 HANDLE hOutput;
556 /** Our backup handle. */
557 HANDLE hBackup;
558 /** Set if this is a console handle and we're in line buffered mode.
559 * When clear, we may buffer multiple lines, though try flush on line
560 * boundraries when ever possible. */
561 KBOOL fIsConsole;
562 /** Compressed GetFileType result. */
563 KU8 fFileType;
564 KU8 abPadding[2];
565 union
566 {
567 /** Line buffer mode (fIsConsole == K_TRUE). */
568 struct
569 {
570 /** Amount of pending console output in wchar_t's. */
571 KU32 cwcBuf;
572 /** The allocated buffer size. */
573 KU32 cwcBufAlloc;
574 /** Pending console output. */
575 wchar_t *pwcBuf;
576 } Con;
577 /** Fully buffered mode (fIsConsole == K_FALSE). */
578 struct
579 {
580 /** Amount of pending output (in chars). */
581 KU32 cchBuf;
582#ifdef WITH_STD_OUT_ERR_BUFFERING
583 /** The allocated buffer size (in chars). */
584 KU32 cchBufAlloc;
585 /** Pending output. */
586 char *pchBuf;
587#endif
588 } Fully;
589 } u;
590} KWOUTPUTSTREAMBUF;
591/** Pointer to a console line buffer. */
592typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
593
594/**
595 * Combined console buffer of complete lines.
596 */
597typedef struct KWCONSOLEOUTPUT
598{
599 /** The console output handle.
600 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
601 * combined output buffering. */
602 HANDLE hOutput;
603 /** The current code page for the console. */
604 KU32 uCodepage;
605 /** Amount of pending console output in wchar_t's. */
606 KU32 cwcBuf;
607 /** Number of times we've flushed it in any way (for cl.exe hack). */
608 KU32 cFlushes;
609 /** Pending console output. */
610 wchar_t wszBuf[8192];
611} KWCONSOLEOUTPUT;
612/** Pointer to a combined console buffer. */
613typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
614
615#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
616
617/** Handle type. */
618typedef enum KWHANDLETYPE
619{
620 KWHANDLETYPE_INVALID = 0,
621 KWHANDLETYPE_FSOBJ_READ_CACHE,
622 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
623 KWHANDLETYPE_TEMP_FILE,
624 KWHANDLETYPE_TEMP_FILE_MAPPING,
625 KWHANDLETYPE_OUTPUT_BUF
626} KWHANDLETYPE;
627
628/** Handle data. */
629typedef struct KWHANDLE
630{
631 KWHANDLETYPE enmType;
632 /** Number of references */
633 KU32 cRefs;
634 /** The current file offset. */
635 KU32 offFile;
636 /** Handle access. */
637 KU32 dwDesiredAccess;
638 /** The handle. */
639 HANDLE hHandle;
640
641 /** Type specific data. */
642 union
643 {
644 /** The file system object. */
645 PKFSWCACHEDFILE pCachedFile;
646 /** Temporary file handle or mapping handle. */
647 PKWFSTEMPFILE pTempFile;
648#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
649 /** Buffered output stream. */
650 PKWOUTPUTSTREAMBUF pOutBuf;
651#endif
652 } u;
653} KWHANDLE;
654typedef KWHANDLE *PKWHANDLE;
655
656/**
657 * Tracking one of our memory mappings.
658 */
659typedef struct KWMEMMAPPING
660{
661 /** Number of references. */
662 KU32 cRefs;
663 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
664 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
665 KWHANDLETYPE enmType;
666 /** The mapping address. */
667 PVOID pvMapping;
668 /** Type specific data. */
669 union
670 {
671 /** The file system object. */
672 PKFSWCACHEDFILE pCachedFile;
673 /** Temporary file handle or mapping handle. */
674 PKWFSTEMPFILE pTempFile;
675 } u;
676} KWMEMMAPPING;
677/** Pointer to a memory mapping tracker. */
678typedef KWMEMMAPPING *PKWMEMMAPPING;
679
680
681/** Pointer to a VirtualAlloc tracker entry. */
682typedef struct KWVIRTALLOC *PKWVIRTALLOC;
683/**
684 * Tracking an VirtualAlloc allocation.
685 */
686typedef struct KWVIRTALLOC
687{
688 PKWVIRTALLOC pNext;
689 void *pvAlloc;
690 KSIZE cbAlloc;
691 /** This is KU32_MAX if not a preallocated chunk. */
692 KU32 idxPreAllocated;
693} KWVIRTALLOC;
694
695
696/** Pointer to a heap (HeapCreate) tracker entry. */
697typedef struct KWHEAP *PKWHEAP;
698/**
699 * Tracking an heap (HeapCreate)
700 */
701typedef struct KWHEAP
702{
703 PKWHEAP pNext;
704 HANDLE hHeap;
705} KWHEAP;
706
707
708/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
709typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
710/**
711 * Tracking an FlsAlloc/TlsAlloc index.
712 */
713typedef struct KWLOCALSTORAGE
714{
715 PKWLOCALSTORAGE pNext;
716 KU32 idx;
717} KWLOCALSTORAGE;
718
719
720/** Pointer to an at exit callback record */
721typedef struct KWEXITCALLACK *PKWEXITCALLACK;
722/**
723 * At exit callback record.
724 */
725typedef struct KWEXITCALLACK
726{
727 PKWEXITCALLACK pNext;
728 _onexit_t pfnCallback;
729 /** At exit doesn't have an exit code. */
730 KBOOL fAtExit;
731} KWEXITCALLACK;
732
733
734typedef enum KWTOOLTYPE
735{
736 KWTOOLTYPE_INVALID = 0,
737 KWTOOLTYPE_SANDBOXED,
738 KWTOOLTYPE_WATCOM,
739 KWTOOLTYPE_EXEC,
740 KWTOOLTYPE_END
741} KWTOOLTYPE;
742
743typedef enum KWTOOLHINT
744{
745 KWTOOLHINT_INVALID = 0,
746 KWTOOLHINT_NONE,
747 KWTOOLHINT_VISUAL_CPP_CL,
748 KWTOOLHINT_VISUAL_CPP_LINK,
749 KWTOOLHINT_END
750} KWTOOLHINT;
751
752
753/**
754 * A kWorker tool.
755 */
756typedef struct KWTOOL
757{
758 /** The user data core structure. */
759 KFSUSERDATA Core;
760
761 /** The normalized path to the program. */
762 const char *pszPath;
763 /** UTF-16 version of pszPath. */
764 wchar_t const *pwszPath;
765 /** The kind of tool. */
766 KWTOOLTYPE enmType;
767
768 union
769 {
770 struct
771 {
772 /** The main entry point. */
773 KUPTR uMainAddr;
774 /** The executable. */
775 PKWMODULE pExe;
776 /** List of dynamically loaded modules.
777 * These will be kept loaded till the tool is destroyed (if we ever do that). */
778 PKWDYNLOAD pDynLoadHead;
779 /** Module array sorted by hOurMod. */
780 PKWMODULE *papModules;
781 /** Number of entries in papModules. */
782 KU32 cModules;
783
784 /** Tool hint (for hacks and such). */
785 KWTOOLHINT enmHint;
786 } Sandboxed;
787 } u;
788} KWTOOL;
789/** Pointer to a tool. */
790typedef struct KWTOOL *PKWTOOL;
791
792
793typedef struct KWSANDBOX *PKWSANDBOX;
794typedef struct KWSANDBOX
795{
796 /** The tool currently running in the sandbox. */
797 PKWTOOL pTool;
798 /** Jump buffer. */
799 jmp_buf JmpBuf;
800 /** The thread ID of the main thread (owner of JmpBuf). */
801 DWORD idMainThread;
802 /** Copy of the NT TIB of the main thread. */
803 NT_TIB TibMainThread;
804 /** The NT_TIB::ExceptionList value inside the try case.
805 * We restore this prior to the longjmp. */
806 void *pOutXcptListHead;
807 /** The exit code in case of longjmp. */
808 int rcExitCode;
809 /** Set if we're running. */
810 KBOOL fRunning;
811 /** Whether to disable caching of ".pch" files. */
812 KBOOL fNoPchCaching;
813
814 /** The command line. */
815 char *pszCmdLine;
816 /** The UTF-16 command line. */
817 wchar_t *pwszCmdLine;
818 /** Number of arguments in papszArgs. */
819 int cArgs;
820 /** The argument vector. */
821 char **papszArgs;
822 /** The argument vector. */
823 wchar_t **papwszArgs;
824
825 /** The _pgmptr msvcrt variable. */
826 char *pgmptr;
827 /** The _wpgmptr msvcrt variable. */
828 wchar_t *wpgmptr;
829
830 /** The _initenv msvcrt variable. */
831 char **initenv;
832 /** The _winitenv msvcrt variable. */
833 wchar_t **winitenv;
834
835 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
836 KSIZE cEnvVarsAllocated;
837 /** The _environ msvcrt variable. */
838 char **environ;
839 /** The _wenviron msvcrt variable. */
840 wchar_t **wenviron;
841 /** The shadow _environ msvcrt variable. */
842 char **papszEnvVars;
843 /** The shadow _wenviron msvcrt variable. */
844 wchar_t **papwszEnvVars;
845
846
847 /** Handle table. */
848 PKWHANDLE *papHandles;
849 /** Size of the handle table. */
850 KU32 cHandles;
851 /** Number of active handles in the table. */
852 KU32 cActiveHandles;
853 /** Number of handles in the handle table that will not be freed. */
854 KU32 cFixedHandles;
855 /** Total number of leaked handles. */
856 KU32 cLeakedHandles;
857
858 /** Number of active memory mappings in paMemMappings. */
859 KU32 cMemMappings;
860 /** The allocated size of paMemMappings. */
861 KU32 cMemMappingsAlloc;
862 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
863 PKWMEMMAPPING paMemMappings;
864
865 /** Head of the list of temporary file. */
866 PKWFSTEMPFILE pTempFileHead;
867
868 /** Head of the virtual alloc allocations. */
869 PKWVIRTALLOC pVirtualAllocHead;
870 /** Head of the heap list (HeapCreate).
871 * This is only done from images we forcibly restore. */
872 PKWHEAP pHeapHead;
873 /** Head of the FlsAlloc indexes. */
874 PKWLOCALSTORAGE pFlsAllocHead;
875 /** Head of the TlsAlloc indexes. */
876 PKWLOCALSTORAGE pTlsAllocHead;
877
878 /** The at exit callback head.
879 * This is only done from images we forcibly restore. */
880 PKWEXITCALLACK pExitCallbackHead;
881
882 MY_UNICODE_STRING SavedCommandLine;
883
884#ifdef WITH_HASH_MD5_CACHE
885 /** The special MD5 hash instance. */
886 PKWHASHMD5 pHashHead;
887 /** ReadFile sets these while CryptHashData claims and clears them.
888 *
889 * This is part of the heuristics we use for MD5 caching for header files. The
890 * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
891 * header, then passes the same buffer and read byte count to CryptHashData.
892 */
893 struct
894 {
895 /** The cached file last read from. */
896 PKFSWCACHEDFILE pCachedFile;
897 /** The file offset of the last cached read. */
898 KU32 offRead;
899 /** The number of bytes read last. */
900 KU32 cbRead;
901 /** The buffer pointer of the last read. */
902 void *pvRead;
903 } LastHashRead;
904#endif
905
906#ifdef WITH_CRYPT_CTX_REUSE
907 /** Reusable crypt contexts. */
908 struct
909 {
910 /** The creation provider type. */
911 KU32 dwProvType;
912 /** The creation flags. */
913 KU32 dwFlags;
914 /** The length of the container name. */
915 KU32 cwcContainer;
916 /** The length of the provider name. */
917 KU32 cwcProvider;
918 /** The container name string. */
919 wchar_t *pwszContainer;
920 /** The provider name string. */
921 wchar_t *pwszProvider;
922 /** The context handle. */
923 HCRYPTPROV hProv;
924 } aCryptCtxs[4];
925 /** Number of reusable crypt conexts in aCryptCtxs. */
926 KU32 cCryptCtxs;
927#endif
928
929
930#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
931 /** The internal standard output handle. */
932 KWHANDLE HandleStdOut;
933 /** The internal standard error handle. */
934 KWHANDLE HandleStdErr;
935 /** Standard output (and whatever else) buffer. */
936 KWOUTPUTSTREAMBUF StdOut;
937 /** Standard error buffer. */
938 KWOUTPUTSTREAMBUF StdErr;
939 /** Combined buffer of completed lines. */
940 KWCONSOLEOUTPUT Combined;
941#endif
942} KWSANDBOX;
943
944
945/** A CRT slot. */
946typedef struct KWCRTSLOT
947{
948 KU32 iSlot;
949
950 /** The CRT module data. */
951 PKWMODULE pModule;
952 /** Pointer to the malloc function. */
953 void * (__cdecl *pfnMalloc)(size_t);
954
955} KWCRTSLOT;
956typedef KWCRTSLOT *PKWCRTSLOT;
957
958
959/** Replacement function entry. */
960typedef struct KWREPLACEMENTFUNCTION
961{
962 /** The function name. */
963 const char *pszFunction;
964 /** The length of the function name. */
965 KSIZE cchFunction;
966 /** The module name (optional). */
967 const char *pszModule;
968 /** The replacement function, data address or CRT slot function array. */
969 KUPTR pfnReplacement;
970 /** Only replace in the executable.
971 * @todo fix the reinitialization of non-native DLLs! */
972 KBOOL fOnlyExe;
973 /** Set if pfnReplacement points to a CRT slot function array. */
974 KBOOL fCrtSlotArray;
975} KWREPLACEMENTFUNCTION;
976typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
977
978#if 0
979/** Replacement function entry. */
980typedef struct KWREPLACEMENTDATA
981{
982 /** The function name. */
983 const char *pszFunction;
984 /** The length of the function name. */
985 KSIZE cchFunction;
986 /** The module name (optional). */
987 const char *pszModule;
988 /** Function providing the replacement. */
989 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
990} KWREPLACEMENTDATA;
991typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
992#endif
993
994/**
995 * One test job (--full-test).
996 */
997typedef struct KWONETEST
998{
999 /** Where this job originated. */
1000 const char *pszJobSrc;
1001 /** The argument number it started with. */
1002 unsigned iJobSrc;
1003 /** Set if virgin, clear if modified. */
1004 KBOOL fVirgin;
1005
1006 /** Number of runs to give it. */
1007 unsigned cRuns;
1008
1009 /** @name kSubmitHandleJobUnpacked arguments
1010 * @{ */
1011 const char *pszExecutable;
1012 const char *pszCwd;
1013 KU32 cArgs;
1014 const char **papszArgs;
1015 KU32 cEnvVars;
1016 const char **papszEnvVars;
1017 const char *pszSpecialEnv;
1018 KBOOL fWatcomBrainDamange;
1019 KBOOL fNoPchCaching;
1020 KU32 cPostCmdArgs;
1021 const char **papszPostCmdArgs;
1022 /** @} */
1023
1024 /** Pointer to the next one. */
1025 struct KWONETEST *pNext;
1026} KWONETEST;
1027/** Pointer to one test job. */
1028typedef KWONETEST *PKWONETEST;
1029
1030
1031/*********************************************************************************************************************************
1032* Global Variables *
1033*********************************************************************************************************************************/
1034/** The sandbox data. */
1035static KWSANDBOX g_Sandbox;
1036
1037/** The module currently occupying g_abDefLdBuf. */
1038static PKWMODULE g_pModInLdBuf = NULL;
1039
1040/** The module that previuosly occupied g_abDefLdBuf. */
1041static PKWMODULE g_pModPrevInLdBuf = NULL;
1042
1043/** Module list head. */
1044static PKWMODULE g_pModuleHead = NULL;
1045/** Where to insert the next module. */
1046static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1047
1048/** Module hash table. */
1049static PKWMODULE g_apModules[127];
1050
1051/** GetModuleHandle cache. */
1052static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1053{
1054#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1055 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1056#if 1
1057 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1058 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1059#endif
1060 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1061};
1062
1063/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1064static PKWMODULE g_pModPendingTlsAlloc = NULL;
1065
1066/** CRT slots.
1067 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1068static KWCRTSLOT g_aCrtSlots[32];
1069
1070/** The file system cache. */
1071static PKFSCACHE g_pFsCache;
1072/** The current directory (referenced). */
1073static PKFSOBJ g_pCurDirObj = NULL;
1074#ifdef KBUILD_OS_WINDOWS
1075/** The windows system32 directory (referenced). */
1076static PKFSDIR g_pWinSys32 = NULL;
1077#endif
1078
1079/** Verbosity level. */
1080static int g_cVerbose = 2;
1081
1082/** Whether we should restart the worker. */
1083static KBOOL g_fRestart = K_FALSE;
1084
1085/** The process group this worker is tied to (--group option), -1 if none. */
1086static KI32 g_iProcessGroup = -1;
1087
1088/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1089static int volatile g_rcCtrlC = 0;
1090
1091/** The communication pipe handle. We break this when we see Ctrl-C such. */
1092#ifdef KBUILD_OS_WINDOWS
1093static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1094#else
1095static int g_hPipe = -1;
1096#endif
1097
1098
1099/* Further down. */
1100extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1101extern KU32 const g_cSandboxReplacements;
1102
1103extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1104extern KU32 const g_cSandboxNativeReplacements;
1105
1106extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1107extern KU32 const g_cSandboxGetProcReplacements;
1108
1109
1110/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1111 * cover the default executable link address of 0x400000.
1112 * @remarks Early main() makes it read+write+executable. Attempts as having
1113 * it as a separate section failed because the linker insists on
1114 * writing out every zero in the uninitialized section, resulting in
1115 * really big binaries. */
1116__declspec(align(0x1000))
1117static KU8 g_abDefLdBuf[16*1024*1024];
1118
1119#ifdef WITH_LOG_FILE
1120/** Log file handle. */
1121static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1122#endif
1123
1124
1125#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1126/** Virtual address space reserved for CL.EXE heap manager.
1127 *
1128 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1129 * address. It's among other things used for precompiled headers, which
1130 * seemingly have addresses hardcoded into them and won't work if mapped
1131 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1132 * for it. (The /Zm option may affect this allocation.)
1133 */
1134static struct
1135{
1136 /** The memory address we need. */
1137 KUPTR const uFixed;
1138 /** How much we need to fix. */
1139 KSIZE const cbFixed;
1140 /** What we actually got, NULL if given back. */
1141 void *pvReserved;
1142 /** Whether it is in use or not. */
1143 KBOOL fInUse;
1144} g_aFixedVirtualAllocs[] =
1145{
1146# if K_ARCH == K_ARCH_X86_32
1147 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1148 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1149 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1150# else
1151 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1152# endif
1153};
1154#endif
1155
1156
1157#ifdef WITH_HISTORY
1158/** The job history. */
1159static char *g_apszHistory[32];
1160/** Index of the next history entry. */
1161static unsigned g_iHistoryNext = 0;
1162#endif
1163
1164
1165/** Number of jobs executed. */
1166static KU32 g_cJobs;
1167/** Number of tools. */
1168static KU32 g_cTools;
1169/** Number of modules. */
1170static KU32 g_cModules;
1171/** Number of non-native modules. */
1172static KU32 g_cNonNativeModules;
1173/** Number of read-cached files. */
1174static KU32 g_cReadCachedFiles;
1175/** Total size of read-cached files. */
1176static KSIZE g_cbReadCachedFiles;
1177
1178/** Total number of ReadFile calls. */
1179static KSIZE g_cReadFileCalls;
1180/** Total bytes read via ReadFile. */
1181static KSIZE g_cbReadFileTotal;
1182/** Total number of read from read-cached files. */
1183static KSIZE g_cReadFileFromReadCached;
1184/** Total bytes read from read-cached files. */
1185static KSIZE g_cbReadFileFromReadCached;
1186/** Total number of read from in-memory temporary files. */
1187static KSIZE g_cReadFileFromInMemTemp;
1188/** Total bytes read from in-memory temporary files. */
1189static KSIZE g_cbReadFileFromInMemTemp;
1190
1191/** Total number of WriteFile calls. */
1192static KSIZE g_cWriteFileCalls;
1193/** Total bytes written via WriteFile. */
1194static KSIZE g_cbWriteFileTotal;
1195/** Total number of written to from in-memory temporary files. */
1196static KSIZE g_cWriteFileToInMemTemp;
1197/** Total bytes written to in-memory temporary files. */
1198static KSIZE g_cbWriteFileToInMemTemp;
1199
1200
1201/*********************************************************************************************************************************
1202* Internal Functions *
1203*********************************************************************************************************************************/
1204static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1205static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1206 const char *pszSearchPath, PKWMODULE *ppMod);
1207static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1208static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1209static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1210#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1211static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1212#endif
1213static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1214
1215
1216
1217
1218/**
1219 * Debug printing.
1220 * @param pszFormat Debug format string.
1221 * @param ... Format argument.
1222 */
1223static void kwDbgPrintfV(const char *pszFormat, va_list va)
1224{
1225 if (g_cVerbose >= 2)
1226 {
1227 DWORD const dwSavedErr = GetLastError();
1228#ifdef WITH_LOG_FILE
1229 DWORD dwIgnored;
1230 char szTmp[2048];
1231 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1232 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1233 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1234 cch += cchPrefix;
1235 else
1236 {
1237 cch = sizeof(szTmp) - 1;
1238 szTmp[cch] = '\0';
1239 }
1240
1241 if (g_hLogFile == INVALID_HANDLE_VALUE)
1242 {
1243 wchar_t wszFilename[128];
1244 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1245 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1246 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1247 }
1248
1249 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1250#else
1251 fprintf(stderr, "debug: ");
1252 vfprintf(stderr, pszFormat, va);
1253#endif
1254
1255 SetLastError(dwSavedErr);
1256 }
1257}
1258
1259
1260/**
1261 * Debug printing.
1262 * @param pszFormat Debug format string.
1263 * @param ... Format argument.
1264 */
1265static void kwDbgPrintf(const char *pszFormat, ...)
1266{
1267 if (g_cVerbose >= 2)
1268 {
1269 va_list va;
1270 va_start(va, pszFormat);
1271 kwDbgPrintfV(pszFormat, va);
1272 va_end(va);
1273 }
1274}
1275
1276
1277/**
1278 * Debugger printing.
1279 * @param pszFormat Debug format string.
1280 * @param ... Format argument.
1281 */
1282static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1283{
1284 if (IsDebuggerPresent())
1285 {
1286 DWORD const dwSavedErr = GetLastError();
1287 char szTmp[2048];
1288
1289 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1290 OutputDebugStringA(szTmp);
1291
1292 SetLastError(dwSavedErr);
1293 }
1294}
1295
1296
1297/**
1298 * Debugger printing.
1299 * @param pszFormat Debug format string.
1300 * @param ... Format argument.
1301 */
1302static void kwDebuggerPrintf(const char *pszFormat, ...)
1303{
1304 va_list va;
1305 va_start(va, pszFormat);
1306 kwDebuggerPrintfV(pszFormat, va);
1307 va_end(va);
1308}
1309
1310
1311
1312/**
1313 * Error printing.
1314 * @param pszFormat Message format string.
1315 * @param ... Format argument.
1316 */
1317static void kwErrPrintfV(const char *pszFormat, va_list va)
1318{
1319 DWORD const dwSavedErr = GetLastError();
1320
1321 fprintf(stderr, "kWorker: error: ");
1322 vfprintf(stderr, pszFormat, va);
1323 fflush(stderr); /* In case it's a pipe. */
1324
1325 SetLastError(dwSavedErr);
1326}
1327
1328
1329/**
1330 * Error printing.
1331 * @param pszFormat Message format string.
1332 * @param ... Format argument.
1333 */
1334static void kwErrPrintf(const char *pszFormat, ...)
1335{
1336 va_list va;
1337 va_start(va, pszFormat);
1338 kwErrPrintfV(pszFormat, va);
1339 va_end(va);
1340}
1341
1342
1343/**
1344 * Error printing.
1345 * @return rc;
1346 * @param rc Return value
1347 * @param pszFormat Message format string.
1348 * @param ... Format argument.
1349 */
1350static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1351{
1352 va_list va;
1353 va_start(va, pszFormat);
1354 kwErrPrintfV(pszFormat, va);
1355 va_end(va);
1356 return rc;
1357}
1358
1359
1360#ifdef K_STRICT
1361
1362KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1363{
1364 DWORD const dwSavedErr = GetLastError();
1365
1366 fprintf(stderr,
1367 "\n"
1368 "!!Assertion failed!!\n"
1369 "Expression: %s\n"
1370 "Function : %s\n"
1371 "File: %s\n"
1372 "Line: %d\n"
1373 , pszExpr, pszFunction, pszFile, iLine);
1374
1375 SetLastError(dwSavedErr);
1376}
1377
1378
1379KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1380{
1381 DWORD const dwSavedErr = GetLastError();
1382 va_list va;
1383
1384 va_start(va, pszFormat);
1385 fprintf(stderr, pszFormat, va);
1386 va_end(va);
1387
1388 SetLastError(dwSavedErr);
1389}
1390
1391#endif /* K_STRICT */
1392
1393
1394/**
1395 * Hashes a string.
1396 *
1397 * @returns 32-bit string hash.
1398 * @param pszString String to hash.
1399 */
1400static KU32 kwStrHash(const char *pszString)
1401{
1402 /* This algorithm was created for sdbm (a public-domain reimplementation of
1403 ndbm) database library. it was found to do well in scrambling bits,
1404 causing better distribution of the keys and fewer splits. it also happens
1405 to be a good general hashing function with good distribution. the actual
1406 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1407 is the faster version used in gawk. [there is even a faster, duff-device
1408 version] the magic constant 65599 was picked out of thin air while
1409 experimenting with different constants, and turns out to be a prime.
1410 this is one of the algorithms used in berkeley db (see sleepycat) and
1411 elsewhere. */
1412 KU32 uHash = 0;
1413 KU32 uChar;
1414 while ((uChar = (unsigned char)*pszString++) != 0)
1415 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1416 return uHash;
1417}
1418
1419
1420/**
1421 * Hashes a string.
1422 *
1423 * @returns The string length.
1424 * @param pszString String to hash.
1425 * @param puHash Where to return the 32-bit string hash.
1426 */
1427static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1428{
1429 const char * const pszStart = pszString;
1430 KU32 uHash = 0;
1431 KU32 uChar;
1432 while ((uChar = (unsigned char)*pszString) != 0)
1433 {
1434 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1435 pszString++;
1436 }
1437 *puHash = uHash;
1438 return pszString - pszStart;
1439}
1440
1441
1442/**
1443 * Hashes a string.
1444 *
1445 * @returns The string length in wchar_t units.
1446 * @param pwszString String to hash.
1447 * @param puHash Where to return the 32-bit string hash.
1448 */
1449static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1450{
1451 const wchar_t * const pwszStart = pwszString;
1452 KU32 uHash = 0;
1453 KU32 uChar;
1454 while ((uChar = *pwszString) != 0)
1455 {
1456 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1457 pwszString++;
1458 }
1459 *puHash = uHash;
1460 return pwszString - pwszStart;
1461}
1462
1463
1464/**
1465 * Converts the given string to unicode.
1466 *
1467 * @returns Length of the resulting string in wchar_t's.
1468 * @param pszSrc The source string.
1469 * @param pwszDst The destination buffer.
1470 * @param cwcDst The size of the destination buffer in wchar_t's.
1471 */
1472static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1473{
1474 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1475 KSIZE offDst = 0;
1476 while (offDst < cwcDst)
1477 {
1478 char ch = *pszSrc++;
1479 pwszDst[offDst++] = ch;
1480 if (!ch)
1481 return offDst - 1;
1482 kHlpAssert((unsigned)ch < 127);
1483 }
1484
1485 pwszDst[offDst - 1] = '\0';
1486 return offDst;
1487}
1488
1489
1490/**
1491 * Converts the given string to UTF-16, allocating the buffer.
1492 *
1493 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1494 * the source string.
1495 * @param pchSrc The source string.
1496 * @param cchSrc The length of the source string.
1497 */
1498static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1499{
1500 DWORD const dwErrSaved = GetLastError();
1501 KSIZE cwcBuf = cchSrc + 1;
1502 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1503 if (pwszBuf)
1504 {
1505 if (cchSrc > 0)
1506 {
1507 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1508 if (cwcRet > 0)
1509 {
1510 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1511 pwszBuf[cwcRet] = '\0';
1512 }
1513 else
1514 {
1515 kHlpFree(pwszBuf);
1516
1517 /* Figure the length and allocate the right buffer size. */
1518 SetLastError(NO_ERROR);
1519 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1520 if (cwcRet)
1521 {
1522 cwcBuf = cwcRet + 2;
1523 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1524 if (pwszBuf)
1525 {
1526 SetLastError(NO_ERROR);
1527 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1528 if (cwcRet)
1529 {
1530 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1531 pwszBuf[cwcRet] = '\0';
1532 }
1533 else
1534 {
1535 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1536 kHlpFree(pwszBuf);
1537 pwszBuf = NULL;
1538 }
1539 }
1540 }
1541 else
1542 {
1543 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1544 pwszBuf = NULL;
1545 }
1546 }
1547 }
1548 else
1549 pwszBuf[0] = '\0';
1550 }
1551 SetLastError(dwErrSaved);
1552 return pwszBuf;
1553}
1554
1555
1556/**
1557 * Converts the given UTF-16 to a normal string.
1558 *
1559 * @returns Length of the resulting string.
1560 * @param pwszSrc The source UTF-16 string.
1561 * @param pszDst The destination buffer.
1562 * @param cbDst The size of the destination buffer in bytes.
1563 */
1564static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1565{
1566 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1567 KSIZE offDst = 0;
1568 while (offDst < cbDst)
1569 {
1570 wchar_t wc = *pwszSrc++;
1571 pszDst[offDst++] = (char)wc;
1572 if (!wc)
1573 return offDst - 1;
1574 kHlpAssert((unsigned)wc < 127);
1575 }
1576
1577 pszDst[offDst - 1] = '\0';
1578 return offDst;
1579}
1580
1581
1582/**
1583 * Converts the given UTF-16 to ASSI, allocating the buffer.
1584 *
1585 * @returns Pointer to the new heap allocation containing the ANSI version of
1586 * the source string.
1587 * @param pwcSrc The source string.
1588 * @param cwcSrc The length of the source string.
1589 */
1590static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1591{
1592 DWORD const dwErrSaved = GetLastError();
1593 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1594 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1595 if (pszBuf)
1596 {
1597 if (cwcSrc > 0)
1598 {
1599 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1600 if (cchRet > 0)
1601 {
1602 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1603 pszBuf[cchRet] = '\0';
1604 }
1605 else
1606 {
1607 kHlpFree(pszBuf);
1608
1609 /* Figure the length and allocate the right buffer size. */
1610 SetLastError(NO_ERROR);
1611 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1612 if (cchRet)
1613 {
1614 cbBuf = cchRet + 2;
1615 pszBuf = (char *)kHlpAlloc(cbBuf);
1616 if (pszBuf)
1617 {
1618 SetLastError(NO_ERROR);
1619 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1620 if (cchRet)
1621 {
1622 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1623 pszBuf[cchRet] = '\0';
1624 }
1625 else
1626 {
1627 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1628 kHlpFree(pszBuf);
1629 pszBuf = NULL;
1630 }
1631 }
1632 }
1633 else
1634 {
1635 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1636 pszBuf = NULL;
1637 }
1638 }
1639 }
1640 else
1641 pszBuf[0] = '\0';
1642 }
1643 SetLastError(dwErrSaved);
1644 return pszBuf;
1645}
1646
1647
1648
1649/** UTF-16 string length. */
1650static KSIZE kwUtf16Len(wchar_t const *pwsz)
1651{
1652 KSIZE cwc = 0;
1653 while (*pwsz != '\0')
1654 cwc++, pwsz++;
1655 return cwc;
1656}
1657
1658/**
1659 * Copy out the UTF-16 string following the convension of GetModuleFileName
1660 */
1661static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1662{
1663 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1664 if (cwcSrc + 1 <= cwcDst)
1665 {
1666 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1667 return (DWORD)cwcSrc;
1668 }
1669 if (cwcDst > 0)
1670 {
1671 KSIZE cwcDstTmp = cwcDst - 1;
1672 pwszDst[cwcDstTmp] = '\0';
1673 if (cwcDstTmp > 0)
1674 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1675 }
1676 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1677 return (DWORD)cwcDst;
1678}
1679
1680
1681/**
1682 * Copy out the ANSI string following the convension of GetModuleFileName
1683 */
1684static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1685{
1686 KSIZE cchSrc = kHlpStrLen(pszSrc);
1687 if (cchSrc + 1 <= cbDst)
1688 {
1689 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1690 return (DWORD)cchSrc;
1691 }
1692 if (cbDst > 0)
1693 {
1694 KSIZE cbDstTmp = cbDst - 1;
1695 pszDst[cbDstTmp] = '\0';
1696 if (cbDstTmp > 0)
1697 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1698 }
1699 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1700 return (DWORD)cbDst;
1701}
1702
1703
1704/**
1705 * Normalizes the path so we get a consistent hash.
1706 *
1707 * @returns status code.
1708 * @param pszPath The path.
1709 * @param pszNormPath The output buffer.
1710 * @param cbNormPath The size of the output buffer.
1711 */
1712static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1713{
1714 KFSLOOKUPERROR enmError;
1715 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1716 if (pFsObj)
1717 {
1718 KBOOL fRc;
1719 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1720 kFsCacheObjRelease(g_pFsCache, pFsObj);
1721 if (fRc)
1722 return 0;
1723 return KERR_BUFFER_OVERFLOW;
1724 }
1725 return KERR_FILE_NOT_FOUND;
1726}
1727
1728
1729/**
1730 * Get the pointer to the filename part of the path.
1731 *
1732 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1733 * @returns Pointer to the terminator char if no filename.
1734 * @param pszPath The path to parse.
1735 */
1736static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1737{
1738 const wchar_t *pwszLast = NULL;
1739 for (;;)
1740 {
1741 wchar_t wc = *pwszPath;
1742#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1743 if (wc == '/' || wc == '\\' || wc == ':')
1744 {
1745 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1746 /* nothing */;
1747 pwszLast = pwszPath;
1748 }
1749#else
1750 if (wc == '/')
1751 {
1752 while ((wc = *++pszFilename) == '/')
1753 /* betsuni */;
1754 pwszLast = pwszPath;
1755 }
1756#endif
1757 if (!wc)
1758 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1759 pwszPath++;
1760 }
1761}
1762
1763
1764
1765/**
1766 * Retains a new reference to the given module
1767 * @returns pMod
1768 * @param pMod The module to retain.
1769 */
1770static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1771{
1772 kHlpAssert(pMod->cRefs > 0);
1773 kHlpAssert(pMod->cRefs < 64);
1774 pMod->cRefs++;
1775 return pMod;
1776}
1777
1778
1779/**
1780 * Releases a module reference.
1781 *
1782 * @param pMod The module to release.
1783 */
1784static void kwLdrModuleRelease(PKWMODULE pMod)
1785{
1786 if (--pMod->cRefs == 0)
1787 {
1788 /* Unlink it from the hash table. */
1789 if (!pMod->fExe)
1790 {
1791 PKWMODULE pPrev = NULL;
1792 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1793 if (g_apModules[idx] == pMod)
1794 g_apModules[idx] = pMod->pNextHash;
1795 else
1796 {
1797 PKWMODULE pPrev = g_apModules[idx];
1798 kHlpAssert(pPrev != NULL);
1799 while (pPrev->pNextHash != pMod)
1800 {
1801 pPrev = pPrev->pNextHash;
1802 kHlpAssert(pPrev != NULL);
1803 }
1804 pPrev->pNextHash = pMod->pNextHash;
1805 }
1806 }
1807
1808 /* Unlink it from the list. */
1809 if (pMod != g_pModuleHead)
1810 {
1811 PKWMODULE pPrev = g_pModuleHead;
1812 while (pPrev)
1813 {
1814 if (pPrev->pNextList == pMod)
1815 {
1816 pPrev->pNextList = pMod->pNextList;
1817 if (!pMod->pNextList)
1818 g_ppModuleNext = &pPrev->pNextList;
1819 break;
1820 }
1821 pPrev = pPrev->pNextList;
1822 }
1823 kHlpAssert(pPrev != NULL);
1824 }
1825 else
1826 {
1827 g_pModuleHead = pMod->pNextList;
1828 if (!pMod->pNextList)
1829 g_ppModuleNext = &g_pModuleHead;
1830 }
1831
1832 /* Release import modules. */
1833 if (!pMod->fNative)
1834 {
1835 KSIZE idx = pMod->u.Manual.cImpMods;
1836 while (idx-- > 0)
1837 if (pMod->u.Manual.apImpMods[idx])
1838 {
1839 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1840 pMod->u.Manual.apImpMods[idx] = NULL;
1841 }
1842 }
1843
1844 /* Free our resources. */
1845 kLdrModClose(pMod->pLdrMod);
1846 pMod->pLdrMod = NULL;
1847
1848 if (!pMod->fNative)
1849 {
1850 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1851 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1852 }
1853
1854 if (pMod->iCrtSlot != KU8_MAX)
1855 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
1856
1857 if (pMod->pszMsPdbSrvEndpoint)
1858 {
1859 kHlpFree(pMod->pszMsPdbSrvEndpoint);
1860 pMod->pszMsPdbSrvEndpoint = NULL;
1861 }
1862
1863 kHlpFree(pMod);
1864 }
1865 else
1866 kHlpAssert(pMod->cRefs < 64);
1867}
1868
1869
1870/**
1871 * Links the module into the module hash table.
1872 *
1873 * @returns pMod
1874 * @param pMod The module to link.
1875 */
1876static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
1877{
1878 if (!pMod->fExe)
1879 {
1880 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1881 pMod->pNextHash = g_apModules[idx];
1882 g_apModules[idx] = pMod;
1883 }
1884
1885 pMod->pNextList = NULL;
1886 *g_ppModuleNext = pMod;
1887 g_ppModuleNext = &pMod->pNextList;
1888
1889 return pMod;
1890}
1891
1892
1893/**
1894 * Replaces imports for this module according to g_aSandboxNativeReplacements.
1895 *
1896 * @param pMod The natively loaded module to process.
1897 */
1898static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
1899{
1900 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
1901 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
1902 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
1903 IMAGE_NT_HEADERS const *pNtHdrs;
1904 IMAGE_DATA_DIRECTORY const *pDirEnt;
1905
1906 kHlpAssert(pMod->fNative);
1907
1908 /*
1909 * Locate the export descriptors.
1910 */
1911 /* MZ header. */
1912 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
1913 {
1914 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
1915 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
1916 }
1917 else
1918 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
1919
1920 /* Check PE header. */
1921 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
1922 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
1923
1924 /* Locate the import descriptor array. */
1925 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
1926 if ( pDirEnt->Size > 0
1927 && pDirEnt->VirtualAddress != 0)
1928 {
1929 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
1930 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
1931 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
1932 KU8 *pbProtRange = NULL;
1933 SIZE_T cbProtRange = 0;
1934 DWORD fOldProt = 0;
1935 KU32 const cbPage = 0x1000;
1936 BOOL fRc;
1937
1938
1939 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
1940 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
1941 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
1942
1943 /*
1944 * Walk the import descriptor array.
1945 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
1946 */
1947 while ( cLeft-- > 0
1948 && pImpDesc->Name > 0
1949 && pImpDesc->FirstThunk > 0)
1950 {
1951 KU32 iThunk;
1952 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
1953 PKWMODULE pImportMod = NULL;
1954 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
1955 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
1956 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
1957 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
1958 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
1959 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
1960 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
1961
1962 /* Iterate the thunks. */
1963 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
1964 {
1965 KUPTR const off = paOrgThunks[iThunk].u1.Function;
1966 kHlpAssertReturnVoid(off < cbImage);
1967 if (!IMAGE_SNAP_BY_ORDINAL(off))
1968 {
1969 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
1970 KSIZE const cchSymbol = kHlpStrLen(pName->Name);
1971 KU32 i = g_cSandboxNativeReplacements;
1972 while (i-- > 0)
1973 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
1974 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
1975 {
1976 if ( !g_aSandboxNativeReplacements[i].pszModule
1977 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
1978 {
1979 KW_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
1980
1981 /* The .rdata section is normally read-only, so we need to make it writable first. */
1982 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
1983 {
1984 /* Restore previous .rdata page. */
1985 if (fOldProt)
1986 {
1987 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
1988 kHlpAssert(fRc);
1989 fOldProt = 0;
1990 }
1991
1992 /* Query attributes for the current .rdata page. */
1993 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
1994 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
1995 kHlpAssert(cbProtRange);
1996 if (cbProtRange)
1997 {
1998 switch (ProtInfo.Protect)
1999 {
2000 case PAGE_READWRITE:
2001 case PAGE_WRITECOPY:
2002 case PAGE_EXECUTE_READWRITE:
2003 case PAGE_EXECUTE_WRITECOPY:
2004 /* Already writable, nothing to do. */
2005 break;
2006
2007 default:
2008 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2009 case PAGE_READONLY:
2010 cbProtRange = cbPage;
2011 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2012 break;
2013
2014 case PAGE_EXECUTE:
2015 case PAGE_EXECUTE_READ:
2016 cbProtRange = cbPage;
2017 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2018 break;
2019 }
2020 kHlpAssertStmt(fRc, fOldProt = 0);
2021 }
2022 }
2023
2024 /*
2025 * Unslotted replacements are simple.
2026 */
2027 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2028 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2029 else
2030 {
2031 /*
2032 * Must find our module entry for this module, possibly creating one.
2033 */
2034 if (!pImportMod)
2035 {
2036 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2037 K_TRUE /*fAlwaysPresent*/);
2038 if (!pImportMod)
2039 {
2040 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2041 pszImport, pMod->pszPath);
2042 break;
2043 }
2044 }
2045 paThunks[iThunk].u1.AddressOfData
2046 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2047 }
2048 break;
2049 }
2050 }
2051 }
2052 }
2053
2054
2055 /* Next import descriptor. */
2056 pImpDesc++;
2057 }
2058
2059
2060 if (fOldProt)
2061 {
2062 DWORD fIgnore = 0;
2063 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2064 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2065 }
2066 }
2067
2068}
2069
2070
2071/**
2072 * Creates a module from a native kLdr module handle.
2073 *
2074 * @returns Module w/ 1 reference on success, NULL on failure.
2075 * @param pLdrMod The native kLdr module.
2076 * @param pszPath The normalized path to the module.
2077 * @param cbPath The module path length with terminator.
2078 * @param uHashPath The module path hash.
2079 * @param fDoReplacements Whether to do import replacements on this
2080 * module.
2081 */
2082static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2083 KBOOL fDoReplacements)
2084{
2085 /*
2086 * Create the entry.
2087 */
2088 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2089 if (pMod)
2090 {
2091 pMod->pwszPath = (wchar_t *)(pMod + 1);
2092 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2093 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2094 pMod->uHashPath = uHashPath;
2095 pMod->cRefs = 1;
2096 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2097 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2098 pMod->fExe = K_FALSE;
2099 pMod->fNative = K_TRUE;
2100 pMod->pLdrMod = pLdrMod;
2101 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2102 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2103 pMod->iCrtSlot = KU8_MAX;
2104 pMod->fNeedReInit = K_FALSE;
2105 pMod->pszMsPdbSrvEndpoint = NULL;
2106 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2107
2108 if (fDoReplacements)
2109 {
2110 DWORD const dwSavedErr = GetLastError();
2111 kwLdrModuleDoNativeImportReplacements(pMod);
2112 SetLastError(dwSavedErr);
2113 }
2114
2115 KW_LOG(("New module: %p LB %#010x %s (native)\n",
2116 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath));
2117 g_cModules++;
2118 return kwLdrModuleLink(pMod);
2119 }
2120 return NULL;
2121}
2122
2123
2124
2125/**
2126 * Creates a module using the native loader.
2127 *
2128 * @returns Module w/ 1 reference on success, NULL on failure.
2129 * @param pszPath The normalized path to the module.
2130 * @param uHashPath The module path hash.
2131 * @param fDoReplacements Whether to do import replacements on this
2132 * module.
2133 */
2134static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2135{
2136 /*
2137 * Open the module and check the type.
2138 */
2139 PKLDRMOD pLdrMod;
2140 int rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2141 if (rc == 0)
2142 {
2143 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, kHlpStrLen(pszPath) + 1,
2144 uHashPath, fDoReplacements);
2145 if (pMod)
2146 return pMod;
2147 kLdrModClose(pLdrMod);
2148 }
2149 return NULL;
2150}
2151
2152
2153/**
2154 * Sets up the quick zero & copy tables for the non-native module.
2155 *
2156 * This is a worker for kwLdrModuleCreateNonNative.
2157 *
2158 * @param pMod The module.
2159 */
2160static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2161{
2162 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2163 KU32 cSegs = pMod->pLdrMod->cSegments;
2164 KU32 iSeg;
2165
2166 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2167 pMod->u.Manual.cQuickCopyChunks = 0;
2168 pMod->u.Manual.cQuickZeroChunks = 0;
2169
2170 for (iSeg = 0; iSeg < cSegs; iSeg++)
2171 switch (paSegs[iSeg].enmProt)
2172 {
2173 case KPROT_READWRITE:
2174 case KPROT_WRITECOPY:
2175 case KPROT_EXECUTE_READWRITE:
2176 case KPROT_EXECUTE_WRITECOPY:
2177 if (paSegs[iSeg].cbMapped)
2178 {
2179 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2180 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2181 {
2182 /*
2183 * Check for trailing zero words.
2184 */
2185 KSIZE cbTrailingZeros;
2186 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2187 && (paSegs[iSeg].cbMapped & 7) == 0
2188 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2189 {
2190 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2191 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2192 KSIZE idxFirstZero = cNatural;
2193 while (idxFirstZero > 0)
2194 if (pauNatural[--idxFirstZero] == 0)
2195 { /* likely */ }
2196 else
2197 {
2198 idxFirstZero++;
2199 break;
2200 }
2201 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2202 if (cbTrailingZeros < 128)
2203 cbTrailingZeros = 0;
2204 }
2205 else
2206 cbTrailingZeros = 0;
2207
2208 /*
2209 * Add quick copy entry.
2210 */
2211 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2212 {
2213 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2214 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2215 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2216 pMod->u.Manual.cQuickCopyChunks = iChunk + 1;
2217 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2218 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2219 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2220 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2221 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2222 }
2223
2224 /*
2225 * Add quick zero entry.
2226 */
2227 if (cbTrailingZeros)
2228 {
2229 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2230 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2231 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2232 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2233 pMod->u.Manual.cQuickZeroChunks = iZero + 1;
2234 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2235 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2236 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2237 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2238 }
2239 }
2240 else
2241 {
2242 /*
2243 * We're out of quick copy table entries, so just copy the whole darn thing.
2244 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2245 */
2246 kHlpAssertFailed();
2247 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2248 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2249 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2250 pMod->u.Manual.cQuickCopyChunks = 1;
2251 KWLDR_LOG(("Quick copy not possible!\n"));
2252 return;
2253 }
2254 }
2255 break;
2256
2257 default:
2258 break;
2259 }
2260}
2261
2262
2263/**
2264 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2265 *
2266 * @param hDll The DLL handle.
2267 * @param idxTls The allocated TLS index.
2268 * @param ppfnTlsCallback Pointer to the TLS callback table entry.
2269 */
2270__declspec(dllexport) void kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, PIMAGE_TLS_CALLBACK *ppfnTlsCallback)
2271{
2272 /*
2273 * Do the module initialization thing first.
2274 */
2275 PKWMODULE pMod = g_pModPendingTlsAlloc;
2276 if (pMod)
2277 {
2278 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2279 LIST_ENTRY *pHead;
2280 LIST_ENTRY *pCur;
2281
2282 pMod->u.Manual.idxTls = idxTls;
2283 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2284
2285 /*
2286 * Try sabotage the DLL name so we can load this module again.
2287 */
2288 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2289 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2290 {
2291 LDR_DATA_TABLE_ENTRY *pMte;
2292 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2293 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2294 {
2295 PUNICODE_STRING pStr = &pMte->FullDllName;
2296 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2297 pStr->Buffer[--off]++;
2298 pStr->Buffer[--off]++;
2299 pStr->Buffer[--off]++;
2300 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2301 break;
2302 }
2303 }
2304 }
2305}
2306
2307
2308/**
2309 * Allocates and initializes TLS variables.
2310 *
2311 * @returns 0 on success, non-zero failure.
2312 * @param pMod The module.
2313 */
2314static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2315{
2316 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2317 IMAGE_NT_HEADERS const *pNtHdrs;
2318 IMAGE_DATA_DIRECTORY const *pTlsDir;
2319
2320 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2321 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2322 else
2323 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2324 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2325
2326 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2327 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2328 {
2329 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2330 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2331 KU32 iEntry;
2332 KUPTR offIndex;
2333 KUPTR offCallbacks;
2334 KUPTR const *puCallbacks;
2335 KSIZE cbData;
2336 const wchar_t *pwszTlsDll;
2337 HMODULE hmodTlsDll;
2338
2339 /*
2340 * Check and log.
2341 */
2342 for (iEntry = 0; iEntry < cEntries; iEntry++)
2343 {
2344 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2345 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2346 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2347 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2348 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2349 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2350 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2351
2352 if (offIndex >= pMod->cbImage)
2353 {
2354 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2355 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2356 return -1;
2357 }
2358 if (offCallbacks >= pMod->cbImage)
2359 {
2360 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2361 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2362 return -1;
2363 }
2364 while (*puCallbacks != 0)
2365 {
2366 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2367 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2368 puCallbacks++;
2369 }
2370 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2371 {
2372 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2373 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2374 return -1;
2375 }
2376 }
2377
2378 if (cEntries > 1)
2379 {
2380 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2381 return -1;
2382 }
2383
2384 /*
2385 * Make the allocation by loading a new instance of one of the TLS dlls.
2386 * The DLL will make a call to
2387 */
2388 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2389 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2390 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2391 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2392 if (cbData <= 1024)
2393 pwszTlsDll = L"kWorkerTls1K.dll";
2394 else if (cbData <= 65536)
2395 pwszTlsDll = L"kWorkerTls64K.dll";
2396 else if (cbData <= 524288)
2397 pwszTlsDll = L"kWorkerTls512K.dll";
2398 else
2399 {
2400 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2401 return -1;
2402 }
2403
2404 pMod->u.Manual.idxTls = KU32_MAX;
2405 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2406 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2407 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2408 pMod->u.Manual.cTlsCallbacks = 0;
2409 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2410 pMod->u.Manual.cTlsCallbacks++;
2411 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2412
2413 g_pModPendingTlsAlloc = pMod;
2414 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2415 g_pModPendingTlsAlloc = NULL;
2416 if (hmodTlsDll == NULL)
2417 {
2418 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2419 return -1;
2420 }
2421 if (pMod->u.Manual.idxTls == KU32_MAX)
2422 {
2423 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2424 return -1;
2425 }
2426
2427 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2428 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x\n",
2429 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData));
2430 }
2431 return 0;
2432}
2433
2434
2435/**
2436 * Creates a module using the our own loader.
2437 *
2438 * @returns Module w/ 1 reference on success, NULL on failure.
2439 * @param pszPath The normalized path to the module.
2440 * @param uHashPath The module path hash.
2441 * @param fExe K_TRUE if this is an executable image, K_FALSE
2442 * if not. Executable images does not get entered
2443 * into the global module table.
2444 * @param pExeMod The executable module of the process (for
2445 * resolving imports). NULL if fExe is set.
2446 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2447 */
2448static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2449 PKWMODULE pExeMod, const char *pszSearchPath)
2450{
2451 /*
2452 * Open the module and check the type.
2453 */
2454 PKLDRMOD pLdrMod;
2455 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2456 if (rc == 0)
2457 {
2458 switch (pLdrMod->enmType)
2459 {
2460 case KLDRTYPE_EXECUTABLE_FIXED:
2461 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2462 case KLDRTYPE_EXECUTABLE_PIC:
2463 if (!fExe)
2464 rc = KERR_GENERAL_FAILURE;
2465 break;
2466
2467 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2468 case KLDRTYPE_SHARED_LIBRARY_PIC:
2469 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2470 if (fExe)
2471 rc = KERR_GENERAL_FAILURE;
2472 break;
2473
2474 default:
2475 rc = KERR_GENERAL_FAILURE;
2476 break;
2477 }
2478 if (rc == 0)
2479 {
2480 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2481 if (cImports >= 0)
2482 {
2483 /*
2484 * Create the entry.
2485 */
2486 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2487 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2488 + sizeof(pMod) * cImports
2489 + cbPath
2490 + cbPath * 2 * sizeof(wchar_t));
2491 if (pMod)
2492 {
2493 KBOOL fFixed;
2494
2495 pMod->cRefs = 1;
2496 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2497 pMod->uHashPath = uHashPath;
2498 pMod->fExe = fExe;
2499 pMod->fNative = K_FALSE;
2500 pMod->pLdrMod = pLdrMod;
2501 pMod->iCrtSlot = KU8_MAX;
2502 pMod->fNeedReInit = K_FALSE;
2503 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2504 pMod->pszMsPdbSrvEndpoint = NULL;
2505 pMod->u.Manual.cImpMods = (KU32)cImports;
2506#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2507 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2508#endif
2509 pMod->u.Manual.fUseLdBuf = K_FALSE;
2510 pMod->u.Manual.fCanDoQuick = K_FALSE;
2511 pMod->u.Manual.cQuickZeroChunks = 0;
2512 pMod->u.Manual.cQuickCopyChunks = 0;
2513 pMod->u.Manual.idxTls = KU32_MAX;
2514 pMod->u.Manual.offTlsInitData = KU32_MAX;
2515 pMod->u.Manual.cbTlsInitData = 0;
2516 pMod->u.Manual.cbTlsAlloc = 0;
2517 pMod->u.Manual.cTlsCallbacks = 0;
2518 pMod->u.Manual.offTlsCallbacks = 0;
2519 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2520 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2521 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2522 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2523
2524 /*
2525 * Figure out where to load it and get memory there.
2526 */
2527 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2528 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2529 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2530 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2531 if ( !fFixed
2532 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2533 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2534 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2535 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2536 else
2537 pMod->u.Manual.fUseLdBuf = K_TRUE;
2538 if (rc == 0)
2539 {
2540 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2541 if (rc == 0)
2542 {
2543 KI32 iImp;
2544
2545 /*
2546 * Link the module (unless it's an executable image) and process the imports.
2547 */
2548 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2549 kwLdrModuleLink(pMod);
2550 KW_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2551 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2552 KW_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2553 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2554
2555 for (iImp = 0; iImp < cImports; iImp++)
2556 {
2557 char szName[1024];
2558 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2559 if (rc == 0)
2560 {
2561 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2562 &pMod->u.Manual.apImpMods[iImp]);
2563 if (rc == 0)
2564 continue;
2565 }
2566 break;
2567 }
2568
2569 if (rc == 0)
2570 {
2571 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2572 kwLdrModuleGetImportCallback, pMod);
2573 if (rc == 0)
2574 {
2575#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2576 /*
2577 * Find the function table. No validation here because the
2578 * loader did that already, right...
2579 */
2580 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2581 IMAGE_NT_HEADERS const *pNtHdrs;
2582 IMAGE_DATA_DIRECTORY const *pXcptDir;
2583 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2584 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2585 else
2586 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2587 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2588 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2589 if (pXcptDir->Size > 0)
2590 {
2591 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2592 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2593 == pXcptDir->Size);
2594 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2595 }
2596 else
2597 {
2598 pMod->u.Manual.cFunctions = 0;
2599 pMod->u.Manual.paFunctions = NULL;
2600 }
2601#endif
2602
2603 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2604
2605 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2606 if (rc == 0)
2607 {
2608 /*
2609 * Final finish.
2610 */
2611 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2612 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2613 g_cModules++;
2614 g_cNonNativeModules++;
2615 return pMod;
2616 }
2617 }
2618 else
2619 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
2620 }
2621
2622 kwLdrModuleRelease(pMod);
2623 return NULL;
2624 }
2625
2626 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2627 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2628 }
2629 else if (fFixed)
2630 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
2631 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
2632 else
2633 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2634 }
2635 }
2636 }
2637 kLdrModClose(pLdrMod);
2638 }
2639 else
2640 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
2641 return NULL;
2642}
2643
2644
2645/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
2646static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
2647 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
2648{
2649 PKWMODULE pCurMod = (PKWMODULE)pvUser;
2650 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
2651 int rc;
2652 K_NOREF(pMod);
2653
2654 if (pImpMod->fNative)
2655 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
2656 iSymbol, pchSymbol, cchSymbol, pszVersion,
2657 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2658 puValue, pfKind);
2659 else
2660 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
2661 iSymbol, pchSymbol, cchSymbol, pszVersion,
2662 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2663 puValue, pfKind);
2664 if (rc == 0)
2665 {
2666 KU32 i = g_cSandboxReplacements;
2667 while (i-- > 0)
2668 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
2669 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
2670 {
2671 if ( !g_aSandboxReplacements[i].pszModule
2672 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
2673 {
2674 if ( pCurMod->fExe
2675 || !g_aSandboxReplacements[i].fOnlyExe)
2676 {
2677 KW_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
2678 *puValue = g_aSandboxReplacements[i].pfnReplacement;
2679 }
2680 break;
2681 }
2682 }
2683 }
2684
2685 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
2686 KW_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
2687 return rc;
2688
2689}
2690
2691
2692/**
2693 * Gets the main entrypoint for a module.
2694 *
2695 * @returns 0 on success, KERR on failure
2696 * @param pMod The module.
2697 * @param puAddrMain Where to return the address.
2698 */
2699static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
2700{
2701 KLDRADDR uLdrAddrMain;
2702 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
2703 if (rc == 0)
2704 {
2705 *puAddrMain = (KUPTR)uLdrAddrMain;
2706 return 0;
2707 }
2708 return rc;
2709}
2710
2711
2712/**
2713 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
2714 *
2715 * @returns K_TRUE/K_FALSE.
2716 * @param pszFilename The filename (no path).
2717 * @param enmLocation The location.
2718 */
2719static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
2720{
2721 if (enmLocation != KWLOCATION_SYSTEM32)
2722 return K_TRUE;
2723 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2724 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2725 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2726}
2727
2728
2729/**
2730 * Lazily initializes the g_pWinSys32 variable.
2731 */
2732static PKFSDIR kwLdrResolveWinSys32(void)
2733{
2734 KFSLOOKUPERROR enmError;
2735 PKFSDIR pWinSys32;
2736
2737 /* Get the path first. */
2738 char szSystem32[MAX_PATH];
2739 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
2740 {
2741 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
2742 strcpy(szSystem32, "C:\\Windows\\System32");
2743 }
2744
2745 /* Look it up and verify it. */
2746 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
2747 if (pWinSys32)
2748 {
2749 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
2750 {
2751 g_pWinSys32 = pWinSys32;
2752 return pWinSys32;
2753 }
2754
2755 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
2756 }
2757 else
2758 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
2759 return NULL;
2760}
2761
2762
2763/**
2764 * Whether we can load this DLL natively or not.
2765 *
2766 * @returns K_TRUE/K_FALSE.
2767 * @param pszFilename The filename (no path).
2768 * @param enmLocation The location.
2769 * @param pszFullPath The full filename and path.
2770 */
2771static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
2772{
2773 if (enmLocation == KWLOCATION_SYSTEM32)
2774 return K_TRUE;
2775 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
2776 return K_TRUE;
2777
2778 /* If the location is unknown, we must check if it's some dynamic loading
2779 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
2780 if (enmLocation == KWLOCATION_UNKNOWN)
2781 {
2782 PKFSDIR pWinSys32 = g_pWinSys32;
2783 if (!pWinSys32)
2784 pWinSys32 = kwLdrResolveWinSys32();
2785 if (pWinSys32)
2786 {
2787 KFSLOOKUPERROR enmError;
2788 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
2789 if (pFsObj)
2790 {
2791 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
2792 kFsCacheObjRelease(g_pFsCache, pFsObj);
2793 if (fInWinSys32)
2794 return K_TRUE;
2795 }
2796 }
2797 }
2798
2799 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2800 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2801 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2802}
2803
2804
2805/**
2806 * Check if the path leads to a regular file (that exists).
2807 *
2808 * @returns K_TRUE / K_FALSE
2809 * @param pszPath Path to the file to check out.
2810 */
2811static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
2812{
2813 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
2814 KSIZE cchPath = kHlpStrLen(pszPath);
2815 if ( cchPath > 3
2816 && pszPath[cchPath - 4] == '.'
2817 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
2818 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
2819 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
2820 {
2821 KFSLOOKUPERROR enmError;
2822 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
2823 if (pFsObj)
2824 {
2825 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
2826 kFsCacheObjRelease(g_pFsCache, pFsObj);
2827 return fRc;
2828 }
2829 }
2830 else
2831 {
2832 BirdStat_T Stat;
2833 int rc = birdStatFollowLink(pszPath, &Stat);
2834 if (rc == 0)
2835 {
2836 if (S_ISREG(Stat.st_mode))
2837 return K_TRUE;
2838 }
2839 }
2840 return K_FALSE;
2841}
2842
2843
2844/**
2845 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
2846 *
2847 * If the file exists, we consult the module hash table before trying to load it
2848 * off the disk.
2849 *
2850 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
2851 * failure.
2852 * @param pszPath The name of the import module.
2853 * @param enmLocation The location we're searching. This is used in
2854 * the heuristics for determining if we can use the
2855 * native loader or need to sandbox the DLL.
2856 * @param pExe The executable (optional).
2857 * @param pszSearchPath The PATH to search (optional).
2858 */
2859static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
2860{
2861 /*
2862 * Does the file exists and is it a regular file?
2863 */
2864 if (kwLdrModuleIsRegularFile(pszPath))
2865 {
2866 /*
2867 * Yes! Normalize it and look it up in the hash table.
2868 */
2869 char szNormPath[1024];
2870 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
2871 if (rc == 0)
2872 {
2873 const char *pszName;
2874 KU32 const uHashPath = kwStrHash(szNormPath);
2875 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2876 PKWMODULE pMod = g_apModules[idxHash];
2877 if (pMod)
2878 {
2879 do
2880 {
2881 if ( pMod->uHashPath == uHashPath
2882 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
2883 return kwLdrModuleRetain(pMod);
2884 pMod = pMod->pNextHash;
2885 } while (pMod);
2886 }
2887
2888 /*
2889 * Not in the hash table, so we have to load it from scratch.
2890 */
2891 pszName = kHlpGetFilename(szNormPath);
2892 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
2893 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
2894 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
2895 else
2896 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
2897 if (pMod)
2898 return pMod;
2899 return (PKWMODULE)~(KUPTR)0;
2900 }
2901 }
2902 return NULL;
2903}
2904
2905
2906/**
2907 * Gets a reference to the module by the given name.
2908 *
2909 * We must do the search path thing, as our hash table may multiple DLLs with
2910 * the same base name due to different tools version and similar. We'll use a
2911 * modified search sequence, though. No point in searching the current
2912 * directory for instance.
2913 *
2914 * @returns 0 on success, KERR on failure.
2915 * @param pszName The name of the import module.
2916 * @param pExe The executable (optional).
2917 * @param pImporter The module doing the importing (optional).
2918 * @param pszSearchPath The PATH to search (optional).
2919 * @param ppMod Where to return the module pointer w/ reference.
2920 */
2921static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
2922 const char *pszSearchPath, PKWMODULE *ppMod)
2923{
2924 KSIZE const cchName = kHlpStrLen(pszName);
2925 char szPath[1024];
2926 char *psz;
2927 PKWMODULE pMod = NULL;
2928 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
2929 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
2930
2931
2932 /* The import path. */
2933 if (pMod == NULL && pImporter != NULL)
2934 {
2935 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
2936 return KERR_BUFFER_OVERFLOW;
2937
2938 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
2939 if (fNeedSuffix)
2940 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2941 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
2942 }
2943
2944 /* Application directory first. */
2945 if (pMod == NULL && pExe != NULL && pExe != pImporter)
2946 {
2947 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
2948 return KERR_BUFFER_OVERFLOW;
2949 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
2950 if (fNeedSuffix)
2951 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2952 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
2953 }
2954
2955 /* The windows directory. */
2956 if (pMod == NULL)
2957 {
2958 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
2959 if ( cchDir <= 2
2960 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
2961 return KERR_BUFFER_OVERFLOW;
2962 szPath[cchDir++] = '\\';
2963 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
2964 if (fNeedSuffix)
2965 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2966 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2967 }
2968
2969 /* The path. */
2970 if ( pMod == NULL
2971 && pszSearchPath)
2972 {
2973 const char *pszCur = pszSearchPath;
2974 while (*pszCur != '\0')
2975 {
2976 /* Find the end of the component */
2977 KSIZE cch = 0;
2978 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
2979 cch++;
2980
2981 if ( cch > 0 /* wrong, but whatever */
2982 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
2983 {
2984 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
2985 if ( szPath[cch - 1] != ':'
2986 && szPath[cch - 1] != '/'
2987 && szPath[cch - 1] != '\\')
2988 *pszDst++ = '\\';
2989 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
2990 if (fNeedSuffix)
2991 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
2992 *pszDst = '\0';
2993
2994 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2995 if (pMod)
2996 break;
2997 }
2998
2999 /* Advance */
3000 pszCur += cch;
3001 while (*pszCur == ';')
3002 pszCur++;
3003 }
3004 }
3005
3006 /* Return. */
3007 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3008 {
3009 *ppMod = pMod;
3010 return 0;
3011 }
3012 *ppMod = NULL;
3013 return KERR_GENERAL_FAILURE;
3014}
3015
3016
3017/**
3018 * Creates a CRT slot for the given module.
3019 *
3020 * @returns 0 on success, non-zero on failure.
3021 * @param pModule The module.
3022 */
3023static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3024{
3025 KSIZE iSlot;
3026 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3027 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3028 if (g_aCrtSlots[iSlot].pModule == NULL)
3029 {
3030 KLDRADDR uAddr;
3031 int rc;
3032
3033 /* Do the linking: */
3034 g_aCrtSlots[iSlot].pModule = pModule;
3035 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3036 pModule->iCrtSlot = (KU8)iSlot;
3037
3038 /* resolve symbols: */
3039 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3040 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3041 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3042 if (rc != 0)
3043 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3044
3045 return 0;
3046 }
3047 kwErrPrintf("Out of CRT slots!\n");
3048 return KERR_NO_MEMORY;
3049}
3050
3051
3052/**
3053 * Locates the module structure for an already loaded native module.
3054 *
3055 * This will create a module structure if needed.
3056 *
3057 * @returns Pointer to the module structure on success, NULL on failure.
3058 * @param pszName The name of the module.
3059 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3060 * @param fAlwaysPresent Whether the module is expected to always be present,
3061 * or not. If not, complain less.
3062 */
3063static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3064{
3065 /*
3066 * Locate the module and get a normalized path for it.
3067 */
3068 HANDLE hModule = GetModuleHandleA(pszName);
3069 if (hModule)
3070 {
3071 char szModPath[1024];
3072 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3073 {
3074 char szNormPath[1024];
3075 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3076 if (rc == 0)
3077 {
3078 /*
3079 * Hash the path and look it up.
3080 */
3081 KU32 uHashPath;
3082 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3083 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3084 PKWMODULE pMod = g_apModules[idxHash];
3085 if (pMod)
3086 {
3087 do
3088 {
3089 if ( pMod->uHashPath == uHashPath
3090 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3091 {
3092 kwLdrModuleRetain(pMod);
3093 break;
3094 }
3095 pMod = pMod->pNextHash;
3096 } while (pMod);
3097 }
3098
3099 /*
3100 * If not in the hash table, so create a module entry.
3101 */
3102 if (!pMod)
3103 {
3104 PKLDRMOD pLdrMod;
3105 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3106 if (rc == 0)
3107 {
3108 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3109 K_FALSE /*fDoReplacements*/);
3110 if (!pMod)
3111 {
3112 kLdrModClose(pLdrMod);
3113 kwErrPrintf("out of memory\n");
3114 }
3115 }
3116 else
3117 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
3118 }
3119 if (pMod)
3120 {
3121 /*
3122 * Create a CRT slot for the module if necessary.
3123 */
3124 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3125 return pMod;
3126 rc = kwLdrModuleCreateCrtSlot(pMod);
3127 if (rc == 0)
3128 return pMod;
3129 kwLdrModuleRelease(pMod);
3130 }
3131 }
3132 else
3133 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszName, GetLastError());
3134 }
3135 else
3136 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszName, GetLastError());
3137 }
3138 else if (fAlwaysPresent)
3139 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3140 return NULL;
3141}
3142
3143
3144/**
3145 * Does the TLS memory initialization for a module on the current thread.
3146 *
3147 * @returns 0 on success, error on failure.
3148 * @param pMod The module.
3149 */
3150static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3151{
3152 if (pMod->u.Manual.idxTls != KU32_MAX)
3153 {
3154 PTEB pTeb = NtCurrentTeb();
3155 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3156 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3157 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3158 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3159 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3160 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3161 if (pMod->u.Manual.cbTlsInitData)
3162 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3163 }
3164 return 0;
3165}
3166
3167
3168/**
3169 * Does the TLS callbacks for a module.
3170 *
3171 * @param pMod The module.
3172 * @param dwReason The callback reason.
3173 */
3174static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3175{
3176 if (pMod->u.Manual.cTlsCallbacks)
3177 {
3178 PIMAGE_TLS_CALLBACK *pCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3179 do
3180 {
3181 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *pCallback, pMod->hOurMod, dwReason));
3182 (*pCallback)(pMod->hOurMod, dwReason, 0);
3183 } while (*++pCallback);
3184 }
3185}
3186
3187
3188/**
3189 * Does module initialization starting at @a pMod.
3190 *
3191 * This is initially used on the executable. Later it is used by the
3192 * LoadLibrary interceptor.
3193 *
3194 * @returns 0 on success, error on failure.
3195 * @param pMod The module to initialize.
3196 */
3197static int kwLdrModuleInitTree(PKWMODULE pMod)
3198{
3199 int rc = 0;
3200 if (!pMod->fNative)
3201 {
3202 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3203 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3204
3205 /*
3206 * Need to copy bits?
3207 */
3208 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3209 {
3210 if (pMod->u.Manual.fUseLdBuf)
3211 {
3212#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3213 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3214 {
3215 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3216 kHlpAssert(fRc); K_NOREF(fRc);
3217 }
3218#endif
3219 g_pModPrevInLdBuf = g_pModInLdBuf;
3220 g_pModInLdBuf = pMod;
3221 }
3222
3223 /* Do quick zeroing and copying when we can. */
3224 pMod->u.Manual.fCanDoQuick = K_FALSE;
3225 if ( pMod->u.Manual.fCanDoQuick
3226 && ( !pMod->u.Manual.fUseLdBuf
3227 || g_pModPrevInLdBuf == pMod))
3228 {
3229 /* Zero first. */
3230 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3231 switch (pMod->u.Manual.cQuickZeroChunks)
3232 {
3233 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3234 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3235 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3236 case 0: break;
3237 }
3238
3239 /* Then copy. */
3240 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3241 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3242 switch (pMod->u.Manual.cQuickCopyChunks)
3243 {
3244 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3245 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3246 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3247 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3248 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3249 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3250 case 0: break;
3251 }
3252 }
3253 /* Must copy the whole image. */
3254 else
3255 {
3256 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3257 pMod->u.Manual.fCanDoQuick = K_TRUE;
3258 }
3259 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3260 }
3261
3262#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3263 /*
3264 * Need to register function table?
3265 */
3266 if ( !pMod->u.Manual.fRegisteredFunctionTable
3267 && pMod->u.Manual.cFunctions > 0)
3268 {
3269 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3270 pMod->u.Manual.cFunctions,
3271 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3272 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3273 }
3274#endif
3275
3276
3277 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3278 {
3279 /*
3280 * Must do imports first, but mark our module as being initialized to avoid
3281 * endless recursion should there be a dependency loop.
3282 */
3283 KSIZE iImp;
3284 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3285
3286 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3287 {
3288 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3289 if (rc != 0)
3290 return rc;
3291 }
3292
3293 /* Do TLS allocations for module init? */
3294 rc = kwLdrCallTlsAllocateAndInit(pMod);
3295 if (rc != 0)
3296 return rc;
3297 if (pMod->u.Manual.cTlsCallbacks > 0)
3298 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3299
3300 /* Finally call the entry point. */
3301 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3302 if (rc == 0)
3303 pMod->u.Manual.enmState = KWMODSTATE_READY;
3304 else
3305 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3306 }
3307 }
3308 /*
3309 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3310 * _MSPDBSRV_ENDPOINT_ changes value.
3311 */
3312 else if (pMod->fNeedReInit)
3313 {
3314 int rc2;
3315 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3316 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3317 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3318 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3319 if (!rc && !rc2)
3320 { /* likely */ }
3321 else
3322 {
3323 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3324 if (rc2 && !rc)
3325 rc = rc2;
3326 }
3327 pMod->fNeedReInit = K_FALSE;
3328 }
3329 return rc;
3330}
3331
3332
3333/**
3334 * Looks up a module handle for a tool.
3335 *
3336 * @returns Referenced loader module on success, NULL on if not found.
3337 * @param pTool The tool.
3338 * @param hmod The module handle.
3339 */
3340static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3341{
3342 KUPTR const uHMod = (KUPTR)hmod;
3343 PKWMODULE *papMods;
3344 KU32 iEnd;
3345 KU32 i;
3346 PKWDYNLOAD pDynLoad;
3347
3348 /* The executable. */
3349 if ( hmod == NULL
3350 || pTool->u.Sandboxed.pExe->hOurMod == hmod)
3351 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3352
3353 /*
3354 * Binary lookup using the module table.
3355 */
3356 papMods = pTool->u.Sandboxed.papModules;
3357 iEnd = pTool->u.Sandboxed.cModules;
3358 if (iEnd)
3359 {
3360 KU32 iStart = 0;
3361 i = iEnd / 2;
3362 for (;;)
3363 {
3364 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3365 if (uHMod < uHModThis)
3366 {
3367 iEnd = i--;
3368 if (iStart <= i)
3369 { }
3370 else
3371 break;
3372 }
3373 else if (uHMod != uHModThis)
3374 {
3375 iStart = ++i;
3376 if (i < iEnd)
3377 { }
3378 else
3379 break;
3380 }
3381 else
3382 return kwLdrModuleRetain(papMods[i]);
3383
3384 i = iStart + (iEnd - iStart) / 2;
3385 }
3386
3387#ifndef NDEBUG
3388 iStart = pTool->u.Sandboxed.cModules;
3389 while (--iStart > 0)
3390 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3391 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3392#endif
3393 }
3394
3395 /*
3396 * Dynamically loaded images.
3397 */
3398 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3399 if (pDynLoad->hmod == hmod)
3400 {
3401 if (pDynLoad->pMod)
3402 return kwLdrModuleRetain(pDynLoad->pMod);
3403 KWFS_TODO();
3404 return NULL;
3405 }
3406
3407 return NULL;
3408}
3409
3410/**
3411 * Adds the given module to the tool import table.
3412 *
3413 * @returns 0 on success, non-zero on failure.
3414 * @param pTool The tool.
3415 * @param pMod The module.
3416 */
3417static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3418{
3419 /*
3420 * Binary lookup. Locating the right slot for it, return if already there.
3421 */
3422 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3423 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3424 KU32 iEnd = pTool->u.Sandboxed.cModules;
3425 KU32 i;
3426 if (iEnd)
3427 {
3428 KU32 iStart = 0;
3429 i = iEnd / 2;
3430 for (;;)
3431 {
3432 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3433 if (uHMod < uHModThis)
3434 {
3435 iEnd = i;
3436 if (iStart < i)
3437 { }
3438 else
3439 break;
3440 }
3441 else if (uHMod != uHModThis)
3442 {
3443 iStart = ++i;
3444 if (i < iEnd)
3445 { }
3446 else
3447 break;
3448 }
3449 else
3450 {
3451 /* Already there in the table. */
3452 return 0;
3453 }
3454
3455 i = iStart + (iEnd - iStart) / 2;
3456 }
3457#ifndef NDEBUG
3458 iStart = pTool->u.Sandboxed.cModules;
3459 while (--iStart > 0)
3460 {
3461 kHlpAssert(papMods[iStart] != pMod);
3462 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3463 }
3464 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3465 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod > uHMod);
3466#endif
3467 }
3468 else
3469 i = 0;
3470
3471 /*
3472 * Grow the table?
3473 */
3474 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3475 {
3476 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3477 if (!pvNew)
3478 return KERR_NO_MEMORY;
3479 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3480 }
3481
3482 /* Insert it. */
3483 if (i != pTool->u.Sandboxed.cModules)
3484 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3485 papMods[i] = kwLdrModuleRetain(pMod);
3486 pTool->u.Sandboxed.cModules++;
3487 KW_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3488 return 0;
3489}
3490
3491
3492/**
3493 * Adds the given module and all its imports to the
3494 *
3495 * @returns 0 on success, non-zero on failure.
3496 * @param pTool The tool.
3497 * @param pMod The module.
3498 */
3499static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
3500{
3501 int rc = kwToolAddModule(pTool, pMod);
3502 if (!pMod->fNative && rc == 0)
3503 {
3504 KSIZE iImp = pMod->u.Manual.cImpMods;
3505 while (iImp-- > 0)
3506 {
3507 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
3508 if (rc == 0)
3509 { }
3510 else
3511 break;
3512 }
3513 }
3514 return 0;
3515}
3516
3517
3518/**
3519 * Creates a tool entry and inserts it.
3520 *
3521 * @returns Pointer to the tool entry. NULL on failure.
3522 * @param pToolFsObj The file object of the tool. The created tool
3523 * will be associated with it.
3524 *
3525 * A reference is donated by the caller and must be
3526 * released.
3527 * @param pszSearchPath The PATH environment variable value, or NULL.
3528 */
3529static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
3530{
3531 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
3532 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
3533 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
3534 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
3535 if (pTool)
3536 {
3537 KBOOL fRc;
3538 pTool->pwszPath = (wchar_t const *)(pTool + 1);
3539 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
3540 kHlpAssert(fRc); K_NOREF(fRc);
3541
3542 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
3543 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
3544 kHlpAssert(fRc);
3545
3546 pTool->enmType = KWTOOLTYPE_SANDBOXED;
3547 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
3548 NULL /*pEexeMod*/, pszSearchPath);
3549 if (pTool->u.Sandboxed.pExe)
3550 {
3551 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
3552 if (rc == 0)
3553 {
3554 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
3555 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
3556 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
3557 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
3558 else
3559 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
3560 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
3561 }
3562 else
3563 {
3564 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
3565 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
3566 pTool->u.Sandboxed.pExe = NULL;
3567 pTool->enmType = KWTOOLTYPE_EXEC;
3568 }
3569 }
3570 else
3571 pTool->enmType = KWTOOLTYPE_EXEC;
3572
3573 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3574 g_cTools++;
3575 return pTool;
3576 }
3577 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3578 return NULL;
3579}
3580
3581
3582/**
3583 * Looks up the given tool, creating a new tool table entry if necessary.
3584 *
3585 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
3586 * @param pszExe The executable for the tool (not normalized).
3587 * @param cEnvVars Number of environment varibles.
3588 * @param papszEnvVars Environment variables. For getting the PATH.
3589 */
3590static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
3591{
3592 /*
3593 * We associate the tools instances with the file system objects.
3594 *
3595 * We'd like to do the lookup without invaliding the volatile parts of the
3596 * cache, thus the double lookup here. The cache gets invalidate later on.
3597 */
3598 KFSLOOKUPERROR enmError;
3599 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3600 if ( !pToolFsObj
3601 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
3602 {
3603 kFsCacheInvalidateCustomBoth(g_pFsCache);
3604 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3605 }
3606 if (pToolFsObj)
3607 {
3608 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
3609 {
3610 const char *pszSearchPath;
3611 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
3612 if (pTool)
3613 {
3614 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3615 return pTool;
3616 }
3617
3618 /*
3619 * Need to create a new tool.
3620 */
3621 pszSearchPath = NULL;
3622 while (cEnvVars-- > 0)
3623 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
3624 {
3625 pszSearchPath = &papszEnvVars[cEnvVars][5];
3626 break;
3627 }
3628
3629 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
3630 if (pTool)
3631 return pTool;
3632
3633 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
3634 }
3635 else
3636 {
3637 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3638 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
3639 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
3640 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
3641 }
3642 }
3643 else
3644 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
3645 return NULL;
3646}
3647
3648
3649
3650/*
3651 *
3652 * File system cache.
3653 * File system cache.
3654 * File system cache.
3655 *
3656 */
3657
3658
3659/**
3660 * This is for kDep.
3661 */
3662int kwFsPathExists(const char *pszPath)
3663{
3664 BirdTimeSpec_T TsIgnored;
3665 KFSLOOKUPERROR enmError;
3666 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3667 if (pFsObj)
3668 {
3669 kFsCacheObjRelease(g_pFsCache, pFsObj);
3670 return 1;
3671 }
3672 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
3673}
3674
3675
3676/* duplicated in dir-nt-bird.c */
3677void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
3678{
3679 KFSLOOKUPERROR enmError;
3680 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
3681 if (pPathObj)
3682 {
3683 KSIZE off = pPathObj->cchParent;
3684 if (off > 0)
3685 {
3686 KSIZE offEnd = off + pPathObj->cchName;
3687 if (offEnd < cbFull)
3688 {
3689 PKFSDIR pAncestor;
3690
3691 pszFull[off + pPathObj->cchName] = '\0';
3692 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
3693
3694 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3695 {
3696 kHlpAssert(off > 1);
3697 kHlpAssert(pAncestor != NULL);
3698 kHlpAssert(pAncestor->Obj.cchName > 0);
3699 pszFull[--off] = '/';
3700 off -= pAncestor->Obj.cchName;
3701 kHlpAssert(pAncestor->Obj.cchParent == off);
3702 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3703 }
3704 kFsCacheObjRelease(g_pFsCache, pPathObj);
3705 return;
3706 }
3707 }
3708 else
3709 {
3710 if ((size_t)pPathObj->cchName + 1 < cbFull)
3711 {
3712 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
3713 pszFull[pPathObj->cchName] = '/';
3714 pszFull[pPathObj->cchName + 1] = '\0';
3715
3716 kFsCacheObjRelease(g_pFsCache, pPathObj);
3717 return;
3718 }
3719 }
3720
3721 /* do fallback. */
3722 kHlpAssertFailed();
3723 kFsCacheObjRelease(g_pFsCache, pPathObj);
3724 }
3725
3726 nt_fullpath(pszPath, pszFull, cbFull);
3727}
3728
3729
3730/**
3731 * Helper for getting the extension of a UTF-16 path.
3732 *
3733 * @returns Pointer to the extension or the terminator.
3734 * @param pwszPath The path.
3735 * @param pcwcExt Where to return the length of the extension.
3736 */
3737static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
3738{
3739 wchar_t const *pwszName = pwszPath;
3740 wchar_t const *pwszExt = NULL;
3741 for (;;)
3742 {
3743 wchar_t const wc = *pwszPath++;
3744 if (wc == '.')
3745 pwszExt = pwszPath;
3746 else if (wc == '/' || wc == '\\' || wc == ':')
3747 {
3748 pwszName = pwszPath;
3749 pwszExt = NULL;
3750 }
3751 else if (wc == '\0')
3752 {
3753 if (pwszExt)
3754 {
3755 *pcwcExt = pwszPath - pwszExt - 1;
3756 return pwszExt;
3757 }
3758 *pcwcExt = 0;
3759 return pwszPath - 1;
3760 }
3761 }
3762}
3763
3764
3765
3766/**
3767 * Parses the argument string passed in as pszSrc.
3768 *
3769 * @returns size of the processed arguments.
3770 * @param pszSrc Pointer to the commandline that's to be parsed.
3771 * @param pcArgs Where to return the number of arguments.
3772 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
3773 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
3774 *
3775 * @remarks Lifted from startuphacks-win.c
3776 */
3777static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
3778{
3779 int bs;
3780 char chQuote;
3781 char *pfFlags;
3782 int cbArgs;
3783 int cArgs;
3784
3785#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
3786#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
3787#define WHITE(c) ((c) == ' ' || (c) == '\t')
3788
3789#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
3790#define _ARG_RESPONSE 0x02 /* Argument read from response file */
3791#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
3792#define _ARG_ENV 0x08 /* Argument from environment */
3793#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
3794
3795 cArgs = 0;
3796 cbArgs = 0;
3797
3798#if 0
3799 /* argv[0] */
3800 PUTC((char)_ARG_NONZERO);
3801 PUTV;
3802 for (;;)
3803 {
3804 PUTC(*pszSrc);
3805 if (*pszSrc == 0)
3806 break;
3807 ++pszSrc;
3808 }
3809 ++pszSrc;
3810#endif
3811
3812 for (;;)
3813 {
3814 while (WHITE(*pszSrc))
3815 ++pszSrc;
3816 if (*pszSrc == 0)
3817 break;
3818 pfFlags = pchPool;
3819 PUTC((char)_ARG_NONZERO);
3820 PUTV;
3821 bs = 0; chQuote = 0;
3822 for (;;)
3823 {
3824 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
3825 {
3826 while (bs >= 2)
3827 {
3828 PUTC('\\');
3829 bs -= 2;
3830 }
3831 if (bs & 1)
3832 PUTC(*pszSrc);
3833 else
3834 {
3835 chQuote = chQuote ? 0 : *pszSrc;
3836 if (pfFlags != NULL)
3837 *pfFlags |= _ARG_DQUOTE;
3838 }
3839 bs = 0;
3840 }
3841 else if (*pszSrc == '\\')
3842 ++bs;
3843 else
3844 {
3845 while (bs != 0)
3846 {
3847 PUTC('\\');
3848 --bs;
3849 }
3850 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
3851 break;
3852 PUTC(*pszSrc);
3853 }
3854 ++pszSrc;
3855 }
3856 PUTC(0);
3857 }
3858
3859 *pcArgs = cArgs;
3860 return cbArgs;
3861}
3862
3863
3864
3865
3866/*
3867 *
3868 * Process and thread related APIs.
3869 * Process and thread related APIs.
3870 * Process and thread related APIs.
3871 *
3872 */
3873
3874/** Common worker for ExitProcess(), exit() and friends. */
3875static void WINAPI kwSandboxDoExit(int uExitCode)
3876{
3877 if (g_Sandbox.idMainThread == GetCurrentThreadId())
3878 {
3879 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
3880
3881 g_Sandbox.rcExitCode = (int)uExitCode;
3882
3883 /* Before we jump, restore the TIB as we're not interested in any
3884 exception chain stuff installed by the sandboxed executable. */
3885 *pTib = g_Sandbox.TibMainThread;
3886 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
3887
3888 longjmp(g_Sandbox.JmpBuf, 1);
3889 }
3890 KWFS_TODO();
3891}
3892
3893
3894/** ExitProcess replacement. */
3895static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
3896{
3897 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
3898 kwSandboxDoExit((int)uExitCode);
3899}
3900
3901
3902/** ExitProcess replacement. */
3903static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
3904{
3905 if (hProcess == GetCurrentProcess())
3906 kwSandboxDoExit(uExitCode);
3907 KWFS_TODO();
3908 return TerminateProcess(hProcess, uExitCode);
3909}
3910
3911
3912/** Normal CRT exit(). */
3913static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
3914{
3915 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
3916 kwSandboxDoExit(rcExitCode);
3917}
3918
3919
3920/** Quick CRT _exit(). */
3921static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
3922{
3923 /* Quick. */
3924 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
3925 kwSandboxDoExit(rcExitCode);
3926}
3927
3928
3929/** Return to caller CRT _cexit(). */
3930static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
3931{
3932 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
3933 kwSandboxDoExit(rcExitCode);
3934}
3935
3936
3937/** Quick return to caller CRT _c_exit(). */
3938static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
3939{
3940 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
3941 kwSandboxDoExit(rcExitCode);
3942}
3943
3944
3945/** Runtime error and exit _amsg_exit(). */
3946static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
3947{
3948 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
3949 kwSandboxDoExit(255);
3950}
3951
3952
3953/** CRT - terminate(). */
3954static void __cdecl kwSandbox_msvcrt_terminate(void)
3955{
3956 KW_LOG(("\nRuntime - terminate!\n"));
3957 kwSandboxDoExit(254);
3958}
3959
3960
3961/** CRT - _onexit */
3962static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
3963{
3964 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3965 {
3966 PKWEXITCALLACK pCallback;
3967 KW_LOG(("_onexit(%p)\n", pfnFunc));
3968 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3969
3970 pCallback = kHlpAlloc(sizeof(*pCallback));
3971 if (pCallback)
3972 {
3973 pCallback->pfnCallback = pfnFunc;
3974 pCallback->fAtExit = K_FALSE;
3975 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3976 g_Sandbox.pExitCallbackHead = pCallback;
3977 return pfnFunc;
3978 }
3979 return NULL;
3980 }
3981 KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
3982 return pfnFunc;
3983}
3984
3985
3986/** CRT - atexit */
3987static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
3988{
3989 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3990 {
3991 PKWEXITCALLACK pCallback;
3992 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3993 KW_LOG(("atexit(%p)\n", pfnFunc));
3994
3995 pCallback = kHlpAlloc(sizeof(*pCallback));
3996 if (pCallback)
3997 {
3998 pCallback->pfnCallback = (_onexit_t)pfnFunc;
3999 pCallback->fAtExit = K_TRUE;
4000 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4001 g_Sandbox.pExitCallbackHead = pCallback;
4002 return 0;
4003 }
4004 return -1;
4005 }
4006 KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4007 return 0;
4008}
4009
4010
4011/** Kernel32 - SetConsoleCtrlHandler(). */
4012static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4013{
4014 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4015 return TRUE;
4016}
4017
4018
4019/** The CRT internal __getmainargs() API. */
4020static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4021 int dowildcard, int const *piNewMode)
4022{
4023 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4024 *pargc = g_Sandbox.cArgs;
4025 *pargv = g_Sandbox.papszArgs;
4026 *penvp = g_Sandbox.environ;
4027
4028 /** @todo startinfo points at a newmode (setmode) value. */
4029 return 0;
4030}
4031
4032
4033/** The CRT internal __wgetmainargs() API. */
4034static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4035 int dowildcard, int const *piNewMode)
4036{
4037 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4038 *pargc = g_Sandbox.cArgs;
4039 *pargv = g_Sandbox.papwszArgs;
4040 *penvp = g_Sandbox.wenviron;
4041
4042 /** @todo startinfo points at a newmode (setmode) value. */
4043 return 0;
4044}
4045
4046
4047
4048/** Kernel32 - GetCommandLineA() */
4049static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4050{
4051 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4052 return g_Sandbox.pszCmdLine;
4053}
4054
4055
4056/** Kernel32 - GetCommandLineW() */
4057static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4058{
4059 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4060 return g_Sandbox.pwszCmdLine;
4061}
4062
4063
4064/** Kernel32 - GetStartupInfoA() */
4065static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4066{
4067 KW_LOG(("GetStartupInfoA\n"));
4068 GetStartupInfoA(pStartupInfo);
4069 pStartupInfo->lpReserved = NULL;
4070 pStartupInfo->lpTitle = NULL;
4071 pStartupInfo->lpReserved2 = NULL;
4072 pStartupInfo->cbReserved2 = 0;
4073}
4074
4075
4076/** Kernel32 - GetStartupInfoW() */
4077static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4078{
4079 KW_LOG(("GetStartupInfoW\n"));
4080 GetStartupInfoW(pStartupInfo);
4081 pStartupInfo->lpReserved = NULL;
4082 pStartupInfo->lpTitle = NULL;
4083 pStartupInfo->lpReserved2 = NULL;
4084 pStartupInfo->cbReserved2 = 0;
4085}
4086
4087
4088/** CRT - __p___argc(). */
4089static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4090{
4091 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4092 return &g_Sandbox.cArgs;
4093}
4094
4095
4096/** CRT - __p___argv(). */
4097static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4098{
4099 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4100 return &g_Sandbox.papszArgs;
4101}
4102
4103
4104/** CRT - __p___sargv(). */
4105static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4106{
4107 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4108 return &g_Sandbox.papwszArgs;
4109}
4110
4111
4112/** CRT - __p__acmdln(). */
4113static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4114{
4115 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4116 return (char **)&g_Sandbox.pszCmdLine;
4117}
4118
4119
4120/** CRT - __p__acmdln(). */
4121static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4122{
4123 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4124 return &g_Sandbox.pwszCmdLine;
4125}
4126
4127
4128/** CRT - __p__pgmptr(). */
4129static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4130{
4131 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4132 return &g_Sandbox.pgmptr;
4133}
4134
4135
4136/** CRT - __p__wpgmptr(). */
4137static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4138{
4139 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4140 return &g_Sandbox.wpgmptr;
4141}
4142
4143
4144/** CRT - _get_pgmptr(). */
4145static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4146{
4147 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4148 *ppszValue = g_Sandbox.pgmptr;
4149 return 0;
4150}
4151
4152
4153/** CRT - _get_wpgmptr(). */
4154static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4155{
4156 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4157 *ppwszValue = g_Sandbox.wpgmptr;
4158 return 0;
4159}
4160
4161/** Just in case. */
4162static void kwSandbox_msvcrt__wincmdln(void)
4163{
4164 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4165 KWFS_TODO();
4166}
4167
4168
4169/** Just in case. */
4170static void kwSandbox_msvcrt__wwincmdln(void)
4171{
4172 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4173 KWFS_TODO();
4174}
4175
4176/** CreateThread interceptor. */
4177static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4178 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4179 DWORD fFlags, PDWORD pidThread)
4180{
4181 HANDLE hThread = NULL;
4182 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4183 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4184 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4185 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4186 {
4187 /* Allow link::DbgThread. */
4188 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4189 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4190 }
4191 else
4192 KWFS_TODO();
4193 return hThread;
4194}
4195
4196
4197/** _beginthread - create a new thread. */
4198static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4199{
4200 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4201 KWFS_TODO();
4202 return 0;
4203}
4204
4205
4206/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4207static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4208 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4209 unsigned fCreate, unsigned *pidThread)
4210{
4211 /*
4212 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4213 * whatever it needs to.
4214 */
4215 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4216 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4217 pfnThreadProc, pvUser, fCreate, pidThread));
4218 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4219 {
4220 uintptr_t rcRet;
4221 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4222 if (!s_pfnReal)
4223 {
4224 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4225 if (!s_pfnReal)
4226 {
4227 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4228 __debugbreak();
4229 }
4230 }
4231 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4232 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4233 return rcRet;
4234 }
4235
4236 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4237 KWFS_TODO();
4238 return 0;
4239}
4240
4241
4242/** _beginthreadex - create a new thread. */
4243static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex(void *pvSecAttr, unsigned cbStack,
4244 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4245 unsigned fCreate, unsigned *pidThread)
4246{
4247 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4248 KWFS_TODO();
4249 return 0;
4250}
4251
4252
4253/*
4254 *
4255 * Environment related APIs.
4256 * Environment related APIs.
4257 * Environment related APIs.
4258 *
4259 */
4260
4261/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4262static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4263{
4264 char *pszzEnv;
4265 char *pszCur;
4266 KSIZE cbNeeded = 1;
4267 KSIZE iVar = 0;
4268
4269 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4270
4271 /* Figure how space much we need first. */
4272 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4273 cbNeeded += kHlpStrLen(pszCur) + 1;
4274
4275 /* Allocate it. */
4276 pszzEnv = kHlpAlloc(cbNeeded);
4277 if (pszzEnv)
4278 {
4279 char *psz = pszzEnv;
4280 iVar = 0;
4281 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4282 {
4283 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4284 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4285 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4286 }
4287 *psz++ = '\0';
4288 kHlpAssert(psz - pszzEnv == cbNeeded);
4289 }
4290
4291 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4292#if 0
4293 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4294 pszCur = pszzEnv;
4295 iVar = 0;
4296 while (*pszCur)
4297 {
4298 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4299 iVar++;
4300 pszCur += kHlpStrLen(pszCur) + 1;
4301 }
4302 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4303 pszCur++;
4304 fprintf(stderr, "ended at %p, after %u bytes (exepcted %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4305#endif
4306 return pszzEnv;
4307}
4308
4309
4310/** Kernel32 - GetEnvironmentStrings */
4311static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4312{
4313 KW_LOG(("GetEnvironmentStrings!\n"));
4314 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4315}
4316
4317
4318/** Kernel32 - GetEnvironmentStringsW */
4319static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4320{
4321 wchar_t *pwszzEnv;
4322 wchar_t *pwszCur;
4323 KSIZE cwcNeeded = 1;
4324 KSIZE iVar = 0;
4325
4326 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4327
4328 /* Figure how space much we need first. */
4329 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4330 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4331
4332 /* Allocate it. */
4333 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4334 if (pwszzEnv)
4335 {
4336 wchar_t *pwsz = pwszzEnv;
4337 iVar = 0;
4338 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4339 {
4340 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4341 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4342 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4343 }
4344 *pwsz++ = '\0';
4345 kHlpAssert(pwsz - pwszzEnv == cwcNeeded);
4346 }
4347
4348 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4349 return pwszzEnv;
4350}
4351
4352
4353/** Kernel32 - FreeEnvironmentStringsA */
4354static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4355{
4356 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4357 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4358 kHlpFree(pszzEnv);
4359 return TRUE;
4360}
4361
4362
4363/** Kernel32 - FreeEnvironmentStringsW */
4364static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4365{
4366 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4367 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4368 kHlpFree(pwszzEnv);
4369 return TRUE;
4370}
4371
4372
4373/**
4374 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4375 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4376 *
4377 * @returns 0 on success, non-zero on failure.
4378 * @param pSandbox The sandbox.
4379 * @param cMin Minimum size, including terminator.
4380 */
4381static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4382{
4383 void *pvNew;
4384 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4385 KSIZE cNew = cOld + 256;
4386 while (cNew < cMin)
4387 cNew += 256;
4388
4389 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4390 if (pvNew)
4391 {
4392 pSandbox->environ = (char **)pvNew;
4393 pSandbox->environ[cOld] = NULL;
4394
4395 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4396 if (pvNew)
4397 {
4398 pSandbox->papszEnvVars = (char **)pvNew;
4399 pSandbox->papszEnvVars[cOld] = NULL;
4400
4401 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4402 if (pvNew)
4403 {
4404 pSandbox->wenviron = (wchar_t **)pvNew;
4405 pSandbox->wenviron[cOld] = NULL;
4406
4407 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4408 if (pvNew)
4409 {
4410 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4411 pSandbox->papwszEnvVars[cOld] = NULL;
4412
4413 pSandbox->cEnvVarsAllocated = cNew;
4414 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4415 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4416 return 0;
4417 }
4418 }
4419 }
4420 }
4421 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4422 return KERR_NO_MEMORY;
4423}
4424
4425
4426/**
4427 * Sets an environment variable, ANSI style.
4428 *
4429 * @returns 0 on success, non-zero on failure.
4430 * @param pSandbox The sandbox.
4431 * @param pchVar The variable name.
4432 * @param cchVar The length of the name.
4433 * @param pszValue The value.
4434 */
4435static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4436{
4437 /* Allocate and construct the new strings. */
4438 KSIZE cchTmp = kHlpStrLen(pszValue);
4439 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4440 if (pszNew)
4441 {
4442 wchar_t *pwszNew;
4443 kHlpMemCopy(pszNew, pchVar, cchVar);
4444 pszNew[cchVar] = '=';
4445 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4446 cchTmp += cchVar + 1;
4447 pszNew[cchTmp] = '\0';
4448
4449 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4450 if (pwszNew)
4451 {
4452 /* Look it up. */
4453 KSIZE iVar = 0;
4454 char *pszEnv;
4455 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4456 {
4457 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4458 && pszEnv[cchVar] == '=')
4459 {
4460 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4461 " iVar=%d: %p='%s' and %p='%ls'\n",
4462 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4463 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4464 iVar, pszNew, pszNew, pwszNew, pwszNew));
4465
4466 kHlpFree(pSandbox->papszEnvVars[iVar]);
4467 pSandbox->papszEnvVars[iVar] = pszNew;
4468 pSandbox->environ[iVar] = pszNew;
4469
4470 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4471 pSandbox->papwszEnvVars[iVar] = pwszNew;
4472 pSandbox->wenviron[iVar] = pwszNew;
4473 return 0;
4474 }
4475 iVar++;
4476 }
4477
4478 /* Not found, do we need to grow the table first? */
4479 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4480 kwSandboxGrowEnv(pSandbox, iVar + 2);
4481 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4482 {
4483 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4484
4485 pSandbox->papszEnvVars[iVar + 1] = NULL;
4486 pSandbox->papszEnvVars[iVar] = pszNew;
4487 pSandbox->environ[iVar + 1] = NULL;
4488 pSandbox->environ[iVar] = pszNew;
4489
4490 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4491 pSandbox->papwszEnvVars[iVar] = pwszNew;
4492 pSandbox->wenviron[iVar + 1] = NULL;
4493 pSandbox->wenviron[iVar] = pwszNew;
4494 return 0;
4495 }
4496
4497 kHlpFree(pwszNew);
4498 }
4499 kHlpFree(pszNew);
4500 }
4501 KW_LOG(("Out of memory!\n"));
4502 return 0;
4503}
4504
4505
4506/**
4507 * Sets an environment variable, UTF-16 style.
4508 *
4509 * @returns 0 on success, non-zero on failure.
4510 * @param pSandbox The sandbox.
4511 * @param pwcVar The variable name.
4512 * @param cwcVar The length of the name.
4513 * @param pwszValue The value.
4514 */
4515static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4516{
4517 /* Allocate and construct the new strings. */
4518 KSIZE cwcTmp = kwUtf16Len(pwszValue);
4519 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4520 if (pwszNew)
4521 {
4522 char *pszNew;
4523 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4524 pwszNew[cwcVar] = '=';
4525 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4526 cwcTmp += cwcVar + 1;
4527 pwszNew[cwcVar] = '\0';
4528
4529 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4530 if (pszNew)
4531 {
4532 /* Look it up. */
4533 KSIZE iVar = 0;
4534 wchar_t *pwszEnv;
4535 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4536 {
4537 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4538 && pwszEnv[cwcVar] == '=')
4539 {
4540 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4541 " iVar=%d: %p='%s' and %p='%ls'\n",
4542 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4543 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4544 iVar, pszNew, pszNew, pwszNew, pwszNew));
4545
4546 kHlpFree(pSandbox->papszEnvVars[iVar]);
4547 pSandbox->papszEnvVars[iVar] = pszNew;
4548 pSandbox->environ[iVar] = pszNew;
4549
4550 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4551 pSandbox->papwszEnvVars[iVar] = pwszNew;
4552 pSandbox->wenviron[iVar] = pwszNew;
4553 return 0;
4554 }
4555 iVar++;
4556 }
4557
4558 /* Not found, do we need to grow the table first? */
4559 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4560 kwSandboxGrowEnv(pSandbox, iVar + 2);
4561 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4562 {
4563 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4564
4565 pSandbox->papszEnvVars[iVar + 1] = NULL;
4566 pSandbox->papszEnvVars[iVar] = pszNew;
4567 pSandbox->environ[iVar + 1] = NULL;
4568 pSandbox->environ[iVar] = pszNew;
4569
4570 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4571 pSandbox->papwszEnvVars[iVar] = pwszNew;
4572 pSandbox->wenviron[iVar + 1] = NULL;
4573 pSandbox->wenviron[iVar] = pwszNew;
4574 return 0;
4575 }
4576
4577 kHlpFree(pwszNew);
4578 }
4579 kHlpFree(pszNew);
4580 }
4581 KW_LOG(("Out of memory!\n"));
4582 return 0;
4583}
4584
4585
4586/** ANSI unsetenv worker. */
4587static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4588{
4589 KSIZE iVar = 0;
4590 char *pszEnv;
4591 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4592 {
4593 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4594 && pszEnv[cchVar] == '=')
4595 {
4596 KSIZE cVars = iVar;
4597 while (pSandbox->papszEnvVars[cVars])
4598 cVars++;
4599 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4600 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4601
4602 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4603 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4604 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4605
4606 kHlpFree(pSandbox->papszEnvVars[iVar]);
4607 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4608 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4609 pSandbox->papszEnvVars[cVars] = NULL;
4610 pSandbox->environ[cVars] = NULL;
4611
4612 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4613 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4614 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4615 pSandbox->papwszEnvVars[cVars] = NULL;
4616 pSandbox->wenviron[cVars] = NULL;
4617 return 0;
4618 }
4619 iVar++;
4620 }
4621 return KERR_ENVVAR_NOT_FOUND;
4622}
4623
4624
4625/** UTF-16 unsetenv worker. */
4626static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4627{
4628 KSIZE iVar = 0;
4629 wchar_t *pwszEnv;
4630 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4631 {
4632 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4633 && pwszEnv[cwcVar] == '=')
4634 {
4635 KSIZE cVars = iVar;
4636 while (pSandbox->papwszEnvVars[cVars])
4637 cVars++;
4638 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
4639 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
4640
4641 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4642 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4643 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4644
4645 kHlpFree(pSandbox->papszEnvVars[iVar]);
4646 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4647 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4648 pSandbox->papszEnvVars[cVars] = NULL;
4649 pSandbox->environ[cVars] = NULL;
4650
4651 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4652 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4653 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4654 pSandbox->papwszEnvVars[cVars] = NULL;
4655 pSandbox->wenviron[cVars] = NULL;
4656 return 0;
4657 }
4658 iVar++;
4659 }
4660 return KERR_ENVVAR_NOT_FOUND;
4661}
4662
4663
4664
4665/** ANSI getenv worker. */
4666static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4667{
4668 KSIZE iVar = 0;
4669 char *pszEnv;
4670 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
4671 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4672 && pszEnv[cchVar] == '=')
4673 return &pszEnv[cchVar + 1];
4674 return NULL;
4675}
4676
4677
4678/** UTF-16 getenv worker. */
4679static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4680{
4681 KSIZE iVar = 0;
4682 wchar_t *pwszEnv;
4683 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
4684 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4685 && pwszEnv[cwcVar] == '=')
4686 return &pwszEnv[cwcVar + 1];
4687 return NULL;
4688}
4689
4690
4691/** Kernel32 - GetEnvironmentVariableA() */
4692static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
4693{
4694 char *pszFoundValue;
4695 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4696
4697 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4698 if (pszFoundValue)
4699 {
4700 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
4701 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
4702 return cchRet;
4703 }
4704 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
4705 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4706 return 0;
4707}
4708
4709
4710/** Kernel32 - GetEnvironmentVariableW() */
4711static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
4712{
4713 wchar_t *pwszFoundValue;
4714 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4715
4716 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4717 if (pwszFoundValue)
4718 {
4719 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
4720 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
4721 return cchRet;
4722 }
4723 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
4724 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4725 return 0;
4726}
4727
4728
4729/** Kernel32 - SetEnvironmentVariableA() */
4730static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
4731{
4732 int rc;
4733 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4734
4735 if (pszValue)
4736 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4737 else
4738 {
4739 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4740 rc = 0; //??
4741 }
4742 if (rc == 0)
4743 {
4744 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
4745 return TRUE;
4746 }
4747 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4748 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
4749 return FALSE;
4750}
4751
4752
4753/** Kernel32 - SetEnvironmentVariableW() */
4754static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
4755{
4756 int rc;
4757 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4758
4759 if (pwszValue)
4760 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4761 else
4762 {
4763 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4764 rc = 0; //??
4765 }
4766 if (rc == 0)
4767 {
4768 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
4769 return TRUE;
4770 }
4771 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4772 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
4773 return FALSE;
4774}
4775
4776
4777/** Kernel32 - ExpandEnvironmentStringsA() */
4778static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
4779{
4780 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4781 KWFS_TODO();
4782 return 0;
4783}
4784
4785
4786/** Kernel32 - ExpandEnvironmentStringsW() */
4787static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
4788{
4789 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4790 KWFS_TODO();
4791 return 0;
4792}
4793
4794
4795/** CRT - _putenv(). */
4796static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
4797{
4798 int rc;
4799 char const *pszEqual;
4800 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4801
4802 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
4803 if (pszEqual)
4804 {
4805 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
4806 if (rc == 0)
4807 { }
4808 else
4809 rc = -1;
4810 }
4811 else
4812 {
4813 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
4814 rc = 0;
4815 }
4816 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
4817 return rc;
4818}
4819
4820
4821/** CRT - _wputenv(). */
4822static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
4823{
4824 int rc;
4825 wchar_t const *pwszEqual;
4826 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4827
4828 pwszEqual = wcschr(pwszVarEqualValue, '=');
4829 if (pwszEqual)
4830 {
4831 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
4832 if (rc == 0)
4833 { }
4834 else
4835 rc = -1;
4836 }
4837 else
4838 {
4839 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
4840 rc = 0;
4841 }
4842 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
4843 return rc;
4844}
4845
4846
4847/** CRT - _putenv_s(). */
4848static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
4849{
4850 char const *pszEqual;
4851 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4852
4853 pszEqual = kHlpStrChr(pszVar, '=');
4854 if (pszEqual == NULL)
4855 {
4856 if (pszValue)
4857 {
4858 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4859 if (rc == 0)
4860 {
4861 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
4862 return 0;
4863 }
4864 }
4865 else
4866 {
4867 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4868 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
4869 return 0;
4870 }
4871 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
4872 return ENOMEM;
4873 }
4874 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
4875 return EINVAL;
4876}
4877
4878
4879/** CRT - _wputenv_s(). */
4880static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
4881{
4882 wchar_t const *pwszEqual;
4883 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4884
4885 pwszEqual = wcschr(pwszVar, '=');
4886 if (pwszEqual == NULL)
4887 {
4888 if (pwszValue)
4889 {
4890 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4891 if (rc == 0)
4892 {
4893 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
4894 return 0;
4895 }
4896 }
4897 else
4898 {
4899 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4900 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
4901 return 0;
4902 }
4903 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
4904 return ENOMEM;
4905 }
4906 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
4907 return EINVAL;
4908}
4909
4910
4911/** CRT - get pointer to the __initenv variable (initial environment). */
4912static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
4913{
4914 KW_LOG(("__p___initenv\n"));
4915 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4916 KWFS_TODO();
4917 return &g_Sandbox.initenv;
4918}
4919
4920
4921/** CRT - get pointer to the __winitenv variable (initial environment). */
4922static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
4923{
4924 KW_LOG(("__p___winitenv\n"));
4925 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4926 KWFS_TODO();
4927 return &g_Sandbox.winitenv;
4928}
4929
4930
4931/** CRT - get pointer to the _environ variable (current environment). */
4932static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
4933{
4934 KW_LOG(("__p__environ\n"));
4935 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4936 return &g_Sandbox.environ;
4937}
4938
4939
4940/** CRT - get pointer to the _wenviron variable (current environment). */
4941static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
4942{
4943 KW_LOG(("__p__wenviron\n"));
4944 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4945 return &g_Sandbox.wenviron;
4946}
4947
4948
4949/** CRT - get the _environ variable (current environment).
4950 * @remarks Not documented or prototyped? */
4951static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
4952{
4953 KWFS_TODO(); /** @todo check the callers expectations! */
4954 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4955 *ppapszEnviron = g_Sandbox.environ;
4956 return 0;
4957}
4958
4959
4960/** CRT - get the _wenviron variable (current environment).
4961 * @remarks Not documented or prototyped? */
4962static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
4963{
4964 KWFS_TODO(); /** @todo check the callers expectations! */
4965 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4966 *ppapwszEnviron = g_Sandbox.wenviron;
4967 return 0;
4968}
4969
4970
4971/** CRT - _wdupenv_s() (see _tdupenv_s(). */
4972static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
4973 PKWCRTSLOT pSlot)
4974{
4975 errno_t rc;
4976 wchar_t *pwszValue;
4977 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4978
4979 if (ppwszValue)
4980 {
4981 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
4982 if (pwszValue)
4983 {
4984 size_t cwcValue = wcslen(pwszValue);
4985 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
4986 if (pwszDst)
4987 {
4988 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
4989 pwszDst[cwcValue] = '\0';
4990 *ppwszValue = pwszDst;
4991 if (pcwcValue)
4992 *pcwcValue = cwcValue;
4993 rc = 0;
4994 }
4995 else
4996 {
4997 *ppwszValue = NULL;
4998 if (pcwcValue)
4999 *pcwcValue = 0;
5000 rc = ENOMEM;
5001 }
5002 }
5003 else
5004 {
5005 *ppwszValue = NULL;
5006 if (pcwcValue)
5007 *pcwcValue = 0;
5008 rc = 0;
5009 }
5010 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5011 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5012 }
5013 else
5014 {
5015 /*
5016 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5017 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5018 * was set to a value.
5019 */
5020 if (pcwcValue)
5021 *pcwcValue = 0;
5022 rc = EINVAL;
5023 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5024 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5025 }
5026 return rc;
5027}
5028CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5029 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5030 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5031
5032
5033
5034/*
5035 *
5036 * Loader related APIs
5037 * Loader related APIs
5038 * Loader related APIs
5039 *
5040 */
5041
5042/**
5043 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5044 */
5045static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5046{
5047 /* Load it first. */
5048 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5049 if (hmod)
5050 {
5051 pDynLoad->hmod = hmod;
5052 pDynLoad->pMod = NULL; /* indicates special */
5053
5054 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5055 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5056 KW_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5057 }
5058 else
5059 kHlpFree(pDynLoad);
5060 return hmod;
5061}
5062
5063
5064/**
5065 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5066 */
5067static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5068{
5069 HMODULE hmod;
5070 PKWMODULE pMod;
5071 KU32 uHashPath;
5072 KSIZE idxHash;
5073 char szNormPath[256];
5074 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5075
5076 /*
5077 * Lower case it and make it ends with .dll.
5078 */
5079 if (cbFilename <= sizeof(szNormPath))
5080 {
5081 static const char s_szDll[] = ".dll";
5082 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5083 _strlwr(szNormPath);
5084 if ( ( cbFilename <= 4
5085 || strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5086 && cbFilename + sizeof(s_szDll) - 1 <= sizeof(szNormPath))
5087 {
5088 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5089 cbFilename += sizeof(s_szDll) - 1;
5090 }
5091 }
5092 else
5093 {
5094 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5095 return NULL;
5096 }
5097
5098 /*
5099 * Check if it has already been loaded so we don't create an unnecessary
5100 * loader module for it.
5101 */
5102 uHashPath = kwStrHash(szNormPath);
5103 idxHash = uHashPath % K_ELEMENTS(g_apModules);
5104 pMod = g_apModules[idxHash];
5105 if (pMod)
5106 {
5107 do
5108 {
5109 if ( pMod->uHashPath == uHashPath
5110 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
5111 {
5112 pDynLoad->pMod = kwLdrModuleRetain(pMod);
5113 pDynLoad->hmod = pMod->hOurMod;
5114
5115 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5116 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5117 KW_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - already loaded]\n", pDynLoad->szRequest, pDynLoad->hmod));
5118 return pDynLoad->hmod;
5119 }
5120 pMod = pMod->pNextHash;
5121 } while (pMod);
5122 }
5123
5124
5125 /*
5126 * Try load it and make a kLdr module for it.
5127 */
5128 hmod = LoadLibraryExA(szNormPath, NULL /*hFile*/, fFlags);
5129 if (hmod)
5130 {
5131 PKLDRMOD pLdrMod;
5132 int rc = kLdrModOpenNativeByHandle((KUPTR)hmod, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
5133 if (rc == 0)
5134 {
5135 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cbFilename, uHashPath,
5136 K_FALSE /*fDoReplacements*/);
5137 if (pMod)
5138 {
5139 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5140
5141 pDynLoad->pMod = pMod;
5142 pDynLoad->hmod = hmod;
5143
5144 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5145 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5146 KW_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5147 return hmod;
5148 }
5149 KWFS_TODO();
5150 }
5151 else
5152 KWFS_TODO();
5153 }
5154 kHlpFree(pDynLoad);
5155 return hmod;
5156}
5157
5158
5159/** Kernel32 - LoadLibraryExA() */
5160static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5161{
5162 KSIZE cchFilename = kHlpStrLen(pszFilename);
5163 const char *pszSearchPath;
5164 PKWDYNLOAD pDynLoad;
5165 PKWMODULE pMod;
5166 int rc;
5167 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5168
5169 /*
5170 * Deal with a couple of extremely unlikely special cases right away.
5171 */
5172 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5173 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5174 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5175 { /* likely */ }
5176 else
5177 {
5178 KWFS_TODO();
5179 return LoadLibraryExA(pszFilename, hFile, fFlags);
5180 }
5181
5182 /*
5183 * Check if we've already got a dynload entry for this one.
5184 */
5185 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5186 if ( pDynLoad->cchRequest == cchFilename
5187 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5188 {
5189 if (pDynLoad->pMod)
5190 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5191 else
5192 rc = 0;
5193 if (rc == 0)
5194 {
5195 KW_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5196 return pDynLoad->hmod;
5197 }
5198 SetLastError(ERROR_DLL_INIT_FAILED);
5199 return NULL;
5200 }
5201
5202 /*
5203 * Allocate a dynload entry for the request.
5204 */
5205 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5206 if (pDynLoad)
5207 {
5208 pDynLoad->cchRequest = cchFilename;
5209 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5210 }
5211 else
5212 {
5213 KW_LOG(("LoadLibraryExA: Out of memory!\n"));
5214 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5215 return NULL;
5216 }
5217
5218 /*
5219 * Deal with resource / data DLLs.
5220 */
5221 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5222 | LOAD_LIBRARY_AS_DATAFILE
5223 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5224 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5225 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5226
5227 /*
5228 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5229 */
5230 if ( strnicmp(pszFilename, TUPLE("api-ms-")) == 0
5231 && kHlpIsFilenameOnly(pszFilename))
5232 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5233
5234 /*
5235 * Normal library loading.
5236 * We start by being very lazy and reusing the code for resolving imports.
5237 */
5238 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5239 if (!kHlpIsFilenameOnly(pszFilename))
5240 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5241 else
5242 {
5243 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5244 if (rc != 0)
5245 pMod = NULL;
5246 }
5247 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5248 {
5249 /* Enter it into the tool module table and dynamic link request cache. */
5250 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5251
5252 pDynLoad->pMod = pMod;
5253 pDynLoad->hmod = pMod->hOurMod;
5254
5255 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5256 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5257
5258 /*
5259 * Make sure it's initialized (need to link it first since DllMain may
5260 * use loader APIs).
5261 */
5262 rc = kwLdrModuleInitTree(pMod);
5263 if (rc == 0)
5264 {
5265 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5266 return pDynLoad->hmod;
5267 }
5268
5269 SetLastError(ERROR_DLL_INIT_FAILED);
5270 }
5271 else
5272 {
5273 KWFS_TODO();
5274 kHlpFree(pDynLoad);
5275 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5276 }
5277 return NULL;
5278}
5279
5280
5281/** Kernel32 - LoadLibraryExA() for native overloads */
5282static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5283{
5284 char szPath[1024];
5285 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5286
5287 /*
5288 * We may have to help resolved unqualified DLLs living in the executable directory.
5289 */
5290 if (kHlpIsFilenameOnly(pszFilename))
5291 {
5292 KSIZE cchFilename = kHlpStrLen(pszFilename);
5293 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5294 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5295 {
5296 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5297 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5298 if (kwFsPathExists(szPath))
5299 {
5300 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5301 pszFilename = szPath;
5302 }
5303 }
5304
5305 if (pszFilename != szPath)
5306 {
5307 KSIZE cchSuffix = 0;
5308 KBOOL fNeedSuffix = K_FALSE;
5309 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5310 while (*pszCur != '\0')
5311 {
5312 /* Find the end of the component */
5313 KSIZE cch = 0;
5314 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5315 cch++;
5316
5317 if ( cch > 0 /* wrong, but whatever */
5318 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5319 {
5320 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5321 if ( szPath[cch - 1] != ':'
5322 && szPath[cch - 1] != '/'
5323 && szPath[cch - 1] != '\\')
5324 *pszDst++ = '\\';
5325 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5326 if (fNeedSuffix)
5327 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5328 *pszDst = '\0';
5329
5330 if (kwFsPathExists(szPath))
5331 {
5332 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5333 pszFilename = szPath;
5334 break;
5335 }
5336 }
5337
5338 /* Advance */
5339 pszCur += cch;
5340 while (*pszCur == ';')
5341 pszCur++;
5342 }
5343 }
5344 }
5345
5346 return LoadLibraryExA(pszFilename, hFile, fFlags);
5347}
5348
5349
5350/** Kernel32 - LoadLibraryExW() */
5351static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5352{
5353 char szTmp[4096];
5354 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5355 if (cchTmp < sizeof(szTmp))
5356 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5357
5358 KWFS_TODO();
5359 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5360 return NULL;
5361}
5362
5363/** Kernel32 - LoadLibraryA() */
5364static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5365{
5366 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5367}
5368
5369
5370/** Kernel32 - LoadLibraryW() */
5371static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5372{
5373 char szTmp[4096];
5374 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5375 if (cchTmp < sizeof(szTmp))
5376 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5377 KWFS_TODO();
5378 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5379 return NULL;
5380}
5381
5382
5383/** Kernel32 - FreeLibrary() */
5384static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5385{
5386 /* Ignored, we like to keep everything loaded. */
5387 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5388 return TRUE;
5389}
5390
5391
5392/** Worker for GetModuleHandleA/W for handling cached modules. */
5393static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5394{
5395 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5396 if (hmod)
5397 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5398 hmod, g_aGetModuleHandleCache[i].pszName));
5399 else
5400 {
5401 /*
5402 * The first time around we have to make sure we have a module table
5403 * entry for it, if not we add one. We need to add it to the tools
5404 * module list to for it to work.
5405 */
5406 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
5407 g_aGetModuleHandleCache[i].fAlwaysPresent);
5408 if (pMod)
5409 {
5410 hmod = pMod->hOurMod;
5411 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5412 {
5413 kwToolAddModule(g_Sandbox.pTool, pMod);
5414 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5415 hmod, g_aGetModuleHandleCache[i].pszName));
5416 }
5417 else
5418 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5419 hmod, g_aGetModuleHandleCache[i].pszName));
5420
5421 }
5422 }
5423 return hmod;
5424}
5425
5426
5427/** Kernel32 - GetModuleHandleA() */
5428static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5429{
5430 KSIZE i;
5431 KSIZE cchModule;
5432 PKWDYNLOAD pDynLoad;
5433 KSIZE cchSuffix;
5434 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5435 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5436
5437 /*
5438 * The executable.
5439 */
5440 if (pszModule == NULL)
5441 {
5442 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5443 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5444 }
5445
5446 /*
5447 * If no path of suffix, pretend it ends with .DLL.
5448 */
5449 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5450
5451 /*
5452 * Cache of system modules we've seen queried.
5453 */
5454 cchModule = kHlpStrLen(pszModule);
5455 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5456 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5457 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5458 || ( cchSuffix > 0
5459 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5460 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5461 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5462 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5463
5464 /*
5465 * Modules we've dynamically loaded.
5466 */
5467 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5468 if (pDynLoad->pMod)
5469 {
5470 const char *pszPath = pDynLoad->pMod->pszPath;
5471 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
5472 if ( stricmp(pszPath, pszModule) == 0
5473 || stricmp(pszName, pszModule) == 0
5474 || ( cchSuffix > 0
5475 && strnicmp(pszName, pszModule, cchModule) == 0
5476 && stricmp(&pszName[cchModule], ".dll") == 0))
5477 {
5478 if ( pDynLoad->pMod->fNative
5479 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5480 {
5481 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
5482 return pDynLoad->hmod;
5483 }
5484 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
5485 SetLastError(ERROR_MOD_NOT_FOUND);
5486 return NULL;
5487 }
5488 }
5489
5490 /*
5491 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5492 * to and go via the g_aGetModuleHandleCache cache.
5493 */
5494 if (strnicmp(pszModule, "api-ms-win-", 11) == 0)
5495 {
5496 HMODULE hmod = GetModuleHandleA(pszModule);
5497 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
5498 if (hmod)
5499 {
5500 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5501 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
5502 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5503 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
5504 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5505 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
5506 }
5507 else
5508 dwErr = GetLastError();
5509 }
5510
5511 kwErrPrintf("pszModule=%s\n", pszModule);
5512 KWFS_TODO();
5513 SetLastError(ERROR_MOD_NOT_FOUND);
5514 return NULL;
5515}
5516
5517
5518/** Kernel32 - GetModuleHandleW() */
5519static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
5520{
5521 KSIZE i;
5522 KSIZE cwcModule;
5523 PKWDYNLOAD pDynLoad;
5524 KSIZE cwcSuffix;
5525 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5526 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5527
5528 /*
5529 * The executable.
5530 */
5531 if (pwszModule == NULL)
5532 {
5533 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5534 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5535 }
5536
5537 /*
5538 * If no path of suffix, pretend it ends with .DLL.
5539 */
5540 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
5541
5542 /*
5543 * Cache of system modules we've seen queried.
5544 */
5545 cwcModule = kwUtf16Len(pwszModule);
5546 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5547 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
5548 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5549 || ( cwcSuffix > 0
5550 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
5551 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
5552 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
5553 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5554
5555 /*
5556 * Modules we've dynamically loaded.
5557 */
5558 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5559 if (pDynLoad->pMod)
5560 {
5561 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
5562 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
5563 if ( _wcsicmp(pwszPath, pwszModule) == 0
5564 || _wcsicmp(pwszName, pwszModule) == 0
5565 || ( cwcSuffix
5566 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
5567 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
5568 {
5569 if ( pDynLoad->pMod->fNative
5570 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5571 {
5572 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5573 return pDynLoad->hmod;
5574 }
5575 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
5576 SetLastError(ERROR_MOD_NOT_FOUND);
5577 return NULL;
5578 }
5579 }
5580
5581 /*
5582 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5583 * to and go via the g_aGetModuleHandleCache cache.
5584 */
5585 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
5586 {
5587 HMODULE hmod = GetModuleHandleW(pwszModule);
5588 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
5589 if (hmod)
5590 {
5591 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5592 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
5593 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5594 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
5595 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5596 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
5597 }
5598 else
5599 dwErr = GetLastError();
5600 }
5601
5602 kwErrPrintf("pwszModule=%ls\n", pwszModule);
5603 KWFS_TODO();
5604 SetLastError(dwErr);
5605 return NULL;
5606}
5607
5608
5609/** Used to debug dynamically resolved procedures. */
5610static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5611{
5612#ifdef _MSC_VER
5613 __debugbreak();
5614#else
5615 KWFS_TODO();
5616#endif
5617 return -1;
5618}
5619
5620
5621#ifndef NDEBUG
5622/*
5623 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5624 */
5625# if K_ARCH == K_ARCH_X86_32
5626static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5627# else
5628static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5629# endif
5630typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5631typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5632typedef struct KWCXINTERCEPTORENTRY
5633{
5634 PFNINVOKECOMPILERPASSW pfnOrg;
5635 PKWMODULE pModule;
5636 PFNINVOKECOMPILERPASSW pfnWrap;
5637} KWCXINTERCEPTORENTRY;
5638
5639static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5640 KWCXINTERCEPTORENTRY *pEntry)
5641{
5642 int i;
5643 KIPTR rcExit;
5644 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5645 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5646 for (i = 0; i < cArgs; i++)
5647 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5648
5649 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5650
5651 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5652 return rcExit;
5653}
5654
5655static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
5656static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
5657static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
5658
5659static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
5660{
5661 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
5662 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
5663 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
5664};
5665
5666static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5667{
5668 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
5669}
5670
5671static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5672{
5673 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
5674}
5675
5676static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5677{
5678 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
5679}
5680
5681#endif /* !NDEBUG */
5682
5683
5684/** Kernel32 - GetProcAddress() */
5685static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
5686{
5687 KSIZE i;
5688 PKWMODULE pMod;
5689 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5690
5691 /*
5692 * Try locate the module.
5693 */
5694 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5695 if (pMod)
5696 {
5697 KLDRADDR uValue;
5698 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
5699 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
5700 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
5701 KU32_MAX /*iSymbol*/,
5702 pszProc,
5703 kHlpStrLen(pszProc),
5704 NULL /*pszVersion*/,
5705 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
5706 &uValue,
5707 NULL /*pfKind*/);
5708 if (rc == 0)
5709 {
5710 //static int s_cDbgGets = 0;
5711 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
5712 KU32 i = g_cSandboxGetProcReplacements;
5713 while (i-- > 0)
5714 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
5715 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
5716 {
5717 if ( !g_aSandboxGetProcReplacements[i].pszModule
5718 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
5719 {
5720 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
5721 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
5722 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
5723 {
5724 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
5725 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5726 }
5727 kwLdrModuleRelease(pMod);
5728 return (FARPROC)(KUPTR)uValue;
5729 }
5730 }
5731
5732#ifndef NDEBUG
5733 /* Intercept the compiler pass method, dumping arguments. */
5734 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
5735 {
5736 KU32 i;
5737 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
5738 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
5739 {
5740 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5741 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
5742 break;
5743 }
5744 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
5745 while (i-- > 0)
5746 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
5747 {
5748 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
5749 g_aCxInterceptorEntries[i].pModule = pMod;
5750 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5751 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
5752 break;
5753 }
5754 }
5755#endif
5756 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5757 kwLdrModuleRelease(pMod);
5758 //s_cDbgGets++;
5759 //if (s_cGets >= 3)
5760 // return (FARPROC)kwSandbox_BreakIntoDebugger;
5761 return (FARPROC)(KUPTR)uValue;
5762 }
5763
5764 KWFS_TODO();
5765 SetLastError(ERROR_PROC_NOT_FOUND);
5766 kwLdrModuleRelease(pMod);
5767 return NULL;
5768 }
5769
5770 /*
5771 * Hmm... could be a cached module-by-name.
5772 */
5773 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5774 if (g_aGetModuleHandleCache[i].hmod == hmod)
5775 return GetProcAddress(hmod, pszProc);
5776
5777 KWFS_TODO();
5778 return GetProcAddress(hmod, pszProc);
5779}
5780
5781
5782/** Kernel32 - GetModuleFileNameA() */
5783static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
5784{
5785 PKWMODULE pMod;
5786 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5787
5788 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5789 if (pMod != NULL)
5790 {
5791 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
5792 kwLdrModuleRelease(pMod);
5793 return cbRet;
5794 }
5795 KWFS_TODO();
5796 return 0;
5797}
5798
5799
5800/** Kernel32 - GetModuleFileNameW() */
5801static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
5802{
5803 PKWMODULE pMod;
5804 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5805
5806 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5807 if (pMod)
5808 {
5809 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
5810 kwLdrModuleRelease(pMod);
5811 return cwcRet;
5812 }
5813
5814 KWFS_TODO();
5815 return 0;
5816}
5817
5818
5819/** NtDll - RtlPcToFileHeader
5820 * This is necessary for msvcr100.dll!CxxThrowException. */
5821static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
5822{
5823 PVOID pvRet;
5824
5825 /*
5826 * Do a binary lookup of the module table for the current tool.
5827 * This will give us a
5828 */
5829 if (g_Sandbox.fRunning)
5830 {
5831 KUPTR const uPC = (KUPTR)pvPC;
5832 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
5833 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
5834 KU32 i;
5835 if (iEnd)
5836 {
5837 KU32 iStart = 0;
5838 i = iEnd / 2;
5839 for (;;)
5840 {
5841 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
5842 if (uPC < uHModThis)
5843 {
5844 iEnd = i;
5845 if (iStart < i)
5846 { }
5847 else
5848 break;
5849 }
5850 else if (uPC != uHModThis)
5851 {
5852 iStart = ++i;
5853 if (i < iEnd)
5854 { }
5855 else
5856 break;
5857 }
5858 else
5859 {
5860 /* This isn't supposed to happen. */
5861 break;
5862 }
5863
5864 i = iStart + (iEnd - iStart) / 2;
5865 }
5866
5867 /* For reasons of simplicity (= copy & paste), we end up with the
5868 module after the one we're interested in here. */
5869 i--;
5870 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
5871 && papMods[i]->pLdrMod)
5872 {
5873 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
5874 if (uRvaPC < papMods[i]->cbImage)
5875 {
5876 *ppvImageBase = papMods[i]->hOurMod;
5877 pvRet = papMods[i]->hOurMod;
5878 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
5879 return pvRet;
5880 }
5881 }
5882 }
5883 else
5884 i = 0;
5885 }
5886
5887 /*
5888 * Call the regular API.
5889 */
5890 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
5891 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
5892 return pvRet;
5893}
5894
5895
5896/*
5897 *
5898 * File access APIs (for speeding them up).
5899 * File access APIs (for speeding them up).
5900 * File access APIs (for speeding them up).
5901 *
5902 */
5903
5904
5905/**
5906 * Converts a lookup error to a windows error code.
5907 *
5908 * @returns The windows error code.
5909 * @param enmError The lookup error.
5910 */
5911static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
5912{
5913 switch (enmError)
5914 {
5915 case KFSLOOKUPERROR_NOT_FOUND:
5916 case KFSLOOKUPERROR_NOT_DIR:
5917 return ERROR_FILE_NOT_FOUND;
5918
5919 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
5920 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
5921 return ERROR_PATH_NOT_FOUND;
5922
5923 case KFSLOOKUPERROR_PATH_TOO_LONG:
5924 return ERROR_FILENAME_EXCED_RANGE;
5925
5926 case KFSLOOKUPERROR_OUT_OF_MEMORY:
5927 return ERROR_NOT_ENOUGH_MEMORY;
5928
5929 default:
5930 return ERROR_PATH_NOT_FOUND;
5931 }
5932}
5933
5934#ifdef WITH_TEMP_MEMORY_FILES
5935
5936/**
5937 * Checks for a cl.exe temporary file.
5938 *
5939 * There are quite a bunch of these. They seems to be passing data between the
5940 * first and second compiler pass. Since they're on disk, they get subjected to
5941 * AV software screening and normal file consistency rules. So, not necessarily
5942 * a very efficient way of handling reasonably small amounts of data.
5943 *
5944 * We make the files live in virtual memory by intercepting their opening,
5945 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
5946 *
5947 * @returns K_TRUE / K_FALSE
5948 * @param pwszFilename The file name being accessed.
5949 */
5950static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
5951{
5952 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
5953 if (pwszName)
5954 {
5955 /* The name starts with _CL_... */
5956 if ( pwszName[0] == '_'
5957 && pwszName[1] == 'C'
5958 && pwszName[2] == 'L'
5959 && pwszName[3] == '_' )
5960 {
5961 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
5962 this check by just checking that it's alpha numerical ascii from here on. */
5963 wchar_t wc;
5964 pwszName += 4;
5965 while ((wc = *pwszName++) != '\0')
5966 {
5967 if (wc < 127 && iswalnum(wc))
5968 { /* likely */ }
5969 else
5970 return K_FALSE;
5971 }
5972 return K_TRUE;
5973 }
5974 }
5975 return K_FALSE;
5976}
5977
5978
5979/**
5980 * Creates a handle to a temporary file.
5981 *
5982 * @returns The handle on success.
5983 * INVALID_HANDLE_VALUE and SetLastError on failure.
5984 * @param pTempFile The temporary file.
5985 * @param dwDesiredAccess The desired access to the handle.
5986 * @param fMapping Whether this is a mapping (K_TRUE) or file
5987 * (K_FALSE) handle type.
5988 */
5989static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
5990{
5991 /*
5992 * Create a handle to the temporary file.
5993 */
5994 HANDLE hFile = INVALID_HANDLE_VALUE;
5995 HANDLE hProcSelf = GetCurrentProcess();
5996 if (DuplicateHandle(hProcSelf, hProcSelf,
5997 hProcSelf, &hFile,
5998 SYNCHRONIZE, FALSE,
5999 0 /*dwOptions*/))
6000 {
6001 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6002 if (pHandle)
6003 {
6004 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6005 pHandle->cRefs = 1;
6006 pHandle->offFile = 0;
6007 pHandle->hHandle = hFile;
6008 pHandle->dwDesiredAccess = dwDesiredAccess;
6009 pHandle->u.pTempFile = pTempFile;
6010 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6011 {
6012 pTempFile->cActiveHandles++;
6013 kHlpAssert(pTempFile->cActiveHandles >= 1);
6014 kHlpAssert(pTempFile->cActiveHandles <= 2);
6015 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6016 return hFile;
6017 }
6018
6019 kHlpFree(pHandle);
6020 }
6021 else
6022 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6023 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6024 }
6025 else
6026 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6027 return INVALID_HANDLE_VALUE;
6028}
6029
6030
6031static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
6032{
6033 HANDLE hFile;
6034 DWORD dwErr;
6035
6036 /*
6037 * Check if we've got an existing temp file.
6038 * ASSUME exact same path for now.
6039 */
6040 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6041 PKWFSTEMPFILE pTempFile;
6042 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6043 {
6044 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6045 if ( pTempFile->cwcPath == cwcFilename
6046 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6047 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6048 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6049 break;
6050 }
6051
6052 /*
6053 * Create a new temporary file instance if not found.
6054 */
6055 if (pTempFile == NULL)
6056 {
6057 KSIZE cbFilename;
6058
6059 switch (dwCreationDisposition)
6060 {
6061 case CREATE_ALWAYS:
6062 case OPEN_ALWAYS:
6063 dwErr = NO_ERROR;
6064 break;
6065
6066 case CREATE_NEW:
6067 kHlpAssertFailed();
6068 SetLastError(ERROR_ALREADY_EXISTS);
6069 return INVALID_HANDLE_VALUE;
6070
6071 case OPEN_EXISTING:
6072 case TRUNCATE_EXISTING:
6073 kHlpAssertFailed();
6074 SetLastError(ERROR_FILE_NOT_FOUND);
6075 return INVALID_HANDLE_VALUE;
6076
6077 default:
6078 kHlpAssertFailed();
6079 SetLastError(ERROR_INVALID_PARAMETER);
6080 return INVALID_HANDLE_VALUE;
6081 }
6082
6083 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6084 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6085 if (pTempFile)
6086 {
6087 pTempFile->cwcPath = (KU16)cwcFilename;
6088 pTempFile->cbFile = 0;
6089 pTempFile->cbFileAllocated = 0;
6090 pTempFile->cActiveHandles = 0;
6091 pTempFile->cMappings = 0;
6092 pTempFile->cSegs = 0;
6093 pTempFile->paSegs = NULL;
6094 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6095
6096 pTempFile->pNext = g_Sandbox.pTempFileHead;
6097 g_Sandbox.pTempFileHead = pTempFile;
6098 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6099 }
6100 else
6101 {
6102 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6103 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6104 return INVALID_HANDLE_VALUE;
6105 }
6106 }
6107 else
6108 {
6109 switch (dwCreationDisposition)
6110 {
6111 case OPEN_EXISTING:
6112 dwErr = NO_ERROR;
6113 break;
6114 case OPEN_ALWAYS:
6115 dwErr = ERROR_ALREADY_EXISTS ;
6116 break;
6117
6118 case TRUNCATE_EXISTING:
6119 case CREATE_ALWAYS:
6120 kHlpAssertFailed();
6121 pTempFile->cbFile = 0;
6122 dwErr = ERROR_ALREADY_EXISTS;
6123 break;
6124
6125 case CREATE_NEW:
6126 kHlpAssertFailed();
6127 SetLastError(ERROR_FILE_EXISTS);
6128 return INVALID_HANDLE_VALUE;
6129
6130 default:
6131 kHlpAssertFailed();
6132 SetLastError(ERROR_INVALID_PARAMETER);
6133 return INVALID_HANDLE_VALUE;
6134 }
6135 }
6136
6137 /*
6138 * Create a handle to the temporary file.
6139 */
6140 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6141 if (hFile != INVALID_HANDLE_VALUE)
6142 SetLastError(dwErr);
6143 return hFile;
6144}
6145
6146#endif /* WITH_TEMP_MEMORY_FILES */
6147
6148/**
6149 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6150 *
6151 * @returns K_TRUE if cacheable, K_FALSE if not.
6152 * @param wcFirst The first extension character.
6153 * @param wcSecond The second extension character.
6154 * @param wcThird The third extension character.
6155 * @param fAttrQuery Set if it's for an attribute query, clear if for
6156 * file creation.
6157 */
6158static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6159{
6160 /* C++ header without an extension or a directory. */
6161 if (wcFirst == '\0')
6162 {
6163 /** @todo exclude temporary files... */
6164 return K_TRUE;
6165 }
6166
6167 /* C Header: .h */
6168 if (wcFirst == 'h' || wcFirst == 'H')
6169 {
6170 if (wcSecond == '\0')
6171 return K_TRUE;
6172
6173 /* C++ Header: .hpp, .hxx */
6174 if ( (wcSecond == 'p' || wcSecond == 'P')
6175 && (wcThird == 'p' || wcThird == 'P'))
6176 return K_TRUE;
6177 if ( (wcSecond == 'x' || wcSecond == 'X')
6178 && (wcThird == 'x' || wcThird == 'X'))
6179 return K_TRUE;
6180 }
6181 /* Misc starting with i. */
6182 else if (wcFirst == 'i' || wcFirst == 'I')
6183 {
6184 if (wcSecond != '\0')
6185 {
6186 if (wcSecond == 'n' || wcSecond == 'N')
6187 {
6188 /* C++ inline header: .inl */
6189 if (wcThird == 'l' || wcThird == 'L')
6190 return K_TRUE;
6191
6192 /* Assembly include file: .inc */
6193 if (wcThird == 'c' || wcThird == 'C')
6194 return K_TRUE;
6195 }
6196 }
6197 }
6198 /* Assembly header: .mac */
6199 else if (wcFirst == 'm' || wcFirst == 'M')
6200 {
6201 if (wcSecond == 'a' || wcSecond == 'A')
6202 {
6203 if (wcThird == 'c' || wcThird == 'C')
6204 return K_TRUE;
6205 }
6206 }
6207#ifdef WITH_PCH_CACHING
6208 /* Precompiled header: .pch */
6209 else if (wcFirst == 'p' || wcFirst == 'P')
6210 {
6211 if (wcSecond == 'c' || wcSecond == 'C')
6212 {
6213 if (wcThird == 'h' || wcThird == 'H')
6214 return !g_Sandbox.fNoPchCaching;
6215 }
6216 }
6217#endif
6218#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6219 /* Linker - Object file: .obj */
6220 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6221 {
6222 if (wcSecond == 'b' || wcSecond == 'B')
6223 {
6224 if (wcThird == 'j' || wcThird == 'J')
6225 return K_TRUE;
6226 }
6227 }
6228#endif
6229 else if (fAttrQuery)
6230 {
6231 /* Dynamic link library: .dll */
6232 if (wcFirst == 'd' || wcFirst == 'D')
6233 {
6234 if (wcSecond == 'l' || wcSecond == 'L')
6235 {
6236 if (wcThird == 'l' || wcThird == 'L')
6237 return K_TRUE;
6238 }
6239 }
6240 /* Executable file: .exe */
6241 else if (wcFirst == 'e' || wcFirst == 'E')
6242 {
6243 if (wcSecond == 'x' || wcSecond == 'X')
6244 {
6245 if (wcThird == 'e' || wcThird == 'E')
6246 return K_TRUE;
6247 }
6248 }
6249 /* Response file: .rsp */
6250 else if (wcFirst == 'r' || wcFirst == 'R')
6251 {
6252 if (wcSecond == 's' || wcSecond == 'S')
6253 {
6254 if (wcThird == 'p' || wcThird == 'P')
6255 return !g_Sandbox.fNoPchCaching;
6256 }
6257 }
6258 /* Linker: */
6259 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6260 {
6261 /* Object file: .obj */
6262 if (wcFirst == 'o' || wcFirst == 'O')
6263 {
6264 if (wcSecond == 'b' || wcSecond == 'B')
6265 {
6266 if (wcThird == 'j' || wcThird == 'J')
6267 return K_TRUE;
6268 }
6269 }
6270 /* Library file: .lib */
6271 else if (wcFirst == 'l' || wcFirst == 'L')
6272 {
6273 if (wcSecond == 'i' || wcSecond == 'I')
6274 {
6275 if (wcThird == 'b' || wcThird == 'B')
6276 return K_TRUE;
6277 }
6278 }
6279 /* Linker definition file: .def */
6280 else if (wcFirst == 'd' || wcFirst == 'D')
6281 {
6282 if (wcSecond == 'e' || wcSecond == 'E')
6283 {
6284 if (wcThird == 'f' || wcThird == 'F')
6285 return K_TRUE;
6286 }
6287 }
6288 }
6289 }
6290
6291 return K_FALSE;
6292}
6293
6294
6295/**
6296 * Checks if the file extension indicates that the file/dir is something we
6297 * ought to cache.
6298 *
6299 * @returns K_TRUE if cachable, K_FALSE if not.
6300 * @param pszExt The kHlpGetExt result.
6301 * @param fAttrQuery Set if it's for an attribute query, clear if for
6302 * file creation.
6303 */
6304static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6305{
6306 wchar_t const wcFirst = *pszExt;
6307 if (wcFirst)
6308 {
6309 wchar_t const wcSecond = pszExt[1];
6310 if (wcSecond)
6311 {
6312 wchar_t const wcThird = pszExt[2];
6313 if (pszExt[3] == '\0')
6314 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6315 return K_FALSE;
6316 }
6317 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6318 }
6319 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6320}
6321
6322
6323/**
6324 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6325 * should be cached.
6326 *
6327 * @returns K_TRUE if cachable, K_FALSE if not.
6328 * @param pwszPath The UTF-16 path to examine.
6329 * @param fAttrQuery Set if it's for an attribute query, clear if for
6330 * file creation.
6331 */
6332static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6333{
6334 KSIZE cwcExt;
6335 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6336 switch (cwcExt)
6337 {
6338 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6339 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6340 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6341 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6342 }
6343 return K_FALSE;
6344}
6345
6346
6347
6348/**
6349 * Creates a new
6350 *
6351 * @returns
6352 * @param pFsObj .
6353 * @param pwszFilename .
6354 */
6355static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6356{
6357 HANDLE hFile;
6358 MY_IO_STATUS_BLOCK Ios;
6359 MY_OBJECT_ATTRIBUTES ObjAttr;
6360 MY_UNICODE_STRING UniStr;
6361 MY_NTSTATUS rcNt;
6362 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6363
6364 /*
6365 * Open the file relative to the parent directory.
6366 */
6367 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6368 kHlpAssert(pFsObj->pParent);
6369 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6370
6371 Ios.Information = -1;
6372 Ios.u.Status = -1;
6373
6374 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6375 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6376 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6377
6378 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6379
6380 rcNt = g_pfnNtCreateFile(&hFile,
6381 GENERIC_READ | SYNCHRONIZE,
6382 &ObjAttr,
6383 &Ios,
6384 NULL, /*cbFileInitialAlloc */
6385 FILE_ATTRIBUTE_NORMAL,
6386 FILE_SHARE_READ,
6387 FILE_OPEN,
6388 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6389 NULL, /*pEaBuffer*/
6390 0); /*cbEaBuffer*/
6391 if (MY_NT_SUCCESS(rcNt))
6392 {
6393 /*
6394 * Read the whole file into memory.
6395 */
6396 LARGE_INTEGER cbFile;
6397 if (GetFileSizeEx(hFile, &cbFile))
6398 {
6399 if ( cbFile.QuadPart >= 0
6400#ifdef WITH_PCH_CACHING
6401 && ( cbFile.QuadPart < 16*1024*1024
6402 || ( cbFile.QuadPart < 96*1024*1024
6403 && pFsObj->cchName > 4
6404 && !g_Sandbox.fNoPchCaching
6405 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
6406#endif
6407 )
6408 {
6409 KU32 cbCache = (KU32)cbFile.QuadPart;
6410 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
6411 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
6412 if (hMapping != NULL)
6413 {
6414 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
6415 if (pbCache)
6416 {
6417 /*
6418 * Create the cached file object.
6419 */
6420 PKFSWCACHEDFILE pCachedFile;
6421 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
6422 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
6423 sizeof(*pCachedFile) + cbPath);
6424 if (pCachedFile)
6425 {
6426 pCachedFile->hCached = hFile;
6427 pCachedFile->hSection = hMapping;
6428 pCachedFile->cbCached = cbCache;
6429 pCachedFile->pbCached = pbCache;
6430 pCachedFile->pFsObj = pFsObj;
6431 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
6432 kFsCacheObjRetain(pFsObj);
6433
6434 g_cReadCachedFiles++;
6435 g_cbReadCachedFiles += cbCache;
6436
6437 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
6438 return pCachedFile;
6439 }
6440
6441 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
6442 }
6443 else
6444 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
6445 CloseHandle(hMapping);
6446 }
6447 else
6448 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
6449 }
6450 else
6451 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
6452 }
6453 else
6454 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
6455 g_pfnNtClose(hFile);
6456 }
6457 else
6458 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
6459 return NULL;
6460}
6461
6462
6463/**
6464 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
6465 */
6466static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
6467 KBOOL fIsFileHandle, HANDLE *phFile)
6468{
6469 HANDLE hProcSelf = GetCurrentProcess();
6470 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
6471 hProcSelf, phFile,
6472 dwDesiredAccess, fInheritHandle,
6473 0 /*dwOptions*/))
6474 {
6475 /*
6476 * Create handle table entry for the duplicate handle.
6477 */
6478 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6479 if (pHandle)
6480 {
6481 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
6482 pHandle->cRefs = 1;
6483 pHandle->offFile = 0;
6484 pHandle->hHandle = *phFile;
6485 pHandle->dwDesiredAccess = dwDesiredAccess;
6486 pHandle->u.pCachedFile = pCachedFile;
6487 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
6488 return K_TRUE;
6489
6490 kHlpFree(pHandle);
6491 }
6492 else
6493 KWFS_LOG(("Out of memory for handle!\n"));
6494
6495 CloseHandle(*phFile);
6496 *phFile = INVALID_HANDLE_VALUE;
6497 }
6498 else
6499 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
6500 return K_FALSE;
6501}
6502
6503
6504/**
6505 * Kernel32 - Common code for CreateFileW and CreateFileA.
6506 */
6507static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
6508{
6509 *phFile = INVALID_HANDLE_VALUE;
6510
6511 /*
6512 * At the moment we only handle existing files.
6513 */
6514 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
6515 {
6516 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
6517 kHlpAssert(pFsObj->fHaveStats);
6518 if ( pCachedFile != NULL
6519 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
6520 {
6521 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
6522 return K_TRUE;
6523 }
6524 }
6525 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
6526
6527 /* Do fallback, please. */
6528 return K_FALSE;
6529}
6530
6531
6532/** Kernel32 - CreateFileA */
6533static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6534 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6535 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6536{
6537 HANDLE hFile;
6538
6539 /*
6540 * Check for include files and similar that we do read-only caching of.
6541 */
6542 if (dwCreationDisposition == FILE_OPEN_IF)
6543 {
6544 if ( dwDesiredAccess == GENERIC_READ
6545 || dwDesiredAccess == FILE_GENERIC_READ)
6546 {
6547 if (dwShareMode & FILE_SHARE_READ)
6548 {
6549 if ( !pSecAttrs
6550 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6551 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6552 {
6553 const char *pszExt = kHlpGetExt(pszFilename);
6554 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
6555 {
6556 KFSLOOKUPERROR enmError;
6557 PKFSOBJ pFsObj;
6558 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6559
6560 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
6561 if (pFsObj)
6562 {
6563 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6564 &hFile);
6565 kFsCacheObjRelease(g_pFsCache, pFsObj);
6566 if (fRc)
6567 {
6568 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6569 return hFile;
6570 }
6571 }
6572 /* These are for nasm and yasm header searching. Cache will already
6573 have checked the directories for the file, no need to call
6574 CreateFile to do it again. */
6575 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6576 {
6577 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6578 return INVALID_HANDLE_VALUE;
6579 }
6580 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6581 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6582 {
6583 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6584 return INVALID_HANDLE_VALUE;
6585 }
6586
6587 /* fallback */
6588 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6589 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6590 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
6591 return hFile;
6592 }
6593 }
6594 }
6595 }
6596 }
6597
6598 /*
6599 * Okay, normal.
6600 */
6601 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6602 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6603 if (hFile != INVALID_HANDLE_VALUE)
6604 {
6605 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6606 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6607 }
6608 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
6609 return hFile;
6610}
6611
6612
6613/** Kernel32 - CreateFileW */
6614static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6615 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6616 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6617{
6618 HANDLE hFile;
6619
6620#ifdef WITH_TEMP_MEMORY_FILES
6621 /*
6622 * Check for temporary files (cl.exe only).
6623 */
6624 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6625 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
6626 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
6627 && kwFsIsClTempFileW(pwszFilename))
6628 {
6629 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition);
6630 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
6631 return hFile;
6632 }
6633#endif
6634
6635 /*
6636 * Check for include files and similar that we do read-only caching of.
6637 */
6638 if (dwCreationDisposition == FILE_OPEN_IF)
6639 {
6640 if ( dwDesiredAccess == GENERIC_READ
6641 || dwDesiredAccess == FILE_GENERIC_READ)
6642 {
6643 if (dwShareMode & FILE_SHARE_READ)
6644 {
6645 if ( !pSecAttrs
6646 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6647 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6648 {
6649 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
6650 {
6651 KFSLOOKUPERROR enmError;
6652 PKFSOBJ pFsObj;
6653 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6654
6655 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6656 if (pFsObj)
6657 {
6658 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6659 &hFile);
6660 kFsCacheObjRelease(g_pFsCache, pFsObj);
6661 if (fRc)
6662 {
6663 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
6664 return hFile;
6665 }
6666 }
6667 /* These are for nasm and yasm style header searching. Cache will
6668 already have checked the directories for the file, no need to call
6669 CreateFile to do it again. */
6670 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6671 {
6672 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
6673 return INVALID_HANDLE_VALUE;
6674 }
6675 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6676 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6677 {
6678 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
6679 return INVALID_HANDLE_VALUE;
6680 }
6681
6682 /* fallback */
6683 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6684 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6685 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
6686 return hFile;
6687 }
6688 }
6689 else
6690 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
6691 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
6692 }
6693 else
6694 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
6695 }
6696 else
6697 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
6698 }
6699 else
6700 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
6701
6702 /*
6703 * Okay, normal.
6704 */
6705 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6706 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6707 if (hFile != INVALID_HANDLE_VALUE)
6708 {
6709 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6710 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6711 }
6712 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
6713 return hFile;
6714}
6715
6716
6717/** Kernel32 - SetFilePointer */
6718static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
6719{
6720 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6721 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6722 if (idxHandle < g_Sandbox.cHandles)
6723 {
6724 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6725 if (pHandle != NULL)
6726 {
6727 KU32 cbFile;
6728 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
6729 switch (pHandle->enmType)
6730 {
6731 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6732 cbFile = pHandle->u.pCachedFile->cbCached;
6733 break;
6734#ifdef WITH_TEMP_MEMORY_FILES
6735 case KWHANDLETYPE_TEMP_FILE:
6736 cbFile = pHandle->u.pTempFile->cbFile;
6737 break;
6738#endif
6739 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6740 case KWHANDLETYPE_OUTPUT_BUF:
6741 default:
6742 kHlpAssertFailed();
6743 SetLastError(ERROR_INVALID_FUNCTION);
6744 return INVALID_SET_FILE_POINTER;
6745 }
6746
6747 switch (dwMoveMethod)
6748 {
6749 case FILE_BEGIN:
6750 break;
6751 case FILE_CURRENT:
6752 offMove += pHandle->offFile;
6753 break;
6754 case FILE_END:
6755 offMove += cbFile;
6756 break;
6757 default:
6758 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6759 SetLastError(ERROR_INVALID_PARAMETER);
6760 return INVALID_SET_FILE_POINTER;
6761 }
6762 if (offMove >= 0)
6763 {
6764 if (offMove >= (KSSIZE)cbFile)
6765 {
6766 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6767 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6768 offMove = (KSSIZE)cbFile;
6769 /* For writable files, seeking beyond the end is fine, but check that we've got
6770 the type range for the request. */
6771 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
6772 {
6773 kHlpAssertMsgFailed(("%#llx\n", offMove));
6774 SetLastError(ERROR_SEEK);
6775 return INVALID_SET_FILE_POINTER;
6776 }
6777 }
6778 pHandle->offFile = (KU32)offMove;
6779 }
6780 else
6781 {
6782 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
6783 SetLastError(ERROR_NEGATIVE_SEEK);
6784 return INVALID_SET_FILE_POINTER;
6785 }
6786 if (pcbMoveHi)
6787 *pcbMoveHi = (KU64)offMove >> 32;
6788 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
6789 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6790 SetLastError(NO_ERROR);
6791 return (KU32)offMove;
6792 }
6793 }
6794 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
6795 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
6796}
6797
6798
6799/** Kernel32 - SetFilePointerEx */
6800static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
6801 DWORD dwMoveMethod)
6802{
6803 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6804 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6805 if (idxHandle < g_Sandbox.cHandles)
6806 {
6807 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6808 if (pHandle != NULL)
6809 {
6810 KI64 offMyMove = offMove.QuadPart;
6811 KU32 cbFile;
6812 switch (pHandle->enmType)
6813 {
6814 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6815 cbFile = pHandle->u.pCachedFile->cbCached;
6816 break;
6817#ifdef WITH_TEMP_MEMORY_FILES
6818 case KWHANDLETYPE_TEMP_FILE:
6819 cbFile = pHandle->u.pTempFile->cbFile;
6820 break;
6821#endif
6822 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6823 case KWHANDLETYPE_OUTPUT_BUF:
6824 default:
6825 kHlpAssertFailed();
6826 SetLastError(ERROR_INVALID_FUNCTION);
6827 return INVALID_SET_FILE_POINTER;
6828 }
6829
6830 switch (dwMoveMethod)
6831 {
6832 case FILE_BEGIN:
6833 break;
6834 case FILE_CURRENT:
6835 offMyMove += pHandle->offFile;
6836 break;
6837 case FILE_END:
6838 offMyMove += cbFile;
6839 break;
6840 default:
6841 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6842 SetLastError(ERROR_INVALID_PARAMETER);
6843 return INVALID_SET_FILE_POINTER;
6844 }
6845 if (offMyMove >= 0)
6846 {
6847 if (offMyMove >= (KSSIZE)cbFile)
6848 {
6849 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6850 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6851 offMyMove = (KSSIZE)cbFile;
6852 /* For writable files, seeking beyond the end is fine, but check that we've got
6853 the type range for the request. */
6854 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
6855 {
6856 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
6857 SetLastError(ERROR_SEEK);
6858 return INVALID_SET_FILE_POINTER;
6859 }
6860 }
6861 pHandle->offFile = (KU32)offMyMove;
6862 }
6863 else
6864 {
6865 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
6866 SetLastError(ERROR_NEGATIVE_SEEK);
6867 return INVALID_SET_FILE_POINTER;
6868 }
6869 if (poffNew)
6870 poffNew->QuadPart = offMyMove;
6871 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
6872 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6873 return TRUE;
6874 }
6875 }
6876 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
6877 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
6878}
6879
6880
6881/** Kernel32 - ReadFile */
6882static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
6883 LPOVERLAPPED pOverlapped)
6884{
6885 BOOL fRet;
6886 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6887 g_cReadFileCalls++;
6888 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6889 if (idxHandle < g_Sandbox.cHandles)
6890 {
6891 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6892 if (pHandle != NULL)
6893 {
6894 switch (pHandle->enmType)
6895 {
6896 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6897 {
6898 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
6899 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
6900 if (cbActually > cbToRead)
6901 cbActually = cbToRead;
6902
6903#ifdef WITH_HASH_MD5_CACHE
6904 if (g_Sandbox.pHashHead)
6905 {
6906 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
6907 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
6908 g_Sandbox.LastHashRead.cbRead = cbActually;
6909 g_Sandbox.LastHashRead.pvRead = pvBuffer;
6910 }
6911#endif
6912
6913 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
6914 pHandle->offFile += cbActually;
6915
6916 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6917 *pcbActuallyRead = cbActually;
6918
6919 g_cbReadFileFromReadCached += cbActually;
6920 g_cbReadFileTotal += cbActually;
6921 g_cReadFileFromReadCached++;
6922
6923 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
6924 return TRUE;
6925 }
6926
6927#ifdef WITH_TEMP_MEMORY_FILES
6928 case KWHANDLETYPE_TEMP_FILE:
6929 {
6930 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6931 KU32 cbActually;
6932 if (pHandle->offFile < pTempFile->cbFile)
6933 {
6934 cbActually = pTempFile->cbFile - pHandle->offFile;
6935 if (cbActually > cbToRead)
6936 cbActually = cbToRead;
6937
6938 /* Copy the data. */
6939 if (cbActually > 0)
6940 {
6941 KU32 cbLeft;
6942 KU32 offSeg;
6943 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
6944
6945 /* Locate the segment containing the byte at offFile. */
6946 KU32 iSeg = pTempFile->cSegs - 1;
6947 kHlpAssert(pTempFile->cSegs > 0);
6948 while (paSegs[iSeg].offData > pHandle->offFile)
6949 iSeg--;
6950
6951 /* Copy out the data. */
6952 cbLeft = cbActually;
6953 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6954 for (;;)
6955 {
6956 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6957 if (cbAvail >= cbLeft)
6958 {
6959 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
6960 break;
6961 }
6962
6963 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
6964 cbLeft -= cbAvail;
6965 offSeg = 0;
6966 iSeg++;
6967 kHlpAssert(iSeg < pTempFile->cSegs);
6968 }
6969
6970 /* Update the file offset. */
6971 pHandle->offFile += cbActually;
6972 }
6973 }
6974 /* Read does not commit file space, so return zero bytes. */
6975 else
6976 cbActually = 0;
6977
6978 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6979 *pcbActuallyRead = cbActually;
6980
6981 g_cbReadFileTotal += cbActually;
6982 g_cbReadFileFromInMemTemp += cbActually;
6983 g_cReadFileFromInMemTemp++;
6984
6985 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
6986 return TRUE;
6987 }
6988#endif /* WITH_TEMP_MEMORY_FILES */
6989
6990 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6991 case KWHANDLETYPE_OUTPUT_BUF:
6992 default:
6993 kHlpAssertFailed();
6994 SetLastError(ERROR_INVALID_FUNCTION);
6995 *pcbActuallyRead = 0;
6996 return FALSE;
6997 }
6998 }
6999 }
7000
7001 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7002 if (fRet && pcbActuallyRead)
7003 g_cbReadFileTotal += *pcbActuallyRead;
7004 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7005 return fRet;
7006}
7007
7008
7009/** Kernel32 - ReadFileEx */
7010static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7011 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7012{
7013 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7014 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7015 if (idxHandle < g_Sandbox.cHandles)
7016 {
7017 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7018 if (pHandle != NULL)
7019 {
7020 kHlpAssertFailed();
7021 }
7022 }
7023
7024 KWFS_LOG(("ReadFile(%p)\n", hFile));
7025 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7026}
7027
7028#ifdef WITH_STD_OUT_ERR_BUFFERING
7029
7030/**
7031 * Write something to a handle, making sure everything is actually written.
7032 *
7033 * @param hHandle Where to write it to.
7034 * @param pchBuf What to write
7035 * @param cchToWrite How much to write.
7036 */
7037static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7038{
7039 if (cchToWrite > 0)
7040 {
7041 DWORD cchWritten = 0;
7042 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7043 {
7044 if (cchWritten == cchToWrite)
7045 { /* likely */ }
7046 else
7047 {
7048 do
7049 {
7050 pchBuf += cchWritten;
7051 cchToWrite -= cchWritten;
7052 cchWritten = 0;
7053 } while ( cchToWrite > 0
7054 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7055 }
7056 }
7057 else
7058 kHlpAssertFailed();
7059 }
7060}
7061
7062
7063/**
7064 * Worker for WriteFile when the output isn't going to the console.
7065 *
7066 * @param pSandbox The sandbox.
7067 * @param pOutBuf The output buffer.
7068 * @param pchBuffer What to write.
7069 * @param cchToWrite How much to write.
7070 */
7071static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7072{
7073 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7074 { /* likely */ }
7075 else
7076 {
7077 /* No realloc, max size is 64KB. */
7078 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7079 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7080 if (!pOutBuf->u.Fully.pchBuf)
7081 {
7082 while ( !pOutBuf->u.Fully.pchBuf
7083 && pOutBuf->u.Fully.cchBufAlloc > 64)
7084 {
7085 pOutBuf->u.Fully.cchBufAlloc /= 2;
7086 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7087 }
7088 if (!pOutBuf->u.Fully.pchBuf)
7089 {
7090 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7091 pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
7092 }
7093 }
7094 }
7095
7096 /*
7097 * Special case: Output ends with newline and fits in the buffer.
7098 */
7099 if ( cchToWrite > 1
7100 && pchBuffer[cchToWrite - 1] == '\n'
7101 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7102 {
7103 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7104 pOutBuf->u.Fully.cchBuf += cchToWrite;
7105 }
7106 else
7107 {
7108 /*
7109 * Work thru the text line by line, flushing the buffer when
7110 * appropriate. The buffer is not a line buffer here, it's a
7111 * full buffer.
7112 */
7113 while (cchToWrite > 0)
7114 {
7115 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7116 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7117 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7118 {
7119 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7120 pOutBuf->u.Fully.cchBuf += cchLine;
7121 }
7122 /*
7123 * Option one: Flush the buffer and the current line.
7124 *
7125 * We choose this one when the line won't ever fit, or when we have
7126 * an incomplete line in the buffer.
7127 */
7128 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7129 || pOutBuf->u.Fully.cchBuf == 0
7130 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7131 {
7132 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7133 if (pOutBuf->u.Fully.cchBuf > 0)
7134 {
7135 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7136 pOutBuf->u.Fully.cchBuf = 0;
7137 }
7138 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7139 }
7140 /*
7141 * Option two: Only flush the lines in the buffer.
7142 */
7143 else
7144 {
7145 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7146 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7147 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7148 pOutBuf->u.Fully.cchBuf = cchLine;
7149 }
7150
7151 /* advance */
7152 pchBuffer += cchLine;
7153 cchToWrite -= cchLine;
7154 }
7155 }
7156}
7157
7158#endif /* WITH_STD_OUT_ERR_BUFFERING */
7159
7160#ifdef WITH_TEMP_MEMORY_FILES
7161static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7162{
7163 KU32 cbMinFile = offFile + cbNeeded;
7164 if (cbMinFile >= offFile)
7165 {
7166 /* Calc how much space we've already allocated and */
7167 if (cbMinFile <= pTempFile->cbFileAllocated)
7168 return K_TRUE;
7169
7170 /* Grow the file. */
7171 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7172 {
7173 int rc;
7174 KU32 cSegs = pTempFile->cSegs;
7175 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7176 do
7177 {
7178 /* grow the segment array? */
7179 if ((cSegs % 16) == 0)
7180 {
7181 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7182 if (!pvNew)
7183 return K_FALSE;
7184 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7185 }
7186
7187 /* Use page alloc here to simplify mapping later. */
7188 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7189 if (rc == 0)
7190 { /* likely */ }
7191 else
7192 {
7193 cbNewSeg = 64*1024;
7194 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7195 if (rc != 0)
7196 {
7197 kHlpAssertFailed();
7198 return K_FALSE;
7199 }
7200 }
7201 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7202 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7203 pTempFile->cbFileAllocated += cbNewSeg;
7204 pTempFile->cSegs = ++cSegs;
7205
7206 } while (pTempFile->cbFileAllocated < cbMinFile);
7207
7208 return K_TRUE;
7209 }
7210 }
7211
7212 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7213 return K_FALSE;
7214}
7215#endif /* WITH_TEMP_MEMORY_FILES */
7216
7217
7218#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7219/** Kernel32 - WriteFile */
7220static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7221 LPOVERLAPPED pOverlapped)
7222{
7223 BOOL fRet;
7224 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7225 g_cWriteFileCalls++;
7226 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7227 if (idxHandle < g_Sandbox.cHandles)
7228 {
7229 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7230 if (pHandle != NULL)
7231 {
7232 switch (pHandle->enmType)
7233 {
7234# ifdef WITH_TEMP_MEMORY_FILES
7235 case KWHANDLETYPE_TEMP_FILE:
7236 {
7237 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7238
7239 kHlpAssert(!pOverlapped);
7240 kHlpAssert(pcbActuallyWritten);
7241
7242 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7243 {
7244 KU32 cbLeft;
7245 KU32 offSeg;
7246
7247 /* Locate the segment containing the byte at offFile. */
7248 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7249 KU32 iSeg = pTempFile->cSegs - 1;
7250 kHlpAssert(pTempFile->cSegs > 0);
7251 while (paSegs[iSeg].offData > pHandle->offFile)
7252 iSeg--;
7253
7254 /* Copy in the data. */
7255 cbLeft = cbToWrite;
7256 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7257 for (;;)
7258 {
7259 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7260 if (cbAvail >= cbLeft)
7261 {
7262 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7263 break;
7264 }
7265
7266 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7267 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7268 cbLeft -= cbAvail;
7269 offSeg = 0;
7270 iSeg++;
7271 kHlpAssert(iSeg < pTempFile->cSegs);
7272 }
7273
7274 /* Update the file offset. */
7275 pHandle->offFile += cbToWrite;
7276 if (pHandle->offFile > pTempFile->cbFile)
7277 pTempFile->cbFile = pHandle->offFile;
7278
7279 *pcbActuallyWritten = cbToWrite;
7280
7281 g_cbWriteFileTotal += cbToWrite;
7282 g_cbWriteFileToInMemTemp += cbToWrite;
7283 g_cWriteFileToInMemTemp++;
7284
7285 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7286 return TRUE;
7287 }
7288
7289 kHlpAssertFailed();
7290 *pcbActuallyWritten = 0;
7291 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7292 return FALSE;
7293 }
7294# endif
7295
7296 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7297 kHlpAssertFailed();
7298 SetLastError(ERROR_ACCESS_DENIED);
7299 *pcbActuallyWritten = 0;
7300 return FALSE;
7301
7302# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7303 /*
7304 * Standard output & error.
7305 */
7306 case KWHANDLETYPE_OUTPUT_BUF:
7307 {
7308 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7309 if (pOutBuf->fIsConsole)
7310 {
7311 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7312 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7313 }
7314 else
7315 {
7316# ifdef WITH_STD_OUT_ERR_BUFFERING
7317 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7318 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7319 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7320# else
7321 kHlpAssertFailed();
7322# endif
7323 }
7324 if (pcbActuallyWritten)
7325 *pcbActuallyWritten = cbToWrite;
7326 g_cbWriteFileTotal += cbToWrite;
7327 return TRUE;
7328 }
7329# endif
7330
7331 default:
7332 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7333 kHlpAssertFailed();
7334 SetLastError(ERROR_INVALID_FUNCTION);
7335 *pcbActuallyWritten = 0;
7336 return FALSE;
7337 }
7338 }
7339 }
7340
7341 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7342 if (fRet && pcbActuallyWritten)
7343 g_cbWriteFileTotal += *pcbActuallyWritten;
7344 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7345 return fRet;
7346}
7347
7348
7349/** Kernel32 - WriteFileEx */
7350static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7351 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7352{
7353 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7354 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7355 if (idxHandle < g_Sandbox.cHandles)
7356 {
7357 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7358 if (pHandle != NULL)
7359 {
7360 kHlpAssertFailed();
7361 }
7362 }
7363
7364 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
7365 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
7366}
7367
7368#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
7369
7370#ifdef WITH_TEMP_MEMORY_FILES
7371
7372/** Kernel32 - SetEndOfFile; */
7373static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
7374{
7375 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7376 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7377 if (idxHandle < g_Sandbox.cHandles)
7378 {
7379 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7380 if (pHandle != NULL)
7381 {
7382 switch (pHandle->enmType)
7383 {
7384 case KWHANDLETYPE_TEMP_FILE:
7385 {
7386 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7387 if ( pHandle->offFile > pTempFile->cbFile
7388 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
7389 {
7390 kHlpAssertFailed();
7391 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7392 return FALSE;
7393 }
7394
7395 pTempFile->cbFile = pHandle->offFile;
7396 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
7397 return TRUE;
7398 }
7399
7400 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7401 kHlpAssertFailed();
7402 SetLastError(ERROR_ACCESS_DENIED);
7403 return FALSE;
7404
7405 case KWHANDLETYPE_OUTPUT_BUF:
7406 kHlpAssertFailed();
7407 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
7408 return FALSE;
7409
7410 default:
7411 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7412 kHlpAssertFailed();
7413 SetLastError(ERROR_INVALID_FUNCTION);
7414 return FALSE;
7415 }
7416 }
7417 }
7418
7419 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
7420 return SetEndOfFile(hFile);
7421}
7422
7423
7424/** Kernel32 - GetFileType */
7425static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
7426{
7427 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7428 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7429 if (idxHandle < g_Sandbox.cHandles)
7430 {
7431 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7432 if (pHandle != NULL)
7433 {
7434 switch (pHandle->enmType)
7435 {
7436 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7437 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
7438 return FILE_TYPE_DISK;
7439
7440 case KWHANDLETYPE_TEMP_FILE:
7441 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
7442 return FILE_TYPE_DISK;
7443
7444 case KWHANDLETYPE_OUTPUT_BUF:
7445 {
7446 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7447 DWORD fRet;
7448 if (pOutBuf->fFileType != KU8_MAX)
7449 {
7450 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
7451 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
7452 }
7453 else
7454 {
7455 fRet = GetFileType(hFile);
7456 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
7457 }
7458 return fRet;
7459 }
7460
7461 }
7462 }
7463 }
7464
7465 KWFS_LOG(("GetFileType(%p)\n", hFile));
7466 return GetFileType(hFile);
7467}
7468
7469
7470/** Kernel32 - GetFileSize */
7471static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
7472{
7473 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7474 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7475 if (idxHandle < g_Sandbox.cHandles)
7476 {
7477 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7478 if (pHandle != NULL)
7479 {
7480 if (pcbHighDword)
7481 *pcbHighDword = 0;
7482 SetLastError(NO_ERROR);
7483 switch (pHandle->enmType)
7484 {
7485 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7486 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7487 return pHandle->u.pCachedFile->cbCached;
7488
7489 case KWHANDLETYPE_TEMP_FILE:
7490 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7491 return pHandle->u.pTempFile->cbFile;
7492
7493 case KWHANDLETYPE_OUTPUT_BUF:
7494 /* do default */
7495 break;
7496
7497 default:
7498 kHlpAssertFailed();
7499 SetLastError(ERROR_INVALID_FUNCTION);
7500 return INVALID_FILE_SIZE;
7501 }
7502 }
7503 }
7504
7505 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
7506 return GetFileSize(hFile, pcbHighDword);
7507}
7508
7509
7510/** Kernel32 - GetFileSizeEx */
7511static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
7512{
7513 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7514 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7515 if (idxHandle < g_Sandbox.cHandles)
7516 {
7517 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7518 if (pHandle != NULL)
7519 {
7520 switch (pHandle->enmType)
7521 {
7522 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7523 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7524 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
7525 return TRUE;
7526
7527 case KWHANDLETYPE_TEMP_FILE:
7528 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7529 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
7530 return TRUE;
7531
7532 case KWHANDLETYPE_OUTPUT_BUF:
7533 /* do default */
7534 break;
7535
7536 default:
7537 kHlpAssertFailed();
7538 SetLastError(ERROR_INVALID_FUNCTION);
7539 return INVALID_FILE_SIZE;
7540 }
7541 }
7542 }
7543
7544 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
7545 return GetFileSizeEx(hFile, pcbFile);
7546}
7547
7548
7549/** Kernel32 - CreateFileMappingW */
7550static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
7551 DWORD fProtect, DWORD dwMaximumSizeHigh,
7552 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
7553{
7554 HANDLE hMapping;
7555 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7556 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7557 if (idxHandle < g_Sandbox.cHandles)
7558 {
7559 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7560 if (pHandle != NULL)
7561 {
7562 switch (pHandle->enmType)
7563 {
7564 case KWHANDLETYPE_TEMP_FILE:
7565 {
7566 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7567 if ( ( fProtect == PAGE_READONLY
7568 || fProtect == PAGE_EXECUTE_READ)
7569 && dwMaximumSizeHigh == 0
7570 && ( dwMaximumSizeLow == 0
7571 || dwMaximumSizeLow == pTempFile->cbFile)
7572 && pwszName == NULL)
7573 {
7574 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
7575 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
7576 return hMapping;
7577 }
7578 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
7579 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7580 SetLastError(ERROR_ACCESS_DENIED);
7581 return INVALID_HANDLE_VALUE;
7582 }
7583
7584 /* moc.exe benefits from this. */
7585 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7586 {
7587 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7588 if ( ( fProtect == PAGE_READONLY
7589 || fProtect == PAGE_EXECUTE_READ)
7590 && dwMaximumSizeHigh == 0
7591 && ( dwMaximumSizeLow == 0
7592 || dwMaximumSizeLow == pCachedFile->cbCached)
7593 && pwszName == NULL)
7594 {
7595 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
7596 K_FALSE /*fIsFileHandle*/, &hMapping))
7597 { /* likely */ }
7598 else
7599 hMapping = NULL;
7600 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
7601 return hMapping;
7602 }
7603
7604 /* Do fallback (for .pch). */
7605 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
7606 ("fProtect=%#x cb=%#x'%08x name=%p\n",
7607 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7608
7609 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7610 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
7611 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7612 return hMapping;
7613 }
7614
7615 /** @todo read cached memory mapped files too for moc. */
7616 }
7617 }
7618 }
7619
7620 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7621 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
7622 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7623 return hMapping;
7624}
7625
7626
7627/** Kernel32 - MapViewOfFile */
7628static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
7629 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
7630{
7631 PVOID pvRet;
7632 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7633 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7634 if (idxHandle < g_Sandbox.cHandles)
7635 {
7636 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7637 if (pHandle != NULL)
7638 {
7639 KU32 idxMapping;
7640
7641 /*
7642 * Ensure one free entry in the mapping tracking table first,
7643 * since this is common to both temporary and cached files.
7644 */
7645 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
7646 { /* likely */ }
7647 else
7648 {
7649 void *pvNew;
7650 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
7651 if (cNew)
7652 cNew *= 2;
7653 else
7654 cNew = 32;
7655 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
7656 if (pvNew)
7657 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
7658 else
7659 {
7660 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
7661 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7662 return NULL;
7663 }
7664 g_Sandbox.cMemMappingsAlloc = cNew;
7665 }
7666
7667 /*
7668 * Type specific work.
7669 */
7670 switch (pHandle->enmType)
7671 {
7672 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7673 case KWHANDLETYPE_TEMP_FILE:
7674 case KWHANDLETYPE_OUTPUT_BUF:
7675 default:
7676 kHlpAssertFailed();
7677 SetLastError(ERROR_INVALID_OPERATION);
7678 return NULL;
7679
7680 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7681 {
7682 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7683 if ( dwDesiredAccess == FILE_MAP_READ
7684 && offFileHigh == 0
7685 && offFileLow == 0
7686 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
7687 {
7688 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
7689 if (pTempFile->cSegs != 1)
7690 {
7691 KU32 iSeg;
7692 KU32 cbLeft;
7693 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
7694 KU8 *pbAll = NULL;
7695 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
7696 if (rc != 0)
7697 {
7698 kHlpAssertFailed();
7699 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7700 return NULL;
7701 }
7702
7703 cbLeft = pTempFile->cbFile;
7704 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
7705 {
7706 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
7707 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
7708 cbLeft -= cbToCopy;
7709 }
7710
7711 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
7712 {
7713 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
7714 pTempFile->paSegs[iSeg].pbData = NULL;
7715 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
7716 }
7717
7718 pTempFile->cSegs = 1;
7719 pTempFile->cbFileAllocated = cbAll;
7720 pTempFile->paSegs[0].cbDataAlloc = cbAll;
7721 pTempFile->paSegs[0].pbData = pbAll;
7722 pTempFile->paSegs[0].offData = 0;
7723 }
7724
7725 pTempFile->cMappings++;
7726 kHlpAssert(pTempFile->cMappings == 1);
7727
7728 pvRet = pTempFile->paSegs[0].pbData;
7729 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
7730 break;
7731 }
7732
7733 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7734 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
7735 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7736 return NULL;
7737 }
7738
7739 /*
7740 * This is simple in comparison to the above temporary file code.
7741 */
7742 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7743 {
7744 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7745 if ( dwDesiredAccess == FILE_MAP_READ
7746 && offFileHigh == 0
7747 && offFileLow == 0
7748 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
7749 {
7750 pvRet = pCachedFile->pbCached;
7751 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
7752 break;
7753 }
7754 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7755 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
7756 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7757 return NULL;
7758 }
7759 }
7760
7761 /*
7762 * Insert into the mapping tracking table. This is common
7763 * and we should only get here with a non-NULL pvRet.
7764 *
7765 * Note! We could look for duplicates and do ref counting, but it's
7766 * easier to just append for now.
7767 */
7768 kHlpAssert(pvRet != NULL);
7769 idxMapping = g_Sandbox.cMemMappings;
7770 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
7771
7772 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
7773 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
7774 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
7775 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
7776 g_Sandbox.cMemMappings++;
7777
7778 return pvRet;
7779 }
7780 }
7781
7782 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7783 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
7784 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
7785 return pvRet;
7786}
7787
7788
7789/** Kernel32 - MapViewOfFileEx */
7790static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
7791 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
7792{
7793 PVOID pvRet;
7794 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7795 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7796 if (idxHandle < g_Sandbox.cHandles)
7797 {
7798 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7799 if (pHandle != NULL)
7800 {
7801 switch (pHandle->enmType)
7802 {
7803 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7804 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
7805 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7806 if (!pvMapAddr)
7807 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7808 kHlpAssertFailed();
7809 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7810 return NULL;
7811
7812 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7813 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
7814 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7815 if (!pvMapAddr)
7816 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7817 /* We can use fallback here as the handle is an actual section handle. */
7818 break;
7819
7820 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7821 case KWHANDLETYPE_TEMP_FILE:
7822 case KWHANDLETYPE_OUTPUT_BUF:
7823 default:
7824 kHlpAssertFailed();
7825 SetLastError(ERROR_INVALID_OPERATION);
7826 return NULL;
7827 }
7828 }
7829 }
7830
7831 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
7832 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
7833 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
7834 return pvRet;
7835
7836}
7837
7838/** Kernel32 - UnmapViewOfFile */
7839static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
7840{
7841 /*
7842 * Consult the memory mapping tracker.
7843 */
7844 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
7845 KU32 idxMapping = g_Sandbox.cMemMappings;
7846 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7847 while (idxMapping-- > 0)
7848 if (paMemMappings[idxMapping].pvMapping == pvBase)
7849 {
7850 /* Type specific stuff. */
7851 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
7852 {
7853 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
7854 paMemMappings[idxMapping].u.pTempFile->cMappings--;
7855 }
7856 else
7857 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
7858
7859 /* Deref and probably free it. */
7860 if (--paMemMappings[idxMapping].cRefs == 0)
7861 {
7862 g_Sandbox.cMemMappings--;
7863 if (idxMapping != g_Sandbox.cMemMappings)
7864 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
7865 }
7866 return TRUE;
7867 }
7868
7869 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
7870 return UnmapViewOfFile(pvBase);
7871}
7872
7873/** @todo UnmapViewOfFileEx */
7874
7875#endif /* WITH_TEMP_MEMORY_FILES */
7876
7877
7878/** Kernel32 - DuplicateHandle */
7879static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
7880 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
7881{
7882 BOOL fRet;
7883
7884 /*
7885 * We must catch our handles being duplicated.
7886 */
7887 if (hSrcProc == GetCurrentProcess())
7888 {
7889 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
7890 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7891 if (idxHandle < g_Sandbox.cHandles)
7892 {
7893 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7894 if (pHandle)
7895 {
7896 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7897 if (fRet)
7898 {
7899 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
7900 {
7901 pHandle->cRefs++;
7902 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
7903 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
7904 pHandle->enmType, pHandle->cRefs));
7905 }
7906 else
7907 {
7908 fRet = FALSE;
7909 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7910 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
7911 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
7912 }
7913 }
7914 else
7915 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
7916 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
7917 return fRet;
7918 }
7919 }
7920 }
7921
7922 /*
7923 * Not one of ours, just do what the caller asks and log it.
7924 */
7925 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7926 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
7927 fInheritHandle, dwOptions, fRet, *phNew));
7928 return fRet;
7929}
7930
7931
7932/** Kernel32 - CloseHandle */
7933static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
7934{
7935 BOOL fRet;
7936 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
7937 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7938 if (idxHandle < g_Sandbox.cHandles)
7939 {
7940 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7941 if (pHandle)
7942 {
7943 /* Prevent the closing of the standard output and error handles. */
7944 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
7945 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
7946 {
7947 fRet = CloseHandle(hObject);
7948 if (fRet)
7949 {
7950 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7951 g_Sandbox.papHandles[idxHandle] = NULL;
7952 g_Sandbox.cActiveHandles--;
7953 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
7954 if (--pHandle->cRefs == 0)
7955 {
7956#ifdef WITH_TEMP_MEMORY_FILES
7957 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
7958 {
7959 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
7960 pHandle->u.pTempFile->cActiveHandles--;
7961 }
7962#endif
7963 kHlpFree(pHandle);
7964 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
7965 }
7966 else
7967 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
7968 }
7969 else
7970 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
7971 }
7972 else
7973 {
7974 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
7975 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
7976 fRet = TRUE;
7977 }
7978 return fRet;
7979 }
7980 }
7981
7982 fRet = CloseHandle(hObject);
7983 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
7984 return fRet;
7985}
7986
7987
7988/** Kernel32 - GetFileAttributesA. */
7989static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
7990{
7991 DWORD fRet;
7992 const char *pszExt = kHlpGetExt(pszFilename);
7993 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
7994 {
7995 KFSLOOKUPERROR enmError;
7996 PKFSOBJ pFsObj;
7997 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7998
7999 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8000 if (pFsObj)
8001 {
8002 kHlpAssert(pFsObj->fHaveStats);
8003 fRet = pFsObj->Stats.st_attribs;
8004 kFsCacheObjRelease(g_pFsCache, pFsObj);
8005 }
8006 else
8007 {
8008 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8009 fRet = INVALID_FILE_ATTRIBUTES;
8010 }
8011
8012 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8013 return fRet;
8014 }
8015
8016 fRet = GetFileAttributesA(pszFilename);
8017 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8018 return fRet;
8019}
8020
8021
8022/** Kernel32 - GetFileAttributesW. */
8023static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8024{
8025 DWORD fRet;
8026 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8027 {
8028 KFSLOOKUPERROR enmError;
8029 PKFSOBJ pFsObj;
8030 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8031
8032 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8033 if (pFsObj)
8034 {
8035 kHlpAssert(pFsObj->fHaveStats);
8036 fRet = pFsObj->Stats.st_attribs;
8037 kFsCacheObjRelease(g_pFsCache, pFsObj);
8038 }
8039 else
8040 {
8041 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8042 fRet = INVALID_FILE_ATTRIBUTES;
8043 }
8044
8045 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8046 return fRet;
8047 }
8048
8049 fRet = GetFileAttributesW(pwszFilename);
8050 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8051 return fRet;
8052}
8053
8054
8055/** Kernel32 - GetFileAttributesExA. */
8056static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8057 WIN32_FILE_ATTRIBUTE_DATA *pData)
8058{
8059 BOOL fRet;
8060 const char *pszExt = kHlpGetExt(pszFilename);
8061 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8062 {
8063 KFSLOOKUPERROR enmError;
8064 PKFSOBJ pFsObj;
8065 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8066
8067 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8068 if (pFsObj)
8069 {
8070 kHlpAssert(pFsObj->fHaveStats);
8071 if (enmLevel == GetFileExInfoStandard)
8072 {
8073 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8074 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8075 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8076 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8077 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8078 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8079 kFsCacheObjRelease(g_pFsCache, pFsObj);
8080 }
8081 else
8082 {
8083 kFsCacheObjRelease(g_pFsCache, pFsObj);
8084 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8085 }
8086 }
8087 else
8088 {
8089 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8090 fRet = FALSE;
8091 }
8092
8093 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8094 return fRet;
8095 }
8096
8097 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8098 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8099 return fRet;
8100}
8101
8102
8103/** Kernel32 - GetFileAttributesExW. */
8104static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8105 WIN32_FILE_ATTRIBUTE_DATA *pData)
8106{
8107 BOOL fRet;
8108 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8109 {
8110 KFSLOOKUPERROR enmError;
8111 PKFSOBJ pFsObj;
8112 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8113
8114 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8115 if (pFsObj)
8116 {
8117 kHlpAssert(pFsObj->fHaveStats);
8118 if (enmLevel == GetFileExInfoStandard)
8119 {
8120 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8121 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8122 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8123 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8124 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8125 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8126 kFsCacheObjRelease(g_pFsCache, pFsObj);
8127 }
8128 else
8129 {
8130 kFsCacheObjRelease(g_pFsCache, pFsObj);
8131 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8132 }
8133 }
8134 else
8135 {
8136 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8137 fRet = FALSE;
8138 }
8139
8140 KWFS_LOG(("GetFileAttributesW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8141 return fRet;
8142 }
8143
8144 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8145 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8146 return fRet;
8147}
8148
8149
8150/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8151 * directory containing each include file. We cache the result to speed
8152 * things up a little. */
8153static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8154{
8155 DWORD cwcRet;
8156 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8157 {
8158 KFSLOOKUPERROR enmError;
8159 PKFSOBJ pObj;
8160 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8161
8162 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8163 if (pObj)
8164 {
8165 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8166 {
8167 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8168 {
8169 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8170
8171 /* Should preserve trailing slash on directory paths. */
8172 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8173 {
8174 if ( cwcRet + 1 < cwcShortPath
8175 && pwszShortPath[cwcRet - 1] != '\\')
8176 {
8177 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8178 if ( cwcIn > 0
8179 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8180 {
8181 pwszShortPath[cwcRet++] = '\\';
8182 pwszShortPath[cwcRet] = '\0';
8183 }
8184 }
8185 }
8186
8187 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8188 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8189 kFsCacheObjRelease(g_pFsCache, pObj);
8190 return cwcRet;
8191 }
8192
8193 /* fall back for complicated cases. */
8194 }
8195 kFsCacheObjRelease(g_pFsCache, pObj);
8196 }
8197 }
8198 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8199 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8200 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8201 return cwcRet;
8202}
8203
8204
8205#ifdef WITH_TEMP_MEMORY_FILES
8206/** Kernel32 - DeleteFileW
8207 * Skip deleting the in-memory files. */
8208static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8209{
8210 BOOL fRc;
8211 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8212 && kwFsIsClTempFileW(pwszFilename))
8213 {
8214 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8215 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8216 fRc = TRUE;
8217 }
8218 else
8219 {
8220 fRc = DeleteFileW(pwszFilename);
8221 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8222 }
8223 return fRc;
8224}
8225#endif /* WITH_TEMP_MEMORY_FILES */
8226
8227
8228
8229#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8230
8231/*
8232 *
8233 * Console output buffering.
8234 * Console output buffering.
8235 * Console output buffering.
8236 *
8237 */
8238
8239
8240/**
8241 * Write a wide char string to the console.
8242 *
8243 * @param pSandbox The sandbox which output buffer to flush.
8244 */
8245static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8246{
8247 if (cwcToWrite > 0)
8248 {
8249 DWORD cwcWritten = 0;
8250 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8251 {
8252 if (cwcWritten == cwcToWrite)
8253 { /* likely */ }
8254 else
8255 {
8256 DWORD off = 0;
8257 do
8258 {
8259 off += cwcWritten;
8260 cwcWritten = 0;
8261 } while ( off < cwcToWrite
8262 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8263 kHlpAssert(off == cwcWritten);
8264 }
8265 }
8266 else
8267 kHlpAssertFailed();
8268 pSandbox->Combined.cFlushes++;
8269 }
8270}
8271
8272
8273/**
8274 * Flushes the combined console output buffer.
8275 *
8276 * @param pSandbox The sandbox which output buffer to flush.
8277 */
8278static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8279{
8280 if (pSandbox->Combined.cwcBuf > 0)
8281 {
8282 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8283 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8284 pSandbox->Combined.cwcBuf = 0;
8285 }
8286}
8287
8288
8289/**
8290 * For handling combined buffer overflow cases line by line.
8291 *
8292 * @param pSandbox The sandbox.
8293 * @param pwcBuf What to add to the combined buffer. Usually a
8294 * line, unless we're really low on buffer space.
8295 * @param cwcBuf The length of what to add.
8296 * @param fBrokenLine Whether this is a broken line.
8297 */
8298static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8299{
8300 if (fBrokenLine)
8301 kwSandboxConsoleFlushCombined(pSandbox);
8302 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8303 {
8304 kwSandboxConsoleFlushCombined(pSandbox);
8305 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
8306 }
8307 else
8308 {
8309 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
8310 pSandbox->Combined.cwcBuf += cwcBuf;
8311 }
8312}
8313
8314
8315/**
8316 * Called to final flush a line buffer via the combined buffer (if applicable).
8317 *
8318 * @param pSandbox The sandbox.
8319 * @param pLineBuf The line buffer.
8320 * @param pszName The line buffer name (for logging)
8321 */
8322static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
8323{
8324 if (pLineBuf->fIsConsole)
8325 {
8326 if (pLineBuf->u.Con.cwcBuf > 0)
8327 {
8328 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
8329
8330 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
8331 {
8332 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
8333 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
8334 }
8335 else
8336 {
8337 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
8338 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
8339 }
8340 pLineBuf->u.Con.cwcBuf = 0;
8341 }
8342 }
8343#ifdef WITH_STD_OUT_ERR_BUFFERING
8344 else if (pLineBuf->u.Fully.cchBuf > 0)
8345 {
8346 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
8347
8348 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
8349 pLineBuf->u.Fully.cchBuf = 0;
8350 }
8351#endif
8352}
8353
8354
8355/**
8356 * Called at the end of sandboxed execution to flush both stream buffers.
8357 *
8358 * @param pSandbox The sandbox.
8359 */
8360static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
8361{
8362 /*
8363 * First do the cl.exe source file supression trick, if applicable.
8364 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
8365 * handle.
8366 */
8367 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8368 && pSandbox->Combined.cFlushes == 0)
8369 {
8370 if ( pSandbox->StdOut.fIsConsole
8371 || pSandbox->StdErr.fIsConsole)
8372 {
8373 if ( pSandbox->Combined.cwcBuf >= 3
8374 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
8375 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
8376 {
8377 KI32 off = pSandbox->Combined.cwcBuf - 1;
8378 if (pSandbox->Combined.wszBuf[off] == '\n')
8379 {
8380 KBOOL fOk = K_TRUE;
8381 while (off-- > 0)
8382 {
8383 wchar_t const wc = pSandbox->Combined.wszBuf[off];
8384 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
8385 { /* likely */ }
8386 else
8387 {
8388 fOk = K_FALSE;
8389 break;
8390 }
8391 }
8392 if (fOk)
8393 {
8394 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
8395 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8396 pSandbox->Combined.cwcBuf = 0;
8397 return;
8398 }
8399 }
8400 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
8401 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8402 }
8403 }
8404#ifdef WITH_STD_OUT_ERR_BUFFERING
8405 /*
8406 * Otherwise, it goes to standard output (redirected).
8407 */
8408 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
8409 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
8410 {
8411 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
8412 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
8413 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
8414
8415 if (pchBuf[off] == '\n')
8416 {
8417 KBOOL fOk = K_TRUE;
8418 if (pchBuf[off - 1] == '\r')
8419 off--;
8420 while (off-- > 0)
8421 {
8422 char const ch = pchBuf[off];
8423 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
8424 { /* likely */ }
8425 else
8426 {
8427 fOk = K_FALSE;
8428 break;
8429 }
8430 }
8431 if (fOk)
8432 {
8433 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
8434 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8435 pSandbox->StdOut.u.Fully.cchBuf = 0;
8436 return;
8437 }
8438 }
8439 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
8440 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8441 }
8442#endif
8443 }
8444
8445 /*
8446 * Flush the two line buffer, then the combined buffer.
8447 */
8448 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
8449 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
8450 kwSandboxConsoleFlushCombined(pSandbox);
8451}
8452
8453
8454/**
8455 * Writes a string to the given output stream.
8456 *
8457 * @param pSandbox The sandbox.
8458 * @param pLineBuf The line buffer for the output stream.
8459 * @param pwcBuffer The buffer to write.
8460 * @param cwcToWrite The number of wchar_t's in the buffer.
8461 */
8462static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
8463{
8464 kHlpAssert(pLineBuf->fIsConsole);
8465 if (cwcToWrite > 0)
8466 {
8467 /*
8468 * First, find the start of the last incomplete line so we can figure
8469 * out how much line buffering we need to do.
8470 */
8471 KU32 cchLastIncompleteLine;
8472 KU32 offLastIncompleteLine = cwcToWrite;
8473 while ( offLastIncompleteLine > 0
8474 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
8475 offLastIncompleteLine--;
8476 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
8477
8478 /* Was there anything to line buffer? */
8479 if (offLastIncompleteLine < cwcToWrite)
8480 {
8481 /* Need to grow the line buffer? */
8482 KU32 cwcNeeded = offLastIncompleteLine == 0
8483 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
8484 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
8485 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
8486 {
8487 void *pvNew;
8488 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
8489 while (cwcNew < cwcNeeded)
8490 cwcNew *= 2;
8491 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
8492 if (pvNew)
8493 {
8494 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8495 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
8496 }
8497 else
8498 {
8499 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
8500 if (pvNew)
8501 {
8502 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8503 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
8504 }
8505 else
8506 {
8507 /* This isn't perfect, but it will have to do for now. */
8508 if (pLineBuf->u.Con.cwcBuf > 0)
8509 {
8510 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8511 K_TRUE /*fBrokenLine*/);
8512 pLineBuf->u.Con.cwcBuf = 0;
8513 }
8514 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
8515 return;
8516 }
8517 }
8518 }
8519
8520 /*
8521 * Handle the case where we only add to the line buffer.
8522 */
8523 if (offLastIncompleteLine == 0)
8524 {
8525 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
8526 pLineBuf->u.Con.cwcBuf += cwcToWrite;
8527 return;
8528 }
8529 }
8530
8531 /*
8532 * If there is sufficient combined buffer to handle this request, this is rather simple.
8533 */
8534 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
8535 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8536 {
8537 if (pLineBuf->u.Con.cwcBuf > 0)
8538 {
8539 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8540 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8541 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8542 pLineBuf->u.Con.cwcBuf = 0;
8543 }
8544
8545 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8546 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
8547 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
8548 }
8549 else
8550 {
8551 /*
8552 * Do line-by-line processing of the input, flusing the combined buffer
8553 * when it becomes necessary. We may have to write lines
8554 */
8555 KU32 off = 0;
8556 KU32 offNextLine = 0;
8557
8558 /* If there are buffered chars, we handle the first line outside the
8559 main loop. We must try our best outputting it as a complete line. */
8560 if (pLineBuf->u.Con.cwcBuf > 0)
8561 {
8562 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
8563 offNextLine++;
8564 offNextLine++;
8565 kHlpAssert(offNextLine <= offLastIncompleteLine);
8566
8567 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8568 {
8569 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8570 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8571 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8572 pLineBuf->u.Con.cwcBuf = 0;
8573
8574 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
8575 pSandbox->Combined.cwcBuf += offNextLine;
8576 }
8577 else
8578 {
8579 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
8580 if (cwcLeft > 0)
8581 {
8582 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
8583 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
8584 pLineBuf->u.Con.cwcBuf += cwcCopy;
8585 off += cwcCopy;
8586 }
8587 if (pLineBuf->u.Con.cwcBuf > 0)
8588 {
8589 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8590 K_TRUE /*fBrokenLine*/);
8591 pLineBuf->u.Con.cwcBuf = 0;
8592 }
8593 if (off < offNextLine)
8594 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
8595 }
8596 off = offNextLine;
8597 }
8598
8599 /* Deal with the remaining lines */
8600 while (off < offLastIncompleteLine)
8601 {
8602 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
8603 offNextLine++;
8604 offNextLine++;
8605 kHlpAssert(offNextLine <= offLastIncompleteLine);
8606 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
8607 off = offNextLine;
8608 }
8609 }
8610
8611 /*
8612 * Buffer any remaining incomplete line chars.
8613 */
8614 if (cchLastIncompleteLine)
8615 {
8616 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
8617 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
8618 }
8619 }
8620}
8621
8622
8623/**
8624 * Worker for WriteConsoleA and WriteFile.
8625 *
8626 * @param pSandbox The sandbox.
8627 * @param pLineBuf The line buffer.
8628 * @param pchBuffer What to write.
8629 * @param cchToWrite How much to write.
8630 */
8631static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
8632{
8633 /*
8634 * Convert it to wide char and use the 'W' to do the work.
8635 */
8636 int cwcRet;
8637 KU32 cwcBuf = cchToWrite * 2 + 1;
8638 wchar_t *pwcBufFree = NULL;
8639 wchar_t *pwcBuf;
8640 kHlpAssert(pLineBuf->fIsConsole);
8641
8642 if (cwcBuf <= 4096)
8643 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
8644 else
8645 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
8646
8647 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
8648 if (cwcRet > 0)
8649 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
8650 else
8651 {
8652 DWORD cchWritten;
8653 kHlpAssertFailed();
8654
8655 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
8656 if (pLineBuf->u.Con.cwcBuf > 0)
8657 {
8658 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
8659 pLineBuf->u.Con.cwcBuf = 0;
8660 }
8661 kwSandboxConsoleFlushCombined(pSandbox);
8662
8663 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
8664 {
8665 if (cchWritten >= cchToWrite)
8666 { /* likely */ }
8667 else
8668 {
8669 KU32 off = 0;
8670 do
8671 {
8672 off += cchWritten;
8673 cchWritten = 0;
8674 } while ( off < cchToWrite
8675 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
8676 }
8677 }
8678 }
8679
8680 if (pwcBufFree)
8681 kHlpFree(pwcBufFree);
8682}
8683
8684
8685/** Kernel32 - WriteConsoleA */
8686BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
8687 PVOID pvReserved)
8688{
8689 BOOL fRc;
8690 PKWOUTPUTSTREAMBUF pLineBuf;
8691 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8692
8693 if (hConOutput == g_Sandbox.StdErr.hOutput)
8694 pLineBuf = &g_Sandbox.StdErr;
8695 else
8696 pLineBuf = &g_Sandbox.StdOut;
8697 if (pLineBuf->fIsConsole)
8698 {
8699 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
8700
8701 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
8702 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
8703 if (pcbWritten)
8704 *pcbWritten = cbToWrite;
8705 fRc = TRUE;
8706 }
8707 else
8708 {
8709 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
8710 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
8711 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
8712 }
8713 return fRc;
8714}
8715
8716
8717/** Kernel32 - WriteConsoleW */
8718BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
8719 PVOID pvReserved)
8720{
8721 BOOL fRc;
8722 PKWOUTPUTSTREAMBUF pLineBuf;
8723 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8724
8725 if (hConOutput == g_Sandbox.StdErr.hOutput)
8726 pLineBuf = &g_Sandbox.StdErr;
8727 else if (hConOutput == g_Sandbox.StdOut.hOutput)
8728 pLineBuf = &g_Sandbox.StdOut;
8729 else
8730 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
8731 if (pLineBuf->fIsConsole)
8732 {
8733 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
8734
8735 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
8736 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
8737 if (pcwcWritten)
8738 *pcwcWritten = cwcToWrite;
8739 fRc = TRUE;
8740 }
8741 else
8742 {
8743 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
8744 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
8745 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
8746 }
8747 return fRc;
8748}
8749
8750#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8751
8752
8753
8754/*
8755 *
8756 * Virtual memory leak prevension.
8757 * Virtual memory leak prevension.
8758 * Virtual memory leak prevension.
8759 *
8760 */
8761
8762#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8763
8764/** For debug logging. */
8765# ifndef NDEBUG
8766static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
8767{
8768 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
8769 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
8770 kHlpAssert(cbMemInfo == sizeof(MemInfo));
8771 if (cbMemInfo != 0)
8772 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
8773 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
8774 MemInfo.BaseAddress,
8775 MemInfo.AllocationBase,
8776 MemInfo.RegionSize,
8777 MemInfo.State,
8778 MemInfo.Protect,
8779 MemInfo.Type));
8780}
8781# else
8782# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
8783# endif
8784
8785/**
8786 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
8787 *
8788 * @param idxFixed The fixed allocation index to "free".
8789 */
8790static void kwSandboxResetFixedAllocation(KU32 idxFixed)
8791{
8792 BOOL fRc;
8793 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
8794 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
8795 kHlpAssert(fRc); K_NOREF(fRc);
8796 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
8797 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
8798}
8799
8800#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
8801
8802
8803/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
8804 * location (~78MB in 32-bit 2010 compiler). */
8805static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
8806{
8807 PVOID pvMem;
8808 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8809 {
8810 KU32 idxPreAllocated = KU32_MAX;
8811
8812#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8813 /*
8814 * Look for a pre-reserved CL.exe heap allocation.
8815 */
8816 pvMem = NULL;
8817 if ( pvAddr != 0
8818 && (fAllocType & MEM_RESERVE))
8819 {
8820 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8821 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
8822 while (idxFixed-- > 0)
8823 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
8824 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
8825 {
8826 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
8827 {
8828 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
8829 {
8830 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
8831 pvMem = pvAddr;
8832 idxPreAllocated = idxFixed;
8833 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
8834 pvAddr, cb, fAllocType, fProt, pvMem));
8835 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
8836 SetLastError(NO_ERROR);
8837 break;
8838 }
8839 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
8840 }
8841 else
8842 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
8843 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
8844 }
8845 }
8846 if (!pvMem)
8847#endif
8848 {
8849 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8850 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8851 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8852 if (pvAddr && pvAddr != pvMem)
8853 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
8854 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
8855 }
8856
8857 if (pvMem)
8858 {
8859 /*
8860 * Track it.
8861 */
8862 PKWVIRTALLOC pTracker;
8863 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8864
8865 pTracker = g_Sandbox.pVirtualAllocHead;
8866 while ( pTracker
8867 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
8868 pTracker = pTracker->pNext;
8869 if (!pTracker)
8870 {
8871 DWORD dwErr = GetLastError();
8872 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
8873 if (pTracker)
8874 {
8875 pTracker->pvAlloc = pvMem;
8876 pTracker->cbAlloc = cb;
8877 pTracker->idxPreAllocated = idxPreAllocated;
8878 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8879 g_Sandbox.pVirtualAllocHead = pTracker;
8880 }
8881 SetLastError(dwErr);
8882 }
8883 }
8884 }
8885 else
8886 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8887 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8888 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8889 return pvMem;
8890}
8891
8892
8893/** Kernel32 - VirtualFree. */
8894static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
8895{
8896 BOOL fRc;
8897 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8898 {
8899 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8900 if (dwFreeType & MEM_RELEASE)
8901 {
8902 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
8903 if (pTracker)
8904 {
8905 if (pTracker->pvAlloc == pvAddr)
8906 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
8907 else
8908 {
8909 PKWVIRTALLOC pPrev;
8910 do
8911 {
8912 pPrev = pTracker;
8913 pTracker = pTracker->pNext;
8914 } while (pTracker && pTracker->pvAlloc != pvAddr);
8915 if (pTracker)
8916 pPrev->pNext = pTracker->pNext;
8917 }
8918 if (pTracker)
8919 {
8920#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8921 if (pTracker->idxPreAllocated != KU32_MAX)
8922 {
8923 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
8924 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
8925 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
8926 kHlpFree(pTracker);
8927 return TRUE;
8928 }
8929#endif
8930
8931 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8932 if (fRc)
8933 kHlpFree(pTracker);
8934 else
8935 {
8936 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8937 g_Sandbox.pVirtualAllocHead = pTracker;
8938 }
8939 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8940 return fRc;
8941 }
8942
8943 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
8944 }
8945 }
8946 }
8947
8948#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8949 /*
8950 * Protect our fixed allocations (this isn't just paranoia, btw.).
8951 */
8952 if (dwFreeType & MEM_RELEASE)
8953 {
8954 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8955 while (idxFixed-- > 0)
8956 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
8957 {
8958 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
8959 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
8960 return TRUE;
8961 }
8962 }
8963#endif
8964
8965 /*
8966 * Not tracker or not actually free the virtual range.
8967 */
8968 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8969 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8970 return fRc;
8971}
8972
8973
8974/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
8975HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
8976{
8977 HANDLE hHeap;
8978 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8979
8980 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
8981 if (hHeap != NULL)
8982 {
8983 DWORD dwErr = GetLastError();
8984 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
8985 if (pTracker)
8986 {
8987 pTracker->hHeap = hHeap;
8988 pTracker->pNext = g_Sandbox.pHeapHead;
8989 g_Sandbox.pHeapHead = pTracker;
8990 }
8991
8992 SetLastError(dwErr);
8993 }
8994 return hHeap;
8995
8996}
8997
8998
8999/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9000BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9001{
9002 BOOL fRc = HeapDestroy(hHeap);
9003 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9004 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9005 if (fRc)
9006 {
9007 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9008 if (pTracker)
9009 {
9010 if (pTracker->hHeap == hHeap)
9011 g_Sandbox.pHeapHead = pTracker->pNext;
9012 else
9013 {
9014 PKWHEAP pPrev;
9015 do
9016 {
9017 pPrev = pTracker;
9018 pTracker = pTracker->pNext;
9019 } while (pTracker && pTracker->hHeap == hHeap);
9020 if (pTracker)
9021 pPrev->pNext = pTracker->pNext;
9022 }
9023 if (pTracker)
9024 kHlpFree(pTracker);
9025 else
9026 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9027 }
9028 }
9029
9030 return fRc;
9031}
9032
9033
9034
9035/*
9036 *
9037 * Thread/Fiber local storage leak prevention.
9038 * Thread/Fiber local storage leak prevention.
9039 * Thread/Fiber local storage leak prevention.
9040 *
9041 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9042 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9043 * we're leaking these indexes, but more importantely we crash during
9044 * worker exit since the callback is triggered multiple times.
9045 */
9046
9047
9048/** Kernel32 - FlsAlloc */
9049DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9050{
9051 DWORD idxFls = FlsAlloc(pfnCallback);
9052 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9053 if (idxFls != FLS_OUT_OF_INDEXES)
9054 {
9055 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9056 if (pTracker)
9057 {
9058 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9059 pTracker->idx = idxFls;
9060 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9061 g_Sandbox.pFlsAllocHead = pTracker;
9062 }
9063 }
9064
9065 return idxFls;
9066}
9067
9068/** Kernel32 - FlsFree */
9069BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9070{
9071 BOOL fRc = FlsFree(idxFls);
9072 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9073 if (fRc)
9074 {
9075 PKWLOCALSTORAGE pTracker;
9076 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9077
9078 pTracker = g_Sandbox.pFlsAllocHead;
9079 if (pTracker)
9080 {
9081 if (pTracker->idx == idxFls)
9082 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9083 else
9084 {
9085 PKWLOCALSTORAGE pPrev;
9086 do
9087 {
9088 pPrev = pTracker;
9089 pTracker = pTracker->pNext;
9090 } while (pTracker && pTracker->idx != idxFls);
9091 if (pTracker)
9092 pPrev->pNext = pTracker->pNext;
9093 }
9094 if (pTracker)
9095 {
9096 pTracker->idx = FLS_OUT_OF_INDEXES;
9097 pTracker->pNext = NULL;
9098 kHlpFree(pTracker);
9099 }
9100 }
9101 }
9102 return fRc;
9103}
9104
9105
9106/** Kernel32 - TlsAlloc */
9107DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9108{
9109 DWORD idxTls = TlsAlloc();
9110 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9111 if (idxTls != TLS_OUT_OF_INDEXES)
9112 {
9113 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9114 if (pTracker)
9115 {
9116 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9117 pTracker->idx = idxTls;
9118 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9119 g_Sandbox.pTlsAllocHead = pTracker;
9120 }
9121 }
9122
9123 return idxTls;
9124}
9125
9126/** Kernel32 - TlsFree */
9127BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9128{
9129 BOOL fRc = TlsFree(idxTls);
9130 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9131 if (fRc)
9132 {
9133 PKWLOCALSTORAGE pTracker;
9134 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9135
9136 pTracker = g_Sandbox.pTlsAllocHead;
9137 if (pTracker)
9138 {
9139 if (pTracker->idx == idxTls)
9140 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9141 else
9142 {
9143 PKWLOCALSTORAGE pPrev;
9144 do
9145 {
9146 pPrev = pTracker;
9147 pTracker = pTracker->pNext;
9148 } while (pTracker && pTracker->idx != idxTls);
9149 if (pTracker)
9150 pPrev->pNext = pTracker->pNext;
9151 }
9152 if (pTracker)
9153 {
9154 pTracker->idx = TLS_OUT_OF_INDEXES;
9155 pTracker->pNext = NULL;
9156 kHlpFree(pTracker);
9157 }
9158 }
9159 }
9160 return fRc;
9161}
9162
9163
9164
9165/*
9166 *
9167 * Header file hashing.
9168 * Header file hashing.
9169 * Header file hashing.
9170 *
9171 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9172 * indicated that ~12% of the time was spent doing MD5 caluclation when
9173 * rebuiling openssl. The hashing it done right after reading the source
9174 * via ReadFile, same buffers and sizes.
9175 */
9176
9177#ifdef WITH_HASH_MD5_CACHE
9178
9179/** AdvApi32 - CryptCreateHash */
9180static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9181 HCRYPTHASH *phHash)
9182{
9183 BOOL fRc;
9184
9185 /*
9186 * Only do this for cl.exe when it request normal MD5.
9187 */
9188 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9189 {
9190 if (idAlg == CALG_MD5)
9191 {
9192 if (hKey == 0)
9193 {
9194 if (dwFlags == 0)
9195 {
9196 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9197 if (pHash)
9198 {
9199 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9200 pHash->uMagic = KWHASHMD5_MAGIC;
9201 pHash->cbHashed = 0;
9202 pHash->fGoneBad = K_FALSE;
9203 pHash->fFallbackMode = K_FALSE;
9204 pHash->fFinal = K_FALSE;
9205
9206 /* link it. */
9207 pHash->pNext = g_Sandbox.pHashHead;
9208 g_Sandbox.pHashHead = pHash;
9209
9210 *phHash = (KUPTR)pHash;
9211 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9212 hProv, *phHash, TRUE));
9213 return TRUE;
9214 }
9215
9216 kwErrPrintf("CryptCreateHash: out of memory!\n");
9217 }
9218 else
9219 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9220 }
9221 else
9222 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9223 }
9224 else
9225 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9226 }
9227
9228 /*
9229 * Fallback.
9230 */
9231 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9232 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9233 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9234 return fRc;
9235}
9236
9237
9238/** AdvApi32 - CryptHashData */
9239static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9240{
9241 BOOL fRc;
9242 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9243 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9244 while (pHash && (KUPTR)pHash != hHash)
9245 pHash = pHash->pNext;
9246 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9247 hHash, pHash, pbData, cbData, dwFlags));
9248 if (pHash)
9249 {
9250 /*
9251 * Validate the state.
9252 */
9253 if ( pHash->uMagic == KWHASHMD5_MAGIC
9254 && !pHash->fFinal)
9255 {
9256 if (!pHash->fFallbackMode)
9257 {
9258 /*
9259 * Does this match the previous ReadFile call to a cached file?
9260 * If it doesn't, try falling back.
9261 */
9262 if ( g_Sandbox.LastHashRead.cbRead == cbData
9263 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9264 {
9265 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9266 if ( pCachedFile
9267 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9268 {
9269
9270 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9271 {
9272 if ( pHash->pCachedFile == NULL
9273 && pHash->cbHashed == 0)
9274 pHash->pCachedFile = pCachedFile;
9275 if (pHash->pCachedFile == pCachedFile)
9276 {
9277 pHash->cbHashed += cbData;
9278 g_Sandbox.LastHashRead.pCachedFile = NULL;
9279 g_Sandbox.LastHashRead.pvRead = NULL;
9280 g_Sandbox.LastHashRead.cbRead = 0;
9281 g_Sandbox.LastHashRead.offRead = 0;
9282 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9283 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9284 return TRUE;
9285 }
9286
9287 /* Note! it's possible to fall back here too, if necessary. */
9288 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
9289 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
9290 }
9291 else
9292 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
9293 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
9294 }
9295 else if (!pCachedFile)
9296 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
9297 else
9298 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
9299 }
9300 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
9301 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
9302 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
9303 if (pHash->cbHashed == 0)
9304 pHash->fFallbackMode = K_TRUE;
9305 if (pHash->fFallbackMode)
9306 {
9307 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
9308 pHash->fFallbackMode = K_TRUE;
9309 MD5Init(&pHash->Md5Ctx);
9310 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9311 pHash->cbHashed = cbData;
9312 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
9313 hHash, pbData, cbData, dwFlags));
9314 return TRUE;
9315 }
9316 pHash->fGoneBad = K_TRUE;
9317 SetLastError(ERROR_INVALID_PARAMETER);
9318 fRc = FALSE;
9319 }
9320 else
9321 {
9322 /* fallback. */
9323 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9324 pHash->cbHashed += cbData;
9325 fRc = TRUE;
9326 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
9327 hHash, pbData, cbData, dwFlags));
9328 }
9329 }
9330 /*
9331 * Bad handle state.
9332 */
9333 else
9334 {
9335 if (pHash->uMagic != KWHASHMD5_MAGIC)
9336 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
9337 else
9338 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
9339 SetLastError(NTE_BAD_HASH);
9340 fRc = FALSE;
9341 }
9342 }
9343 else
9344 {
9345
9346 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
9347 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
9348 }
9349 return fRc;
9350}
9351
9352
9353/** AdvApi32 - CryptGetHashParam */
9354static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
9355 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
9356{
9357 BOOL fRc;
9358 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9359 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9360 while (pHash && (KUPTR)pHash != hHash)
9361 pHash = pHash->pNext;
9362 if (pHash)
9363 {
9364 if (pHash->uMagic == KWHASHMD5_MAGIC)
9365 {
9366 if (dwFlags == 0)
9367 {
9368 DWORD cbRet;
9369 void *pvRet;
9370 union
9371 {
9372 DWORD dw;
9373 } uBuf;
9374
9375 switch (dwParam)
9376 {
9377 case HP_HASHVAL:
9378 {
9379 /* Check the hash progress. */
9380 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
9381 if (pCachedFile)
9382 {
9383 if ( pCachedFile->cbCached == pHash->cbHashed
9384 && !pHash->fGoneBad)
9385 {
9386 if (pCachedFile->fValidMd5)
9387 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
9388 else
9389 {
9390 MD5Init(&pHash->Md5Ctx);
9391 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
9392 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
9393 pCachedFile->fValidMd5 = K_TRUE;
9394 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
9395 }
9396 pvRet = pCachedFile->abMd5Digest;
9397 }
9398 else
9399 {
9400 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
9401 from what I can tell, so just deal with it. */
9402 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
9403 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
9404 pHash, pCachedFile, pCachedFile->szPath));
9405 pHash->fFallbackMode = K_TRUE;
9406 pHash->pCachedFile = NULL;
9407 MD5Init(&pHash->Md5Ctx);
9408 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
9409 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9410 pvRet = pHash->abDigest;
9411 }
9412 pHash->fFinal = K_TRUE;
9413 cbRet = 16;
9414 break;
9415 }
9416 else if (pHash->fFallbackMode)
9417 {
9418 if (!pHash->fFinal)
9419 {
9420 pHash->fFinal = K_TRUE;
9421 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9422 }
9423 pvRet = pHash->abDigest;
9424 cbRet = 16;
9425 break;
9426 }
9427 else
9428 {
9429 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
9430 SetLastError(ERROR_INVALID_SERVER_STATE);
9431 }
9432 return FALSE;
9433 }
9434
9435 case HP_HASHSIZE:
9436 uBuf.dw = 16;
9437 pvRet = &uBuf;
9438 cbRet = sizeof(DWORD);
9439 break;
9440
9441 case HP_ALGID:
9442 uBuf.dw = CALG_MD5;
9443 pvRet = &uBuf;
9444 cbRet = sizeof(DWORD);
9445 break;
9446
9447 default:
9448 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
9449 SetLastError(NTE_BAD_TYPE);
9450 return FALSE;
9451 }
9452
9453 /*
9454 * Copy out cbRet from pvRet.
9455 */
9456 if (pbData)
9457 {
9458 if (*pcbData >= cbRet)
9459 {
9460 *pcbData = cbRet;
9461 kHlpMemCopy(pbData, pvRet, cbRet);
9462 if (cbRet == 4)
9463 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
9464 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
9465 else if (cbRet == 16)
9466 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",
9467 dwParam, pHash, pHash->pCachedFile, cbRet,
9468 pbData[0], pbData[1], pbData[2], pbData[3],
9469 pbData[4], pbData[5], pbData[6], pbData[7],
9470 pbData[8], pbData[9], pbData[10], pbData[11],
9471 pbData[12], pbData[13], pbData[14], pbData[15]));
9472 else
9473 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
9474 dwParam, pHash, pHash->pCachedFile, cbRet));
9475 return TRUE;
9476 }
9477
9478 kHlpMemCopy(pbData, pvRet, *pcbData);
9479 }
9480 SetLastError(ERROR_MORE_DATA);
9481 *pcbData = cbRet;
9482 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
9483 }
9484 else
9485 {
9486 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
9487 SetLastError(NTE_BAD_FLAGS);
9488 }
9489 }
9490 else
9491 {
9492 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
9493 SetLastError(NTE_BAD_HASH);
9494 }
9495 fRc = FALSE;
9496 }
9497 /*
9498 * Regular handle.
9499 */
9500 else
9501 {
9502 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
9503 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
9504 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
9505 }
9506
9507 return fRc;
9508}
9509
9510
9511/** AdvApi32 - CryptDestroyHash */
9512static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
9513{
9514 BOOL fRc;
9515 PKWHASHMD5 pPrev = NULL;
9516 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9517 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9518 while (pHash && (KUPTR)pHash != hHash)
9519 {
9520 pPrev = pHash;
9521 pHash = pHash->pNext;
9522 }
9523 if (pHash)
9524 {
9525 if (pHash->uMagic == KWHASHMD5_MAGIC)
9526 {
9527 pHash->uMagic = 0;
9528 if (!pPrev)
9529 g_Sandbox.pHashHead = pHash->pNext;
9530 else
9531 pPrev->pNext = pHash->pNext;
9532 kHlpFree(pHash);
9533 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
9534 fRc = TRUE;
9535 }
9536 else
9537 {
9538 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
9539 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
9540 SetLastError(ERROR_INVALID_HANDLE);
9541 fRc = FALSE;
9542 }
9543 }
9544 /*
9545 * Regular handle.
9546 */
9547 else
9548 {
9549 fRc = CryptDestroyHash(hHash);
9550 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
9551 }
9552 return fRc;
9553}
9554
9555#endif /* WITH_HASH_MD5_CACHE */
9556
9557
9558/*
9559 *
9560 * Reuse crypt context.
9561 * Reuse crypt context.
9562 * Reuse crypt context.
9563 *
9564 *
9565 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
9566 *
9567 */
9568
9569#ifdef WITH_CRYPT_CTX_REUSE
9570
9571/** AdvApi32 - CryptAcquireContextW. */
9572static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
9573 DWORD dwProvType, DWORD dwFlags)
9574{
9575 BOOL fRet;
9576
9577 /*
9578 * Lookup reusable context based on the input.
9579 */
9580 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
9581 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
9582 KU32 iCtx = g_Sandbox.cCryptCtxs;
9583 while (iCtx-- > 0)
9584 {
9585 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
9586 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
9587 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
9588 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
9589 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
9590 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
9591 {
9592 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
9593 {
9594 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
9595 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
9596 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9597 return TRUE;
9598 }
9599 }
9600 }
9601
9602 /*
9603 * Create it and enter it into the reused array if possible.
9604 */
9605 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
9606 if (fRet)
9607 {
9608 iCtx = g_Sandbox.cCryptCtxs;
9609 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
9610 {
9611 /* Try duplicate the input strings. */
9612 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
9613 (cwcContainer + 1) * sizeof(wchar_t));
9614 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
9615 {
9616 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
9617 (cwcProvider + 1) * sizeof(wchar_t));
9618 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
9619 {
9620 /* Add a couple of references just to be on the safe side and all that. */
9621 HCRYPTPROV hProv = *phProv;
9622 if (CryptContextAddRef(hProv, NULL, 0))
9623 {
9624 if (CryptContextAddRef(hProv, NULL, 0))
9625 {
9626 /* Okay, finish the entry and return success */
9627 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
9628 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
9629 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
9630 g_Sandbox.cCryptCtxs = iCtx + 1;
9631
9632 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
9633 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9634 return TRUE;
9635 }
9636 CryptReleaseContext(hProv, 0);
9637 }
9638 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
9639
9640 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
9641 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
9642 }
9643 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
9644 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
9645 }
9646 }
9647 else
9648 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
9649 }
9650
9651 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
9652 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9653 return fRet;
9654}
9655
9656
9657/** AdvApi32 - CryptReleaseContext */
9658static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
9659{
9660 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
9661 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
9662 return fRet;
9663}
9664
9665
9666/** AdvApi32 - CryptContextAddRef */
9667static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
9668{
9669 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
9670 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
9671 return fRet;
9672}
9673
9674#endif /* WITH_CRYPT_CTX_REUSE */
9675
9676/*
9677 *
9678 * Structured exception handling.
9679 * Structured exception handling.
9680 * Structured exception handling.
9681 *
9682 */
9683#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9684
9685# define EH_NONCONTINUABLE KU32_C(0x00000001)
9686# define EH_UNWINDING KU32_C(0x00000002)
9687# define EH_EXIT_UNWIND KU32_C(0x00000004)
9688# define EH_STACK_INVALID KU32_C(0x00000008)
9689# define EH_NESTED_CALL KU32_C(0x00000010)
9690
9691typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
9692 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
9693typedef struct _EXCEPTION_REGISTRATION_RECORD
9694{
9695 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
9696 PFNXCPTHANDLER pfnXcptHandler;
9697};
9698
9699
9700/**
9701 * Calls @a pfnHandler.
9702 */
9703static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
9704 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
9705 PFNXCPTHANDLER pfnHandler)
9706{
9707# if 1
9708 /* This is a more robust version that isn't subject to calling
9709 convension cleanup disputes and such. */
9710 KU32 uSavedEdi;
9711 KU32 uSavedEsi;
9712 KU32 uSavedEbx;
9713 KU32 rcHandler;
9714
9715 __asm
9716 {
9717 mov [uSavedEdi], edi
9718 mov [uSavedEsi], esi
9719 mov [uSavedEbx], ebx
9720 mov esi, esp
9721 mov edi, esp
9722 mov edi, [pXcptRec]
9723 mov edx, [pRegRec]
9724 mov eax, [pXcptCtx]
9725 mov ebx, [ppRegRec]
9726 mov ecx, [pfnHandler]
9727 sub esp, 16
9728 and esp, 0fffffff0h
9729 mov [esp ], edi
9730 mov [esp + 4], edx
9731 mov [esp + 8], eax
9732 mov [esp + 12], ebx
9733 mov edi, esi
9734 call ecx
9735 mov esp, esi
9736 cmp esp, edi
9737 je stack_ok
9738 int 3
9739 stack_ok:
9740 mov edi, [uSavedEdi]
9741 mov esi, [uSavedEsi]
9742 mov ebx, [uSavedEbx]
9743 mov [rcHandler], eax
9744 }
9745 return rcHandler;
9746# else
9747 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
9748# endif
9749}
9750
9751
9752/**
9753 * Vectored exception handler that emulates x86 chained exception handler.
9754 *
9755 * This is necessary because the RtlIsValidHandler check fails for self loaded
9756 * code and prevents cl.exe from working. (On AMD64 we can register function
9757 * tables, but on X86 cooking your own handling seems to be the only viabke
9758 * alternative.)
9759 *
9760 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
9761 * @param pXcptPtrs The exception details.
9762 */
9763static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
9764{
9765 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9766 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
9767 if (g_Sandbox.fRunning)
9768 {
9769 HANDLE const hCurProc = GetCurrentProcess();
9770 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
9771 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
9772 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9773 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
9774 {
9775 /* Read the exception record in a safe manner. */
9776 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9777 DWORD cbActuallyRead = 0;
9778 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9779 && cbActuallyRead == sizeof(RegRec))
9780 {
9781 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9782 KU32 rcHandler;
9783 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9784 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9785 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9786 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9787 if (rcHandler == ExceptionContinueExecution)
9788 {
9789 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
9790 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
9791 return EXCEPTION_CONTINUE_EXECUTION;
9792 }
9793
9794 if (rcHandler == ExceptionContinueSearch)
9795 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9796 else if (rcHandler == ExceptionNestedException)
9797 kHlpAssertMsgFailed(("Nested exceptions.\n"));
9798 else
9799 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9800 }
9801 else
9802 {
9803 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9804 break;
9805 }
9806
9807 /*
9808 * Next.
9809 */
9810 pRegRec = RegRec.pPrevRegRec;
9811 }
9812 }
9813 return EXCEPTION_CONTINUE_SEARCH;
9814}
9815
9816
9817/** NtDll,Kernel32 - RtlUnwind */
9818static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
9819 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
9820{
9821 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9822 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
9823 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
9824 if (g_Sandbox.fRunning)
9825 {
9826 HANDLE const hCurProc = GetCurrentProcess();
9827 PCONTEXT pXcptCtx = NULL;
9828 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9829
9830 /*
9831 * Update / create an exception record.
9832 */
9833 if (pXcptRec)
9834 pXcptRec->ExceptionFlags |= EH_UNWINDING;
9835 else
9836 {
9837 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
9838 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
9839 pXcptRec->ExceptionCode = STATUS_UNWIND;
9840 pXcptRec->ExceptionFlags = EH_UNWINDING;
9841 }
9842 if (!pStopXcptRec)
9843 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
9844
9845 /*
9846 * Walk the chain till we find pStopXctpRec.
9847 */
9848 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
9849 && pRegRec != NULL
9850 && pRegRec != pStopXcptRec)
9851 {
9852 /* Read the exception record in a safe manner. */
9853 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9854 DWORD cbActuallyRead = 0;
9855 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9856 && cbActuallyRead == sizeof(RegRec))
9857 {
9858 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9859 KU32 rcHandler;
9860 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9861 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9862 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9863 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9864
9865 if (rcHandler == ExceptionContinueSearch)
9866 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9867 else if (rcHandler == ExceptionCollidedUnwind)
9868 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
9869 else
9870 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9871 }
9872 else
9873 {
9874 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9875 break;
9876 }
9877
9878 /*
9879 * Pop next.
9880 */
9881 pTib->ExceptionList = RegRec.pPrevRegRec;
9882 pRegRec = RegRec.pPrevRegRec;
9883 }
9884 return;
9885 }
9886
9887 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
9888}
9889
9890#endif /* WINDOWS + X86 */
9891
9892
9893/*
9894 *
9895 * Misc function only intercepted while debugging.
9896 * Misc function only intercepted while debugging.
9897 * Misc function only intercepted while debugging.
9898 *
9899 */
9900
9901#ifndef NDEBUG
9902
9903/** CRT - memcpy */
9904static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
9905{
9906 KU8 const *pbSrc = (KU8 const *)pvSrc;
9907 KU8 *pbDst = (KU8 *)pvDst;
9908 KSIZE cbLeft = cb;
9909 while (cbLeft-- > 0)
9910 *pbDst++ = *pbSrc++;
9911 return pvDst;
9912}
9913
9914
9915/** CRT - memset */
9916static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
9917{
9918 KU8 *pbDst = (KU8 *)pvDst;
9919 KSIZE cbLeft = cb;
9920 while (cbLeft-- > 0)
9921 *pbDst++ = bFiller;
9922 return pvDst;
9923}
9924
9925#endif /* NDEBUG */
9926
9927
9928
9929/**
9930 * Functions that needs replacing for sandboxed execution.
9931 */
9932KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
9933{
9934 /*
9935 * Kernel32.dll and friends.
9936 */
9937 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
9938 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9939
9940 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
9941 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
9942 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
9943 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
9944 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
9945 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
9946 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
9947 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
9948 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
9949 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
9950 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9951
9952 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
9953 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
9954 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
9955 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
9956
9957 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
9958
9959 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
9960 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
9961 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
9962 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
9963 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
9964 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
9965 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
9966 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
9967 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
9968 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
9969 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
9970
9971 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
9972 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
9973 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
9974 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9975#ifdef WITH_TEMP_MEMORY_FILES
9976 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
9977 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9978 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9979 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
9980 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
9981 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9982 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9983 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9984 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9985 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9986#endif
9987 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9988 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9989 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9990 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
9991 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9992 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9993 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
9994 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
9995 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9996#ifdef WITH_TEMP_MEMORY_FILES
9997 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9998#endif
9999
10000 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10001 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10002
10003 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10004 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10005
10006 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10007 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10008
10009 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10010 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10011 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10012 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10013
10014 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10015
10016#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10017 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10018#endif
10019
10020#ifdef WITH_HASH_MD5_CACHE
10021 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10022 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10023 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10024 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10025#endif
10026
10027#ifdef WITH_CRYPT_CTX_REUSE
10028 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10029 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10030 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10031#endif
10032
10033 /*
10034 * MS Visual C++ CRTs.
10035 */
10036 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10037 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10038 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10039 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10040 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10041 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10042
10043 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10044 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10045 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10046
10047 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10048 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10049 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
10050
10051 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
10052 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
10053 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
10054 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
10055 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
10056 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
10057 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
10058 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
10059 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
10060 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
10061 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
10062 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
10063 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
10064 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
10065 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
10066 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
10067 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
10068 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
10069 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
10070 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
10071
10072 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
10073 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
10074 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
10075 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
10076 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
10077 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
10078 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
10079 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
10080 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
10081 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
10082 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
10083 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
10084 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
10085 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
10086
10087#ifndef NDEBUG
10088 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
10089 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
10090#endif
10091};
10092/** Number of entries in g_aReplacements. */
10093KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
10094
10095
10096/**
10097 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
10098 * execution.
10099 */
10100KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
10101{
10102 /*
10103 * Kernel32.dll and friends.
10104 */
10105 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10106 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10107
10108#if 0
10109 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10110#endif
10111
10112 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10113 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10114 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10115 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10116#ifdef WITH_TEMP_MEMORY_FILES
10117 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10118 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10119 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10120 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10121 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10122 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10123 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10124 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10125 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10126 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10127#endif
10128 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10129 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10130 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10131 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10132 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10133 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10134 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10135 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10136 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10137#ifdef WITH_TEMP_MEMORY_FILES
10138 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10139#endif
10140 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10141 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10142
10143 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10144 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10145
10146#ifdef WITH_HASH_MD5_CACHE
10147 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10148 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10149 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10150 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10151#endif
10152
10153 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10154
10155#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10156 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10157#endif
10158
10159 /*
10160 * MS Visual C++ CRTs.
10161 */
10162 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10163 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10164 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10165 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10166 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10167 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10168 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10169
10170#if 0 /* used by mspdbXXX.dll */
10171 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10172 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10173#endif
10174};
10175/** Number of entries in g_aSandboxNativeReplacements. */
10176KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10177
10178
10179/**
10180 * Functions that needs replacing when queried by GetProcAddress.
10181 */
10182KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10183{
10184 /*
10185 * Kernel32.dll and friends.
10186 */
10187 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10188 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10189 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10190 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10191};
10192/** Number of entries in g_aSandboxGetProcReplacements. */
10193KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10194
10195
10196/**
10197 * Control handler.
10198 *
10199 * @returns TRUE if handled, FALSE if not.
10200 * @param dwCtrlType The signal.
10201 */
10202static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10203{
10204 DWORD cbIgn;
10205 int volatile rc; /* volatile for debugging */
10206 int volatile rcPrev;
10207 const char *pszMsg;
10208 switch (dwCtrlType)
10209 {
10210 case CTRL_C_EVENT:
10211 rc = 9;
10212 pszMsg = "kWorker: Ctrl-C\r\n";
10213 break;
10214
10215 case CTRL_BREAK_EVENT:
10216 rc = 10;
10217 pszMsg = "kWorker: Ctrl-Break\r\n";
10218 break;
10219
10220 case CTRL_CLOSE_EVENT:
10221 rc = 11;
10222 pszMsg = "kWorker: console closed\r\n";
10223 break;
10224
10225 case CTRL_LOGOFF_EVENT:
10226 rc = 11;
10227 pszMsg = "kWorker: logoff event\r\n";
10228 break;
10229
10230 case CTRL_SHUTDOWN_EVENT:
10231 rc = 11;
10232 pszMsg = "kWorker: shutdown event\r\n";
10233 break;
10234
10235 default:
10236 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10237 return TRUE;
10238 }
10239
10240 /*
10241 * Terminate the process after 5 seconds.
10242 * If we get here a second time we just terminate the process ourselves.
10243 *
10244 * Note! We do no try call exit() here as it turned out to deadlock a lot
10245 * flusing file descriptors (stderr back when we first wrote to it).
10246 */
10247 rcPrev = g_rcCtrlC;
10248 g_rcCtrlC = rc;
10249 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10250 if (rcPrev == 0)
10251 {
10252 int i;
10253 for (i = 0; i < 10; i++)
10254 {
10255 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10256 Sleep(500);
10257 }
10258 }
10259 TerminateProcess(GetCurrentProcess(), rc);
10260 return TRUE;
10261}
10262
10263
10264/**
10265 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10266 */
10267static void kwSandboxResetModuleVisited(void)
10268{
10269 PKWMODULE pMod = g_pModuleHead;
10270 while (pMod)
10271 {
10272 pMod->fVisited = K_FALSE;
10273 pMod = pMod->pNextList;
10274 }
10275}
10276
10277
10278/**
10279 * Used by kwSandboxExec to reset the state of the module tree.
10280 *
10281 * This is done recursively.
10282 *
10283 * @param pMod The root of the tree to consider.
10284 */
10285static void kwSandboxResetModuleState(PKWMODULE pMod)
10286{
10287 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
10288 if (!pMod->fNative)
10289 {
10290 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
10291 if (!pMod->fVisited) /* Avoid loops. */
10292 {
10293 KSIZE iImp;
10294 pMod->fVisited = K_TRUE;
10295 iImp = pMod->u.Manual.cImpMods;
10296 while (iImp-- > 0)
10297 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
10298 }
10299 }
10300 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10301 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
10302 {
10303 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10304 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10305 {
10306 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10307 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10308 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10309 pMod->pszPath, pszValue ? pszValue : "<null>"));
10310 }
10311 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10312 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10313 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10314 pMod->pszPath, pszValue ? pszValue : "<null>"));
10315 else
10316 {
10317 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10318 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10319 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10320 if (pszValue != NULL)
10321 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10322 else
10323 pMod->pszMsPdbSrvEndpoint = NULL;
10324 pMod->fNeedReInit = K_TRUE;
10325 }
10326 }
10327}
10328
10329static PPEB kwSandboxGetProcessEnvironmentBlock(void)
10330{
10331#if K_ARCH == K_ARCH_X86_32
10332 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
10333#elif K_ARCH == K_ARCH_AMD64
10334 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
10335#else
10336# error "Port me!"
10337#endif
10338}
10339
10340
10341/**
10342 * Enters the given handle into the handle table.
10343 *
10344 * @returns K_TRUE on success, K_FALSE on failure.
10345 * @param pSandbox The sandbox.
10346 * @param pHandle The handle.
10347 * @param hHandle The handle value to enter it under (for the
10348 * duplicate handle API).
10349 */
10350static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
10351{
10352 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
10353 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
10354
10355 /*
10356 * Grow handle table.
10357 */
10358 if (idxHandle >= pSandbox->cHandles)
10359 {
10360 void *pvNew;
10361 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
10362 while (cHandles <= idxHandle)
10363 cHandles *= 2;
10364 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
10365 if (!pvNew)
10366 {
10367 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
10368 return K_FALSE;
10369 }
10370 pSandbox->papHandles = (PKWHANDLE *)pvNew;
10371 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
10372 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
10373 pSandbox->cHandles = cHandles;
10374 }
10375
10376 /*
10377 * Check that the entry is unused then insert it.
10378 */
10379 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
10380 pSandbox->papHandles[idxHandle] = pHandle;
10381 pSandbox->cActiveHandles++;
10382 return K_TRUE;
10383}
10384
10385
10386/**
10387 * Creates a correctly quoted ANSI command line string from the given argv.
10388 *
10389 * @returns Pointer to the command line.
10390 * @param cArgs Number of arguments.
10391 * @param papszArgs The argument vector.
10392 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10393 * @param pcbCmdLine Where to return the command line length,
10394 * including one terminator.
10395 */
10396static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
10397{
10398 KU32 i;
10399 KSIZE cbCmdLine;
10400 char *pszCmdLine;
10401
10402 /* Make a copy of the argument vector that we'll be quoting. */
10403 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
10404 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
10405
10406 /* Quote the arguments that need it. */
10407 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
10408
10409 /* figure out cmd line length. */
10410 cbCmdLine = 0;
10411 for (i = 0; i < cArgs; i++)
10412 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
10413 *pcbCmdLine = cbCmdLine;
10414
10415 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
10416 if (pszCmdLine)
10417 {
10418 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
10419 if (papszQuotedArgs[0] != papszArgs[0])
10420 free(papszQuotedArgs[0]);
10421
10422 for (i = 1; i < cArgs; i++)
10423 {
10424 *psz++ = ' ';
10425 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
10426 if (papszQuotedArgs[i] != papszArgs[i])
10427 free(papszQuotedArgs[i]);
10428 }
10429 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
10430
10431 *psz++ = '\0';
10432 *psz++ = '\0';
10433 }
10434
10435 return pszCmdLine;
10436}
10437
10438
10439
10440static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
10441 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10442 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10443{
10444 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10445 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10446 wchar_t *pwcPool;
10447 KSIZE cbStrings;
10448 KSIZE cwc;
10449 KSIZE cbCmdLine;
10450 KU32 i;
10451
10452 /* Simple stuff. */
10453 pSandbox->rcExitCode = 256;
10454 pSandbox->pTool = pTool;
10455 pSandbox->idMainThread = GetCurrentThreadId();
10456 pSandbox->pgmptr = (char *)pTool->pszPath;
10457 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
10458#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10459 if (pSandbox->StdOut.fIsConsole)
10460 pSandbox->StdOut.u.Con.cwcBuf = 0;
10461 else
10462 pSandbox->StdOut.u.Fully.cchBuf = 0;
10463 if (pSandbox->StdErr.fIsConsole)
10464 pSandbox->StdErr.u.Con.cwcBuf = 0;
10465 else
10466 pSandbox->StdErr.u.Fully.cchBuf = 0;
10467 pSandbox->Combined.cwcBuf = 0;
10468 pSandbox->Combined.cFlushes = 0;
10469#endif
10470 pSandbox->fNoPchCaching = fNoPchCaching;
10471 pSandbox->cArgs = cArgs;
10472 pSandbox->papszArgs = (char **)papszArgs;
10473 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
10474 if (!pSandbox->pszCmdLine)
10475 return KERR_NO_MEMORY;
10476
10477 /*
10478 * Convert command line and argv to UTF-16.
10479 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
10480 */
10481 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
10482 if (!pSandbox->papwszArgs)
10483 return KERR_NO_MEMORY;
10484 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
10485 for (i = 0; i < cArgs; i++)
10486 {
10487 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
10488 pSandbox->papwszArgs[i] = pwcPool;
10489 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
10490 pwcPool++;
10491 }
10492 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
10493 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
10494
10495 /*
10496 * Convert the commandline string to UTF-16, same pessimistic approach as above.
10497 */
10498 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
10499 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
10500 if (!pSandbox->pwszCmdLine)
10501 return KERR_NO_MEMORY;
10502 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
10503
10504 pSandbox->SavedCommandLine = pProcParams->CommandLine;
10505 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
10506 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
10507
10508 /*
10509 * Setup the environment.
10510 */
10511 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
10512 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
10513 {
10514 KU32 iDst = 0;
10515 for (i = 0; i < cEnvVars; i++)
10516 {
10517 const char *pszVar = papszEnvVars[i];
10518 KSIZE cchVar = kHlpStrLen(pszVar);
10519 const char *pszEqual;
10520 if ( cchVar > 0
10521 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
10522 {
10523 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
10524 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
10525 if (pszCopy && pwszCopy)
10526 {
10527 pSandbox->papszEnvVars[iDst] = pszCopy;
10528 pSandbox->environ[iDst] = pszCopy;
10529 pSandbox->papwszEnvVars[iDst] = pwszCopy;
10530 pSandbox->wenviron[iDst] = pwszCopy;
10531
10532 /* When we see the path, we must tell the system or native exec and module loading won't work . */
10533 if ( (pszEqual - pszVar) == 4
10534 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
10535 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
10536 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
10537 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
10538 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
10539 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
10540
10541 iDst++;
10542 }
10543 else
10544 {
10545 kHlpFree(pszCopy);
10546 kHlpFree(pwszCopy);
10547 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
10548 }
10549 }
10550 else
10551 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
10552 }
10553 pSandbox->papszEnvVars[iDst] = NULL;
10554 pSandbox->environ[iDst] = NULL;
10555 pSandbox->papwszEnvVars[iDst] = NULL;
10556 pSandbox->wenviron[iDst] = NULL;
10557 }
10558 else
10559 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
10560
10561 /*
10562 * Invalidate the volatile parts of cache (kBuild output directory,
10563 * temporary directory, whatever).
10564 */
10565 kFsCacheInvalidateCustomBoth(g_pFsCache);
10566
10567#ifdef WITH_HISTORY
10568 /*
10569 * Record command line in debug history.
10570 */
10571 kHlpFree(g_apszHistory[g_iHistoryNext]);
10572 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
10573 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
10574#endif
10575
10576 return 0;
10577}
10578
10579
10580/**
10581 * Does sandbox cleanup between jobs.
10582 *
10583 * We postpone whatever isn't externally visible (i.e. files) and doesn't
10584 * influence the result, so that kmk can get on with things ASAP.
10585 *
10586 * @param pSandbox The sandbox.
10587 */
10588static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
10589{
10590 PROCESS_MEMORY_COUNTERS MemInfo;
10591 PKWVIRTALLOC pTracker;
10592 PKWHEAP pHeap;
10593 PKWLOCALSTORAGE pLocalStorage;
10594#ifdef WITH_HASH_MD5_CACHE
10595 PKWHASHMD5 pHash;
10596#endif
10597#ifdef WITH_TEMP_MEMORY_FILES
10598 PKWFSTEMPFILE pTempFile;
10599#endif
10600 PKWEXITCALLACK pExitCallback;
10601
10602 /*
10603 * First stuff that may cause code to run.
10604 */
10605
10606 /* Do exit callback first. */
10607 pExitCallback = g_Sandbox.pExitCallbackHead;
10608 g_Sandbox.pExitCallbackHead = NULL;
10609 while (pExitCallback)
10610 {
10611 PKWEXITCALLACK pNext = pExitCallback->pNext;
10612 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
10613 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
10614 __try
10615 {
10616 pExitCallback->pfnCallback();
10617 }
10618 __except (EXCEPTION_EXECUTE_HANDLER)
10619 {
10620 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
10621 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
10622 kHlpAssertFailed();
10623 }
10624 kHlpFree(pExitCallback);
10625 pExitCallback = pNext;
10626 }
10627
10628 /* Free left behind FlsAlloc leaks. */
10629 pLocalStorage = g_Sandbox.pFlsAllocHead;
10630 g_Sandbox.pFlsAllocHead = NULL;
10631 while (pLocalStorage)
10632 {
10633 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10634 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
10635 FlsFree(pLocalStorage->idx);
10636 kHlpFree(pLocalStorage);
10637 pLocalStorage = pNext;
10638 }
10639
10640 /* Free left behind TlsAlloc leaks. */
10641 pLocalStorage = g_Sandbox.pTlsAllocHead;
10642 g_Sandbox.pTlsAllocHead = NULL;
10643 while (pLocalStorage)
10644 {
10645 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10646 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
10647 TlsFree(pLocalStorage->idx);
10648 kHlpFree(pLocalStorage);
10649 pLocalStorage = pNext;
10650 }
10651
10652
10653 /*
10654 * Then free resources associated with the sandbox run.
10655 */
10656
10657 /* Open handles, except fixed handles (stdout and stderr). */
10658 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
10659 {
10660 KU32 idxHandle = pSandbox->cHandles;
10661 while (idxHandle-- > 0)
10662 if (pSandbox->papHandles[idxHandle] == NULL)
10663 { /* likely */ }
10664 else
10665 {
10666 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
10667 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
10668 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
10669 {
10670 pSandbox->papHandles[idxHandle] = NULL;
10671 pSandbox->cLeakedHandles++;
10672
10673 switch (pHandle->enmType)
10674 {
10675 case KWHANDLETYPE_FSOBJ_READ_CACHE:
10676 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
10677 idxHandle, pHandle->hHandle, pHandle->cRefs));
10678 break;
10679 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
10680 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
10681 idxHandle, pHandle->hHandle, pHandle->cRefs));
10682 break;
10683 case KWHANDLETYPE_OUTPUT_BUF:
10684 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
10685 idxHandle, pHandle->hHandle, pHandle->cRefs));
10686 break;
10687 case KWHANDLETYPE_TEMP_FILE:
10688 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
10689 idxHandle, pHandle->hHandle, pHandle->cRefs));
10690 pHandle->u.pTempFile->cActiveHandles--;
10691 break;
10692 case KWHANDLETYPE_TEMP_FILE_MAPPING:
10693 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
10694 idxHandle, pHandle->hHandle, pHandle->cRefs));
10695 pHandle->u.pTempFile->cActiveHandles--;
10696 break;
10697 default:
10698 kHlpAssertFailed();
10699 }
10700 if (--pHandle->cRefs == 0)
10701 kHlpFree(pHandle);
10702 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
10703 break;
10704 }
10705 }
10706 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
10707 }
10708
10709 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
10710 g_Sandbox.cMemMappings = 0;
10711
10712#ifdef WITH_TEMP_MEMORY_FILES
10713 /* The temporary files aren't externally visible, they're all in memory. */
10714 pTempFile = pSandbox->pTempFileHead;
10715 pSandbox->pTempFileHead = NULL;
10716 while (pTempFile)
10717 {
10718 PKWFSTEMPFILE pNext = pTempFile->pNext;
10719 KU32 iSeg = pTempFile->cSegs;
10720 while (iSeg-- > 0)
10721 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
10722 kHlpFree(pTempFile->paSegs);
10723 pTempFile->pNext = NULL;
10724 kHlpFree(pTempFile);
10725
10726 pTempFile = pNext;
10727 }
10728#endif
10729
10730 /* Free left behind HeapCreate leaks. */
10731 pHeap = g_Sandbox.pHeapHead;
10732 g_Sandbox.pHeapHead = NULL;
10733 while (pHeap != NULL)
10734 {
10735 PKWHEAP pNext = pHeap->pNext;
10736 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
10737 HeapDestroy(pHeap->hHeap);
10738 pHeap = pNext;
10739 }
10740
10741 /* Free left behind VirtualAlloc leaks. */
10742 pTracker = g_Sandbox.pVirtualAllocHead;
10743 g_Sandbox.pVirtualAllocHead = NULL;
10744 while (pTracker)
10745 {
10746 PKWVIRTALLOC pNext = pTracker->pNext;
10747 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
10748
10749#ifdef WITH_FIXED_VIRTUAL_ALLOCS
10750 if (pTracker->idxPreAllocated != KU32_MAX)
10751 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
10752 else
10753#endif
10754 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
10755 kHlpFree(pTracker);
10756 pTracker = pNext;
10757 }
10758
10759 /* Free the environment. */
10760 if (pSandbox->papszEnvVars)
10761 {
10762 KU32 i;
10763 for (i = 0; pSandbox->papszEnvVars[i]; i++)
10764 kHlpFree(pSandbox->papszEnvVars[i]);
10765 pSandbox->environ[0] = NULL;
10766 pSandbox->papszEnvVars[0] = NULL;
10767
10768 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
10769 kHlpFree(pSandbox->papwszEnvVars[i]);
10770 pSandbox->wenviron[0] = NULL;
10771 pSandbox->papwszEnvVars[0] = NULL;
10772 }
10773
10774#ifdef WITH_HASH_MD5_CACHE
10775 /*
10776 * Hash handles.
10777 */
10778 pHash = pSandbox->pHashHead;
10779 pSandbox->pHashHead = NULL;
10780 while (pHash)
10781 {
10782 PKWHASHMD5 pNext = pHash->pNext;
10783 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
10784 kHlpFree(pHash);
10785 pHash = pNext;
10786 }
10787#endif
10788
10789 /*
10790 * Check the memory usage. If it's getting high, trigger a respawn
10791 * after the next job.
10792 */
10793 MemInfo.WorkingSetSize = 0;
10794 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
10795 {
10796 /* The first time thru, we figure out approximately when to restart
10797 based on installed RAM and CPU threads. */
10798 static KU64 s_cbMaxWorkingSet = 0;
10799 if (s_cbMaxWorkingSet != 0)
10800 { /* likely */ }
10801 else
10802 {
10803 SYSTEM_INFO SysInfo;
10804 MEMORYSTATUSEX GlobalMemInfo;
10805 const char *pszValue;
10806
10807 /* Calculate a reasonable estimate. */
10808 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
10809 GetNativeSystemInfo(&SysInfo);
10810
10811 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
10812 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
10813 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
10814#if K_ARCH_BITS >= 64
10815 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
10816#else
10817 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
10818#endif
10819 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
10820 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10821
10822 /* User limit. */
10823 pszValue = getenv("KWORKER_MEMORY_LIMIT");
10824 if (pszValue != NULL)
10825 {
10826 char *pszNext;
10827 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
10828 if (*pszNext == '\0' || *pszNext == 'M')
10829 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
10830 else if (*pszNext == 'K')
10831 s_cbMaxWorkingSet = ulValue * (KU64)1024;
10832 else if (*pszNext == 'G')
10833 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
10834 else
10835 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
10836 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10837 }
10838
10839 /* Clamp it a little. */
10840 if (s_cbMaxWorkingSet < 168*1024*1024)
10841 s_cbMaxWorkingSet = 168*1024*1024;
10842#if K_ARCH_BITS < 64
10843 else
10844 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
10845 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
10846 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
10847 : 1536*1024*1024 /* got 4GB VA */);
10848#endif
10849 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
10850 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
10851 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10852 }
10853
10854 /* Finally the check. */
10855 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
10856 {
10857 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
10858 g_fRestart = K_TRUE;
10859 }
10860 }
10861
10862 /*
10863 * The CRT has a max of 8192 handles, so we better restart after a while if
10864 * someone is leaking handles or we risk running out of descriptors.
10865 *
10866 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
10867 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
10868 */
10869 if (pSandbox->cLeakedHandles > 6000)
10870 {
10871 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
10872 g_fRestart = K_TRUE;
10873 }
10874}
10875
10876
10877/**
10878 * Does essential cleanups and restoring, anything externally visible.
10879 *
10880 * All cleanups that aren't externally visible are postponed till after we've
10881 * informed kmk of the result, so it can be done in the dead time between jobs.
10882 *
10883 * @param pSandbox The sandbox.
10884 */
10885static void kwSandboxCleanup(PKWSANDBOX pSandbox)
10886{
10887 /*
10888 * Restore the parent command line string.
10889 */
10890 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10891 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10892 pProcParams->CommandLine = pSandbox->SavedCommandLine;
10893 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
10894 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
10895}
10896
10897
10898static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10899 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10900{
10901 int rcExit = 42;
10902 int rc;
10903
10904 /*
10905 * Initialize the sandbox environment.
10906 */
10907 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
10908 if (rc == 0)
10909 {
10910 /*
10911 * Do module initialization.
10912 */
10913 kwSandboxResetModuleVisited();
10914 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
10915 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
10916 if (rc == 0)
10917 {
10918 /*
10919 * Call the main function.
10920 */
10921#if K_ARCH == K_ARCH_AMD64
10922 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
10923#elif K_ARCH == K_ARCH_X86_32
10924 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
10925#else
10926# error "Port me!"
10927#endif
10928
10929 /* Save the NT TIB first (should do that here, not in some other function). */
10930 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10931 pSandbox->TibMainThread = *pTib;
10932
10933 /* Make the call in a guarded fashion. */
10934#if K_ARCH == K_ARCH_AMD64
10935 /* AMD64 */
10936 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
10937 __try
10938 {
10939 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10940 if (setjmp(pSandbox->JmpBuf) == 0)
10941 {
10942 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10943 pSandbox->fRunning = K_TRUE;
10944 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
10945 pSandbox->fRunning = K_FALSE;
10946 }
10947 else
10948 rcExit = pSandbox->rcExitCode;
10949 }
10950#elif K_ARCH == K_ARCH_X86_32
10951 /* x86 (see _tmainCRTStartup) */
10952 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
10953 __try
10954 {
10955 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10956 if (setjmp(pSandbox->JmpBuf) == 0)
10957 {
10958 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10959 pSandbox->fRunning = K_TRUE;
10960 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
10961 pSandbox->fRunning = K_FALSE;
10962 }
10963 else
10964 rcExit = pSandbox->rcExitCode;
10965 }
10966#endif
10967 __except (EXCEPTION_EXECUTE_HANDLER)
10968 {
10969 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
10970#ifdef WITH_HISTORY
10971 {
10972 KU32 cPrinted = 0;
10973 while (cPrinted++ < 5)
10974 {
10975 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
10976 if (g_apszHistory[idx])
10977 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
10978 }
10979 }
10980#endif
10981 rcExit = 512;
10982 }
10983 pSandbox->fRunning = K_FALSE;
10984
10985 /* Now, restore the NT TIB. */
10986 *pTib = pSandbox->TibMainThread;
10987 }
10988 else
10989 rcExit = 42 + 4;
10990
10991 /*
10992 * Flush and clean up the essential bits only, postpone whatever we
10993 * can till after we've replied to kmk.
10994 */
10995#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10996 kwSandboxConsoleFlushAll(&g_Sandbox);
10997#endif
10998 kwSandboxCleanup(&g_Sandbox);
10999 /** @todo Flush sandboxed native CRTs too. */
11000 }
11001 else
11002 rcExit = 42 + 3;
11003
11004 return rcExit;
11005}
11006
11007
11008/**
11009 * Does the post command part of a job (optional).
11010 *
11011 * @returns The exit code of the job.
11012 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11013 * @param papszPostCmdArgs The post command and its argument.
11014 */
11015static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11016{
11017 const char *pszCmd = papszPostCmdArgs[0];
11018
11019 /* Allow the kmk builtin prefix. */
11020 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
11021 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
11022 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
11023
11024 /* Command switch. */
11025 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
11026 {
11027 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
11028 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
11029 }
11030
11031 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
11032}
11033
11034
11035/**
11036 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11037 */
11038static unsigned kwGetCurrentProcessorGroup(void)
11039{
11040 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
11041 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
11042 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
11043 if (pfnGetter)
11044 {
11045 GROUP_AFFINITY GroupAffinity;
11046 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
11047 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
11048 return GroupAffinity.Group;
11049 }
11050 return 0;
11051}
11052
11053
11054/**
11055 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11056 */
11057static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
11058{
11059 KSIZE cchRet = 0;
11060 HANDLE hToken;
11061 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
11062 {
11063 DWORD cbRet;
11064 TOKEN_STATISTICS TokenStats;
11065 memset(&TokenStats, 0, sizeof(TokenStats));
11066 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
11067 cchRet = sprintf(pszValue, "%" KX64_PRI,
11068 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
11069 else
11070 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
11071 CloseHandle(hToken);
11072 }
11073 else
11074 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
11075 return cchRet;
11076}
11077
11078
11079/**
11080 * Look for and expand the special environment variable.
11081 *
11082 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
11083 * couldn't accuratly determine. Currently the following variables are
11084 * implemented:
11085 * - "@@PROCESSOR_GROUP@@" - The processor group number.
11086 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
11087 * - "@@PID@@" - The kWorker process ID.
11088 * - "@@@@" - Escaped "@@".
11089 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
11090 */
11091static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
11092{
11093 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
11094 KU32 i = cEnvVars;
11095 while (i-- > 0)
11096 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
11097 && papszEnvVars[i][cchSpecialEnv] == '=')
11098 {
11099 /* We will expand stuff like @@NAME@@ */
11100 const char *pszValue = papszEnvVars[i];
11101 KSIZE offDst = 0;
11102 char szTmp[1024];
11103 for (;;)
11104 {
11105 const char *pszAt = kHlpStrChr(pszValue, '@');
11106 while (pszAt && pszAt[1] != '@')
11107 pszAt = kHlpStrChr(pszAt + 1, '@');
11108 if (pszAt)
11109 {
11110 KSIZE cchSrc = pszAt - pszValue;
11111 if (offDst + cchSrc < sizeof(szTmp))
11112 {
11113 char szSrc[64];
11114
11115 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11116 offDst += cchSrc;
11117 pszValue = pszAt + 2;
11118
11119 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11120 {
11121 pszValue += 15;
11122 if (g_iProcessGroup == -1)
11123 g_iProcessGroup = kwGetCurrentProcessorGroup();
11124 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11125 }
11126 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11127 {
11128 pszValue += 19;
11129 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11130 }
11131 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11132 {
11133 pszValue += 5;
11134 cchSrc = sprintf(szSrc, "%d", getpid());
11135 }
11136 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
11137 {
11138 pszValue += 2;
11139 szSrc[0] = '@';
11140 szSrc[1] = '@';
11141 szSrc[2] = '\0';
11142 cchSrc = 2;
11143 }
11144 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
11145 {
11146 static unsigned int s_iCounter = 0;
11147 pszValue += 15;
11148 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
11149 }
11150 else
11151 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
11152 pszValue - 2);
11153 if (offDst + cchSrc < sizeof(szTmp))
11154 {
11155 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
11156 offDst += cchSrc;
11157 continue;
11158 }
11159 }
11160 }
11161 else
11162 {
11163 KSIZE cchSrc = kHlpStrLen(pszValue);
11164 if (offDst + cchSrc < sizeof(szTmp))
11165 {
11166 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11167 offDst += cchSrc;
11168 break;
11169 }
11170 }
11171 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
11172 }
11173 szTmp[offDst] = '\0';
11174
11175 /* Return a copy of it: */
11176 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
11177 if (papszEnvVars[i])
11178 {
11179 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
11180 return 0;
11181 }
11182 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
11183 }
11184
11185 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
11186}
11187
11188
11189/**
11190 * Part 2 of the "JOB" command handler.
11191 *
11192 * @returns The exit code of the job.
11193 * @param pszExecutable The executable to execute.
11194 * @param pszCwd The current working directory of the job.
11195 * @param cArgs The number of arguments.
11196 * @param papszArgs The argument vector.
11197 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11198 * @param cEnvVars The number of environment variables.
11199 * @param papszEnvVars The environment vector.
11200 * @param pszSpecialEnv Name of special environment variable that
11201 * requires selective expansion here.
11202 * @param fNoPchCaching Whether to disable precompiled header file
11203 * caching. Avoid trouble when creating them.
11204 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11205 * @param papszPostCmdArgs The post command and its argument.
11206 */
11207static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
11208 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11209 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
11210 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11211{
11212 int rcExit;
11213 PKWTOOL pTool;
11214 char *pszSpecialEnvFree = NULL;
11215
11216 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
11217 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
11218#ifdef KW_LOG_ENABLED
11219 {
11220 KU32 i;
11221 for (i = 0; i < cArgs; i++)
11222 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
11223 for (i = 0; i < cPostCmdArgs; i++)
11224 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
11225 }
11226#endif
11227 g_cJobs++;
11228
11229 /*
11230 * Expand pszSpecialEnv if present.
11231 */
11232 if (*pszSpecialEnv)
11233 {
11234 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
11235 if (!rcExit)
11236 { /* likely */ }
11237 else
11238 return rcExit;
11239 }
11240
11241 /*
11242 * Lookup the tool.
11243 */
11244 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
11245 if (pTool)
11246 {
11247 /*
11248 * Change the directory if we're going to execute the job inside
11249 * this process. Then invoke the tool type specific handler.
11250 */
11251 switch (pTool->enmType)
11252 {
11253 case KWTOOLTYPE_SANDBOXED:
11254 case KWTOOLTYPE_WATCOM:
11255 {
11256 /* Change dir. */
11257 KFSLOOKUPERROR enmError;
11258 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
11259 if ( pNewCurDir == g_pCurDirObj
11260 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
11261 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11262 else if (SetCurrentDirectoryA(pszCwd))
11263 {
11264 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
11265 g_pCurDirObj = pNewCurDir;
11266 }
11267 else
11268 {
11269 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
11270 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11271 rcExit = 42 + 1;
11272 break;
11273 }
11274
11275 /* Call specific handler. */
11276 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
11277 {
11278 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
11279 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
11280 cEnvVars, papszEnvVars, fNoPchCaching);
11281 }
11282 else
11283 {
11284 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
11285 rcExit = 42 + 2;
11286 }
11287 break;
11288 }
11289
11290 case KWTOOLTYPE_EXEC:
11291 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
11292 rcExit = 42 + 2;
11293 break;
11294
11295 default:
11296 kHlpAssertFailed();
11297 kwErrPrintf("Internal tool type corruption!!\n");
11298 rcExit = 42 + 2;
11299 g_fRestart = K_TRUE;
11300 break;
11301 }
11302
11303 /*
11304 * Do the post command, if present.
11305 */
11306 if (cPostCmdArgs && rcExit == 0)
11307 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
11308 }
11309 else
11310 rcExit = 42 + 1;
11311 if (pszSpecialEnvFree)
11312 {
11313 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
11314 kHlpFree(pszSpecialEnvFree);
11315 }
11316 return rcExit;
11317}
11318
11319
11320/**
11321 * Handles a "JOB" command.
11322 *
11323 * @returns The exit code of the job.
11324 * @param pszMsg Points to the "JOB" command part of the message.
11325 * @param cbMsg Number of message bytes at @a pszMsg. There are
11326 * 4 more zero bytes after the message body to
11327 * simplify parsing.
11328 */
11329static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
11330{
11331 int rcExit = 42;
11332
11333 /*
11334 * Unpack the message.
11335 */
11336 const char *pszExecutable;
11337 KSIZE cbTmp;
11338
11339 pszMsg += sizeof("JOB");
11340 cbMsg -= sizeof("JOB");
11341
11342 /* Executable name. */
11343 pszExecutable = pszMsg;
11344 cbTmp = kHlpStrLen(pszMsg) + 1;
11345 pszMsg += cbTmp;
11346 if ( cbTmp < cbMsg
11347 && cbTmp > 2)
11348 {
11349 const char *pszCwd;
11350 cbMsg -= cbTmp;
11351
11352 /* Current working directory. */
11353 pszCwd = pszMsg;
11354 cbTmp = kHlpStrLen(pszMsg) + 1;
11355 pszMsg += cbTmp;
11356 if ( cbTmp + sizeof(KU32) < cbMsg
11357 && cbTmp >= 2)
11358 {
11359 KU32 cArgs;
11360 cbMsg -= cbTmp;
11361
11362 /* Argument count. */
11363 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
11364 pszMsg += sizeof(cArgs);
11365 cbMsg -= sizeof(cArgs);
11366
11367 if (cArgs > 0 && cArgs < 4096)
11368 {
11369 /* The argument vector. */
11370 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
11371 if (papszArgs)
11372 {
11373 KU32 i;
11374 for (i = 0; i < cArgs; i++)
11375 {
11376 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
11377 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
11378 pszMsg += cbTmp;
11379 if (cbTmp < cbMsg)
11380 cbMsg -= cbTmp;
11381 else
11382 {
11383 cbMsg = 0;
11384 break;
11385 }
11386
11387 }
11388 papszArgs[cArgs] = 0;
11389
11390 /* Environment variable count. */
11391 if (cbMsg > sizeof(KU32))
11392 {
11393 KU32 cEnvVars;
11394 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
11395 pszMsg += sizeof(cEnvVars);
11396 cbMsg -= sizeof(cEnvVars);
11397
11398 if (cEnvVars >= 0 && cEnvVars < 4096)
11399 {
11400 /* The argument vector. */
11401 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
11402 if (papszEnvVars)
11403 {
11404 for (i = 0; i < cEnvVars; i++)
11405 {
11406 papszEnvVars[i] = pszMsg;
11407 cbTmp = kHlpStrLen(pszMsg) + 1;
11408 pszMsg += cbTmp;
11409 if (cbTmp < cbMsg)
11410 cbMsg -= cbTmp;
11411 else
11412 {
11413 cbMsg = 0;
11414 break;
11415 }
11416 }
11417 papszEnvVars[cEnvVars] = 0;
11418
11419 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
11420 if (cbMsg >= sizeof(KU8) * 2)
11421 {
11422 KBOOL fWatcomBrainDamange = *pszMsg++;
11423 KBOOL fNoPchCaching = *pszMsg++;
11424 cbMsg -= 2;
11425
11426 /* Name of special enviornment variable requiring selective expansion. */
11427 if (cbMsg >= 1)
11428 {
11429 const char *pszSpecialEnv = pszMsg;
11430 cbTmp = kHlpStrLen(pszMsg);
11431 pszMsg += cbTmp + 1;
11432 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
11433
11434 /* Post command argument count (can be zero). */
11435 if (cbMsg >= sizeof(KU32))
11436 {
11437 KU32 cPostCmdArgs;
11438 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
11439 pszMsg += sizeof(cPostCmdArgs);
11440 cbMsg -= sizeof(cPostCmdArgs);
11441
11442 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
11443 {
11444 char const *apszPostCmdArgs[32+1];
11445 for (i = 0; i < cPostCmdArgs; i++)
11446 {
11447 apszPostCmdArgs[i] = pszMsg;
11448 cbTmp = kHlpStrLen(pszMsg) + 1;
11449 pszMsg += cbTmp;
11450 if ( cbTmp < cbMsg
11451 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
11452 cbMsg -= cbTmp;
11453 else
11454 {
11455 cbMsg = KSIZE_MAX;
11456 break;
11457 }
11458 }
11459 if (cbMsg == 0)
11460 {
11461 apszPostCmdArgs[cPostCmdArgs] = NULL;
11462
11463 /*
11464 * The next step.
11465 */
11466 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
11467 cArgs, papszArgs, fWatcomBrainDamange,
11468 cEnvVars, papszEnvVars, pszSpecialEnv,
11469 fNoPchCaching,
11470 cPostCmdArgs, apszPostCmdArgs);
11471 }
11472 else if (cbMsg == KSIZE_MAX)
11473 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
11474 else
11475 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
11476 }
11477 else
11478 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
11479 }
11480 else
11481 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
11482 }
11483 else
11484 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
11485 }
11486 else
11487 kwErrPrintf("Detected bogus message unpacking flags!\n");
11488 kHlpFree((void *)papszEnvVars);
11489 }
11490 else
11491 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
11492 }
11493 else
11494 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
11495 }
11496 else
11497 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
11498 kHlpFree((void *)papszArgs);
11499 }
11500 else
11501 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
11502 }
11503 else
11504 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
11505 }
11506 else
11507 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
11508 }
11509 else
11510 kwErrPrintf("Detected bogus message unpacking executable path!\n");
11511 return rcExit;
11512}
11513
11514
11515/**
11516 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
11517 *
11518 * @retval 0 on success.
11519 * @retval -1 on error (fully bitched).
11520 *
11521 * @param hPipe The pipe handle.
11522 * @param pvBuf The buffer to write out out.
11523 * @param cbToWrite The number of bytes to write.
11524 */
11525static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
11526{
11527 KU8 const *pbBuf = (KU8 const *)pvBuf;
11528 KU32 cbLeft = cbToWrite;
11529 while (g_rcCtrlC == 0)
11530 {
11531 DWORD cbActuallyWritten = 0;
11532 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
11533 {
11534 cbLeft -= cbActuallyWritten;
11535 if (!cbLeft)
11536 return 0;
11537 pbBuf += cbActuallyWritten;
11538 }
11539 else
11540 {
11541 DWORD dwErr = GetLastError();
11542 if (cbLeft == cbToWrite)
11543 kwErrPrintf("WriteFile failed: %u\n", dwErr);
11544 else
11545 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
11546 return -1;
11547 }
11548 }
11549 return -1;
11550}
11551
11552
11553/**
11554 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
11555 *
11556 * @retval 0 on success.
11557 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
11558 * @retval -1 on error (fully bitched).
11559 * @param hPipe The pipe handle.
11560 * @param pvBuf The buffer to read into.
11561 * @param cbToRead The number of bytes to read.
11562 * @param fShutdownOkay Whether connection shutdown while reading the
11563 * first byte is okay or not.
11564 */
11565static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
11566{
11567 KU8 *pbBuf = (KU8 *)pvBuf;
11568 KU32 cbLeft = cbToRead;
11569 while (g_rcCtrlC == 0)
11570 {
11571 DWORD cbActuallyRead = 0;
11572 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
11573 {
11574 cbLeft -= cbActuallyRead;
11575 if (!cbLeft)
11576 return 0;
11577 pbBuf += cbActuallyRead;
11578 }
11579 else
11580 {
11581 DWORD dwErr = GetLastError();
11582 if (cbLeft == cbToRead)
11583 {
11584 if ( fMayShutdown
11585 && dwErr == ERROR_BROKEN_PIPE)
11586 return 1;
11587 kwErrPrintf("ReadFile failed: %u\n", dwErr);
11588 }
11589 else
11590 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
11591 return -1;
11592 }
11593 }
11594 return -1;
11595}
11596
11597
11598/**
11599 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
11600 *
11601 * @returns pszBuf
11602 * @param pszBuf The buffer (sufficiently large).
11603 * @param uValue The value.
11604 */
11605static const char *kwFmtU64(char *pszBuf, KU64 uValue)
11606{
11607 char szTmp[64];
11608 char *psz = &szTmp[63];
11609 int cch = 4;
11610
11611 *psz-- = '\0';
11612 do
11613 {
11614 if (--cch == 0)
11615 {
11616 *psz-- = ' ';
11617 cch = 3;
11618 }
11619 *psz-- = (uValue % 10) + '0';
11620 uValue /= 10;
11621 } while (uValue != 0);
11622
11623 return strcpy(pszBuf, psz + 1);
11624}
11625
11626
11627/**
11628 * Prints statistics.
11629 */
11630static void kwPrintStats(void)
11631{
11632 PROCESS_MEMORY_COUNTERS_EX MemInfo;
11633 MEMORYSTATUSEX MemStatus;
11634 IO_COUNTERS IoCounters;
11635 DWORD cHandles;
11636 KSIZE cMisses;
11637 char szBuf[16*1024];
11638 int off = 0;
11639 char szPrf[24];
11640 char sz1[64];
11641 char sz2[64];
11642 char sz3[64];
11643 char sz4[64];
11644
11645 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
11646
11647 szBuf[off++] = '\n';
11648
11649 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
11650 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
11651 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
11652 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
11653 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
11654
11655 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
11656 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
11657
11658 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
11659 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
11660 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
11661
11662 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
11663 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
11664 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
11665
11666 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
11667 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
11668 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
11669 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
11670 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
11671 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
11672 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
11673
11674 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
11675 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
11676 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
11677 kwFmtU64(sz1, g_pFsCache->cObjects),
11678 kwFmtU64(sz2, g_pFsCache->cbObjects),
11679 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
11680 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
11681 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
11682 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
11683 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
11684 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
11685#ifdef KFSCACHE_CFG_UTF16
11686 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
11687 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
11688 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
11689 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
11690 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
11691#endif
11692 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
11693 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
11694 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
11695 kwFmtU64(sz3, g_pFsCache->cChildHashed),
11696 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
11697
11698 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
11699 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
11700 kwFmtU64(sz1, g_pFsCache->cLookups),
11701 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
11702 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11703 kwFmtU64(sz3, g_pFsCache->cWalkHits),
11704 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11705 kwFmtU64(sz4, cMisses),
11706 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
11707
11708 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
11709 kwFmtU64(sz1, g_pFsCache->cChildSearches),
11710 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
11711 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
11712 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
11713 kwFmtU64(sz1, g_pFsCache->cNameChanges),
11714 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
11715 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
11716
11717
11718 /*
11719 * Process & Memory details.
11720 */
11721 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
11722 cHandles = 0;
11723 MemInfo.cb = sizeof(MemInfo);
11724 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
11725 memset(&MemInfo, 0, sizeof(MemInfo));
11726 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
11727 kwFmtU64(sz1, cHandles),
11728 kwFmtU64(sz2, MemInfo.PageFaultCount),
11729 kwFmtU64(sz3, MemInfo.PagefileUsage),
11730 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
11731 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
11732 kwFmtU64(sz1, MemInfo.WorkingSetSize),
11733 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
11734 kwFmtU64(sz3, MemInfo.PrivateUsage));
11735 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
11736 szPrf,
11737 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
11738 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
11739 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
11740 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
11741
11742 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
11743 memset(&IoCounters, 0, sizeof(IoCounters));
11744 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
11745 kwFmtU64(sz1, IoCounters.ReadTransferCount),
11746 kwFmtU64(sz2, IoCounters.ReadOperationCount));
11747 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
11748 kwFmtU64(sz1, IoCounters.WriteTransferCount),
11749 kwFmtU64(sz2, IoCounters.WriteOperationCount));
11750 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
11751 kwFmtU64(sz1, IoCounters.OtherTransferCount),
11752 kwFmtU64(sz2, IoCounters.OtherOperationCount));
11753
11754 MemStatus.dwLength = sizeof(MemStatus);
11755 if (!GlobalMemoryStatusEx(&MemStatus))
11756 memset(&MemStatus, 0, sizeof(MemStatus));
11757 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
11758 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
11759 MemStatus.ullAvailVirtual);
11760 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
11761
11762 maybe_con_fwrite(szBuf, off, 1, stdout);
11763 fflush(stdout);
11764}
11765
11766
11767/**
11768 * Handles what comes after --test.
11769 *
11770 * @returns Exit code.
11771 * @param argc Number of arguments after --test.
11772 * @param argv Arguments after --test.
11773 */
11774static int kwTestRun(int argc, char **argv)
11775{
11776 int i;
11777 int j;
11778 int rcExit;
11779 int cRepeats;
11780 char szCwd[MAX_PATH];
11781 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
11782 KU32 cEnvVars;
11783 char **papszEnvVars;
11784 const char *pszSpecialEnv = "";
11785 const char *pszSpecialEnvFull = NULL;
11786 KBOOL fWatcomBrainDamange = K_FALSE;
11787 KBOOL fNoPchCaching = K_FALSE;
11788
11789 /*
11790 * Parse arguments.
11791 */
11792 /* Repeat count. */
11793 i = 0;
11794 if (i >= argc)
11795 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
11796 if (strcmp(argv[i], "--") != 0)
11797 {
11798 cRepeats = atoi(argv[i]);
11799 if (cRepeats <= 0)
11800 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
11801 i++;
11802
11803 /* Optional directory change. */
11804 if ( i < argc
11805 && ( strcmp(argv[i], "--chdir") == 0
11806 || strcmp(argv[i], "-C") == 0 ) )
11807 {
11808 i++;
11809 if (i >= argc)
11810 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
11811 pszCwd = argv[i++];
11812 }
11813
11814 /* Optional watcom flag directory change. */
11815 if ( i < argc
11816 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
11817 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
11818 {
11819 fWatcomBrainDamange = K_TRUE;
11820 i++;
11821 }
11822
11823 /* Optional watcom flag directory change. */
11824 if ( i < argc
11825 && strcmp(argv[i], "--no-pch-caching") == 0)
11826 {
11827 fNoPchCaching = K_TRUE;
11828 i++;
11829 }
11830
11831 /* Optional directory change. */
11832 if ( i < argc
11833 && ( strcmp(argv[i], "--set-special") == 0
11834 || strcmp(argv[i], "-s") == 0 ) )
11835 {
11836 i++;
11837 if (i >= argc)
11838 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
11839 pszSpecialEnvFull = argv[i++];
11840 putenv(pszSpecialEnvFull);
11841 pszSpecialEnv = strdup(pszSpecialEnvFull);
11842 *strchr(pszSpecialEnv, '=') = '\0';
11843 }
11844
11845 /* Trigger breakpoint */
11846 if ( i < argc
11847 && strcmp(argv[i], "--breakpoint") == 0)
11848 {
11849 __debugbreak();
11850 i++;
11851 }
11852
11853 /* Check for '--'. */
11854 if (i >= argc)
11855 return kwErrPrintfRc(2, "Missing '--'\n");
11856 if (strcmp(argv[i], "--") != 0)
11857 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
11858 i++;
11859 }
11860 else
11861 {
11862 cRepeats = 1;
11863 i++;
11864 }
11865 if (i >= argc)
11866 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
11867
11868 /*
11869 * Duplicate the environment.
11870 */
11871 cEnvVars = 0;
11872 while (environ[cEnvVars] != NULL)
11873 cEnvVars++;
11874 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
11875
11876 /*
11877 * Do the job.
11878 */
11879 for (j = 0; j < cRepeats; j++)
11880 {
11881 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
11882 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
11883 argc - i, &argv[i], fWatcomBrainDamange,
11884 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
11885 0, NULL);
11886 KW_LOG(("rcExit=%d\n", rcExit));
11887 kwSandboxCleanupLate(&g_Sandbox);
11888 }
11889
11890 if (getenv("KWORKER_STATS") != NULL)
11891 kwPrintStats();
11892
11893# ifdef WITH_LOG_FILE
11894 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
11895 CloseHandle(g_hLogFile);
11896# endif
11897 return rcExit;
11898}
11899
11900
11901/**
11902 * Reads @a pszFile into memory and chops it up into an argument vector.
11903 *
11904 * @returns Pointer to the argument vector on success, NULL on failure.
11905 * @param pszFile The file to load.
11906 * @param pcArgs Where to return the number of arguments.
11907 * @param ppszFileContent Where to return the allocation.
11908 */
11909static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
11910{
11911 char **papszArgs = NULL;
11912 FILE *pFile = fopen(pszFile, "r");
11913 if (pFile)
11914 {
11915 long cbFile;
11916 if ( fseek(pFile, 0, SEEK_END) == 0
11917 && (cbFile = ftell(pFile)) >= 0
11918 && fseek(pFile, 0, SEEK_SET) == 0)
11919 {
11920 char *pszFile = kHlpAllocZ(cbFile + 3);
11921 if (pszFile)
11922 {
11923 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
11924 if ( feof(pFile)
11925 && !ferror(pFile))
11926 {
11927 size_t off = 0;
11928 int cArgs = 0;
11929 int cAllocated = 0;
11930 char ch;
11931
11932 pszFile[cbRead] = '\0';
11933 pszFile[cbRead + 1] = '\0';
11934 pszFile[cbRead + 2] = '\0';
11935
11936 while ((ch = pszFile[off]) != '\0')
11937 {
11938 char *pszArg;
11939 switch (ch)
11940 {
11941 case ' ':
11942 case '\t':
11943 case '\n':
11944 case '\r':
11945 off++;
11946 continue;
11947
11948 case '\\':
11949 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
11950 {
11951 off += 2;
11952 continue;
11953 }
11954 /* fall thru */
11955 default:
11956 pszArg = &pszFile[off];
11957 do
11958 ch = pszFile[++off];
11959 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
11960 pszFile[off++] = '\0';
11961 break;
11962
11963 case '\'':
11964 pszArg = &pszFile[++off];
11965 while ((ch = pszFile[off]) != '\0' && ch != '\'')
11966 off++;
11967 pszFile[off++] = '\0';
11968 break;
11969
11970 case '\"': /** @todo escape sequences */
11971 pszArg = &pszFile[++off];
11972 while ((ch = pszFile[off]) != '\0' && ch != '"')
11973 off++;
11974 pszFile[off++] = '\0';
11975 break;
11976 }
11977 if (cArgs + 1 >= cAllocated)
11978 {
11979 void *pvNew;
11980 cAllocated = cAllocated ? cAllocated * 2 : 16;
11981 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
11982 if (pvNew)
11983 papszArgs = (char **)pvNew;
11984 else
11985 {
11986 kHlpFree(papszArgs);
11987 papszArgs = NULL;
11988 break;
11989 }
11990 }
11991 papszArgs[cArgs] = pszArg;
11992 papszArgs[++cArgs] = NULL;
11993 }
11994 *pcArgs = cArgs;
11995 }
11996 else
11997 kwErrPrintf("Error reading '%s'!\n", pszFile);
11998 }
11999 else
12000 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
12001 }
12002 else
12003 kwErrPrintf("Error seeking '%s'!\n", pszFile);
12004 fclose(pFile);
12005 }
12006 else
12007 kwErrPrintf("Error opening '%s'!\n", pszFile);
12008 return papszArgs;
12009}
12010
12011/**
12012 * Appends a string to an string vector (arguments or enviornment).
12013 *
12014 * @returns 0 on success, non-zero on failure (exit code).
12015 * @param ppapszVector Pointer to the string pointer array.
12016 * @param pcEntries Pointer to the array size.
12017 * @param pszAppend The string to append.
12018 */
12019static int kwFullTestVectorAppend(const char ***ppapszVector, int *pcEntries, char const *pszAppend)
12020{
12021 unsigned cEntries = *pcEntries;
12022 if (!(cEntries & 15))
12023 {
12024 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
12025 if (pvNew)
12026 *ppapszVector = (const char **)pvNew;
12027 else
12028 return kwErrPrintfRc(2, "Out of memory!\n");
12029 }
12030 (*ppapszVector)[cEntries] = pszAppend;
12031 (*ppapszVector)[++cEntries] = NULL;
12032 *pcEntries = cEntries;
12033 return 0;
12034}
12035
12036
12037/**
12038 * Parses arguments for --full-test.
12039 *
12040 * @returns 0 on success, non-zero on failure (exit code).
12041 */
12042static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
12043 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
12044{
12045 PKWONETEST pCur = *ppHead;
12046 int i;
12047 for (i = 0; i < argc; i++)
12048 {
12049 int rc = 0;
12050 const char *pszArg = argv[i];
12051 if (*pszArg == 'k' && kHlpStrComp(pszArg, "kSubmit") == 0)
12052 {
12053 if (*piState != 0)
12054 {
12055 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
12056 if (!pCur)
12057 return kwErrPrintfRc(2, "Out of memory!\n");
12058 pCur->fVirgin = K_TRUE;
12059 pCur->pszCwd = pszDefaultCwd;
12060 pCur->cRuns = 1;
12061 pCur->pNext = *ppHead;
12062 *ppHead = pCur;
12063 *piState = 0;
12064 }
12065 else if (!pCur->fVirgin)
12066 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
12067 pCur->pszJobSrc = pszJobSrc;
12068 pCur->iJobSrc = i;
12069 continue; /* (to stay virgin) */
12070 }
12071 else if (*pszArg == '-' && *piState == 0)
12072 {
12073 const char *pszValue = NULL;
12074 char ch = *++pszArg;
12075 pszArg++;
12076 if (ch == '-')
12077 {
12078 ch = '\0';
12079 if (*pszArg == '\0') /* -- */
12080 *piState = 2;
12081 /* Translate or handle long options: */
12082 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
12083 ch = 'E';
12084 else if (kHlpStrComp(pszArg, "special-env") == 0)
12085 ch = 's';
12086 else if (kHlpStrComp(pszArg, "default-env") == 0)
12087 {
12088 unsigned i;
12089 pCur->cEnvVars = 0;
12090 for (i = 0; environ[i] && rc == 0; i++)
12091 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
12092 }
12093 else if (kHlpStrComp(pszArg, "chdir") == 0)
12094 ch = 'C';
12095 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
12096 ch = 'P';
12097 else if (kHlpStrComp(pszArg, "response-file") == 0)
12098 ch = '@';
12099 else if (kHlpStrComp(pszArg, "runs") == 0)
12100 ch = 'R';
12101 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
12102 pCur->fWatcomBrainDamange = K_TRUE;
12103 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
12104 pCur->fNoPchCaching = K_TRUE;
12105 else if (kHlpStrComp(pszArg, "executable") == 0)
12106 ch = 'e';
12107 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12108 {
12109 __debugbreak();
12110 continue; /* (to stay virgin) */
12111 }
12112 else
12113 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12114 pszArg = "";
12115 }
12116
12117 while (ch != '\0' && rc == 0)
12118 {
12119 /* Fetch value if needed: */
12120 switch (ch)
12121 {
12122 case '@':
12123 case 'e':
12124 case 'E':
12125 case 's':
12126 case 'C':
12127 case 'R':
12128 if (*pszArg == ':' || *pszArg == '=')
12129 pszValue = &pszArg[1];
12130 else if (*pszArg)
12131 pszValue = pszArg;
12132 else if (i + 1 < argc)
12133 pszValue = argv[++i];
12134 else
12135 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
12136 pszArg = "";
12137 break;
12138 }
12139
12140 /* Handle the option: */
12141 switch (ch)
12142 {
12143 case 'E':
12144 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
12145 break;
12146 case 'C':
12147 pCur->pszCwd = pszValue;
12148 break;
12149 case 's':
12150 pCur->pszSpecialEnv = pszValue;
12151 break;
12152 case 'e':
12153 pCur->pszExecutable = pszValue;
12154 break;
12155 case 'P':
12156 *piState = 1;
12157 if (*pszArg)
12158 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
12159 break;
12160 case 'R':
12161 pCur->cRuns = atoi(pszValue);
12162 if ((int)pCur->cRuns < 0)
12163 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
12164 break;
12165 case '@':
12166 if (cRecursions < 5)
12167 {
12168 char *pszLeaked = NULL;
12169 int cArgs = 0;
12170 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
12171 if (papszArgsLeaked)
12172 {
12173 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
12174 cRecursions + 1, pszValue);
12175 pCur = *ppHead;
12176 }
12177 else
12178 return 2;
12179 }
12180 else
12181 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
12182 break;
12183 }
12184
12185 /* next */
12186 ch = *pszArg++;
12187 }
12188 }
12189 else if (*piState == 2)
12190 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
12191 else if (*piState == 1)
12192 {
12193 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
12194 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
12195 else
12196 *piState = 2;
12197 }
12198 else
12199 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
12200 if (rc)
12201 return rc;
12202 pCur->fVirgin = K_FALSE;
12203 }
12204 return 0;
12205}
12206
12207
12208/**
12209 * Handles what comes after --full-test.
12210 *
12211 * @returns Exit code.
12212 * @param argc Number of arguments after --full-test.
12213 * @param argv Arguments after --full-test.
12214 */
12215static int kwFullTestRun(int argc, char **argv)
12216{
12217 char szDefaultCwd[MAX_PATH];
12218 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
12219 KWONETEST FirstTest;
12220 PKWONETEST pHead = &FirstTest;
12221 PKWONETEST pCur;
12222 int iState = 0;
12223 int rcExit;
12224
12225 /*
12226 * Parse arguments.
12227 */
12228 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
12229 FirstTest.pszJobSrc = "command-line";
12230 FirstTest.iJobSrc = 1;
12231 FirstTest.fVirgin = K_TRUE;
12232 FirstTest.pszCwd = pszDefaultCwd;
12233 FirstTest.cRuns = 1;
12234
12235 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
12236 if (rcExit)
12237 return rcExit;
12238
12239 /*
12240 * Do the job. LIFO ordering (see kSubmit).
12241 */
12242 for (pCur = pHead; pCur; pCur = pCur->pNext)
12243 {
12244 if (!pCur->pszExecutable && pCur->papszArgs)
12245 pCur->pszExecutable = pCur->papszArgs[0];
12246 if ( pCur->pszExecutable
12247 && pCur->cArgs > 0
12248 && pCur->cEnvVars > 0)
12249 {
12250 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
12251 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
12252 unsigned iRun;
12253
12254 for (iRun = 0; iRun < pCur->cRuns; iRun++)
12255 {
12256 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
12257 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
12258 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
12259 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
12260
12261 KW_LOG(("rcExit=%d\n", rcExit));
12262 kwSandboxCleanupLate(&g_Sandbox);
12263
12264 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
12265 }
12266 kHlpFree(papszEnvVarsCopy);
12267 }
12268 else
12269 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
12270 pCur->pszExecutable ? "" : " No executable!",
12271 pCur->cArgs < 1 ? " No arguments!" : "",
12272 pCur->cEnvVars < 1 ? " No environment!" : "",
12273 pCur->iJobSrc, pCur->pszJobSrc);
12274 }
12275
12276 if (getenv("KWORKER_STATS") != NULL)
12277 kwPrintStats();
12278
12279# ifdef WITH_LOG_FILE
12280 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12281 CloseHandle(g_hLogFile);
12282# endif
12283 return rcExit;
12284}
12285
12286
12287/**
12288 * Helper for main() argument handling that sets the processor group if
12289 * possible.
12290 */
12291static void kwSetProcessorGroup(unsigned long uGroup)
12292{
12293 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
12294 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
12295 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
12296
12297 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
12298 if (pfnSetThreadGroupAffinity)
12299 {
12300 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
12301 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
12302 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
12303 if (NewAff.Mask && (WORD)uGroup == uGroup)
12304 {
12305 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
12306 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
12307 }
12308 else if (GetLastError() == NO_ERROR)
12309 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
12310 else
12311 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
12312 uGroup, GetLastError());
12313 }
12314 else
12315 {
12316 OSVERSIONINFOA VerInfo = {0};
12317 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
12318 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
12319 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
12320 }
12321}
12322
12323
12324int main(int argc, char **argv)
12325{
12326 KSIZE cbMsgBuf = 0;
12327 KU8 *pbMsgBuf = NULL;
12328 int i;
12329 HANDLE hPipe = INVALID_HANDLE_VALUE;
12330 const char *pszTmp;
12331 KFSLOOKUPERROR enmIgnored;
12332#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
12333 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
12334 kwSandboxVecXcptEmulateChained);
12335#endif
12336#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12337 HANDLE hCurProc = GetCurrentProcess();
12338 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12339 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12340 DWORD dwType;
12341#endif
12342
12343
12344#ifdef WITH_FIXED_VIRTUAL_ALLOCS
12345 /*
12346 * Reserve memory for cl.exe
12347 */
12348 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
12349 {
12350 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
12351 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
12352 g_aFixedVirtualAllocs[i].cbFixed,
12353 MEM_RESERVE, PAGE_READWRITE);
12354 if ( !g_aFixedVirtualAllocs[i].pvReserved
12355 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
12356 {
12357 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
12358 GetLastError());
12359 if (g_aFixedVirtualAllocs[i].pvReserved)
12360 {
12361 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
12362 g_aFixedVirtualAllocs[i].pvReserved = NULL;
12363 }
12364 }
12365 }
12366#endif
12367
12368 /*
12369 * Register our Control-C and Control-Break handlers.
12370 */
12371 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
12372 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
12373
12374 /*
12375 * Create the cache and mark the temporary directory as using the custom revision.
12376 */
12377 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
12378 if (!g_pFsCache)
12379 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
12380
12381 pszTmp = getenv("TEMP");
12382 if (pszTmp && *pszTmp != '\0')
12383 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12384 pszTmp = getenv("TMP");
12385 if (pszTmp && *pszTmp != '\0')
12386 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12387 pszTmp = getenv("TMPDIR");
12388 if (pszTmp && *pszTmp != '\0')
12389 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12390
12391 /*
12392 * Make g_abDefLdBuf executable.
12393 */
12394 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
12395 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
12396 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
12397
12398#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12399 /*
12400 * Get and duplicate the console handles.
12401 */
12402 /* Standard output. */
12403 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
12404 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
12405 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12406 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
12407 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
12408 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
12409 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12410 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12411 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
12412 g_Sandbox.HandleStdOut.cRefs = 0x10001;
12413 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
12414 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
12415 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
12416 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
12417 {
12418 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
12419 g_Sandbox.cFixedHandles++;
12420 else
12421 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
12422 }
12423 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12424 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
12425
12426 /* Standard error. */
12427 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
12428 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
12429 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12430 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
12431 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
12432 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
12433 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12434 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12435 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
12436 g_Sandbox.HandleStdErr.cRefs = 0x10001;
12437 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
12438 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
12439 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
12440 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
12441 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
12442 {
12443 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
12444 g_Sandbox.cFixedHandles++;
12445 else
12446 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
12447 }
12448 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12449 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
12450
12451 /* Combined console buffer. */
12452 if (g_Sandbox.StdErr.fIsConsole)
12453 {
12454 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
12455 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12456 }
12457 else if (g_Sandbox.StdOut.fIsConsole)
12458 {
12459 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
12460 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12461 }
12462 else
12463 {
12464 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
12465 g_Sandbox.Combined.uCodepage = CP_ACP;
12466 }
12467 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
12468#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
12469
12470
12471 /*
12472 * Parse arguments.
12473 */
12474 for (i = 1; i < argc; i++)
12475 {
12476 if (strcmp(argv[i], "--pipe") == 0)
12477 {
12478 i++;
12479 if (i < argc)
12480 {
12481 char *pszEnd = NULL;
12482 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
12483 if ( *argv[i]
12484 && pszEnd != NULL
12485 && *pszEnd == '\0'
12486 && u64Value != 0
12487 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
12488 && (uintptr_t)u64Value == u64Value)
12489 hPipe = (HANDLE)(uintptr_t)u64Value;
12490 else
12491 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
12492 }
12493 else
12494 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
12495 }
12496 else if (strcmp(argv[i], "--volatile") == 0)
12497 {
12498 i++;
12499 if (i < argc)
12500 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
12501 else
12502 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
12503 }
12504 else if (strcmp(argv[i], "--test") == 0)
12505 return kwTestRun(argc - i - 1, &argv[i + 1]);
12506 else if (strcmp(argv[i], "--full-test") == 0)
12507 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
12508 else if (strcmp(argv[i], "--priority") == 0)
12509 {
12510 i++;
12511 if (i < argc)
12512 {
12513 char *pszEnd = NULL;
12514 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12515 if ( *argv[i]
12516 && pszEnd != NULL
12517 && *pszEnd == '\0'
12518 && uValue >= 1
12519 && uValue <= 5)
12520 {
12521 DWORD dwClass, dwPriority;
12522 switch (uValue)
12523 {
12524 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
12525 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
12526 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
12527 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12528 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12529 }
12530 SetPriorityClass(GetCurrentProcess(), dwClass);
12531 if (dwPriority != 0xffffffff)
12532 SetThreadPriority(GetCurrentThread(), dwPriority);
12533 }
12534 else
12535 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12536 }
12537 else
12538 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12539 }
12540 else if (strcmp(argv[i], "--group") == 0)
12541 {
12542 i++;
12543 if (i < argc)
12544 {
12545 char *pszEnd = NULL;
12546 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12547 if ( *argv[i]
12548 && pszEnd != NULL
12549 && *pszEnd == '\0'
12550 && uValue == (WORD)uValue)
12551 kwSetProcessorGroup(uValue);
12552 else
12553 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12554 }
12555 else
12556 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12557 }
12558 else if ( strcmp(argv[i], "--help") == 0
12559 || strcmp(argv[i], "-h") == 0
12560 || strcmp(argv[i], "-?") == 0)
12561 {
12562 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
12563 "usage: kWorker <--help|-h>\n"
12564 "usage: kWorker <--version|-V>\n"
12565 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
12566 "\n"
12567 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
12568 return 0;
12569 }
12570 else if ( strcmp(argv[i], "--version") == 0
12571 || strcmp(argv[i], "-V") == 0)
12572 return kbuild_version(argv[0]);
12573 else
12574 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
12575 }
12576
12577 /*
12578 * If no --pipe argument, then assume its standard input.
12579 * We need to carefully replace the CRT stdin with a handle to "nul".
12580 */
12581 if (hPipe == INVALID_HANDLE_VALUE)
12582 {
12583 hPipe = GetStdHandle(STD_INPUT_HANDLE);
12584 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
12585 {
12586 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
12587 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
12588 {
12589 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
12590 if (fdNul >= 0)
12591 {
12592 if (_dup2(fdNul, 0) >= 0)
12593 {
12594 close(fdNul);
12595 hPipe = hDuplicate;
12596 }
12597 else
12598 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12599 }
12600 else
12601 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12602 }
12603 else
12604 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12605 }
12606 else
12607 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
12608 GetFileType(hPipe), GetLastError());
12609 }
12610 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
12611 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
12612 GetFileType(hPipe), GetLastError());
12613 g_hPipe = hPipe;
12614
12615 /*
12616 * Serve the pipe.
12617 */
12618 for (;;)
12619 {
12620 KU32 cbMsg = 0;
12621 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
12622 if (rc == 0)
12623 {
12624 /* Make sure the message length is within sane bounds. */
12625 if ( cbMsg > 4
12626 && cbMsg <= 256*1024*1024)
12627 {
12628 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
12629 if (cbMsg + 4 <= cbMsgBuf)
12630 { /* likely */ }
12631 else
12632 {
12633 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
12634 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
12635 if (!pbMsgBuf)
12636 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
12637 }
12638
12639 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
12640 *(KU32 *)pbMsgBuf = cbMsg;
12641 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
12642 if (rc == 0)
12643 {
12644 const char *psz;
12645
12646 pbMsgBuf[cbMsg] = '\0';
12647 pbMsgBuf[cbMsg + 1] = '\0';
12648 pbMsgBuf[cbMsg + 2] = '\0';
12649 pbMsgBuf[cbMsg + 3] = '\0';
12650
12651 /* The first string after the header is the command. */
12652 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
12653 if ( strcmp(psz, "JOB") == 0
12654 && g_rcCtrlC == 0)
12655 {
12656 struct
12657 {
12658 KI32 rcExitCode;
12659 KU8 bExiting;
12660 KU8 abZero[3];
12661 } Reply;
12662 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
12663 Reply.bExiting = g_fRestart;
12664 Reply.abZero[0] = 0;
12665 Reply.abZero[1] = 0;
12666 Reply.abZero[2] = 0;
12667 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
12668 if ( rc == 0
12669 && !g_fRestart)
12670 {
12671 kwSandboxCleanupLate(&g_Sandbox);
12672 if (g_rcCtrlC == 0)
12673 continue;
12674 }
12675 }
12676 else
12677 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
12678 }
12679 }
12680 else
12681 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
12682 }
12683
12684 /*
12685 * If we're exitting because we're restarting, we need to delay till
12686 * kmk/kSubmit has read the result. Windows documentation says it
12687 * immediately discards pipe buffers once the pipe is broken by the
12688 * server (us). So, We flush the buffer and queues a 1 byte read
12689 * waiting for kSubmit to close the pipe when it receives the
12690 * bExiting = K_TRUE result.
12691 */
12692 if (g_fRestart)
12693 {
12694 KU8 b;
12695 FlushFileBuffers(hPipe);
12696 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
12697 }
12698
12699 CloseHandle(hPipe);
12700#ifdef WITH_LOG_FILE
12701 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12702 CloseHandle(g_hLogFile);
12703#endif
12704 if (getenv("KWORKER_STATS") != NULL)
12705 kwPrintStats();
12706 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
12707 }
12708}
12709
12710
12711/** @page pg_kWorker kSubmit / kWorker
12712 *
12713 * @section sec_kWorker_Motivation Motivation / Inspiration
12714 *
12715 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
12716 * builds on machines "infested" by Anti Virus protection and disk encryption
12717 * software. Build times jumping from 35-40 min to 77-82 min after the machine
12718 * got "infected".
12719 *
12720 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
12721 * mainly a bunch of tiny assembly and C files being compiler a million times.
12722 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
12723 * own toolchain from within the same process, saving a lot of process creation
12724 * and teardown overhead.
12725 *
12726 *
12727 * @section sec_kWorker_kSubmit About kSubmit
12728 *
12729 * When wanting to execute a job in a kWorker instance, it must be submitted
12730 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
12731 * built into kmk and does not exist as an external program. The reason for
12732 * this is that it keep track of the kWorker instances.
12733 *
12734 * The kSubmit command has the --32-bit and --64-bit options for selecting
12735 * between 32-bit and 64-bit worker instance. We generally assume the user of
12736 * the command knows which bit count the executable has, so kSubmit is spared
12737 * the extra work of finding out.
12738 *
12739 * The kSubmit command shares a environment and current directory manipulation
12740 * with the kRedirect command, but not the file redirection. So long no file
12741 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
12742 * hand for tools like OpenWatcom, NASM and YASM which all require environment
12743 * and/or current directory changes to work.
12744 *
12745 * Unlike the kRedirect command, the kSubmit command can also specify an
12746 * internall post command to be executed after the main command succeeds.
12747 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
12748 * information from Microsoft COFF object files and Watcom OMF object files and
12749 * is scheduled to replace kDepIDB.
12750 *
12751 *
12752 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
12753 *
12754 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
12755 * A job request is written by kSubmit and kWorker read, unpacks it and executes
12756 * it. When the job is completed, kWorker writes a short reply with the exit
12757 * code and an internal status indicating whether it is going to restart.
12758 *
12759 * The kWorker intance will reply to kSubmit before completing all the internal
12760 * cleanup work, so as not to delay the next job execution unnecessarily. This
12761 * includes checking its own memory consumption and checking whether it needs
12762 * restarting. So, a decision to restart unfortunately have to wait till after
12763 * the next job has completed. This is a little bit unfortunate if the next job
12764 * requires a lot of memory and kWorker has already leaked/used a lot.
12765 *
12766 *
12767 * @section sec_kWorker_How_Works How kWorker Works
12768 *
12769 * kWorker will load the executable specified by kSubmit into memory and call
12770 * it's entrypoint in a lightly sandbox'ed environment.
12771 *
12772 *
12773 * @subsection ssec_kWorker_Loaing Image loading
12774 *
12775 * kWorker will manually load all the executable images into memory, fix them
12776 * up, and make a copy of the virgin image so it can be restored using memcpy
12777 * the next time it is used.
12778 *
12779 * Imported functions are monitored and replacements used for a few of them.
12780 * These replacements are serve the following purposes:
12781 * - Provide a different command line.
12782 * - Provide a different environment.
12783 * - Intercept process termination.
12784 * - Intercept thread creation (only linker is allowed to create threads).
12785 * - Intercept file reading for caching (header files, ++) as file system
12786 * access is made even slower by anti-virus software.
12787 * - Intercept crypto hash APIs to cache MD5 digests of header files
12788 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
12789 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
12790 * in memory as writing files grows expensive with encryption and
12791 * anti-virus software active.
12792 * - Intercept some file system queries to use the kFsCache instead of
12793 * going to the kernel and slowly worm thru the AV filter driver.
12794 * - Intercept standard output/error and console writes to aggressivly
12795 * buffer the output. The MS CRT does not buffer either when it goes to
12796 * the console, resulting in terrible performance and mixing up output
12797 * with other compile jobs.
12798 * This also allows us to filter out the annoying source file announcements
12799 * by cl.exe.
12800 * - Intercept VirtualAlloc and VirtualFree to prevent
12801 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
12802 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
12803 * the callbacks run after each job.
12804 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
12805 * executables and tools using custom heaps (like the microsoft linker).
12806 * [exectuable images only]
12807 * - Intercept atexit and _onexit registration to be able run them after
12808 * each job instead of crashing as kWorker exits. This also helps avoid
12809 * some leaks. [executable image only]
12810 *
12811 * DLLs falls into two categories, system DLLs which we always load using the
12812 * native loader, and tool DLLs which can be handled like the executable or
12813 * optionally using the native loader. We maintain a hardcoded white listing of
12814 * tool DLLs we trust to load using the native loader.
12815 *
12816 * Imports of natively loaded DLLs are processed too, but we only replace a
12817 * subset of the functions compared to natively loaded excutable and DLL images.
12818 *
12819 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
12820 * This is to speed up job execution.
12821 *
12822 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
12823 * for each job run, but so far this hasn't been necessary.
12824 *
12825 *
12826 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
12827 *
12828 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
12829 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
12830 * intermediate representation between the first (c1/c1xx.dll) and second pass
12831 * (c2.dll).
12832 *
12833 * kWorker helps the compiler as best as it can. Given a little knowledge about
12834 * stable and volatile file system areas, it can do a lot of caching that a
12835 * normal compiler driver cannot easily do when given a single file.
12836 *
12837 *
12838 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
12839 *
12840 * The preprocessor part will open and process header files exactly as they are
12841 * encountered in the source files. If string.h is included by the main source
12842 * and five other header files, it will be searched for (include path), opened,
12843 * read, MD5-summed, and pre-processed six times. The last five times is just a
12844 * waste of time because of the guards or \#pragma once. A smart compiler would
12845 * make a little extra effort and realize this.
12846 *
12847 * kWorker will cache help the preprocessor by remembering places where the
12848 * header was not found with help of kFsCache, and cache the file in memory when
12849 * found. The first part is taken care of by intercepting GetFileAttributesW,
12850 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
12851 * cached, the file is kept open and the CreateFileW call returns a duplicate of
12852 * that handle. An internal handle table is used by ReadFile and CloseFile to
12853 * keep track of intercepted handles (also used for temporary file, temporary
12854 * file mappings, console buffering, and standard out/err buffering).
12855 *
12856 * PS. The header search optimization also comes in handy when cl.exe goes on
12857 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
12858 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
12859 * optionally compile the three pass DLLs as executables during development
12860 * and problem analysis.
12861 *
12862 *
12863 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
12864 *
12865 * The issues of the temporary files is pretty severe on the Dell machine used
12866 * for benchmarking with full AV and encryption. The synthetic benchmark
12867 * improved by 30% when kWorker implemented measures to keep them entirely in
12868 * memory.
12869 *
12870 * kWorker implement these by recognizing the filename pattern in CreateFileW
12871 * and creating/opening the given file as needed. The handle returned is a
12872 * duplicate of the current process, thus giving us a good chance of catching
12873 * API calls we're not intercepting.
12874 *
12875 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
12876 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
12877 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
12878 * UnmapViewOfFile.
12879 *
12880 *
12881 * @section sec_kWorker_Numbers Some measurements.
12882 *
12883 * - r2881 building src/VBox/Runtime:
12884 * - without: 2m01.016388s = 120.016388 s
12885 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
12886 * - r2884 building vbox/debug (r110512):
12887 * - without: 11m14.446609s = 674.446609 s
12888 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
12889 * - r2896 building vbox/debug (r110577):
12890 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
12891 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
12892 * MS Defender as AV):
12893 * - without: 10m24.990389s = 624.990389s
12894 * - with: 08m04.738184s = 484.738184s
12895 * - delta: 624.99s - 484.74s = 140.25s
12896 * - saved: 140.25/624.99 = 22% faster
12897 *
12898 *
12899 * @subsection subsec_kWorker_Early_Numbers Early Experiments
12900 *
12901 * These are some early experiments doing 1024 compilations of
12902 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
12903 * main function:
12904 *
12905 * Skylake (W10/amd64, only stdandard MS defender):
12906 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
12907 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
12908 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
12909 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
12910 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
12911 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
12912 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
12913 *
12914 * Dell (W7/amd64, infected by mcafee):
12915 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
12916 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
12917 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
12918 *
12919 * The command line:
12920 * @code{.cpp}
12921 "\"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"
12922 * @endcode
12923 */
12924
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