VirtualBox

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

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

kWorker: Hacked the LoadLibrary code to only have one mspdb140.dll loaded, as the a 2nd one won't be able to connect to the PDB server for some reason. Intercept a few more imports in native binaries.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 520.9 KB
Line 
1/* $Id: kWorker.c 3369 2020-06-10 10:43:44Z 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 /** Parent (real) module if this is a virtual API module (api-ms-*.dll or
323 * ext-ms-*.dll). Referenced. */
324 PKWMODULE pVirtualApiMod;
325 /** The of the loaded image bits. */
326 KSIZE cbImage;
327 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
328 KU8 iCrtSlot;
329 /** Loop prevention when working the tree. */
330 KBOOL fVisited;
331 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
332 KBOOL fNeedReInit;
333 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
334 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
335 KU8 fReInitOnMsPdbSrvEndpointChange;
336 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
337 char *pszMsPdbSrvEndpoint;
338
339 union
340 {
341 /** Data for a manually loaded image. */
342 struct
343 {
344 /** Where we load the image. */
345 KU8 *pbLoad;
346 /** Virgin copy of the image. */
347 KU8 *pbCopy;
348 /** Ldr pvBits argument. This is NULL till we've successfully resolved
349 * the imports. */
350 void *pvBits;
351 /** The state. */
352 KWMODSTATE enmState;
353 /** The re-init state. */
354 KWMODSTATE enmReInitState;
355#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
356 /** The number of entries in the table. */
357 KU32 cFunctions;
358 /** The function table address (in the copy). */
359 PRUNTIME_FUNCTION paFunctions;
360 /** Set if we've already registered a function table already. */
361 KBOOL fRegisteredFunctionTable;
362#endif
363 /** Set if we share memory with other executables. */
364 KBOOL fUseLdBuf;
365 /** Set after the first whole image copy is done. */
366 KBOOL fCanDoQuick;
367 /** Number of quick copy chunks. */
368 KU8 cQuickCopyChunks;
369 /** Number of quick zero chunks. */
370 KU8 cQuickZeroChunks;
371 /** Quicker image copy instructions that skips non-writable parts when
372 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
373 * image. */
374 struct
375 {
376 /** The copy destination. */
377 KU8 *pbDst;
378 /** The copy source. */
379 KU8 const *pbSrc;
380 /** How much to copy. */
381 KSIZE cbToCopy;
382 } aQuickCopyChunks[3];
383 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
384 struct
385 {
386 /** Where to start zeroing. */
387 KU8 *pbDst;
388 /** How much to zero. */
389 KSIZE cbToZero;
390 } aQuickZeroChunks[3];
391
392 /** Pointer to g_abInitData of the kWorkerTlsXxxK.c instance.
393 * This member is set by kwLdrTlsAllocationHook. */
394 KU8 *pabTlsInitData;
395 /** Pointer to the g_pvWorkerModule variable in kWorkerTlsXxxK.c (our instance
396 * of it). This member is set by kwLdrTlsAllocationHook. Used by our
397 * destructor to prevent after-free references. */
398 PKWMODULE *ppTlsWorkerModuleVar;
399 /** TLS index if one was allocated, otherwise KU32_MAX.
400 * This member is set by kwLdrTlsAllocationHook. */
401 KU32 idxTls;
402 /** Offset (RVA) of the TLS initialization data. */
403 KU32 offTlsInitData;
404 /** Number of bytes of TLS initialization data. */
405 KU32 cbTlsInitData;
406 /** Number of allocated bytes for TLS. */
407 KU32 cbTlsAlloc;
408 /** Number of TLS callbacks. */
409 KU32 cTlsCallbacks;
410 /** Offset (RVA) of the TLS callback table. */
411 KU32 offTlsCallbacks;
412
413 /** Number of imported modules. */
414 KSIZE cImpMods;
415 /** Import array (variable size). */
416 PKWMODULE apImpMods[1];
417 } Manual;
418 } u;
419} KWMODULE;
420
421
422typedef struct KWDYNLOAD *PKWDYNLOAD;
423typedef struct KWDYNLOAD
424{
425 /** Pointer to the next in the list. */
426 PKWDYNLOAD pNext;
427
428 /** The module handle we present to the application.
429 * This is the LoadLibraryEx return value for special modules and the
430 * KWMODULE.hOurMod value for the others. */
431 HMODULE hmod;
432
433 /** The module for non-special resource stuff, NULL if special. */
434 PKWMODULE pMod;
435
436 /** The length of the LoadLibary filename. */
437 KSIZE cchRequest;
438 /** The LoadLibrary filename. */
439 char szRequest[1];
440} KWDYNLOAD;
441
442
443/**
444 * GetModuleHandle cache for system modules frequently queried.
445 */
446typedef struct KWGETMODULEHANDLECACHE
447{
448 const char *pszName;
449 const wchar_t *pwszName;
450 KU8 cchName;
451 KU8 cwcName;
452 KBOOL fAlwaysPresent;
453 HANDLE hmod;
454} KWGETMODULEHANDLECACHE;
455typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
456
457
458/** One TLS DLL. */
459typedef struct KWTLSDLL
460{
461 const wchar_t *pwszName; /**< The DLL name. */
462 KBOOL fUsed; /**< Set if used, clear if not. */
463} KWTLSDLL;
464typedef KWTLSDLL *PKWTLSDLL;
465
466/**
467 * TLS DLL tracker.
468 */
469typedef struct KWTLSDLLENTRY
470{
471 KU32 cbTls; /**< Max TLS size. */
472 KU32 cDlls; /**< Number of DLLs we ship (paDlls). */
473 PKWTLSDLL paDlls; /**< Array of DLLs we ship. */
474} KWTLSDLLENTRY;
475typedef KWTLSDLLENTRY *PKWTLSDLLENTRY;
476
477
478/**
479 * A cached file.
480 */
481typedef struct KFSWCACHEDFILE
482{
483 /** The user data core. */
484 KFSUSERDATA Core;
485
486 /** Cached file handle. */
487 HANDLE hCached;
488 /** Cached file section handle. */
489 HANDLE hSection;
490 /** Cached file content. */
491 KU8 *pbCached;
492 /** The file size. */
493 KU32 cbCached;
494#ifdef WITH_HASH_MD5_CACHE
495 /** Set if we've got a valid MD5 hash in abMd5Digest. */
496 KBOOL fValidMd5;
497 /** The MD5 digest if fValidMd5 is set. */
498 KU8 abMd5Digest[16];
499#endif
500
501 /** Circular self reference. Prevents the object from ever going away and
502 * keeps it handy for debugging. */
503 PKFSOBJ pFsObj;
504 /** The file path (for debugging). */
505 char szPath[1];
506} KFSWCACHEDFILE;
507/** Pointer to a cached filed. */
508typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
509
510#ifdef WITH_HASH_MD5_CACHE
511
512/** Pointer to a MD5 hash instance. */
513typedef struct KWHASHMD5 *PKWHASHMD5;
514/**
515 * A MD5 hash instance.
516 */
517typedef struct KWHASHMD5
518{
519 /** The magic value. */
520 KUPTR uMagic;
521 /** Pointer to the next hash handle. */
522 PKWHASHMD5 pNext;
523 /** The cached file we've associated this handle with. */
524 PKFSWCACHEDFILE pCachedFile;
525 /** The number of bytes we've hashed. */
526 KU32 cbHashed;
527 /** Set if this has gone wrong. */
528 KBOOL fGoneBad;
529 /** Set if we're in fallback mode (file not cached). */
530 KBOOL fFallbackMode;
531 /** Set if we've already finalized the digest. */
532 KBOOL fFinal;
533 /** The MD5 fallback context. */
534 struct MD5Context Md5Ctx;
535 /** The finalized digest. */
536 KU8 abDigest[16];
537
538} KWHASHMD5;
539/** Magic value for KWHASHMD5::uMagic (Les McCann). */
540# define KWHASHMD5_MAGIC KUPTR_C(0x19350923)
541
542#endif /* WITH_HASH_MD5_CACHE */
543#ifdef WITH_TEMP_MEMORY_FILES
544
545typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
546typedef struct KWFSTEMPFILESEG
547{
548 /** File offset of data. */
549 KU32 offData;
550 /** The size of the buffer pbData points to. */
551 KU32 cbDataAlloc;
552 /** The segment data. */
553 KU8 *pbData;
554} KWFSTEMPFILESEG;
555
556typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
557typedef struct KWFSTEMPFILE
558{
559 /** Pointer to the next temporary file for this run. */
560 PKWFSTEMPFILE pNext;
561 /** The UTF-16 path. (Allocated after this structure.) */
562 const wchar_t *pwszPath;
563 /** The path length. */
564 KU16 cwcPath;
565 /** Number of active handles using this file/mapping (<= 2). */
566 KU8 cActiveHandles;
567 /** Number of active mappings (mapped views) (0 or 1). */
568 KU8 cMappings;
569 /** The amount of space allocated in the segments. */
570 KU32 cbFileAllocated;
571 /** The current file size. */
572 KU32 cbFile;
573 /** The number of segments. */
574 KU32 cSegs;
575 /** Segments making up the file. */
576 PKWFSTEMPFILESEG paSegs;
577} KWFSTEMPFILE;
578
579#endif /* WITH_TEMP_MEMORY_FILES */
580#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
581
582/**
583 * Console line buffer or output full buffer.
584 */
585typedef struct KWOUTPUTSTREAMBUF
586{
587 /** The main output handle. */
588 HANDLE hOutput;
589 /** Our backup handle. */
590 HANDLE hBackup;
591 /** Set if this is a console handle and we're in line buffered mode.
592 * When clear, we may buffer multiple lines, though try flush on line
593 * boundraries when ever possible. */
594 KBOOL fIsConsole;
595 /** Compressed GetFileType result. */
596 KU8 fFileType;
597 KU8 abPadding[2];
598 union
599 {
600 /** Line buffer mode (fIsConsole == K_TRUE). */
601 struct
602 {
603 /** Amount of pending console output in wchar_t's. */
604 KU32 cwcBuf;
605 /** The allocated buffer size. */
606 KU32 cwcBufAlloc;
607 /** Pending console output. */
608 wchar_t *pwcBuf;
609 } Con;
610 /** Fully buffered mode (fIsConsole == K_FALSE). */
611 struct
612 {
613 /** Amount of pending output (in chars). */
614 KU32 cchBuf;
615#ifdef WITH_STD_OUT_ERR_BUFFERING
616 /** The allocated buffer size (in chars). */
617 KU32 cchBufAlloc;
618 /** Pending output. */
619 char *pchBuf;
620#endif
621 } Fully;
622 } u;
623} KWOUTPUTSTREAMBUF;
624/** Pointer to a console line buffer. */
625typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
626
627/**
628 * Combined console buffer of complete lines.
629 */
630typedef struct KWCONSOLEOUTPUT
631{
632 /** The console output handle.
633 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
634 * combined output buffering. */
635 HANDLE hOutput;
636 /** The current code page for the console. */
637 KU32 uCodepage;
638 /** Amount of pending console output in wchar_t's. */
639 KU32 cwcBuf;
640 /** Number of times we've flushed it in any way (for cl.exe hack). */
641 KU32 cFlushes;
642 /** Pending console output. */
643 wchar_t wszBuf[8192];
644} KWCONSOLEOUTPUT;
645/** Pointer to a combined console buffer. */
646typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
647
648#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
649
650/** Handle type. */
651typedef enum KWHANDLETYPE
652{
653 KWHANDLETYPE_INVALID = 0,
654 KWHANDLETYPE_FSOBJ_READ_CACHE,
655 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
656#ifdef WITH_TEMP_MEMORY_FILES
657 KWHANDLETYPE_TEMP_FILE,
658 KWHANDLETYPE_TEMP_FILE_MAPPING,
659#endif
660 KWHANDLETYPE_OUTPUT_BUF
661} KWHANDLETYPE;
662
663/** Handle data. */
664typedef struct KWHANDLE
665{
666 KWHANDLETYPE enmType;
667 /** Number of references */
668 KU32 cRefs;
669 /** The current file offset. */
670 KU32 offFile;
671 /** Handle access. */
672 KU32 dwDesiredAccess;
673 /** The handle. */
674 HANDLE hHandle;
675 /** The current owner (GetCurrentThreadId). */
676 KU32 tidOwner;
677
678 /** Type specific data. */
679 union
680 {
681 /** The file system object. */
682 PKFSWCACHEDFILE pCachedFile;
683#ifdef WITH_TEMP_MEMORY_FILES
684 /** Temporary file handle or mapping handle. */
685 PKWFSTEMPFILE pTempFile;
686#endif
687#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
688 /** Buffered output stream. */
689 PKWOUTPUTSTREAMBUF pOutBuf;
690#endif
691 } u;
692} KWHANDLE;
693typedef KWHANDLE *PKWHANDLE;
694
695/**
696 * Tracking one of our memory mappings.
697 */
698typedef struct KWMEMMAPPING
699{
700 /** Number of references. */
701 KU32 cRefs;
702 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
703 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
704 KWHANDLETYPE enmType;
705 /** The mapping address. */
706 PVOID pvMapping;
707 /** Type specific data. */
708 union
709 {
710 /** The file system object. */
711 PKFSWCACHEDFILE pCachedFile;
712#ifdef WITH_TEMP_MEMORY_FILES
713 /** Temporary file handle or mapping handle. */
714 PKWFSTEMPFILE pTempFile;
715#endif
716 } u;
717} KWMEMMAPPING;
718/** Pointer to a memory mapping tracker. */
719typedef KWMEMMAPPING *PKWMEMMAPPING;
720
721
722/** Pointer to a VirtualAlloc tracker entry. */
723typedef struct KWVIRTALLOC *PKWVIRTALLOC;
724/**
725 * Tracking an VirtualAlloc allocation.
726 */
727typedef struct KWVIRTALLOC
728{
729 PKWVIRTALLOC pNext;
730 void *pvAlloc;
731 KSIZE cbAlloc;
732 /** This is KU32_MAX if not a preallocated chunk. */
733 KU32 idxPreAllocated;
734} KWVIRTALLOC;
735
736
737/** Pointer to a heap (HeapCreate) tracker entry. */
738typedef struct KWHEAP *PKWHEAP;
739/**
740 * Tracking an heap (HeapCreate)
741 */
742typedef struct KWHEAP
743{
744 PKWHEAP pNext;
745 HANDLE hHeap;
746} KWHEAP;
747
748
749/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
750typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
751/**
752 * Tracking an FlsAlloc/TlsAlloc index.
753 */
754typedef struct KWLOCALSTORAGE
755{
756 PKWLOCALSTORAGE pNext;
757 KU32 idx;
758} KWLOCALSTORAGE;
759
760
761/** Pointer to an at exit callback record */
762typedef struct KWEXITCALLACK *PKWEXITCALLACK;
763/**
764 * At exit callback record.
765 */
766typedef struct KWEXITCALLACK
767{
768 PKWEXITCALLACK pNext;
769 _onexit_t pfnCallback;
770 /** At exit doesn't have an exit code. */
771 KBOOL fAtExit;
772} KWEXITCALLACK;
773
774
775typedef enum KWTOOLTYPE
776{
777 KWTOOLTYPE_INVALID = 0,
778 KWTOOLTYPE_SANDBOXED,
779 KWTOOLTYPE_WATCOM,
780 KWTOOLTYPE_EXEC,
781 KWTOOLTYPE_END
782} KWTOOLTYPE;
783
784typedef enum KWTOOLHINT
785{
786 KWTOOLHINT_INVALID = 0,
787 KWTOOLHINT_NONE,
788 KWTOOLHINT_VISUAL_CPP_CL,
789 KWTOOLHINT_VISUAL_CPP_LINK,
790 KWTOOLHINT_END
791} KWTOOLHINT;
792
793
794/**
795 * A kWorker tool.
796 */
797typedef struct KWTOOL
798{
799 /** The user data core structure. */
800 KFSUSERDATA Core;
801
802 /** The normalized path to the program. */
803 const char *pszPath;
804 /** UTF-16 version of pszPath. */
805 wchar_t const *pwszPath;
806 /** The kind of tool. */
807 KWTOOLTYPE enmType;
808
809 union
810 {
811 struct
812 {
813 /** The main entry point. */
814 KUPTR uMainAddr;
815 /** The executable. */
816 PKWMODULE pExe;
817 /** List of dynamically loaded modules.
818 * These will be kept loaded till the tool is destroyed (if we ever do that). */
819 PKWDYNLOAD pDynLoadHead;
820 /** Module array sorted by hOurMod. */
821 PKWMODULE *papModules;
822 /** Number of entries in papModules. */
823 KU32 cModules;
824
825 /** Tool hint (for hacks and such). */
826 KWTOOLHINT enmHint;
827 } Sandboxed;
828 } u;
829} KWTOOL;
830/** Pointer to a tool. */
831typedef struct KWTOOL *PKWTOOL;
832
833
834typedef struct KWSANDBOX *PKWSANDBOX;
835typedef struct KWSANDBOX
836{
837 /** Jump buffer (first for alignment reasons). */
838 jmp_buf JmpBuf;
839 /** The tool currently running in the sandbox. */
840 PKWTOOL pTool;
841 /** The thread ID of the main thread (owner of JmpBuf). */
842 DWORD idMainThread;
843 /** Copy of the NT TIB of the main thread. */
844 NT_TIB TibMainThread;
845 /** The NT_TIB::ExceptionList value inside the try case.
846 * We restore this prior to the longjmp. */
847 void *pOutXcptListHead;
848 /** The exit code in case of longjmp. */
849 int rcExitCode;
850 /** Set if we're running. */
851 KBOOL fRunning;
852 /** Whether to disable caching of ".pch" files. */
853 KBOOL fNoPchCaching;
854
855 /** The command line. */
856 char *pszCmdLine;
857 /** The UTF-16 command line. */
858 wchar_t *pwszCmdLine;
859 /** Number of arguments in papszArgs. */
860 int cArgs;
861 /** The argument vector. */
862 char **papszArgs;
863 /** The argument vector. */
864 wchar_t **papwszArgs;
865
866 /** The _pgmptr msvcrt variable. */
867 char *pgmptr;
868 /** The _wpgmptr msvcrt variable. */
869 wchar_t *wpgmptr;
870
871 /** The _initenv msvcrt variable. */
872 char **initenv;
873 /** The _winitenv msvcrt variable. */
874 wchar_t **winitenv;
875
876 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
877 KSIZE cEnvVarsAllocated;
878 /** The _environ msvcrt variable. */
879 char **environ;
880 /** The _wenviron msvcrt variable. */
881 wchar_t **wenviron;
882 /** The shadow _environ msvcrt variable. */
883 char **papszEnvVars;
884 /** The shadow _wenviron msvcrt variable. */
885 wchar_t **papwszEnvVars;
886
887
888 /** Critical section protecting the below handle members below.
889 * @note Does not protect the individual handles. */
890 CRITICAL_SECTION HandlesLock;
891 /** Handle table. */
892 PKWHANDLE *papHandles;
893 /** Size of the handle table. */
894 KU32 cHandles;
895 /** Number of active handles in the table. */
896 KU32 cActiveHandles;
897 /** Number of handles in the handle table that will not be freed. */
898 KU32 cFixedHandles;
899 /** Total number of leaked handles. */
900 KU32 cLeakedHandles;
901
902 /** Number of active memory mappings in paMemMappings. */
903 KU32 cMemMappings;
904 /** The allocated size of paMemMappings. */
905 KU32 cMemMappingsAlloc;
906 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
907 PKWMEMMAPPING paMemMappings;
908
909#ifdef WITH_TEMP_MEMORY_FILES
910 /** Head of the list of temporary file. */
911 PKWFSTEMPFILE pTempFileHead;
912#endif
913
914 /** Critical section protecting pVirtualAllocHead. */
915 CRITICAL_SECTION VirtualAllocLock;
916 /** Head of the virtual alloc allocations. */
917 PKWVIRTALLOC pVirtualAllocHead;
918 /** Head of the heap list (HeapCreate).
919 * This is only done from images we forcibly restore. */
920 PKWHEAP pHeapHead;
921 /** Head of the FlsAlloc indexes. */
922 PKWLOCALSTORAGE pFlsAllocHead;
923 /** Head of the TlsAlloc indexes. */
924 PKWLOCALSTORAGE pTlsAllocHead;
925
926 /** The at exit callback head.
927 * This is only done from images we forcibly restore. */
928 PKWEXITCALLACK pExitCallbackHead;
929
930 MY_UNICODE_STRING SavedCommandLine;
931
932#ifdef WITH_HASH_MD5_CACHE
933 /** The special MD5 hash instance. */
934 PKWHASHMD5 pHashHead;
935 /** ReadFile sets these while CryptHashData claims and clears them.
936 *
937 * This is part of the heuristics we use for MD5 caching for header files. The
938 * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
939 * header, then passes the same buffer and read byte count to CryptHashData.
940 */
941 struct
942 {
943 /** The cached file last read from. */
944 PKFSWCACHEDFILE pCachedFile;
945 /** The file offset of the last cached read. */
946 KU32 offRead;
947 /** The number of bytes read last. */
948 KU32 cbRead;
949 /** The buffer pointer of the last read. */
950 void *pvRead;
951 } LastHashRead;
952#endif
953
954#ifdef WITH_CRYPT_CTX_REUSE
955 /** Reusable crypt contexts. */
956 struct
957 {
958 /** The creation provider type. */
959 KU32 dwProvType;
960 /** The creation flags. */
961 KU32 dwFlags;
962 /** The length of the container name. */
963 KU32 cwcContainer;
964 /** The length of the provider name. */
965 KU32 cwcProvider;
966 /** The container name string. */
967 wchar_t *pwszContainer;
968 /** The provider name string. */
969 wchar_t *pwszProvider;
970 /** The context handle. */
971 HCRYPTPROV hProv;
972 } aCryptCtxs[4];
973 /** Number of reusable crypt conexts in aCryptCtxs. */
974 KU32 cCryptCtxs;
975#endif
976
977
978#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
979 /** The internal standard output handle. */
980 KWHANDLE HandleStdOut;
981 /** The internal standard error handle. */
982 KWHANDLE HandleStdErr;
983 /** Standard output (and whatever else) buffer. */
984 KWOUTPUTSTREAMBUF StdOut;
985 /** Standard error buffer. */
986 KWOUTPUTSTREAMBUF StdErr;
987 /** Combined buffer of completed lines. */
988 KWCONSOLEOUTPUT Combined;
989#endif
990} KWSANDBOX;
991
992
993/** A CRT slot. */
994typedef struct KWCRTSLOT
995{
996 KU32 iSlot;
997
998 /** The CRT module data. */
999 PKWMODULE pModule;
1000 /** Pointer to the malloc function. */
1001 void * (__cdecl *pfnMalloc)(size_t);
1002 /** Pointer to the beginthreadex function. */
1003 uintptr_t (__cdecl *pfnBeginThreadEx)(void *, unsigned, unsigned (__stdcall *)(void *), void *, unsigned, unsigned *);
1004
1005} KWCRTSLOT;
1006typedef KWCRTSLOT *PKWCRTSLOT;
1007
1008
1009/** Replacement function entry. */
1010typedef struct KWREPLACEMENTFUNCTION
1011{
1012 /** The function name. */
1013 const char *pszFunction;
1014 /** The length of the function name. */
1015 KSIZE cchFunction;
1016 /** The module name (optional). */
1017 const char *pszModule;
1018 /** The replacement function, data address or CRT slot function array. */
1019 KUPTR pfnReplacement;
1020 /** Only replace in the executable.
1021 * @todo fix the reinitialization of non-native DLLs! */
1022 KBOOL fOnlyExe;
1023 /** Set if pfnReplacement points to a CRT slot function array. */
1024 KBOOL fCrtSlotArray;
1025} KWREPLACEMENTFUNCTION;
1026typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
1027
1028#if 0
1029/** Replacement function entry. */
1030typedef struct KWREPLACEMENTDATA
1031{
1032 /** The function name. */
1033 const char *pszFunction;
1034 /** The length of the function name. */
1035 KSIZE cchFunction;
1036 /** The module name (optional). */
1037 const char *pszModule;
1038 /** Function providing the replacement. */
1039 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
1040} KWREPLACEMENTDATA;
1041typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
1042#endif
1043
1044/**
1045 * One test job (--full-test).
1046 */
1047typedef struct KWONETEST
1048{
1049 /** Where this job originated. */
1050 const char *pszJobSrc;
1051 /** The argument number it started with. */
1052 unsigned iJobSrc;
1053 /** Set if virgin, clear if modified. */
1054 KBOOL fVirgin;
1055
1056 /** Number of runs to give it. */
1057 unsigned cRuns;
1058
1059 /** @name kSubmitHandleJobUnpacked arguments
1060 * @{ */
1061 const char *pszExecutable;
1062 const char *pszCwd;
1063 KU32 cArgs;
1064 const char **papszArgs;
1065 KU32 cEnvVars;
1066 const char **papszEnvVars;
1067 const char *pszSpecialEnv;
1068 KBOOL fWatcomBrainDamange;
1069 KBOOL fNoPchCaching;
1070 KU32 cPostCmdArgs;
1071 const char **papszPostCmdArgs;
1072 /** @} */
1073
1074 /** Pointer to the next one. */
1075 struct KWONETEST *pNext;
1076} KWONETEST;
1077/** Pointer to one test job. */
1078typedef KWONETEST *PKWONETEST;
1079
1080
1081/*********************************************************************************************************************************
1082* Global Variables *
1083*********************************************************************************************************************************/
1084/** The sandbox data. */
1085static KWSANDBOX g_Sandbox;
1086
1087/** The module currently occupying g_abDefLdBuf. */
1088static PKWMODULE g_pModInLdBuf = NULL;
1089
1090/** The module that previuosly occupied g_abDefLdBuf. */
1091static PKWMODULE g_pModPrevInLdBuf = NULL;
1092
1093/** Module list head. */
1094static PKWMODULE g_pModuleHead = NULL;
1095/** Where to insert the next module. */
1096static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1097
1098/** Module hash table. */
1099static PKWMODULE g_apModules[127];
1100
1101/** GetModuleHandle cache. */
1102static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1103{
1104#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1105 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1106#if 1
1107 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1108 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1109#endif
1110 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1111};
1112
1113/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1114static PKWMODULE g_pModPendingTlsAlloc = NULL;
1115
1116/** The 1KB TLS DLLs. */
1117static KWTLSDLL g_aTls1KDlls[] =
1118{
1119 { L"kWorkerTls1K.dll", K_FALSE },
1120 { L"kWorkerTls1K01.dll", K_FALSE },
1121 { L"kWorkerTls1K02.dll", K_FALSE },
1122 { L"kWorkerTls1K03.dll", K_FALSE },
1123 { L"kWorkerTls1K04.dll", K_FALSE },
1124 { L"kWorkerTls1K05.dll", K_FALSE },
1125 { L"kWorkerTls1K06.dll", K_FALSE },
1126 { L"kWorkerTls1K07.dll", K_FALSE },
1127};
1128
1129/** The 64KB TLS DLLs. */
1130static KWTLSDLL g_aTls64KDlls[] =
1131{
1132 { L"kWorkerTls64K.dll", K_FALSE },
1133 { L"kWorkerTls64K01.dll", K_FALSE },
1134 { L"kWorkerTls64K02.dll", K_FALSE },
1135 { L"kWorkerTls64K03.dll", K_FALSE },
1136 { L"kWorkerTls64K04.dll", K_FALSE },
1137 { L"kWorkerTls64K05.dll", K_FALSE },
1138 { L"kWorkerTls64K06.dll", K_FALSE },
1139 { L"kWorkerTls64K07.dll", K_FALSE },
1140};
1141
1142/** The 128KB TLS DLLs. */
1143static KWTLSDLL g_aTls128KDlls[] =
1144{
1145 { L"kWorkerTls128K.dll", K_FALSE },
1146 { L"kWorkerTls128K01.dll", K_FALSE },
1147 { L"kWorkerTls128K02.dll", K_FALSE },
1148 { L"kWorkerTls128K03.dll", K_FALSE },
1149 { L"kWorkerTls128K04.dll", K_FALSE },
1150 { L"kWorkerTls128K05.dll", K_FALSE },
1151 { L"kWorkerTls128K06.dll", K_FALSE },
1152 { L"kWorkerTls128K07.dll", K_FALSE },
1153};
1154
1155/** The 512KB TLS DLLs. */
1156static KWTLSDLL g_aTls512KDlls[] =
1157{
1158 { L"kWorkerTls512K.dll", K_FALSE },
1159 { L"kWorkerTls512K01.dll", K_FALSE },
1160 { L"kWorkerTls512K02.dll", K_FALSE },
1161 { L"kWorkerTls512K03.dll", K_FALSE },
1162 { L"kWorkerTls512K04.dll", K_FALSE },
1163 { L"kWorkerTls512K05.dll", K_FALSE },
1164 { L"kWorkerTls512K06.dll", K_FALSE },
1165 { L"kWorkerTls512K07.dll", K_FALSE },
1166};
1167
1168/** The TLS DLLs grouped by size. */
1169static KWTLSDLLENTRY const g_aTlsDlls[] =
1170{
1171 { 1024, K_ELEMENTS(g_aTls1KDlls), g_aTls1KDlls },
1172 { 64*1024, K_ELEMENTS(g_aTls64KDlls), g_aTls64KDlls },
1173 { 128*1024, K_ELEMENTS(g_aTls128KDlls), g_aTls128KDlls },
1174 { 512*1024, K_ELEMENTS(g_aTls512KDlls), g_aTls512KDlls },
1175};
1176
1177/** CRT slots.
1178 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1179static KWCRTSLOT g_aCrtSlots[32];
1180
1181/** windbg .reload statements. vs */
1182char g_szReloads[4096];
1183/** Current offset into g_szReloads. */
1184KU32 volatile g_cchReloads;
1185
1186/** The file system cache. */
1187static PKFSCACHE g_pFsCache;
1188/** The current directory (referenced). */
1189static PKFSOBJ g_pCurDirObj = NULL;
1190#ifdef KBUILD_OS_WINDOWS
1191/** The windows system32 directory (referenced). */
1192static PKFSDIR g_pWinSys32 = NULL;
1193#endif
1194
1195/** Verbosity level. */
1196static int g_cVerbose = 2;
1197
1198/** Whether we should restart the worker. */
1199static KBOOL g_fRestart = K_FALSE;
1200
1201/** The process group this worker is tied to (--group option), -1 if none. */
1202static KI32 g_iProcessGroup = -1;
1203
1204/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1205static int volatile g_rcCtrlC = 0;
1206
1207/** The communication pipe handle. We break this when we see Ctrl-C such. */
1208#ifdef KBUILD_OS_WINDOWS
1209static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1210#else
1211static int g_hPipe = -1;
1212#endif
1213
1214
1215/* Further down. */
1216extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1217extern KU32 const g_cSandboxReplacements;
1218
1219extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1220extern KU32 const g_cSandboxNativeReplacements;
1221
1222extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1223extern KU32 const g_cSandboxGetProcReplacements;
1224
1225
1226/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1227 * cover the default executable link address of 0x400000.
1228 * @remarks Early main() makes it read+write+executable. Attempts as having
1229 * it as a separate section failed because the linker insists on
1230 * writing out every zero in the uninitialized section, resulting in
1231 * really big binaries. */
1232__declspec(align(0x1000))
1233static KU8 g_abDefLdBuf[16*1024*1024];
1234
1235#ifdef WITH_LOG_FILE
1236/** Log file handle. */
1237static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1238#endif
1239
1240
1241#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1242/** Virtual address space reserved for CL.EXE heap manager.
1243 *
1244 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1245 * address. It's among other things used for precompiled headers, which
1246 * seemingly have addresses hardcoded into them and won't work if mapped
1247 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1248 * for it. (The /Zm option may affect this allocation.)
1249 */
1250static struct
1251{
1252 /** The memory address we need. */
1253 KUPTR const uFixed;
1254 /** How much we need to fix. */
1255 KSIZE const cbFixed;
1256 /** What we actually got, NULL if given back. */
1257 void *pvReserved;
1258 /** Whether it is in use or not. */
1259 KBOOL fInUse;
1260} g_aFixedVirtualAllocs[] =
1261{
1262# if K_ARCH == K_ARCH_X86_32
1263 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1264 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1265 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1266# else
1267 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1268# endif
1269};
1270#endif
1271
1272
1273#ifdef WITH_HISTORY
1274/** The job history. */
1275static char *g_apszHistory[32];
1276/** Index of the next history entry. */
1277static unsigned g_iHistoryNext = 0;
1278#endif
1279
1280
1281/** Number of jobs executed. */
1282static KU32 g_cJobs;
1283/** Number of tools. */
1284static KU32 g_cTools;
1285/** Number of modules. */
1286static KU32 g_cModules;
1287/** Number of non-native modules. */
1288static KU32 g_cNonNativeModules;
1289/** Number of read-cached files. */
1290static KU32 g_cReadCachedFiles;
1291/** Total size of read-cached files. */
1292static KSIZE g_cbReadCachedFiles;
1293
1294/** Total number of ReadFile calls. */
1295static KSIZE g_cReadFileCalls;
1296/** Total bytes read via ReadFile. */
1297static KSIZE g_cbReadFileTotal;
1298/** Total number of read from read-cached files. */
1299static KSIZE g_cReadFileFromReadCached;
1300/** Total bytes read from read-cached files. */
1301static KSIZE g_cbReadFileFromReadCached;
1302/** Total number of read from in-memory temporary files. */
1303static KSIZE g_cReadFileFromInMemTemp;
1304/** Total bytes read from in-memory temporary files. */
1305static KSIZE g_cbReadFileFromInMemTemp;
1306
1307/** Total number of WriteFile calls. */
1308static KSIZE g_cWriteFileCalls;
1309/** Total bytes written via WriteFile. */
1310static KSIZE g_cbWriteFileTotal;
1311/** Total number of written to from in-memory temporary files. */
1312static KSIZE g_cWriteFileToInMemTemp;
1313/** Total bytes written to in-memory temporary files. */
1314static KSIZE g_cbWriteFileToInMemTemp;
1315
1316
1317/*********************************************************************************************************************************
1318* Internal Functions *
1319*********************************************************************************************************************************/
1320static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1321static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1322 const char *pszSearchPath, PKWMODULE *ppMod);
1323static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1324static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName);
1325static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule);
1326static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod);
1327static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1328static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1329static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile);
1330static PKWHANDLE kwSandboxHandleGet(HANDLE hFile);
1331K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle);
1332#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1333static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1334#endif
1335static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1336
1337
1338
1339
1340/**
1341 * Debug printing.
1342 * @param pszFormat Debug format string.
1343 * @param ... Format argument.
1344 */
1345static void kwDbgPrintfV(const char *pszFormat, va_list va)
1346{
1347 if (g_cVerbose >= 2)
1348 {
1349 DWORD const dwSavedErr = GetLastError();
1350#ifdef WITH_LOG_FILE
1351 DWORD dwIgnored;
1352 char szTmp[2048];
1353 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1354 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1355 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1356 cch += cchPrefix;
1357 else
1358 {
1359 cch = sizeof(szTmp) - 1;
1360 szTmp[cch] = '\0';
1361 }
1362
1363 if (g_hLogFile == INVALID_HANDLE_VALUE)
1364 {
1365 wchar_t wszFilename[128];
1366 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1367 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1368 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1369 }
1370
1371 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1372#else
1373 fprintf(stderr, "debug: ");
1374 vfprintf(stderr, pszFormat, va);
1375#endif
1376
1377 SetLastError(dwSavedErr);
1378 }
1379}
1380
1381
1382/**
1383 * Debug printing.
1384 * @param pszFormat Debug format string.
1385 * @param ... Format argument.
1386 */
1387static void kwDbgPrintf(const char *pszFormat, ...)
1388{
1389 if (g_cVerbose >= 2)
1390 {
1391 va_list va;
1392 va_start(va, pszFormat);
1393 kwDbgPrintfV(pszFormat, va);
1394 va_end(va);
1395 }
1396}
1397
1398
1399/**
1400 * Debugger printing.
1401 * @param pszFormat Debug format string.
1402 * @param ... Format argument.
1403 */
1404static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1405{
1406 if (IsDebuggerPresent())
1407 {
1408 DWORD const dwSavedErr = GetLastError();
1409 char szTmp[2048];
1410
1411 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1412 OutputDebugStringA(szTmp);
1413
1414 SetLastError(dwSavedErr);
1415 }
1416}
1417
1418
1419/**
1420 * Debugger printing.
1421 * @param pszFormat Debug format string.
1422 * @param ... Format argument.
1423 */
1424static void kwDebuggerPrintf(const char *pszFormat, ...)
1425{
1426 va_list va;
1427 va_start(va, pszFormat);
1428 kwDebuggerPrintfV(pszFormat, va);
1429 va_end(va);
1430}
1431
1432
1433
1434/**
1435 * Error printing.
1436 * @param pszFormat Message format string.
1437 * @param ... Format argument.
1438 */
1439static void kwErrPrintfV(const char *pszFormat, va_list va)
1440{
1441 DWORD const dwSavedErr = GetLastError();
1442
1443#if defined(KW_LOG_ENABLED) && defined(WITH_LOG_FILE)
1444 va_list vaCopy;
1445# if defined(va_copy) || !defined(_MSC_VER) || _MSC_VER >= 1700 /*??*/
1446 va_copy(vaCopy, va);
1447# else
1448 vaCopy = va;
1449# endif
1450 kwDebuggerPrintf("kWorker: error: ");
1451 kwDebuggerPrintfV(pszFormat, vaCopy);
1452#endif
1453
1454 fprintf(stderr, "kWorker: error: ");
1455 vfprintf(stderr, pszFormat, va);
1456 fflush(stderr); /* In case it's a pipe. */
1457
1458 SetLastError(dwSavedErr);
1459}
1460
1461
1462/**
1463 * Error printing.
1464 * @param pszFormat Message format string.
1465 * @param ... Format argument.
1466 */
1467static void kwErrPrintf(const char *pszFormat, ...)
1468{
1469 va_list va;
1470 va_start(va, pszFormat);
1471 kwErrPrintfV(pszFormat, va);
1472 va_end(va);
1473}
1474
1475
1476/**
1477 * Error printing.
1478 * @return rc;
1479 * @param rc Return value
1480 * @param pszFormat Message format string.
1481 * @param ... Format argument.
1482 */
1483static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1484{
1485 va_list va;
1486 va_start(va, pszFormat);
1487 kwErrPrintfV(pszFormat, va);
1488 va_end(va);
1489 return rc;
1490}
1491
1492
1493#ifdef K_STRICT
1494
1495KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1496{
1497 DWORD const dwSavedErr = GetLastError();
1498
1499 fprintf(stderr,
1500 "\n"
1501 "!!Assertion failed!!\n"
1502 "Expression: %s\n"
1503 "Function : %s\n"
1504 "File: %s\n"
1505 "Line: %d\n"
1506 , pszExpr, pszFunction, pszFile, iLine);
1507
1508 SetLastError(dwSavedErr);
1509}
1510
1511
1512KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1513{
1514 DWORD const dwSavedErr = GetLastError();
1515 va_list va;
1516
1517 va_start(va, pszFormat);
1518 fprintf(stderr, pszFormat, va);
1519 va_end(va);
1520
1521 SetLastError(dwSavedErr);
1522}
1523
1524#endif /* K_STRICT */
1525
1526
1527/**
1528 * Hashes a string.
1529 *
1530 * @returns 32-bit string hash.
1531 * @param pszString String to hash.
1532 */
1533static KU32 kwStrHash(const char *pszString)
1534{
1535 /* This algorithm was created for sdbm (a public-domain reimplementation of
1536 ndbm) database library. it was found to do well in scrambling bits,
1537 causing better distribution of the keys and fewer splits. it also happens
1538 to be a good general hashing function with good distribution. the actual
1539 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1540 is the faster version used in gawk. [there is even a faster, duff-device
1541 version] the magic constant 65599 was picked out of thin air while
1542 experimenting with different constants, and turns out to be a prime.
1543 this is one of the algorithms used in berkeley db (see sleepycat) and
1544 elsewhere. */
1545 KU32 uHash = 0;
1546 KU32 uChar;
1547 while ((uChar = (unsigned char)*pszString++) != 0)
1548 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1549 return uHash;
1550}
1551
1552
1553/**
1554 * Hashes a string.
1555 *
1556 * @returns The string length.
1557 * @param pszString String to hash.
1558 * @param puHash Where to return the 32-bit string hash.
1559 */
1560static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1561{
1562 const char * const pszStart = pszString;
1563 KU32 uHash = 0;
1564 KU32 uChar;
1565 while ((uChar = (unsigned char)*pszString) != 0)
1566 {
1567 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1568 pszString++;
1569 }
1570 *puHash = uHash;
1571 return pszString - pszStart;
1572}
1573
1574
1575/**
1576 * Hashes a string.
1577 *
1578 * @returns The string length in wchar_t units.
1579 * @param pwszString String to hash.
1580 * @param puHash Where to return the 32-bit string hash.
1581 */
1582static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1583{
1584 const wchar_t * const pwszStart = pwszString;
1585 KU32 uHash = 0;
1586 KU32 uChar;
1587 while ((uChar = *pwszString) != 0)
1588 {
1589 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1590 pwszString++;
1591 }
1592 *puHash = uHash;
1593 return pwszString - pwszStart;
1594}
1595
1596
1597/**
1598 * Converts the given string to unicode.
1599 *
1600 * @returns Length of the resulting string in wchar_t's.
1601 * @param pszSrc The source string.
1602 * @param pwszDst The destination buffer.
1603 * @param cwcDst The size of the destination buffer in wchar_t's.
1604 */
1605static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1606{
1607 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1608 KSIZE offDst = 0;
1609 while (offDst < cwcDst)
1610 {
1611 char ch = *pszSrc++;
1612 pwszDst[offDst++] = ch;
1613 if (!ch)
1614 return offDst - 1;
1615 kHlpAssert((unsigned)ch < 127);
1616 }
1617
1618 pwszDst[offDst - 1] = '\0';
1619 return offDst;
1620}
1621
1622
1623/**
1624 * Converts the given string to UTF-16, allocating the buffer.
1625 *
1626 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1627 * the source string.
1628 * @param pchSrc The source string.
1629 * @param cchSrc The length of the source string.
1630 */
1631static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1632{
1633 DWORD const dwErrSaved = GetLastError();
1634 KSIZE cwcBuf = cchSrc + 1;
1635 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1636 if (pwszBuf)
1637 {
1638 if (cchSrc > 0)
1639 {
1640 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1641 if (cwcRet > 0)
1642 {
1643 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1644 pwszBuf[cwcRet] = '\0';
1645 }
1646 else
1647 {
1648 kHlpFree(pwszBuf);
1649
1650 /* Figure the length and allocate the right buffer size. */
1651 SetLastError(NO_ERROR);
1652 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1653 if (cwcRet)
1654 {
1655 cwcBuf = cwcRet + 2;
1656 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1657 if (pwszBuf)
1658 {
1659 SetLastError(NO_ERROR);
1660 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1661 if (cwcRet)
1662 {
1663 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1664 pwszBuf[cwcRet] = '\0';
1665 }
1666 else
1667 {
1668 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1669 kHlpFree(pwszBuf);
1670 pwszBuf = NULL;
1671 }
1672 }
1673 }
1674 else
1675 {
1676 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1677 pwszBuf = NULL;
1678 }
1679 }
1680 }
1681 else
1682 pwszBuf[0] = '\0';
1683 }
1684 SetLastError(dwErrSaved);
1685 return pwszBuf;
1686}
1687
1688
1689/**
1690 * Converts the given UTF-16 to a normal string.
1691 *
1692 * @returns Length of the resulting string.
1693 * @param pwszSrc The source UTF-16 string.
1694 * @param pszDst The destination buffer.
1695 * @param cbDst The size of the destination buffer in bytes.
1696 */
1697static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1698{
1699 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1700 KSIZE offDst = 0;
1701 while (offDst < cbDst)
1702 {
1703 wchar_t wc = *pwszSrc++;
1704 pszDst[offDst++] = (char)wc;
1705 if (!wc)
1706 return offDst - 1;
1707 kHlpAssert((unsigned)wc < 127);
1708 }
1709
1710 pszDst[offDst - 1] = '\0';
1711 return offDst;
1712}
1713
1714
1715/**
1716 * Converts the given UTF-16 to ASSI, allocating the buffer.
1717 *
1718 * @returns Pointer to the new heap allocation containing the ANSI version of
1719 * the source string.
1720 * @param pwcSrc The source string.
1721 * @param cwcSrc The length of the source string.
1722 */
1723static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1724{
1725 DWORD const dwErrSaved = GetLastError();
1726 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1727 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1728 if (pszBuf)
1729 {
1730 if (cwcSrc > 0)
1731 {
1732 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1733 if (cchRet > 0)
1734 {
1735 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1736 pszBuf[cchRet] = '\0';
1737 }
1738 else
1739 {
1740 kHlpFree(pszBuf);
1741
1742 /* Figure the length and allocate the right buffer size. */
1743 SetLastError(NO_ERROR);
1744 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1745 if (cchRet)
1746 {
1747 cbBuf = cchRet + 2;
1748 pszBuf = (char *)kHlpAlloc(cbBuf);
1749 if (pszBuf)
1750 {
1751 SetLastError(NO_ERROR);
1752 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1753 if (cchRet)
1754 {
1755 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1756 pszBuf[cchRet] = '\0';
1757 }
1758 else
1759 {
1760 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1761 kHlpFree(pszBuf);
1762 pszBuf = NULL;
1763 }
1764 }
1765 }
1766 else
1767 {
1768 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1769 pszBuf = NULL;
1770 }
1771 }
1772 }
1773 else
1774 pszBuf[0] = '\0';
1775 }
1776 SetLastError(dwErrSaved);
1777 return pszBuf;
1778}
1779
1780
1781
1782/** UTF-16 string length. */
1783static KSIZE kwUtf16Len(wchar_t const *pwsz)
1784{
1785 KSIZE cwc = 0;
1786 while (*pwsz != '\0')
1787 cwc++, pwsz++;
1788 return cwc;
1789}
1790
1791/**
1792 * Copy out the UTF-16 string following the convension of GetModuleFileName
1793 */
1794static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1795{
1796 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1797 if (cwcSrc + 1 <= cwcDst)
1798 {
1799 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1800 return (DWORD)cwcSrc;
1801 }
1802 if (cwcDst > 0)
1803 {
1804 KSIZE cwcDstTmp = cwcDst - 1;
1805 pwszDst[cwcDstTmp] = '\0';
1806 if (cwcDstTmp > 0)
1807 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1808 }
1809 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1810 return (DWORD)cwcDst;
1811}
1812
1813
1814/**
1815 * Copy out the ANSI string following the convension of GetModuleFileName
1816 */
1817static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1818{
1819 KSIZE cchSrc = kHlpStrLen(pszSrc);
1820 if (cchSrc + 1 <= cbDst)
1821 {
1822 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1823 return (DWORD)cchSrc;
1824 }
1825 if (cbDst > 0)
1826 {
1827 KSIZE cbDstTmp = cbDst - 1;
1828 pszDst[cbDstTmp] = '\0';
1829 if (cbDstTmp > 0)
1830 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1831 }
1832 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1833 return (DWORD)cbDst;
1834}
1835
1836
1837/**
1838 * Normalizes the path so we get a consistent hash.
1839 *
1840 * @returns status code.
1841 * @param pszPath The path.
1842 * @param pszNormPath The output buffer.
1843 * @param cbNormPath The size of the output buffer.
1844 */
1845static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1846{
1847 KFSLOOKUPERROR enmError;
1848 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1849 if (pFsObj)
1850 {
1851 KBOOL fRc;
1852 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1853 kFsCacheObjRelease(g_pFsCache, pFsObj);
1854 if (fRc)
1855 return 0;
1856 return KERR_BUFFER_OVERFLOW;
1857 }
1858 return KERR_FILE_NOT_FOUND;
1859}
1860
1861
1862/**
1863 * Get the pointer to the filename part of the path.
1864 *
1865 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1866 * @returns Pointer to the terminator char if no filename.
1867 * @param pszPath The path to parse.
1868 */
1869static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1870{
1871 const wchar_t *pwszLast = NULL;
1872 for (;;)
1873 {
1874 wchar_t wc = *pwszPath;
1875#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1876 if (wc == '/' || wc == '\\' || wc == ':')
1877 {
1878 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1879 /* nothing */;
1880 pwszLast = pwszPath;
1881 }
1882#else
1883 if (wc == '/')
1884 {
1885 while ((wc = *++pszFilename) == '/')
1886 /* betsuni */;
1887 pwszLast = pwszPath;
1888 }
1889#endif
1890 if (!wc)
1891 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1892 pwszPath++;
1893 }
1894}
1895
1896
1897
1898/**
1899 * Retains a new reference to the given module
1900 * @returns pMod
1901 * @param pMod The module to retain.
1902 */
1903static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1904{
1905 kHlpAssert(pMod->cRefs > 0);
1906 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1907 pMod->cRefs++;
1908 return pMod;
1909}
1910
1911
1912/**
1913 * Releases a module reference.
1914 *
1915 * @param pMod The module to release.
1916 */
1917static void kwLdrModuleRelease(PKWMODULE pMod)
1918{
1919 if (--pMod->cRefs == 0)
1920 {
1921 /* Make sure it doesn't receive any more native TLS callbacks.if non-native. */
1922 if (!pMod->fNative && pMod->u.Manual.ppTlsWorkerModuleVar)
1923 {
1924 *pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1925 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1926 }
1927
1928 /* Unlink it from the hash table. */
1929 if (!pMod->fExe)
1930 {
1931 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1932 if (g_apModules[idx] == pMod)
1933 g_apModules[idx] = pMod->pNextHash;
1934 else
1935 {
1936 PKWMODULE pPrev = g_apModules[idx];
1937 kHlpAssert(pPrev != NULL);
1938 while (pPrev->pNextHash != pMod)
1939 {
1940 pPrev = pPrev->pNextHash;
1941 kHlpAssert(pPrev != NULL);
1942 }
1943 pPrev->pNextHash = pMod->pNextHash;
1944 }
1945 }
1946
1947 /* Unlink it from the list. */
1948 if (pMod != g_pModuleHead)
1949 {
1950 PKWMODULE pPrev = g_pModuleHead;
1951 while (pPrev)
1952 {
1953 if (pPrev->pNextList == pMod)
1954 {
1955 pPrev->pNextList = pMod->pNextList;
1956 if (!pMod->pNextList)
1957 g_ppModuleNext = &pPrev->pNextList;
1958 break;
1959 }
1960 pPrev = pPrev->pNextList;
1961 }
1962 kHlpAssert(pPrev != NULL);
1963 }
1964 else
1965 {
1966 g_pModuleHead = pMod->pNextList;
1967 if (!pMod->pNextList)
1968 g_ppModuleNext = &g_pModuleHead;
1969 }
1970
1971 /* Release import modules. */
1972 if (!pMod->fNative)
1973 {
1974 KSIZE idx = pMod->u.Manual.cImpMods;
1975 while (idx-- > 0)
1976 if (pMod->u.Manual.apImpMods[idx])
1977 {
1978 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1979 pMod->u.Manual.apImpMods[idx] = NULL;
1980 }
1981 }
1982
1983 /* Free our resources. */
1984 kLdrModClose(pMod->pLdrMod);
1985 pMod->pLdrMod = NULL;
1986
1987 if (!pMod->fNative)
1988 {
1989 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1990 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1991 }
1992
1993 if (pMod->iCrtSlot != KU8_MAX)
1994 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
1995
1996 if (pMod->pszMsPdbSrvEndpoint)
1997 {
1998 kHlpFree(pMod->pszMsPdbSrvEndpoint);
1999 pMod->pszMsPdbSrvEndpoint = NULL;
2000 }
2001
2002 kHlpFree(pMod);
2003 }
2004 else
2005 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
2006}
2007
2008
2009/**
2010 * Links the module into the module hash table.
2011 *
2012 * @returns pMod
2013 * @param pMod The module to link.
2014 */
2015static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
2016{
2017 if (!pMod->fExe)
2018 {
2019 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
2020 pMod->pNextHash = g_apModules[idx];
2021 g_apModules[idx] = pMod;
2022 }
2023
2024 pMod->pNextList = NULL;
2025 *g_ppModuleNext = pMod;
2026 g_ppModuleNext = &pMod->pNextList;
2027
2028 return pMod;
2029}
2030
2031
2032/**
2033 * Replaces imports for this module according to g_aSandboxNativeReplacements.
2034 *
2035 * @param pMod The natively loaded module to process.
2036 */
2037static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
2038{
2039 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
2040 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
2041 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
2042 IMAGE_NT_HEADERS const *pNtHdrs;
2043 IMAGE_DATA_DIRECTORY const *pDirEnt;
2044
2045 kHlpAssert(pMod->fNative);
2046
2047 /*
2048 * Locate the export descriptors.
2049 */
2050 /* MZ header. */
2051 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
2052 {
2053 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
2054 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
2055 }
2056 else
2057 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
2058
2059 /* Check PE header. */
2060 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2061 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
2062
2063 /* Locate the import descriptor array. */
2064 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2065 if ( pDirEnt->Size > 0
2066 && pDirEnt->VirtualAddress != 0)
2067 {
2068 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
2069 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
2070 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
2071 KU8 *pbProtRange = NULL;
2072 SIZE_T cbProtRange = 0;
2073 DWORD fOldProt = 0;
2074 KU32 const cbPage = 0x1000;
2075 BOOL fRc;
2076
2077
2078 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
2079 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
2080 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
2081
2082 /*
2083 * Walk the import descriptor array.
2084 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
2085 */
2086 while ( cLeft-- > 0
2087 && pImpDesc->Name > 0
2088 && pImpDesc->FirstThunk > 0)
2089 {
2090 KU32 iThunk;
2091 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
2092 PKWMODULE pImportMod = NULL;
2093 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
2094 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
2095 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
2096 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
2097 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
2098 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
2099 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
2100
2101 /* Iterate the thunks. */
2102 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
2103 {
2104 KUPTR const off = paOrgThunks[iThunk].u1.Function;
2105 kHlpAssertReturnVoid(off < cbImage);
2106 if (!IMAGE_SNAP_BY_ORDINAL(off))
2107 {
2108 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
2109 KSIZE const cchSymbol = kHlpStrLen((const char *)&pName->Name[0]);
2110 KU32 i = g_cSandboxNativeReplacements;
2111 while (i-- > 0)
2112 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
2113 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
2114 {
2115 if ( !g_aSandboxNativeReplacements[i].pszModule
2116 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
2117 {
2118 KWLDR_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
2119
2120 /* The .rdata section is normally read-only, so we need to make it writable first. */
2121 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
2122 {
2123 /* Restore previous .rdata page. */
2124 if (fOldProt)
2125 {
2126 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
2127 kHlpAssert(fRc);
2128 fOldProt = 0;
2129 }
2130
2131 /* Query attributes for the current .rdata page. */
2132 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
2133 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
2134 kHlpAssert(cbProtRange);
2135 if (cbProtRange)
2136 {
2137 switch (ProtInfo.Protect)
2138 {
2139 case PAGE_READWRITE:
2140 case PAGE_WRITECOPY:
2141 case PAGE_EXECUTE_READWRITE:
2142 case PAGE_EXECUTE_WRITECOPY:
2143 /* Already writable, nothing to do. */
2144 fRc = TRUE;
2145 break;
2146
2147 default:
2148 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2149 case PAGE_READONLY:
2150 cbProtRange = cbPage;
2151 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2152 break;
2153
2154 case PAGE_EXECUTE:
2155 case PAGE_EXECUTE_READ:
2156 cbProtRange = cbPage;
2157 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2158 break;
2159 }
2160 kHlpAssertStmt(fRc, fOldProt = 0);
2161 }
2162 }
2163
2164 /*
2165 * Unslotted replacements are simple.
2166 */
2167 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2168 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2169 else
2170 {
2171 /*
2172 * Must find our module entry for this module, possibly creating one.
2173 */
2174 if (!pImportMod)
2175 {
2176 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2177 K_TRUE /*fAlwaysPresent*/);
2178 if (!pImportMod)
2179 {
2180 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2181 pszImport, pMod->pszPath);
2182 break;
2183 }
2184 }
2185 paThunks[iThunk].u1.AddressOfData
2186 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2187 }
2188 break;
2189 }
2190 }
2191 }
2192 }
2193
2194
2195 /* Next import descriptor. */
2196 pImpDesc++;
2197 }
2198
2199
2200 if (fOldProt)
2201 {
2202 DWORD fIgnore = 0;
2203 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2204 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2205 }
2206 }
2207
2208}
2209
2210
2211/**
2212 * Creates a module from a native kLdr module handle.
2213 *
2214 * @returns Module w/ 1 reference on success, NULL on failure.
2215 * @param pLdrMod The native kLdr module.
2216 * @param pszPath The normalized path to the module.
2217 * @param cbPath The module path length with terminator.
2218 * @param uHashPath The module path hash.
2219 * @param fDoReplacements Whether to do import replacements on this
2220 * module.
2221 */
2222static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2223 KBOOL fDoReplacements, PKWMODULE pVirtualApiMod)
2224{
2225 /*
2226 * Create the entry.
2227 */
2228 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2229 if (pMod)
2230 {
2231 pMod->pwszPath = (wchar_t *)(pMod + 1);
2232 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2233 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2234 pMod->uHashPath = uHashPath;
2235 pMod->cRefs = 1;
2236 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2237 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2238 pMod->fExe = K_FALSE;
2239 pMod->fNative = K_TRUE;
2240 pMod->pLdrMod = pLdrMod;
2241 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2242 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2243 pMod->iCrtSlot = KU8_MAX;
2244 pMod->fNeedReInit = K_FALSE;
2245 pMod->pszMsPdbSrvEndpoint = NULL;
2246 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2247 pMod->pVirtualApiMod = pVirtualApiMod;
2248 if (pVirtualApiMod)
2249 kwLdrModuleRetain(pVirtualApiMod);
2250
2251 if (fDoReplacements)
2252 {
2253 DWORD const dwSavedErr = GetLastError();
2254 kwLdrModuleDoNativeImportReplacements(pMod);
2255 SetLastError(dwSavedErr);
2256 }
2257
2258 KWLDR_LOG(("New module: %p LB %#010x %s (native%s%s)\n",
2259 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath,
2260 pVirtualApiMod ? ", virtual api => " : "", pVirtualApiMod ? pVirtualApiMod->pszPath : ""));
2261 g_cModules++;
2262 return kwLdrModuleLink(pMod);
2263 }
2264 return NULL;
2265}
2266
2267
2268
2269/**
2270 * Creates a module using the native loader.
2271 *
2272 * @returns Module w/ 1 reference on success, NULL on failure.
2273 * @param pszPath The normalized path to the module.
2274 * @param uHashPath The module path hash.
2275 * @param fDoReplacements Whether to do import replacements on this
2276 * module.
2277 */
2278static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2279{
2280 PKLDRMOD pLdrMod;
2281 int rc;
2282
2283 /*
2284 * HACK ALERT! Make sure the application path is searched when looking for
2285 * imports in the module we're loading.
2286 */
2287 /** @todo improve on this hack! */
2288 PKWMODULE pExe = g_Sandbox.pTool ? g_Sandbox.pTool->u.Sandboxed.pExe : NULL;
2289 if (pExe)
2290 {
2291 /* HACK ALERT! */
2292 wchar_t *pwzFilename = (wchar_t *)&pExe->pwszPath[pExe->offFilenameW];
2293 wchar_t wcSaved = pExe->pwszPath[pExe->offFilenameW];
2294 *pwzFilename = '\0';
2295 if (!SetDllDirectoryW(pExe->pwszPath))
2296 kwErrPrintf("SetDllDirectoryW failed: %u\n", GetLastError());
2297 KW_LOG(("kwLdrModuleCreateNative: Applied SetDllDirectoryW hack (%ls)\n", pExe->pwszPath));
2298 *pwzFilename = wcSaved;
2299 }
2300 else
2301 KW_LOG(("kwLdrModuleCreateNative: Warning! Too early for SetDllDirectoryW hack\n"));
2302
2303
2304 /*
2305 * Load the library and create a module structure for it.
2306 */
2307 rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2308 if (rc == 0)
2309 {
2310 KSIZE cchPath = kHlpStrLen(pszPath);
2311 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, cchPath + 1, uHashPath,
2312 fDoReplacements, NULL /*pVirtualApiMod*/);
2313 if (pMod)
2314 return pMod;
2315 kLdrModClose(pLdrMod);
2316 }
2317 return NULL;
2318}
2319
2320
2321/**
2322 * Checks if the given name could be a virtual API module or not.
2323 */
2324static KBOOL kwLdrIsVirtualApiModule(const char *pszName, KSIZE cchName)
2325{
2326 if (cchName <= 7)
2327 return K_FALSE;
2328 switch (*pszName)
2329 {
2330 default:
2331 return K_FALSE;
2332 case 'a':
2333 case 'A':
2334 if (pszName[1] != 'p' && pszName[1] != 'P')
2335 return K_FALSE;
2336 if (pszName[2] != 'i' && pszName[2] != 'I')
2337 return K_FALSE;
2338 break;
2339 case 'e':
2340 case 'E':
2341 if (pszName[1] != 'x' && pszName[1] != 'X')
2342 return K_FALSE;
2343 if (pszName[2] != 't' && pszName[2] != 'T')
2344 return K_FALSE;
2345 break;
2346 }
2347 if (pszName[3] != '-')
2348 return K_FALSE;
2349 if (pszName[4] != 'm' && pszName[4] != 'M')
2350 return K_FALSE;
2351 if (pszName[5] != 's' && pszName[5] != 'S')
2352 return K_FALSE;
2353 if (pszName[6] != '-')
2354 return K_FALSE;
2355 return K_TRUE;
2356}
2357
2358
2359/**
2360 * Try load what seems to be a virtual API DLL.
2361 *
2362 * This is a worker for kwLdrModuleResolveAndLookup and
2363 * kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule.
2364 *
2365 * @returns Pointer to module on success, NULL on failure.
2366 * @param pszName The name of the module. This must be
2367 * normalized already!
2368 * @param cchName The length of the name.
2369 */
2370static PKWMODULE kwLdrModuleTryLoadVirtualDll(const char *pszName, KSIZE cchName)
2371{
2372 HMODULE hModule;
2373
2374 /*
2375 * Look it up in the hash table.
2376 */
2377 KU32 const uHashPath = kwStrHash(pszName);
2378 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2379 PKWMODULE pMod = g_apModules[idxHash];
2380 if (pMod)
2381 {
2382 do
2383 {
2384 if ( pMod->uHashPath == uHashPath
2385 && kHlpStrComp(pMod->pszPath, pszName) == 0)
2386 return kwLdrModuleRetain(pMod);
2387 pMod = pMod->pNextHash;
2388 } while (pMod);
2389 }
2390
2391 /*
2392 * Not found. Try load it.
2393 */
2394 hModule = LoadLibraryA(pszName);
2395 if (!hModule)
2396 {
2397 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s failed (%u)\n", pszName, GetLastError()));
2398 return NULL;
2399 }
2400
2401 /*
2402 * Loaded successfully. Create a module for the real module.
2403 */
2404 pMod = kwLdrModuleForLoadedNativeByHandle(hModule, K_FALSE /*fEnsureCrtSlot*/, pszName);
2405 if (pMod)
2406 {
2407 /* Create a module for the virtual API name too, unless it is actually a real DLL. */
2408 if (stricmp(&pMod->pszPath[pMod->offFilename], pszName) != 0)
2409 {
2410 PKLDRMOD pLdrMod;
2411 int rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2412 if (rc == 0)
2413 {
2414 PKWMODULE pVirtMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszName, cchName + 1, kwStrHash(pszName),
2415 K_FALSE /*fDoReplacements*/, pMod /*pVirtualApiMod*/);
2416 if (pVirtMod)
2417 {
2418 kwLdrModuleRelease(pMod);
2419 pMod = pVirtMod;
2420 }
2421 else
2422 {
2423 kLdrModClose(pLdrMod);
2424 kwErrPrintf("out of memory\n");
2425 }
2426 }
2427 else
2428 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
2429 }
2430 else
2431 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s -> %s - A real DLL!\n", pszName, pMod->pszPath));
2432 }
2433
2434 return pMod;
2435}
2436
2437
2438/**
2439 * Sets up the quick zero & copy tables for the non-native module.
2440 *
2441 * This is a worker for kwLdrModuleCreateNonNative.
2442 *
2443 * @param pMod The module.
2444 */
2445static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2446{
2447 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2448 KU32 cSegs = pMod->pLdrMod->cSegments;
2449 KU32 iSeg;
2450
2451 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2452 pMod->u.Manual.cQuickCopyChunks = 0;
2453 pMod->u.Manual.cQuickZeroChunks = 0;
2454
2455 for (iSeg = 0; iSeg < cSegs; iSeg++)
2456 switch (paSegs[iSeg].enmProt)
2457 {
2458 case KPROT_READWRITE:
2459 case KPROT_WRITECOPY:
2460 case KPROT_EXECUTE_READWRITE:
2461 case KPROT_EXECUTE_WRITECOPY:
2462 if (paSegs[iSeg].cbMapped)
2463 {
2464 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2465 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2466 {
2467 /*
2468 * Check for trailing zero words.
2469 */
2470 KSIZE cbTrailingZeros;
2471 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2472 && (paSegs[iSeg].cbMapped & 7) == 0
2473 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2474 {
2475 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2476 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2477 KSIZE idxFirstZero = cNatural;
2478 while (idxFirstZero > 0)
2479 if (pauNatural[--idxFirstZero] == 0)
2480 { /* likely */ }
2481 else
2482 {
2483 idxFirstZero++;
2484 break;
2485 }
2486 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2487 if (cbTrailingZeros < 128)
2488 cbTrailingZeros = 0;
2489 }
2490 else
2491 cbTrailingZeros = 0;
2492
2493 /*
2494 * Add quick copy entry.
2495 */
2496 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2497 {
2498 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2499 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2500 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2501 pMod->u.Manual.cQuickCopyChunks = (KU8)(iChunk + 1);
2502 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2503 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2504 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2505 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2506 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2507 }
2508
2509 /*
2510 * Add quick zero entry.
2511 */
2512 if (cbTrailingZeros)
2513 {
2514 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2515 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2516 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2517 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2518 pMod->u.Manual.cQuickZeroChunks = (KU8)(iZero + 1);
2519 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2520 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2521 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2522 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2523 }
2524 }
2525 else
2526 {
2527 /*
2528 * We're out of quick copy table entries, so just copy the whole darn thing.
2529 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2530 */
2531 kHlpAssertFailed();
2532 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2533 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2534 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2535 pMod->u.Manual.cQuickCopyChunks = 1;
2536 KWLDR_LOG(("Quick copy not possible!\n"));
2537 return;
2538 }
2539 }
2540 break;
2541
2542 default:
2543 break;
2544 }
2545}
2546
2547
2548/**
2549 * Called from the TLS allocation DLL when ever the native loader wants to issue
2550 * a TLS callback after the initial kwLdrTlsAllocationHook callout.
2551 *
2552 * @param hDll The DLL handle.
2553 * @param dwReason The callback reason.
2554 * @param pvContext Some context value that seems to always be NULL.
2555 * @param pMod Out internal module.
2556 */
2557static void kwLdrTlsNativeLoaderCallback(void *hDll, DWORD dwReason, void *pvContext, PKWMODULE pMod)
2558{
2559 if ( pMod
2560 && pMod->u.Manual.enmState == KWMODSTATE_READY)
2561 {
2562 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p\n",
2563 hDll, dwReason, pvContext, pMod));
2564 if (pMod->u.Manual.cTlsCallbacks)
2565 {
2566 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
2567 do
2568 {
2569 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: Calling TLS callback %p(%p, %#x, %p) - %s\n",
2570 *ppfnCallback, pMod->hOurMod, dwReason, pvContext, pMod->pszPath));
2571 (*ppfnCallback)(pMod->hOurMod, dwReason, pvContext);
2572 ppfnCallback++;
2573 } while (*ppfnCallback);
2574 }
2575 }
2576 else
2577 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p - skipped\n",
2578 hDll, dwReason, pvContext, pMod));
2579}
2580
2581
2582/**
2583 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2584 *
2585 * @returns Address of the callback function (kwLdrTlsNativeLoaderCallback).
2586 * @param hDll The DLL handle.
2587 * @param idxTls The allocated TLS index.
2588 * @param pabInitData The init data in the TLS allocation DLL
2589 * (g_abInitData).
2590 * @param ppWorkerModuleVar Pointer to the variable holding the pMod
2591 * callback parameter value (g_pvWorkerModule).
2592 *
2593 * @see KWLDRTLSALLOCATIONHOOK in kWorkerTlsXxxxK.c
2594 */
2595__declspec(dllexport) KUPTR kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, KU8 *pabInitData, PKWMODULE *ppWorkerModuleVar)
2596{
2597 /*
2598 * Do the module initialization thing first.
2599 */
2600 PKWMODULE pMod = g_pModPendingTlsAlloc;
2601 if (pMod)
2602 {
2603 if ( pMod->u.Manual.idxTls == KU32_MAX
2604 && pMod->u.Manual.pabTlsInitData == NULL)
2605 {
2606 pMod->u.Manual.idxTls = idxTls;
2607 pMod->u.Manual.pabTlsInitData = pabInitData;
2608 pMod->u.Manual.ppTlsWorkerModuleVar = ppWorkerModuleVar;
2609 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2610
2611#if 0 /** @todo this doesn't work W10 18363 */
2612 {
2613 /*
2614 * Try sabotage the DLL name so we can load this module again.
2615 */
2616 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2617 LIST_ENTRY *pHead;
2618 LIST_ENTRY *pCur;
2619
2620 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2621 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2622 {
2623 LDR_DATA_TABLE_ENTRY *pMte;
2624 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2625 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2626 {
2627 PUNICODE_STRING pStr = &pMte->FullDllName;
2628 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2629 pStr->Buffer[--off]++;
2630 pStr->Buffer[--off]++;
2631 pStr->Buffer[--off]++;
2632 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2633 break;
2634 }
2635 }
2636 }
2637#endif
2638
2639 /*
2640 * Don't return a callback function unless the module has callbacks to service.
2641 */
2642 if (pMod->u.Manual.cTlsCallbacks > 0)
2643 {
2644 *ppWorkerModuleVar = pMod;
2645 return (KUPTR)kwLdrTlsNativeLoaderCallback;
2646 }
2647 return 0;
2648 }
2649 KWLDR_LOG(("kwLdrTlsAllocationHook: WTF? pMod=%p: idxTls=%#x pabTlsInitData=%p\n",
2650 pMod, pMod->u.Manual.idxTls, pMod->u.Manual.pabTlsInitData));
2651 }
2652 return 0;
2653}
2654
2655
2656/**
2657 * Allocates and initializes TLS variables.
2658 *
2659 * @returns 0 on success, non-zero failure.
2660 * @param pMod The module.
2661 */
2662static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2663{
2664 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2665 IMAGE_NT_HEADERS const *pNtHdrs;
2666 IMAGE_DATA_DIRECTORY const *pTlsDir;
2667
2668 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2669 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2670 else
2671 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2672 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2673
2674 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2675 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2676 {
2677 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2678 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2679 KU32 iEntry;
2680 KU32 iTlsDll;
2681 KU32 iTlsDllSub;
2682 KUPTR offIndex;
2683 KUPTR offCallbacks;
2684 KUPTR const *puCallbacks;
2685 KSIZE cbData;
2686 const wchar_t *pwszTlsDll;
2687 HMODULE hmodTlsDll;
2688
2689 /*
2690 * Check and log.
2691 */
2692 for (iEntry = 0; iEntry < cEntries; iEntry++)
2693 {
2694 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2695 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2696 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2697 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2698 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2699 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2700 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2701
2702 if (offIndex >= pMod->cbImage)
2703 {
2704 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2705 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2706 return -1;
2707 }
2708 if (offCallbacks >= pMod->cbImage)
2709 {
2710 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2711 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2712 return -1;
2713 }
2714 while (*puCallbacks != 0)
2715 {
2716 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2717 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2718 puCallbacks++;
2719 }
2720 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2721 {
2722 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2723 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2724 return -1;
2725 }
2726 }
2727
2728 if (cEntries > 1)
2729 {
2730 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2731 return -1;
2732 }
2733
2734 /*
2735 * Make the allocation by loading a new instance of one of the TLS dlls.
2736 * The DLL will make a call to kwLdrTlsAllocationHook.
2737 */
2738 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2739 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2740 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2741 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2742
2743 /** @todo find better strategy here. Like temporary copy or whatever when
2744 * there is more than a single user. */
2745 for (iTlsDll = 0; cbData > g_aTlsDlls[iTlsDll].cbTls;)
2746 if (++iTlsDll >= K_ELEMENTS(g_aTlsDlls))
2747 {
2748 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2749 return -1;
2750 }
2751 for (iTlsDllSub = 0; g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed;)
2752 if (++iTlsDllSub >= g_aTlsDlls[iTlsDll].cDlls)
2753 {
2754 kwErrPrintf("No unused TLS DLLs for %s of size %u!\n", pMod->pszPath, (unsigned)cbData);
2755 return -1;
2756 }
2757
2758 g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed = K_TRUE;
2759 pwszTlsDll = g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].pwszName;
2760
2761 pMod->u.Manual.pabTlsInitData = NULL;
2762 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2763 pMod->u.Manual.idxTls = KU32_MAX;
2764
2765 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2766 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2767 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2768 pMod->u.Manual.cTlsCallbacks = 0;
2769 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2770 pMod->u.Manual.cTlsCallbacks++;
2771 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2772
2773 g_pModPendingTlsAlloc = pMod;
2774 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2775 g_pModPendingTlsAlloc = NULL;
2776 if (hmodTlsDll == NULL)
2777 {
2778 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2779 return -1;
2780 }
2781 if (pMod->u.Manual.idxTls == KU32_MAX)
2782 {
2783 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2784 return -1;
2785 }
2786
2787 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2788 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x pabTlsInitData=%p\n",
2789 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData, pMod->u.Manual.pabTlsInitData));
2790
2791 kHlpAssert(pMod->u.Manual.pabTlsInitData);
2792 if (pMod->u.Manual.pabTlsInitData && pMod->u.Manual.cbTlsInitData)
2793 kHlpMemCopy(pMod->u.Manual.pabTlsInitData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData],
2794 pMod->u.Manual.cbTlsInitData);
2795 }
2796 return 0;
2797}
2798
2799
2800/**
2801 * Creates a module using the our own loader.
2802 *
2803 * @returns Module w/ 1 reference on success, NULL on failure.
2804 * @param pszPath The normalized path to the module.
2805 * @param uHashPath The module path hash.
2806 * @param fExe K_TRUE if this is an executable image, K_FALSE
2807 * if not. Executable images does not get entered
2808 * into the global module table.
2809 * @param pExeMod The executable module of the process (for
2810 * resolving imports). NULL if fExe is set.
2811 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2812 */
2813static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2814 PKWMODULE pExeMod, const char *pszSearchPath)
2815{
2816 /*
2817 * Open the module and check the type.
2818 */
2819 PKLDRMOD pLdrMod;
2820 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2821 if (rc == 0)
2822 {
2823 switch (pLdrMod->enmType)
2824 {
2825 case KLDRTYPE_EXECUTABLE_FIXED:
2826 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2827 case KLDRTYPE_EXECUTABLE_PIC:
2828 if (!fExe)
2829 rc = KERR_GENERAL_FAILURE;
2830 break;
2831
2832 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2833 case KLDRTYPE_SHARED_LIBRARY_PIC:
2834 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2835 if (fExe)
2836 rc = KERR_GENERAL_FAILURE;
2837 break;
2838
2839 default:
2840 rc = KERR_GENERAL_FAILURE;
2841 break;
2842 }
2843 if (rc == 0)
2844 {
2845 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2846 if (cImports >= 0)
2847 {
2848 /*
2849 * Create the entry.
2850 */
2851 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2852 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2853 + sizeof(pMod) * cImports
2854 + cbPath
2855 + cbPath * 2 * sizeof(wchar_t));
2856 if (pMod)
2857 {
2858 KBOOL fFixed;
2859
2860 pMod->cRefs = 1;
2861 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2862 pMod->uHashPath = uHashPath;
2863 pMod->fExe = fExe;
2864 pMod->fNative = K_FALSE;
2865 pMod->pLdrMod = pLdrMod;
2866 pMod->iCrtSlot = KU8_MAX;
2867 pMod->fNeedReInit = K_FALSE;
2868 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2869 pMod->pszMsPdbSrvEndpoint = NULL;
2870 pMod->u.Manual.cImpMods = (KU32)cImports;
2871#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2872 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2873#endif
2874 pMod->u.Manual.fUseLdBuf = K_FALSE;
2875 pMod->u.Manual.fCanDoQuick = K_FALSE;
2876 pMod->u.Manual.cQuickZeroChunks = 0;
2877 pMod->u.Manual.cQuickCopyChunks = 0;
2878 pMod->u.Manual.pabTlsInitData = NULL;
2879 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2880 pMod->u.Manual.idxTls = KU32_MAX;
2881 pMod->u.Manual.offTlsInitData = KU32_MAX;
2882 pMod->u.Manual.cbTlsInitData = 0;
2883 pMod->u.Manual.cbTlsAlloc = 0;
2884 pMod->u.Manual.cTlsCallbacks = 0;
2885 pMod->u.Manual.offTlsCallbacks = 0;
2886 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2887 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2888 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2889 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2890
2891 /*
2892 * Figure out where to load it and get memory there.
2893 */
2894 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2895 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2896 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2897 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2898 if ( !fFixed
2899 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2900 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2901 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2902 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2903 else
2904 pMod->u.Manual.fUseLdBuf = K_TRUE;
2905 if (rc == 0)
2906 {
2907 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2908 if (rc == 0)
2909 {
2910 KI32 iImp;
2911 KU32 cchReloads;
2912
2913 /*
2914 * Link the module (unless it's an executable image) and process the imports.
2915 */
2916 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2917 kwLdrModuleLink(pMod);
2918 KWLDR_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2919 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2920 KWLDR_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2921 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2922 cchReloads = g_cchReloads;
2923 if (cchReloads + 80 < sizeof(g_szReloads))
2924 {
2925 cchReloads += _snprintf(&g_szReloads[cchReloads], sizeof(g_szReloads) - cchReloads,
2926 "%s.reload /f %s=%p\n", cchReloads ? "; " : "",
2927 pMod->pszPath, pMod->u.Manual.pbLoad);
2928 g_cchReloads = cchReloads;
2929 }
2930
2931 for (iImp = 0; iImp < cImports; iImp++)
2932 {
2933 char szName[1024];
2934 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2935 if (rc == 0)
2936 {
2937 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2938 &pMod->u.Manual.apImpMods[iImp]);
2939 if (rc == 0)
2940 continue;
2941 }
2942 break;
2943 }
2944
2945 if (rc == 0)
2946 {
2947 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2948 kwLdrModuleGetImportCallback, pMod);
2949 if (rc == 0)
2950 {
2951#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2952 /*
2953 * Find the function table. No validation here because the
2954 * loader did that already, right...
2955 */
2956 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2957 IMAGE_NT_HEADERS const *pNtHdrs;
2958 IMAGE_DATA_DIRECTORY const *pXcptDir;
2959 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2960 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2961 else
2962 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2963 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2964 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2965 if (pXcptDir->Size > 0)
2966 {
2967 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2968 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2969 == pXcptDir->Size);
2970 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2971 }
2972 else
2973 {
2974 pMod->u.Manual.cFunctions = 0;
2975 pMod->u.Manual.paFunctions = NULL;
2976 }
2977#endif
2978
2979 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2980
2981 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2982 if (rc == 0)
2983 {
2984 /*
2985 * Final finish.
2986 */
2987 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2988 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2989 pMod->u.Manual.enmReInitState = KWMODSTATE_NEEDS_BITS;
2990 if ( g_Sandbox.pTool
2991 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
2992 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
2993 && !pMod->fExe)
2994 pMod->u.Manual.enmReInitState = KWMODSTATE_READY;
2995 g_cModules++;
2996 g_cNonNativeModules++;
2997 return pMod;
2998 }
2999 }
3000 else
3001 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
3002 }
3003
3004 kwLdrModuleRelease(pMod);
3005 return NULL;
3006 }
3007
3008 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
3009 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3010 }
3011 else if (fFixed)
3012 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
3013 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
3014 else
3015 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3016 }
3017 }
3018 }
3019 kLdrModClose(pLdrMod);
3020 }
3021 else
3022 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
3023 return NULL;
3024}
3025
3026
3027/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
3028static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
3029 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
3030{
3031 PKWMODULE pCurMod = (PKWMODULE)pvUser;
3032 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
3033 int rc;
3034 K_NOREF(pMod);
3035
3036 if (pImpMod->fNative)
3037 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
3038 iSymbol, pchSymbol, cchSymbol, pszVersion,
3039 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3040 puValue, pfKind);
3041 else
3042 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
3043 iSymbol, pchSymbol, cchSymbol, pszVersion,
3044 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3045 puValue, pfKind);
3046 if (rc == 0)
3047 {
3048 KU32 i = g_cSandboxReplacements;
3049 while (i-- > 0)
3050 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
3051 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
3052 {
3053 if ( !g_aSandboxReplacements[i].pszModule
3054 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
3055 {
3056 if ( pCurMod->fExe
3057 || !g_aSandboxReplacements[i].fOnlyExe)
3058 {
3059 KWLDR_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
3060 if (!g_aSandboxReplacements[i].fCrtSlotArray)
3061 *puValue = g_aSandboxReplacements[i].pfnReplacement;
3062 else
3063 {
3064 if (pImpMod->iCrtSlot == KU8_MAX)
3065 {
3066 rc = kwLdrModuleCreateCrtSlot(pImpMod);
3067 if (rc)
3068 KWLDR_LOG(("kwLdrModuleGetImportCallback: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
3069 }
3070 *puValue = ((KUPTR *)g_aSandboxReplacements[i].pfnReplacement)[pImpMod->iCrtSlot];
3071 }
3072 }
3073 break;
3074 }
3075 }
3076 }
3077
3078 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
3079 KWLDR_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
3080 return rc;
3081
3082}
3083
3084
3085/**
3086 * Gets the main entrypoint for a module.
3087 *
3088 * @returns 0 on success, KERR on failure
3089 * @param pMod The module.
3090 * @param puAddrMain Where to return the address.
3091 */
3092static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
3093{
3094 KLDRADDR uLdrAddrMain;
3095 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
3096 if (rc == 0)
3097 {
3098 *puAddrMain = (KUPTR)uLdrAddrMain;
3099 return 0;
3100 }
3101 return rc;
3102}
3103
3104
3105/**
3106 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
3107 *
3108 * @returns K_TRUE/K_FALSE.
3109 * @param pszFilename The filename (no path).
3110 * @param enmLocation The location.
3111 */
3112static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
3113{
3114 if (enmLocation != KWLOCATION_SYSTEM32)
3115 return K_TRUE;
3116 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3117 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3118 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3119 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3120 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3121 || kHlpStrNICompAscii(pszFilename, TUPLE("api-ms-win-crt-")) == 0
3122#if 0 /* for debugging, only for debugging. */
3123 || kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3124 || kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3125 || kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3126#endif
3127 ;
3128}
3129
3130
3131/**
3132 * Lazily initializes the g_pWinSys32 variable.
3133 */
3134static PKFSDIR kwLdrResolveWinSys32(void)
3135{
3136 KFSLOOKUPERROR enmError;
3137 PKFSDIR pWinSys32;
3138
3139 /* Get the path first. */
3140 char szSystem32[MAX_PATH];
3141 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
3142 {
3143 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
3144 strcpy(szSystem32, "C:\\Windows\\System32");
3145 }
3146
3147 /* Look it up and verify it. */
3148 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
3149 if (pWinSys32)
3150 {
3151 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
3152 {
3153 g_pWinSys32 = pWinSys32;
3154 return pWinSys32;
3155 }
3156
3157 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
3158 }
3159 else
3160 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
3161 return NULL;
3162}
3163
3164
3165/**
3166 * Whether we can load this DLL natively or not.
3167 *
3168 * @returns K_TRUE/K_FALSE.
3169 * @param pszFilename The filename (no path).
3170 * @param enmLocation The location.
3171 * @param pszFullPath The full filename and path.
3172 */
3173static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
3174{
3175 if (enmLocation == KWLOCATION_SYSTEM32)
3176 return K_TRUE;
3177 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
3178 return K_TRUE;
3179 if ( enmLocation == KWLOCATION_UNKNOWN
3180 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3181 return K_TRUE;
3182
3183 /* If the location is unknown, we must check if it's some dynamic loading
3184 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
3185 if (enmLocation == KWLOCATION_UNKNOWN)
3186 {
3187 PKFSDIR pWinSys32 = g_pWinSys32;
3188 if (!pWinSys32)
3189 pWinSys32 = kwLdrResolveWinSys32();
3190 if (pWinSys32)
3191 {
3192 KFSLOOKUPERROR enmError;
3193 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
3194 if (pFsObj)
3195 {
3196 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
3197 kFsCacheObjRelease(g_pFsCache, pFsObj);
3198 if (fInWinSys32)
3199 return K_TRUE;
3200 }
3201 }
3202 }
3203
3204 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3205 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3206 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3207 || kHlpStrNICompAscii(pszFilename, TUPLE("tbbmalloc")) == 0
3208 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3209 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3210 || ( enmLocation != KWLOCATION_UNKNOWN
3211 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3212#if 0 /* for debugging, only for debugging. */
3213 //|| kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3214 //|| kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3215 //|| kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3216#endif
3217 ;
3218}
3219
3220
3221/**
3222 * Check if the path leads to a regular file (that exists).
3223 *
3224 * @returns K_TRUE / K_FALSE
3225 * @param pszPath Path to the file to check out.
3226 */
3227static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
3228{
3229 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
3230 KSIZE cchPath = kHlpStrLen(pszPath);
3231 if ( cchPath > 3
3232 && pszPath[cchPath - 4] == '.'
3233 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
3234 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
3235 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
3236 {
3237 KFSLOOKUPERROR enmError;
3238 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3239 if (pFsObj)
3240 {
3241 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
3242 kFsCacheObjRelease(g_pFsCache, pFsObj);
3243 return fRc;
3244 }
3245 }
3246 else
3247 {
3248 BirdStat_T Stat;
3249 int rc = birdStatFollowLink(pszPath, &Stat);
3250 if (rc == 0)
3251 {
3252 if (S_ISREG(Stat.st_mode))
3253 return K_TRUE;
3254 }
3255 }
3256 return K_FALSE;
3257}
3258
3259
3260/**
3261 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
3262 *
3263 * If the file exists, we consult the module hash table before trying to load it
3264 * off the disk.
3265 *
3266 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
3267 * failure.
3268 * @param pszPath The name of the import module.
3269 * @param enmLocation The location we're searching. This is used in
3270 * the heuristics for determining if we can use the
3271 * native loader or need to sandbox the DLL.
3272 * @param pExe The executable (optional).
3273 * @param pszSearchPath The PATH to search (optional).
3274 */
3275static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
3276{
3277 /*
3278 * Does the file exists and is it a regular file?
3279 */
3280 if (kwLdrModuleIsRegularFile(pszPath))
3281 {
3282 /*
3283 * Yes! Normalize it and look it up in the hash table.
3284 */
3285 char szNormPath[1024];
3286 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
3287 if (rc == 0)
3288 {
3289 const char *pszName;
3290 KU32 const uHashPath = kwStrHash(szNormPath);
3291 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3292 PKWMODULE pMod = g_apModules[idxHash];
3293 if (pMod)
3294 {
3295 do
3296 {
3297 if ( pMod->uHashPath == uHashPath
3298 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3299 return kwLdrModuleRetain(pMod);
3300 pMod = pMod->pNextHash;
3301 } while (pMod);
3302 }
3303
3304 /*
3305 * Not in the hash table, so we have to load it from scratch.
3306 */
3307 pszName = kHlpGetFilename(szNormPath);
3308 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
3309 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
3310 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
3311 else
3312 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
3313 if (pMod)
3314 return pMod;
3315 return (PKWMODULE)~(KUPTR)0;
3316 }
3317 }
3318 return NULL;
3319}
3320
3321
3322/**
3323 * Gets a reference to the module by the given name.
3324 *
3325 * We must do the search path thing, as our hash table may multiple DLLs with
3326 * the same base name due to different tools version and similar. We'll use a
3327 * modified search sequence, though. No point in searching the current
3328 * directory for instance.
3329 *
3330 * @returns 0 on success, KERR on failure.
3331 * @param pszName The name of the import module.
3332 * @param pExe The executable (optional).
3333 * @param pImporter The module doing the importing (optional).
3334 * @param pszSearchPath The PATH to search (optional).
3335 * @param ppMod Where to return the module pointer w/ reference.
3336 */
3337static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
3338 const char *pszSearchPath, PKWMODULE *ppMod)
3339{
3340 KSIZE const cchName = kHlpStrLen(pszName);
3341 char szPath[1024];
3342 char *psz;
3343 PKWMODULE pMod = NULL;
3344 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
3345 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
3346
3347 /* Virtual API module. Normalize and try load it. */
3348 if (pMod == NULL && cchName > 7 && kwLdrIsVirtualApiModule(pszName, cchName))
3349 {
3350 if (cchName + cchSuffix >= sizeof(szPath))
3351 return KERR_BUFFER_OVERFLOW;
3352 kHlpMemCopy(szPath, pszName, cchName);
3353 if (fNeedSuffix)
3354 kHlpMemCopy(&szPath[cchName], ".dll", sizeof(".dll"));
3355 szPath[cchName + cchSuffix] = '\0';
3356 _strlwr(szPath);
3357 pMod = kwLdrModuleTryLoadVirtualDll(szPath, cchName + cchSuffix);
3358 }
3359
3360 /* The import path. */
3361 if (pMod == NULL && pImporter != NULL)
3362 {
3363 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
3364 return KERR_BUFFER_OVERFLOW;
3365
3366 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
3367 if (fNeedSuffix)
3368 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3369 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
3370 }
3371
3372 /* Application directory first. */
3373 if (pMod == NULL && pExe != NULL && pExe != pImporter)
3374 {
3375 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
3376 return KERR_BUFFER_OVERFLOW;
3377 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
3378 if (fNeedSuffix)
3379 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3380 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
3381 }
3382
3383 /* The windows directory. */
3384 if (pMod == NULL)
3385 {
3386 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
3387 if ( cchDir <= 2
3388 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
3389 return KERR_BUFFER_OVERFLOW;
3390 szPath[cchDir++] = '\\';
3391 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
3392 if (fNeedSuffix)
3393 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3394 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3395 }
3396
3397 /* The path. */
3398 if ( pMod == NULL
3399 && pszSearchPath)
3400 {
3401 const char *pszCur = pszSearchPath;
3402 while (*pszCur != '\0')
3403 {
3404 /* Find the end of the component */
3405 KSIZE cch = 0;
3406 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
3407 cch++;
3408
3409 if ( cch > 0 /* wrong, but whatever */
3410 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
3411 {
3412 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
3413 if ( szPath[cch - 1] != ':'
3414 && szPath[cch - 1] != '/'
3415 && szPath[cch - 1] != '\\')
3416 *pszDst++ = '\\';
3417 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
3418 if (fNeedSuffix)
3419 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
3420 *pszDst = '\0';
3421
3422 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3423 if (pMod)
3424 break;
3425 }
3426
3427 /* Advance */
3428 pszCur += cch;
3429 while (*pszCur == ';')
3430 pszCur++;
3431 }
3432 }
3433
3434 /* Return. */
3435 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3436 {
3437 *ppMod = pMod;
3438 return 0;
3439 }
3440 *ppMod = NULL;
3441 return KERR_GENERAL_FAILURE;
3442}
3443
3444
3445/**
3446 * Creates a CRT slot for the given module.
3447 *
3448 * @returns 0 on success, non-zero on failure.
3449 * @param pModule The module.
3450 */
3451static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3452{
3453 KSIZE iSlot;
3454 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3455 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3456 if (g_aCrtSlots[iSlot].pModule == NULL)
3457 {
3458 KLDRADDR uAddr;
3459 int rc;
3460
3461 /* Do the linking: */
3462 g_aCrtSlots[iSlot].pModule = pModule;
3463 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3464 pModule->iCrtSlot = (KU8)iSlot;
3465
3466 /* resolve symbols: */
3467 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3468 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3469 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3470 if (rc != 0)
3471 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3472
3473 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "_beginthreadex", 14,
3474 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3475 *(KUPTR *)&g_aCrtSlots[iSlot].pfnBeginThreadEx = rc == 0 ? (KUPTR)uAddr : 0;
3476 //if (rc != 0)
3477 // kwErrPrintf("Failed to resolved '_beginthreadex' in '%s': %d\n", pModule->pszPath, rc);
3478
3479 return 0;
3480 }
3481 kwErrPrintf("Out of CRT slots!\n");
3482 return KERR_NO_MEMORY;
3483}
3484
3485
3486/**
3487 * Locates the module structure for an already loaded native module.
3488 *
3489 * This will create a module structure if needed.
3490 *
3491 * @returns Pointer to the module structure on success, NULL on failure.
3492 * @param hModule The native module handle.
3493 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3494 * @param pszLogName The name to use for logging/errors.
3495 */
3496static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName)
3497{
3498 /*
3499 * Get a normalized path for it.
3500 */
3501 char szModPath[1024];
3502 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3503 {
3504 char szNormPath[1024];
3505 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3506 if (rc == 0)
3507 {
3508 /*
3509 * Hash the path and look it up.
3510 */
3511 KU32 uHashPath;
3512 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3513 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3514 PKWMODULE pMod = g_apModules[idxHash];
3515 if (pMod)
3516 {
3517 do
3518 {
3519 if ( pMod->uHashPath == uHashPath
3520 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3521 {
3522 kwLdrModuleRetain(pMod);
3523 break;
3524 }
3525 pMod = pMod->pNextHash;
3526 } while (pMod);
3527 }
3528
3529 /*
3530 * If not in the hash table, so create a module entry.
3531 */
3532 if (!pMod)
3533 {
3534 PKLDRMOD pLdrMod;
3535 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3536 if (rc == 0)
3537 {
3538 /** @todo more accurately determine location */
3539 const char *pszFilename = kHlpGetFilename(szNormPath);
3540 KBOOL fDoReplacements = kwLdrModuleShouldDoNativeReplacements(pszFilename, KWLOCATION_SYSTEM32);
3541 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3542 fDoReplacements, NULL /*pVirtualApiMod*/);
3543 if (!pMod)
3544 {
3545 kLdrModClose(pLdrMod);
3546 kwErrPrintf("out of memory\n");
3547 }
3548 }
3549 else
3550 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszLogName, rc);
3551 }
3552 if (pMod)
3553 {
3554 /*
3555 * Create a CRT slot for the module if necessary.
3556 */
3557 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3558 return pMod;
3559 rc = kwLdrModuleCreateCrtSlot(pMod);
3560 if (rc == 0)
3561 return pMod;
3562 kwLdrModuleRelease(pMod);
3563 }
3564 }
3565 else
3566 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszLogName, GetLastError());
3567 }
3568 else
3569 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszLogName, GetLastError());
3570 return NULL;
3571}
3572
3573
3574/**
3575 * Locates the module structure for an already loaded native module.
3576 *
3577 * This will create a module structure if needed.
3578 *
3579 * @returns Pointer to the module structure on success, NULL on failure.
3580 * @param pszName The name of the module.
3581 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3582 * @param fAlwaysPresent Whether the module is expected to always be present,
3583 * or not. If not, complain less.
3584 */
3585static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3586{
3587 /*
3588 * Locate the module handle and pass it to kwLdrModuleForLoadedNativeByHandle.
3589 */
3590 HANDLE hModule = GetModuleHandleA(pszName);
3591 if (hModule)
3592 return kwLdrModuleForLoadedNativeByHandle(hModule, fEnsureCrtSlot, pszName);
3593 if (fAlwaysPresent)
3594 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3595 return NULL;
3596}
3597
3598
3599/**
3600 * Does the TLS memory initialization for a module on the current thread.
3601 *
3602 * @returns 0 on success, error on failure.
3603 * @param pMod The module.
3604 */
3605static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3606{
3607 if (pMod->u.Manual.idxTls != KU32_MAX)
3608 {
3609 PTEB pTeb = NtCurrentTeb();
3610 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3611 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3612 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3613 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3614 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3615 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3616 if (pMod->u.Manual.cbTlsInitData)
3617 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3618 }
3619 return 0;
3620}
3621
3622
3623/**
3624 * Does the TLS callbacks for a module.
3625 *
3626 * @param pMod The module.
3627 * @param dwReason The callback reason.
3628 */
3629static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3630{
3631 if (pMod->u.Manual.cTlsCallbacks)
3632 {
3633 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3634 do
3635 {
3636 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *ppfnCallback, pMod->hOurMod, dwReason));
3637 (*ppfnCallback)(pMod->hOurMod, dwReason, 0);
3638 } while (*++ppfnCallback);
3639 }
3640}
3641
3642
3643/**
3644 * Does module initialization starting at @a pMod.
3645 *
3646 * This is initially used on the executable. Later it is used by the
3647 * LoadLibrary interceptor.
3648 *
3649 * @returns 0 on success, error on failure.
3650 * @param pMod The module to initialize.
3651 */
3652static int kwLdrModuleInitTree(PKWMODULE pMod)
3653{
3654 int rc = 0;
3655 if (!pMod->fNative)
3656 {
3657 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3658 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3659
3660 /*
3661 * Need to copy bits?
3662 */
3663 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3664 {
3665 if (pMod->u.Manual.fUseLdBuf)
3666 {
3667#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3668 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3669 {
3670 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3671 kHlpAssert(fRc); K_NOREF(fRc);
3672 }
3673#endif
3674 g_pModPrevInLdBuf = g_pModInLdBuf;
3675 g_pModInLdBuf = pMod;
3676 }
3677
3678 /* Do quick zeroing and copying when we can. */
3679 pMod->u.Manual.fCanDoQuick = K_FALSE;
3680 if ( pMod->u.Manual.fCanDoQuick
3681 && ( !pMod->u.Manual.fUseLdBuf
3682 || g_pModPrevInLdBuf == pMod))
3683 {
3684 /* Zero first. */
3685 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3686 switch (pMod->u.Manual.cQuickZeroChunks)
3687 {
3688 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3689 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3690 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3691 case 0: break;
3692 }
3693
3694 /* Then copy. */
3695 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3696 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3697 switch (pMod->u.Manual.cQuickCopyChunks)
3698 {
3699 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3700 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3701 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3702 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3703 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3704 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3705 case 0: break;
3706 }
3707 }
3708 /* Must copy the whole image. */
3709 else
3710 {
3711 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3712 pMod->u.Manual.fCanDoQuick = K_TRUE;
3713 }
3714 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3715 }
3716
3717#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3718 /*
3719 * Need to register function table?
3720 */
3721 if ( !pMod->u.Manual.fRegisteredFunctionTable
3722 && pMod->u.Manual.cFunctions > 0)
3723 {
3724 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3725 pMod->u.Manual.cFunctions,
3726 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3727 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3728 }
3729#endif
3730
3731
3732 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3733 {
3734 /*
3735 * Must do imports first, but mark our module as being initialized to avoid
3736 * endless recursion should there be a dependency loop.
3737 */
3738 KSIZE iImp;
3739 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3740
3741 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3742 {
3743 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3744 if (rc != 0)
3745 return rc;
3746 }
3747
3748 /* Do TLS allocations for module init? */
3749 rc = kwLdrCallTlsAllocateAndInit(pMod);
3750 if (rc != 0)
3751 return rc;
3752 if (pMod->u.Manual.cTlsCallbacks > 0)
3753 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3754
3755 /* Finally call the entry point. */
3756 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3757 if (rc == 0)
3758 pMod->u.Manual.enmState = KWMODSTATE_READY;
3759 else
3760 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3761 }
3762 }
3763 /*
3764 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3765 * _MSPDBSRV_ENDPOINT_ changes value.
3766 */
3767 else if (pMod->fNeedReInit)
3768 {
3769 int rc2;
3770 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3771 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3772 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3773 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3774 if (!rc && !rc2)
3775 { /* likely */ }
3776 else
3777 {
3778 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3779 if (rc2 && !rc)
3780 rc = rc2;
3781 }
3782 pMod->fNeedReInit = K_FALSE;
3783 }
3784 return rc;
3785}
3786
3787
3788/**
3789 * Looks up a module handle for a tool.
3790 *
3791 * @returns Referenced loader module on success, NULL on if not found.
3792 * @param pTool The tool.
3793 * @param hmod The module handle.
3794 */
3795static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3796{
3797 KUPTR const uHMod = (KUPTR)hmod;
3798 PKWMODULE *papMods;
3799 KU32 iEnd;
3800 KU32 i;
3801 PKWDYNLOAD pDynLoad;
3802
3803 if (pTool)
3804 { /* likely */ }
3805 else
3806 return NULL;
3807
3808 /* The executable. */
3809 if ( hmod == NULL
3810 || (pTool->u.Sandboxed.pExe && pTool->u.Sandboxed.pExe->hOurMod == hmod))
3811 {
3812 if (pTool->u.Sandboxed.pExe)
3813 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3814 return NULL;
3815 }
3816
3817 /*
3818 * Binary lookup using the module table.
3819 */
3820 papMods = pTool->u.Sandboxed.papModules;
3821 iEnd = pTool->u.Sandboxed.cModules;
3822 if (iEnd)
3823 {
3824 KU32 iStart = 0;
3825 i = iEnd / 2;
3826 for (;;)
3827 {
3828 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
3829 if (uHMod < uHModCur)
3830 {
3831 iEnd = i--;
3832 if (iStart <= i)
3833 { }
3834 else
3835 break;
3836 }
3837 else if (uHMod != uHModCur)
3838 {
3839 iStart = ++i;
3840 if (i < iEnd)
3841 { }
3842 else
3843 break;
3844 }
3845 /* We've got a match. Always return the non-virtual module (first) when there is one. */
3846 else if (!papMods[i]->pVirtualApiMod)
3847 return kwLdrModuleRetain(papMods[i]);
3848 else
3849 {
3850 while (i > 0 && papMods[i - 1]->pVirtualApiMod && papMods[i - 1]->hOurMod == hmod)
3851 i--;
3852 return kwLdrModuleRetain(papMods[i]);
3853 }
3854
3855 i = iStart + (iEnd - iStart) / 2;
3856 }
3857
3858#ifndef NDEBUG
3859 iStart = pTool->u.Sandboxed.cModules;
3860 while (--iStart > 0)
3861 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3862 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3863#endif
3864 }
3865
3866 /*
3867 * Dynamically loaded images.
3868 */
3869 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3870 if (pDynLoad->hmod == hmod)
3871 {
3872 if (pDynLoad->pMod)
3873 return kwLdrModuleRetain(pDynLoad->pMod);
3874 KWFS_TODO();
3875 return NULL;
3876 }
3877
3878 return NULL;
3879}
3880
3881/**
3882 * Adds the given module to the tool import table.
3883 *
3884 * @returns 0 on success, non-zero on failure.
3885 * @param pTool The tool.
3886 * @param pMod The module.
3887 */
3888static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3889{
3890 /*
3891 * Binary lookup. Locating the right slot for it, return if already there.
3892 */
3893 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3894 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3895 KU32 iEnd = pTool->u.Sandboxed.cModules;
3896 KU32 i;
3897 if (iEnd)
3898 {
3899 KU32 iStart = 0;
3900 i = iEnd / 2;
3901 for (;;)
3902 {
3903 PKWMODULE pCurMod = papMods[i];
3904 KUPTR const uHModCur = (KUPTR)pCurMod->hOurMod;
3905 if (uHMod < uHModCur)
3906 {
3907 iEnd = i;
3908 if (iStart < i)
3909 { }
3910 else
3911 break;
3912 }
3913 else if (uHMod != uHModCur)
3914 {
3915 iStart = ++i;
3916 if (i < iEnd)
3917 { }
3918 else
3919 break;
3920 }
3921 else
3922 {
3923 /* Already there in the table. The non-virtual module must be the first
3924 entry if we've got duplicate hmod values because of virtual modules. */
3925 if (pMod != pCurMod)
3926 {
3927 /* Skip to the last module with the same hmod. */
3928 while (i + 1 < iEnd && (KUPTR)(pCurMod = papMods[i + 1])->hOurMod == uHMod)
3929 {
3930 if (pMod == pCurMod)
3931 return 0;
3932 i++;
3933 }
3934
3935 /* Then scan backwards till the first one. */
3936 while (i > 0 && (KUPTR)(pCurMod = papMods[i - 1])->hOurMod == uHMod)
3937 {
3938 if (pMod == pCurMod)
3939 return 0;
3940 i--;
3941 }
3942 pCurMod = papMods[i];
3943 if (pMod != pCurMod)
3944 {
3945 if (pMod->pVirtualApiMod && !pCurMod->pVirtualApiMod)
3946 i++;
3947 break;
3948 }
3949 }
3950 return 0;
3951 }
3952
3953 i = iStart + (iEnd - iStart) / 2;
3954 }
3955#ifndef NDEBUG
3956 iStart = pTool->u.Sandboxed.cModules;
3957 while (--iStart > 0)
3958 {
3959 kHlpAssert(papMods[iStart] != pMod);
3960 kHlpAssert( (KUPTR)papMods[iStart]->hOurMod != uHMod
3961 || pMod->pVirtualApiMod
3962 || papMods[iStart]->pVirtualApiMod);
3963 }
3964 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod <= uHMod);
3965 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod >= uHMod);
3966#endif
3967 }
3968 else
3969 i = 0;
3970
3971 /*
3972 * Grow the table?
3973 */
3974 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3975 {
3976 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3977 if (!pvNew)
3978 return KERR_NO_MEMORY;
3979 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3980 }
3981
3982 /* Insert it. */
3983 if (i != pTool->u.Sandboxed.cModules)
3984 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3985 papMods[i] = kwLdrModuleRetain(pMod);
3986 pTool->u.Sandboxed.cModules++;
3987 KWLDR_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3988 return 0;
3989}
3990
3991
3992/**
3993 * Adds the given module and all its imports to the
3994 *
3995 * @returns 0 on success, non-zero on failure.
3996 * @param pTool The tool.
3997 * @param pMod The module.
3998 */
3999static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
4000{
4001 int rc = kwToolAddModule(pTool, pMod);
4002 if (pMod->pVirtualApiMod && rc == 0)
4003 rc = kwToolAddModule(pTool, pMod->pVirtualApiMod);
4004 if (!pMod->fNative && rc == 0)
4005 {
4006 KSIZE iImp = pMod->u.Manual.cImpMods;
4007 while (iImp-- > 0)
4008 {
4009 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
4010 if (rc == 0)
4011 { }
4012 else
4013 break;
4014 }
4015 }
4016
4017 return 0;
4018}
4019
4020
4021/**
4022 * Creates a tool entry and inserts it.
4023 *
4024 * @returns Pointer to the tool entry. NULL on failure.
4025 * @param pToolFsObj The file object of the tool. The created tool
4026 * will be associated with it.
4027 *
4028 * A reference is donated by the caller and must be
4029 * released.
4030 * @param pszSearchPath The PATH environment variable value, or NULL.
4031 */
4032static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
4033{
4034 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
4035 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
4036 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
4037 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
4038 if (pTool)
4039 {
4040 KBOOL fRc;
4041 pTool->pwszPath = (wchar_t const *)(pTool + 1);
4042 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
4043 kHlpAssert(fRc); K_NOREF(fRc);
4044
4045 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
4046 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
4047 kHlpAssert(fRc);
4048
4049 pTool->enmType = KWTOOLTYPE_SANDBOXED;
4050 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
4051 NULL /*pEexeMod*/, pszSearchPath);
4052 if (pTool->u.Sandboxed.pExe)
4053 {
4054 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
4055 if (rc == 0)
4056 {
4057 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
4058 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
4059 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
4060 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
4061 else
4062 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
4063 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
4064 }
4065 else
4066 {
4067 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
4068 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
4069 pTool->u.Sandboxed.pExe = NULL;
4070 pTool->enmType = KWTOOLTYPE_EXEC;
4071 }
4072 }
4073 else
4074 pTool->enmType = KWTOOLTYPE_EXEC;
4075
4076 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4077 g_cTools++;
4078 return pTool;
4079 }
4080 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4081 return NULL;
4082}
4083
4084
4085/**
4086 * Looks up the given tool, creating a new tool table entry if necessary.
4087 *
4088 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
4089 * @param pszExe The executable for the tool (not normalized).
4090 * @param cEnvVars Number of environment varibles.
4091 * @param papszEnvVars Environment variables. For getting the PATH.
4092 */
4093static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
4094{
4095 /*
4096 * We associate the tools instances with the file system objects.
4097 *
4098 * We'd like to do the lookup without invaliding the volatile parts of the
4099 * cache, thus the double lookup here. The cache gets invalidate later on.
4100 */
4101 KFSLOOKUPERROR enmError;
4102 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4103 if ( !pToolFsObj
4104 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
4105 {
4106 kFsCacheInvalidateCustomBoth(g_pFsCache);
4107 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4108 }
4109 if (pToolFsObj)
4110 {
4111 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
4112 {
4113 const char *pszSearchPath;
4114 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
4115 if (pTool)
4116 {
4117 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4118 return pTool;
4119 }
4120
4121 /*
4122 * Need to create a new tool.
4123 */
4124 pszSearchPath = NULL;
4125 while (cEnvVars-- > 0)
4126 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
4127 {
4128 pszSearchPath = &papszEnvVars[cEnvVars][5];
4129 break;
4130 }
4131
4132 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
4133 if (pTool)
4134 return pTool;
4135
4136 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
4137 }
4138 else
4139 {
4140 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4141 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
4142 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
4143 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
4144 }
4145 }
4146 else
4147 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
4148 return NULL;
4149}
4150
4151
4152
4153/*
4154 *
4155 * File system cache.
4156 * File system cache.
4157 * File system cache.
4158 *
4159 */
4160
4161
4162/**
4163 * This is for kDep.
4164 */
4165int kwFsPathExists(const char *pszPath)
4166{
4167 BirdTimeSpec_T TsIgnored;
4168 KFSLOOKUPERROR enmError;
4169 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
4170 if (pFsObj)
4171 {
4172 kFsCacheObjRelease(g_pFsCache, pFsObj);
4173 return 1;
4174 }
4175 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
4176}
4177
4178
4179/* duplicated in dir-nt-bird.c */
4180void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
4181{
4182 KFSLOOKUPERROR enmError;
4183 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
4184 if (pPathObj)
4185 {
4186 KSIZE off = pPathObj->cchParent;
4187 if (off > 0)
4188 {
4189 KSIZE offEnd = off + pPathObj->cchName;
4190 if (offEnd < cbFull)
4191 {
4192 PKFSDIR pAncestor;
4193
4194 pszFull[off + pPathObj->cchName] = '\0';
4195 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
4196
4197 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4198 {
4199 kHlpAssert(off > 1);
4200 kHlpAssert(pAncestor != NULL);
4201 kHlpAssert(pAncestor->Obj.cchName > 0);
4202 pszFull[--off] = '/';
4203 off -= pAncestor->Obj.cchName;
4204 kHlpAssert(pAncestor->Obj.cchParent == off);
4205 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4206 }
4207 kFsCacheObjRelease(g_pFsCache, pPathObj);
4208 return;
4209 }
4210 }
4211 else
4212 {
4213 if ((size_t)pPathObj->cchName + 1 < cbFull)
4214 {
4215 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
4216 pszFull[pPathObj->cchName] = '/';
4217 pszFull[pPathObj->cchName + 1] = '\0';
4218
4219 kFsCacheObjRelease(g_pFsCache, pPathObj);
4220 return;
4221 }
4222 }
4223
4224 /* do fallback. */
4225 kHlpAssertFailed();
4226 kFsCacheObjRelease(g_pFsCache, pPathObj);
4227 }
4228
4229 nt_fullpath(pszPath, pszFull, cbFull);
4230}
4231
4232
4233/**
4234 * Helper for getting the extension of a UTF-16 path.
4235 *
4236 * @returns Pointer to the extension or the terminator.
4237 * @param pwszPath The path.
4238 * @param pcwcExt Where to return the length of the extension.
4239 */
4240static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
4241{
4242 wchar_t const *pwszName = pwszPath;
4243 wchar_t const *pwszExt = NULL;
4244 for (;;)
4245 {
4246 wchar_t const wc = *pwszPath++;
4247 if (wc == '.')
4248 pwszExt = pwszPath;
4249 else if (wc == '/' || wc == '\\' || wc == ':')
4250 {
4251 pwszName = pwszPath;
4252 pwszExt = NULL;
4253 }
4254 else if (wc == '\0')
4255 {
4256 if (pwszExt)
4257 {
4258 *pcwcExt = pwszPath - pwszExt - 1;
4259 return pwszExt;
4260 }
4261 *pcwcExt = 0;
4262 return pwszPath - 1;
4263 }
4264 }
4265}
4266
4267
4268
4269/**
4270 * Parses the argument string passed in as pszSrc.
4271 *
4272 * @returns size of the processed arguments.
4273 * @param pszSrc Pointer to the commandline that's to be parsed.
4274 * @param pcArgs Where to return the number of arguments.
4275 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
4276 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
4277 *
4278 * @remarks Lifted from startuphacks-win.c
4279 */
4280static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
4281{
4282 int bs;
4283 char chQuote;
4284 char *pfFlags;
4285 int cbArgs;
4286 int cArgs;
4287
4288#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
4289#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
4290#define WHITE(c) ((c) == ' ' || (c) == '\t')
4291
4292#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
4293#define _ARG_RESPONSE 0x02 /* Argument read from response file */
4294#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
4295#define _ARG_ENV 0x08 /* Argument from environment */
4296#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
4297
4298 cArgs = 0;
4299 cbArgs = 0;
4300
4301#if 0
4302 /* argv[0] */
4303 PUTC((char)_ARG_NONZERO);
4304 PUTV;
4305 for (;;)
4306 {
4307 PUTC(*pszSrc);
4308 if (*pszSrc == 0)
4309 break;
4310 ++pszSrc;
4311 }
4312 ++pszSrc;
4313#endif
4314
4315 for (;;)
4316 {
4317 while (WHITE(*pszSrc))
4318 ++pszSrc;
4319 if (*pszSrc == 0)
4320 break;
4321 pfFlags = pchPool;
4322 PUTC((unsigned char)_ARG_NONZERO);
4323 PUTV;
4324 bs = 0; chQuote = 0;
4325 for (;;)
4326 {
4327 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
4328 {
4329 while (bs >= 2)
4330 {
4331 PUTC('\\');
4332 bs -= 2;
4333 }
4334 if (bs & 1)
4335 PUTC(*pszSrc);
4336 else
4337 {
4338 chQuote = chQuote ? 0 : *pszSrc;
4339 if (pfFlags != NULL)
4340 *pfFlags |= _ARG_DQUOTE;
4341 }
4342 bs = 0;
4343 }
4344 else if (*pszSrc == '\\')
4345 ++bs;
4346 else
4347 {
4348 while (bs != 0)
4349 {
4350 PUTC('\\');
4351 --bs;
4352 }
4353 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
4354 break;
4355 PUTC(*pszSrc);
4356 }
4357 ++pszSrc;
4358 }
4359 PUTC(0);
4360 }
4361
4362 *pcArgs = cArgs;
4363 return cbArgs;
4364}
4365
4366
4367
4368
4369/*
4370 *
4371 * Process and thread related APIs.
4372 * Process and thread related APIs.
4373 * Process and thread related APIs.
4374 *
4375 */
4376
4377/** Common worker for ExitProcess(), exit() and friends. */
4378static void WINAPI kwSandboxDoExit(int uExitCode)
4379{
4380 if (g_Sandbox.idMainThread == GetCurrentThreadId())
4381 {
4382 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
4383
4384 g_Sandbox.rcExitCode = (int)uExitCode;
4385
4386 /* Before we jump, restore the TIB as we're not interested in any
4387 exception chain stuff installed by the sandboxed executable. */
4388 *pTib = g_Sandbox.TibMainThread;
4389 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
4390
4391 longjmp(g_Sandbox.JmpBuf, 1);
4392 }
4393 KWFS_TODO();
4394}
4395
4396
4397/** ExitProcess replacement. */
4398static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
4399{
4400 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
4401 kwSandboxDoExit((int)uExitCode);
4402}
4403
4404
4405/** ExitProcess replacement. */
4406static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
4407{
4408 if (hProcess == GetCurrentProcess())
4409 kwSandboxDoExit(uExitCode);
4410 KWFS_TODO();
4411 return TerminateProcess(hProcess, uExitCode);
4412}
4413
4414
4415/** Normal CRT exit(). */
4416static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
4417{
4418 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
4419 kwSandboxDoExit(rcExitCode);
4420}
4421
4422
4423/** Quick CRT _exit(). */
4424static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
4425{
4426 /* Quick. */
4427 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
4428 kwSandboxDoExit(rcExitCode);
4429}
4430
4431
4432/** Return to caller CRT _cexit(). */
4433static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
4434{
4435 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
4436 kwSandboxDoExit(rcExitCode);
4437}
4438
4439
4440/** Quick return to caller CRT _c_exit(). */
4441static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
4442{
4443 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
4444 kwSandboxDoExit(rcExitCode);
4445}
4446
4447
4448/** Runtime error and exit _amsg_exit(). */
4449static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
4450{
4451 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
4452 kwSandboxDoExit(255);
4453}
4454
4455
4456/** CRT - terminate(). */
4457static void __cdecl kwSandbox_msvcrt_terminate(void)
4458{
4459 KW_LOG(("\nRuntime - terminate!\n"));
4460 kwSandboxDoExit(254);
4461}
4462
4463
4464/** CRT - _onexit */
4465static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
4466{
4467 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4468 {
4469 PKWEXITCALLACK pCallback;
4470 KW_LOG(("_onexit(%p)\n", pfnFunc));
4471 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4472
4473 pCallback = kHlpAlloc(sizeof(*pCallback));
4474 if (pCallback)
4475 {
4476 pCallback->pfnCallback = pfnFunc;
4477 pCallback->fAtExit = K_FALSE;
4478 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4479 g_Sandbox.pExitCallbackHead = pCallback;
4480 return pfnFunc;
4481 }
4482 return NULL;
4483 }
4484 //KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
4485 //return pfnFunc;
4486}
4487
4488
4489/** CRT - atexit */
4490static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
4491{
4492 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4493 {
4494 PKWEXITCALLACK pCallback;
4495 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4496 KW_LOG(("atexit(%p)\n", pfnFunc));
4497
4498 pCallback = kHlpAlloc(sizeof(*pCallback));
4499 if (pCallback)
4500 {
4501 pCallback->pfnCallback = (_onexit_t)pfnFunc;
4502 pCallback->fAtExit = K_TRUE;
4503 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4504 g_Sandbox.pExitCallbackHead = pCallback;
4505 return 0;
4506 }
4507 return -1;
4508 }
4509 //KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4510 //return 0;
4511}
4512
4513
4514/** Kernel32 - SetConsoleCtrlHandler(). */
4515static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4516{
4517 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4518 return TRUE;
4519}
4520
4521
4522/** The CRT internal __getmainargs() API. */
4523static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4524 int dowildcard, int const *piNewMode)
4525{
4526 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4527 *pargc = g_Sandbox.cArgs;
4528 *pargv = g_Sandbox.papszArgs;
4529 *penvp = g_Sandbox.environ;
4530
4531 /** @todo startinfo points at a newmode (setmode) value. */
4532 return 0;
4533}
4534
4535
4536/** The CRT internal __wgetmainargs() API. */
4537static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4538 int dowildcard, int const *piNewMode)
4539{
4540 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4541 *pargc = g_Sandbox.cArgs;
4542 *pargv = g_Sandbox.papwszArgs;
4543 *penvp = g_Sandbox.wenviron;
4544
4545 /** @todo startinfo points at a newmode (setmode) value. */
4546 return 0;
4547}
4548
4549
4550
4551/** Kernel32 - GetCommandLineA() */
4552static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4553{
4554 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4555 return g_Sandbox.pszCmdLine;
4556}
4557
4558
4559/** Kernel32 - GetCommandLineW() */
4560static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4561{
4562 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4563 return g_Sandbox.pwszCmdLine;
4564}
4565
4566
4567/** Kernel32 - GetStartupInfoA() */
4568static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4569{
4570 KW_LOG(("GetStartupInfoA\n"));
4571 GetStartupInfoA(pStartupInfo);
4572 pStartupInfo->lpReserved = NULL;
4573 pStartupInfo->lpTitle = NULL;
4574 pStartupInfo->lpReserved2 = NULL;
4575 pStartupInfo->cbReserved2 = 0;
4576}
4577
4578
4579/** Kernel32 - GetStartupInfoW() */
4580static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4581{
4582 KW_LOG(("GetStartupInfoW\n"));
4583 GetStartupInfoW(pStartupInfo);
4584 pStartupInfo->lpReserved = NULL;
4585 pStartupInfo->lpTitle = NULL;
4586 pStartupInfo->lpReserved2 = NULL;
4587 pStartupInfo->cbReserved2 = 0;
4588}
4589
4590
4591/** CRT - __p___argc(). */
4592static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4593{
4594 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4595 return &g_Sandbox.cArgs;
4596}
4597
4598
4599/** CRT - __p___argv(). */
4600static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4601{
4602 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4603 return &g_Sandbox.papszArgs;
4604}
4605
4606
4607/** CRT - __p___sargv(). */
4608static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4609{
4610 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4611 return &g_Sandbox.papwszArgs;
4612}
4613
4614
4615/** CRT - __p__acmdln(). */
4616static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4617{
4618 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4619 return (char **)&g_Sandbox.pszCmdLine;
4620}
4621
4622
4623/** CRT - __p__acmdln(). */
4624static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4625{
4626 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4627 return &g_Sandbox.pwszCmdLine;
4628}
4629
4630
4631/** CRT - __p__pgmptr(). */
4632static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4633{
4634 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4635 return &g_Sandbox.pgmptr;
4636}
4637
4638
4639/** CRT - __p__wpgmptr(). */
4640static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4641{
4642 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4643 return &g_Sandbox.wpgmptr;
4644}
4645
4646
4647/** CRT - _get_pgmptr(). */
4648static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4649{
4650 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4651 *ppszValue = g_Sandbox.pgmptr;
4652 return 0;
4653}
4654
4655
4656/** CRT - _get_wpgmptr(). */
4657static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4658{
4659 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4660 *ppwszValue = g_Sandbox.wpgmptr;
4661 return 0;
4662}
4663
4664/** Just in case. */
4665static void kwSandbox_msvcrt__wincmdln(void)
4666{
4667 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4668 KWFS_TODO();
4669}
4670
4671
4672/** Just in case. */
4673static void kwSandbox_msvcrt__wwincmdln(void)
4674{
4675 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4676 KWFS_TODO();
4677}
4678
4679/** CreateThread interceptor. */
4680static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4681 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4682 DWORD fFlags, PDWORD pidThread)
4683{
4684 HANDLE hThread = NULL;
4685 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4686 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4687 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4688 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4689 {
4690 /* Allow link::DbgThread. */
4691 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4692 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4693 }
4694 else
4695 KWFS_TODO();
4696 return hThread;
4697}
4698
4699
4700/** _beginthread - create a new thread. */
4701static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4702{
4703 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4704 KWFS_TODO();
4705 return 0;
4706}
4707
4708
4709/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4710static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4711 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4712 unsigned fCreate, unsigned *pidThread)
4713{
4714 /*
4715 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4716 * whatever it needs to.
4717 */
4718 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4719 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4720 pfnThreadProc, pvUser, fCreate, pidThread));
4721 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4722 {
4723 uintptr_t rcRet;
4724 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4725 if (!s_pfnReal)
4726 {
4727 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4728 if (!s_pfnReal)
4729 {
4730 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4731 __debugbreak();
4732 }
4733 }
4734 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4735 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4736 return rcRet;
4737 }
4738
4739 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4740 KWFS_TODO();
4741 return 0;
4742}
4743
4744
4745/** _beginthreadex - create a new thread. */
4746static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex_wrapped(void *pvSecAttr, unsigned cbStack,
4747 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4748 unsigned fCreate, unsigned *pidThread, PKWCRTSLOT pSlot)
4749{
4750 /*
4751 * Since the VC++ 12 (VS 2013) compiler, the 2nd pass is now threaded.
4752 * Let it do whatever it needs to.
4753 */
4754 KW_LOG(("kwSandbox_msvcrt__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4755 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4756 pfnThreadProc, pvUser, fCreate, pidThread));
4757 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4758 && pSlot->pfnBeginThreadEx)
4759 {
4760 uintptr_t rcRet = pSlot->pfnBeginThreadEx(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4761 KW_LOG(("kwSandbox_msvcrt__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4762 return rcRet;
4763 }
4764
4765 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4766 KWFS_TODO();
4767 return 0;
4768}
4769
4770CRT_SLOT_FUNCTION_WRAPPER(uintptr_t __cdecl, kwSandbox_msvcrt__beginthreadex,
4771 (void *pvSecAttr, unsigned cbStack, unsigned (__stdcall *pfnThreadProc)(void *),
4772 void *pvUser, unsigned fCreate, unsigned *pidThread),
4773 (pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread, &g_aCrtSlots[iCrtSlot]));
4774
4775
4776
4777/*
4778 *
4779 * Environment related APIs.
4780 * Environment related APIs.
4781 * Environment related APIs.
4782 *
4783 */
4784
4785/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4786static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4787{
4788 char *pszzEnv;
4789 char *pszCur;
4790 KSIZE cbNeeded = 1;
4791 KSIZE iVar = 0;
4792
4793 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4794
4795 /* Figure how space much we need first. */
4796 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4797 cbNeeded += kHlpStrLen(pszCur) + 1;
4798
4799 /* Allocate it. */
4800 pszzEnv = kHlpAlloc(cbNeeded);
4801 if (pszzEnv)
4802 {
4803 char *psz = pszzEnv;
4804 iVar = 0;
4805 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4806 {
4807 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4808 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4809 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4810 }
4811 *psz++ = '\0';
4812 kHlpAssert((KUPTR)(psz - pszzEnv) == cbNeeded);
4813 }
4814
4815 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4816#if 0
4817 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4818 pszCur = pszzEnv;
4819 iVar = 0;
4820 while (*pszCur)
4821 {
4822 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4823 iVar++;
4824 pszCur += kHlpStrLen(pszCur) + 1;
4825 }
4826 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4827 pszCur++;
4828 fprintf(stderr, "ended at %p, after %u bytes (expected %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4829#endif
4830 return pszzEnv;
4831}
4832
4833
4834/** Kernel32 - GetEnvironmentStrings */
4835static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4836{
4837 KW_LOG(("GetEnvironmentStrings!\n"));
4838 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4839}
4840
4841
4842/** Kernel32 - GetEnvironmentStringsW */
4843static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4844{
4845 wchar_t *pwszzEnv;
4846 wchar_t *pwszCur;
4847 KSIZE cwcNeeded = 1;
4848 KSIZE iVar = 0;
4849
4850 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4851
4852 /* Figure how space much we need first. */
4853 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4854 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4855
4856 /* Allocate it. */
4857 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4858 if (pwszzEnv)
4859 {
4860 wchar_t *pwsz = pwszzEnv;
4861 iVar = 0;
4862 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4863 {
4864 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4865 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4866 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4867 }
4868 *pwsz++ = '\0';
4869 kHlpAssert((KUPTR)(pwsz - pwszzEnv) == cwcNeeded);
4870 }
4871
4872 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4873 return pwszzEnv;
4874}
4875
4876
4877/** Kernel32 - FreeEnvironmentStringsA */
4878static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4879{
4880 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4881 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4882 kHlpFree(pszzEnv);
4883 return TRUE;
4884}
4885
4886
4887/** Kernel32 - FreeEnvironmentStringsW */
4888static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4889{
4890 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4891 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4892 kHlpFree(pwszzEnv);
4893 return TRUE;
4894}
4895
4896
4897/**
4898 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4899 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4900 *
4901 * @returns 0 on success, non-zero on failure.
4902 * @param pSandbox The sandbox.
4903 * @param cMin Minimum size, including terminator.
4904 */
4905static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4906{
4907 void *pvNew;
4908 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4909 KSIZE cNew = cOld + 256;
4910 while (cNew < cMin)
4911 cNew += 256;
4912
4913 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4914 if (pvNew)
4915 {
4916 pSandbox->environ = (char **)pvNew;
4917 pSandbox->environ[cOld] = NULL;
4918
4919 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4920 if (pvNew)
4921 {
4922 pSandbox->papszEnvVars = (char **)pvNew;
4923 pSandbox->papszEnvVars[cOld] = NULL;
4924
4925 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4926 if (pvNew)
4927 {
4928 pSandbox->wenviron = (wchar_t **)pvNew;
4929 pSandbox->wenviron[cOld] = NULL;
4930
4931 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4932 if (pvNew)
4933 {
4934 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4935 pSandbox->papwszEnvVars[cOld] = NULL;
4936
4937 pSandbox->cEnvVarsAllocated = cNew;
4938 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4939 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4940 return 0;
4941 }
4942 }
4943 }
4944 }
4945 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4946 return KERR_NO_MEMORY;
4947}
4948
4949
4950/**
4951 * Sets an environment variable, ANSI style.
4952 *
4953 * @returns 0 on success, non-zero on failure.
4954 * @param pSandbox The sandbox.
4955 * @param pchVar The variable name.
4956 * @param cchVar The length of the name.
4957 * @param pszValue The value.
4958 */
4959static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4960{
4961 /* Allocate and construct the new strings. */
4962 KSIZE cchTmp = kHlpStrLen(pszValue);
4963 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4964 if (pszNew)
4965 {
4966 wchar_t *pwszNew;
4967 kHlpMemCopy(pszNew, pchVar, cchVar);
4968 pszNew[cchVar] = '=';
4969 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4970 cchTmp += cchVar + 1;
4971 pszNew[cchTmp] = '\0';
4972
4973 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4974 if (pwszNew)
4975 {
4976 /* Look it up. */
4977 KSIZE iVar = 0;
4978 char *pszEnv;
4979 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4980 {
4981 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4982 && pszEnv[cchVar] == '=')
4983 {
4984 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4985 " iVar=%d: %p='%s' and %p='%ls'\n",
4986 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4987 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4988 iVar, pszNew, pszNew, pwszNew, pwszNew));
4989
4990 kHlpFree(pSandbox->papszEnvVars[iVar]);
4991 pSandbox->papszEnvVars[iVar] = pszNew;
4992 pSandbox->environ[iVar] = pszNew;
4993
4994 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4995 pSandbox->papwszEnvVars[iVar] = pwszNew;
4996 pSandbox->wenviron[iVar] = pwszNew;
4997 return 0;
4998 }
4999 iVar++;
5000 }
5001
5002 /* Not found, do we need to grow the table first? */
5003 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5004 kwSandboxGrowEnv(pSandbox, iVar + 2);
5005 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5006 {
5007 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5008
5009 pSandbox->papszEnvVars[iVar + 1] = NULL;
5010 pSandbox->papszEnvVars[iVar] = pszNew;
5011 pSandbox->environ[iVar + 1] = NULL;
5012 pSandbox->environ[iVar] = pszNew;
5013
5014 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5015 pSandbox->papwszEnvVars[iVar] = pwszNew;
5016 pSandbox->wenviron[iVar + 1] = NULL;
5017 pSandbox->wenviron[iVar] = pwszNew;
5018 return 0;
5019 }
5020
5021 kHlpFree(pwszNew);
5022 }
5023 kHlpFree(pszNew);
5024 }
5025 KW_LOG(("Out of memory!\n"));
5026 return 0;
5027}
5028
5029
5030/**
5031 * Sets an environment variable, UTF-16 style.
5032 *
5033 * @returns 0 on success, non-zero on failure.
5034 * @param pSandbox The sandbox.
5035 * @param pwcVar The variable name.
5036 * @param cwcVar The length of the name.
5037 * @param pwszValue The value.
5038 */
5039static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
5040{
5041 /* Allocate and construct the new strings. */
5042 KSIZE cwcTmp = kwUtf16Len(pwszValue);
5043 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
5044 if (pwszNew)
5045 {
5046 char *pszNew;
5047 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
5048 pwszNew[cwcVar] = '=';
5049 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
5050 cwcTmp += cwcVar + 1;
5051 pwszNew[cwcVar] = '\0';
5052
5053 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
5054 if (pszNew)
5055 {
5056 /* Look it up. */
5057 KSIZE iVar = 0;
5058 wchar_t *pwszEnv;
5059 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5060 {
5061 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
5062 && pwszEnv[cwcVar] == '=')
5063 {
5064 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5065 " iVar=%d: %p='%s' and %p='%ls'\n",
5066 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5067 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5068 iVar, pszNew, pszNew, pwszNew, pwszNew));
5069
5070 kHlpFree(pSandbox->papszEnvVars[iVar]);
5071 pSandbox->papszEnvVars[iVar] = pszNew;
5072 pSandbox->environ[iVar] = pszNew;
5073
5074 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5075 pSandbox->papwszEnvVars[iVar] = pwszNew;
5076 pSandbox->wenviron[iVar] = pwszNew;
5077 return 0;
5078 }
5079 iVar++;
5080 }
5081
5082 /* Not found, do we need to grow the table first? */
5083 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5084 kwSandboxGrowEnv(pSandbox, iVar + 2);
5085 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5086 {
5087 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5088
5089 pSandbox->papszEnvVars[iVar + 1] = NULL;
5090 pSandbox->papszEnvVars[iVar] = pszNew;
5091 pSandbox->environ[iVar + 1] = NULL;
5092 pSandbox->environ[iVar] = pszNew;
5093
5094 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5095 pSandbox->papwszEnvVars[iVar] = pwszNew;
5096 pSandbox->wenviron[iVar + 1] = NULL;
5097 pSandbox->wenviron[iVar] = pwszNew;
5098 return 0;
5099 }
5100
5101 kHlpFree(pwszNew);
5102 }
5103 kHlpFree(pszNew);
5104 }
5105 KW_LOG(("Out of memory!\n"));
5106 return 0;
5107}
5108
5109
5110/** ANSI unsetenv worker. */
5111static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5112{
5113 KSIZE iVar = 0;
5114 char *pszEnv;
5115 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5116 {
5117 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5118 && pszEnv[cchVar] == '=')
5119 {
5120 KSIZE cVars = iVar;
5121 while (pSandbox->papszEnvVars[cVars])
5122 cVars++;
5123 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
5124 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
5125
5126 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5127 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5128 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5129
5130 kHlpFree(pSandbox->papszEnvVars[iVar]);
5131 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5132 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5133 pSandbox->papszEnvVars[cVars] = NULL;
5134 pSandbox->environ[cVars] = NULL;
5135
5136 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5137 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5138 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5139 pSandbox->papwszEnvVars[cVars] = NULL;
5140 pSandbox->wenviron[cVars] = NULL;
5141 return 0;
5142 }
5143 iVar++;
5144 }
5145 return KERR_ENVVAR_NOT_FOUND;
5146}
5147
5148
5149/** UTF-16 unsetenv worker. */
5150static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5151{
5152 KSIZE iVar = 0;
5153 wchar_t *pwszEnv;
5154 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5155 {
5156 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5157 && pwszEnv[cwcVar] == '=')
5158 {
5159 KSIZE cVars = iVar;
5160 while (pSandbox->papwszEnvVars[cVars])
5161 cVars++;
5162 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
5163 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
5164
5165 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5166 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5167 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5168
5169 kHlpFree(pSandbox->papszEnvVars[iVar]);
5170 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5171 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5172 pSandbox->papszEnvVars[cVars] = NULL;
5173 pSandbox->environ[cVars] = NULL;
5174
5175 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5176 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5177 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5178 pSandbox->papwszEnvVars[cVars] = NULL;
5179 pSandbox->wenviron[cVars] = NULL;
5180 return 0;
5181 }
5182 iVar++;
5183 }
5184 return KERR_ENVVAR_NOT_FOUND;
5185}
5186
5187
5188
5189/** ANSI getenv worker. */
5190static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5191{
5192 KSIZE iVar = 0;
5193 char *pszEnv;
5194 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
5195 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5196 && pszEnv[cchVar] == '=')
5197 return &pszEnv[cchVar + 1];
5198 return NULL;
5199}
5200
5201
5202/** UTF-16 getenv worker. */
5203static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5204{
5205 KSIZE iVar = 0;
5206 wchar_t *pwszEnv;
5207 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
5208 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5209 && pwszEnv[cwcVar] == '=')
5210 return &pwszEnv[cwcVar + 1];
5211 return NULL;
5212}
5213
5214
5215/** Kernel32 - GetEnvironmentVariableA() */
5216static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
5217{
5218 char *pszFoundValue;
5219 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5220
5221 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5222 if (pszFoundValue)
5223 {
5224 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
5225 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
5226 return cchRet;
5227 }
5228 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
5229 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5230 return 0;
5231}
5232
5233
5234/** Kernel32 - GetEnvironmentVariableW() */
5235static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
5236{
5237 wchar_t *pwszFoundValue;
5238 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5239
5240 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5241 if (pwszFoundValue)
5242 {
5243 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
5244 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
5245 return cchRet;
5246 }
5247 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
5248 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5249 return 0;
5250}
5251
5252
5253/** Kernel32 - SetEnvironmentVariableA() */
5254static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
5255{
5256 int rc;
5257 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5258
5259 if (pszValue)
5260 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5261 else
5262 {
5263 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5264 rc = 0; //??
5265 }
5266 if (rc == 0)
5267 {
5268 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
5269 return TRUE;
5270 }
5271 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5272 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
5273 return FALSE;
5274}
5275
5276
5277/** Kernel32 - SetEnvironmentVariableW() */
5278static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
5279{
5280 int rc;
5281 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5282
5283 if (pwszValue)
5284 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5285 else
5286 {
5287 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5288 rc = 0; //??
5289 }
5290 if (rc == 0)
5291 {
5292 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
5293 return TRUE;
5294 }
5295 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5296 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
5297 return FALSE;
5298}
5299
5300
5301/** Kernel32 - ExpandEnvironmentStringsA() */
5302static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
5303{
5304 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5305 KWFS_TODO();
5306 return 0;
5307}
5308
5309
5310/** Kernel32 - ExpandEnvironmentStringsW() */
5311static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
5312{
5313 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5314 KWFS_TODO();
5315 return 0;
5316}
5317
5318
5319/** CRT - _putenv(). */
5320static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
5321{
5322 int rc;
5323 char const *pszEqual;
5324 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5325
5326 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
5327 if (pszEqual)
5328 {
5329 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
5330 if (rc == 0)
5331 { }
5332 else
5333 rc = -1;
5334 }
5335 else
5336 {
5337 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
5338 rc = 0;
5339 }
5340 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
5341 return rc;
5342}
5343
5344
5345/** CRT - _wputenv(). */
5346static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
5347{
5348 int rc;
5349 wchar_t const *pwszEqual;
5350 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5351
5352 pwszEqual = wcschr(pwszVarEqualValue, '=');
5353 if (pwszEqual)
5354 {
5355 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
5356 if (rc == 0)
5357 { }
5358 else
5359 rc = -1;
5360 }
5361 else
5362 {
5363 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
5364 rc = 0;
5365 }
5366 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
5367 return rc;
5368}
5369
5370
5371/** CRT - _putenv_s(). */
5372static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
5373{
5374 char const *pszEqual;
5375 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5376
5377 pszEqual = kHlpStrChr(pszVar, '=');
5378 if (pszEqual == NULL)
5379 {
5380 if (pszValue)
5381 {
5382 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5383 if (rc == 0)
5384 {
5385 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
5386 return 0;
5387 }
5388 }
5389 else
5390 {
5391 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5392 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
5393 return 0;
5394 }
5395 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
5396 return ENOMEM;
5397 }
5398 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
5399 return EINVAL;
5400}
5401
5402
5403/** CRT - _wputenv_s(). */
5404static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
5405{
5406 wchar_t const *pwszEqual;
5407 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5408
5409 pwszEqual = wcschr(pwszVar, '=');
5410 if (pwszEqual == NULL)
5411 {
5412 if (pwszValue)
5413 {
5414 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5415 if (rc == 0)
5416 {
5417 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
5418 return 0;
5419 }
5420 }
5421 else
5422 {
5423 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5424 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
5425 return 0;
5426 }
5427 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
5428 return ENOMEM;
5429 }
5430 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
5431 return EINVAL;
5432}
5433
5434
5435/** CRT - get pointer to the __initenv variable (initial environment). */
5436static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
5437{
5438 KW_LOG(("__p___initenv\n"));
5439 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5440 KWFS_TODO();
5441 return &g_Sandbox.initenv;
5442}
5443
5444
5445/** CRT - get pointer to the __winitenv variable (initial environment). */
5446static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
5447{
5448 KW_LOG(("__p___winitenv\n"));
5449 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5450 KWFS_TODO();
5451 return &g_Sandbox.winitenv;
5452}
5453
5454
5455/** CRT - get pointer to the _environ variable (current environment). */
5456static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
5457{
5458 KW_LOG(("__p__environ\n"));
5459 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5460 return &g_Sandbox.environ;
5461}
5462
5463
5464/** CRT - get pointer to the _wenviron variable (current environment). */
5465static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
5466{
5467 KW_LOG(("__p__wenviron\n"));
5468 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5469 return &g_Sandbox.wenviron;
5470}
5471
5472
5473/** CRT - get the _environ variable (current environment).
5474 * @remarks Not documented or prototyped? */
5475static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
5476{
5477 KWFS_TODO(); /** @todo check the callers expectations! */
5478 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5479 *ppapszEnviron = g_Sandbox.environ;
5480 return 0;
5481}
5482
5483
5484/** CRT - get the _wenviron variable (current environment).
5485 * @remarks Not documented or prototyped? */
5486static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
5487{
5488 KWFS_TODO(); /** @todo check the callers expectations! */
5489 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5490 *ppapwszEnviron = g_Sandbox.wenviron;
5491 return 0;
5492}
5493
5494
5495/** CRT - _wdupenv_s() (see _tdupenv_s(). */
5496static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
5497 PKWCRTSLOT pSlot)
5498{
5499 errno_t rc;
5500 wchar_t *pwszValue;
5501 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5502
5503 if (ppwszValue)
5504 {
5505 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
5506 if (pwszValue)
5507 {
5508 size_t cwcValue = wcslen(pwszValue);
5509 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
5510 if (pwszDst)
5511 {
5512 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
5513 pwszDst[cwcValue] = '\0';
5514 *ppwszValue = pwszDst;
5515 if (pcwcValue)
5516 *pcwcValue = cwcValue;
5517 rc = 0;
5518 }
5519 else
5520 {
5521 *ppwszValue = NULL;
5522 if (pcwcValue)
5523 *pcwcValue = 0;
5524 rc = ENOMEM;
5525 }
5526 }
5527 else
5528 {
5529 *ppwszValue = NULL;
5530 if (pcwcValue)
5531 *pcwcValue = 0;
5532 rc = 0;
5533 }
5534 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5535 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5536 }
5537 else
5538 {
5539 /*
5540 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5541 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5542 * was set to a value.
5543 */
5544 if (pcwcValue)
5545 *pcwcValue = 0;
5546 rc = EINVAL;
5547 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5548 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5549 }
5550 return rc;
5551}
5552CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5553 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5554 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5555
5556
5557
5558/*
5559 *
5560 * Loader related APIs
5561 * Loader related APIs
5562 * Loader related APIs
5563 *
5564 */
5565
5566/**
5567 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5568 */
5569static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5570{
5571 /* Load it first. */
5572 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5573 if (hmod)
5574 {
5575 pDynLoad->hmod = hmod;
5576 pDynLoad->pMod = NULL; /* indicates special */
5577
5578 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5579 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5580 KWLDR_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5581 }
5582 else
5583 kHlpFree(pDynLoad);
5584 return hmod;
5585}
5586
5587
5588/**
5589 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5590 */
5591static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5592{
5593 static const char s_szDll[] = ".dll";
5594 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5595 PKWMODULE pMod;
5596 char szNormPath[256];
5597
5598 /*
5599 * Lower case it and make sure it ends with .dll.
5600 */
5601 if (cbFilename > sizeof(szNormPath))
5602 {
5603 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5604 return NULL;
5605 }
5606 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5607 _strlwr(szNormPath);
5608 kHlpAssert(cbFilename > 7 /* api-ms- */ );
5609 if (strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5610 {
5611 if (cbFilename + sizeof(s_szDll) - 1 > sizeof(szNormPath))
5612 {
5613 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5614 return NULL;
5615 }
5616
5617 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5618 cbFilename += sizeof(s_szDll) - 1;
5619 }
5620
5621 /*
5622 * Try load it.
5623 */
5624 pMod = kwLdrModuleTryLoadVirtualDll(szNormPath, cbFilename - 1);
5625 if (pMod)
5626 {
5627 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5628
5629 pDynLoad->pMod = pMod;
5630 pDynLoad->hmod = pMod->hOurMod;
5631
5632 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5633 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5634 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5635 return pDynLoad->hmod;
5636 }
5637 kHlpFree(pDynLoad);
5638 return NULL;
5639}
5640
5641
5642/** Kernel32 - LoadLibraryExA() */
5643static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5644{
5645 KSIZE cchFilename = kHlpStrLen(pszFilename);
5646 const char *pszSearchPath;
5647 PKWDYNLOAD pDynLoad;
5648 PKWMODULE pMod;
5649 int rc;
5650 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5651 //fprintf(stderr, "LoadLibraryExA: %s, %#x\n", pszFilename, fFlags);
5652
5653 /*
5654 * Deal with a couple of extremely unlikely special cases right away.
5655 */
5656 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5657 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5658 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5659 { /* likely */ }
5660 else
5661 {
5662 KWFS_TODO();
5663 return LoadLibraryExA(pszFilename, hFile, fFlags);
5664 }
5665
5666 /*
5667 * Check if we've already got a dynload entry for this one.
5668 */
5669 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5670 if ( pDynLoad->cchRequest == cchFilename
5671 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5672 {
5673 if (pDynLoad->pMod)
5674 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5675 else
5676 rc = 0;
5677 if (rc == 0)
5678 {
5679 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5680 return pDynLoad->hmod;
5681 }
5682 SetLastError(ERROR_DLL_INIT_FAILED);
5683 return NULL;
5684 }
5685
5686 /*
5687 * Allocate a dynload entry for the request.
5688 */
5689 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5690 if (pDynLoad)
5691 {
5692 pDynLoad->cchRequest = cchFilename;
5693 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5694 }
5695 else
5696 {
5697 KWLDR_LOG(("LoadLibraryExA: Out of memory!\n"));
5698 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5699 return NULL;
5700 }
5701
5702 /*
5703 * Deal with resource / data DLLs.
5704 */
5705 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5706 | LOAD_LIBRARY_AS_DATAFILE
5707 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5708 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5709 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5710
5711 /*
5712 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5713 */
5714 if ( kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5715 && kHlpIsFilenameOnly(pszFilename))
5716 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5717
5718 /*
5719 * Normal library loading.
5720 * We start by being very lazy and reusing the code for resolving imports.
5721 */
5722 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5723 if (!kHlpIsFilenameOnly(pszFilename))
5724 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5725#if 1 /* HACK ALERT! We run into trouble with a 2nd mspdb140.dll instance (x64 + x86), so use the one already loaded. A call
5726 * to NdrClientCall2 at ConnectToServer+0x426 fails with E_INVALIDARG. Problems with multiple connections from same PID? */
5727 else if ( strcmp(pszFilename, "mspdb140.dll") == 0
5728 && GetModuleHandleA(pszFilename) != NULL)
5729 {
5730 pMod = kwLdrModuleForLoadedNativeByHandle(GetModuleHandleA(pszFilename), K_FALSE, pszFilename);
5731 KWLDR_LOG(("LoadLibraryExA: mspdb140 hack: pMod=%p\n", pMod));
5732 }
5733#endif
5734 else
5735 {
5736 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5737 if (rc != 0)
5738 pMod = NULL;
5739 }
5740 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5741 {
5742 /* Enter it into the tool module table and dynamic link request cache. */
5743 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5744
5745 pDynLoad->pMod = pMod;
5746 pDynLoad->hmod = pMod->hOurMod;
5747
5748 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5749 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5750
5751 /*
5752 * Make sure it's initialized (need to link it first since DllMain may
5753 * use loader APIs).
5754 */
5755 rc = kwLdrModuleInitTree(pMod);
5756 if (rc == 0)
5757 {
5758 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5759 return pDynLoad->hmod;
5760 }
5761
5762 SetLastError(ERROR_DLL_INIT_FAILED);
5763 }
5764 else
5765 {
5766 KWFS_TODO();
5767 kHlpFree(pDynLoad);
5768 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5769 }
5770 return NULL;
5771}
5772
5773
5774/** Kernel32 - LoadLibraryExA() for native overloads */
5775static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5776{
5777 char szPath[1024];
5778 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5779
5780 /*
5781 * We may have to help resolved unqualified DLLs living in the executable directory.
5782 */
5783 if ( kHlpIsFilenameOnly(pszFilename)
5784 && g_Sandbox.pTool
5785 && g_Sandbox.pTool->u.Sandboxed.pExe)
5786 {
5787 KSIZE const cchFilename = kHlpStrLen(pszFilename);
5788#define MY_IMATCH(a_szName) (cchFilename == sizeof(a_szName) - 1 && kHlpStrICompAscii(pszFilename, a_szName) == 0)
5789 if ( !kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5790 && !MY_IMATCH("ntdll")
5791 && !MY_IMATCH("kernel32")
5792 && !MY_IMATCH("ntdll.dll")
5793 && !MY_IMATCH("kernelbase")
5794 && !MY_IMATCH("kernel32.dll")
5795 && !MY_IMATCH("kernelbase.dll")
5796 )
5797#undef MY_IMATCH
5798 {
5799 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5800 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5801 {
5802 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5803 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5804 if (kwFsPathExists(szPath))
5805 {
5806 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5807 pszFilename = szPath;
5808 }
5809 }
5810
5811 if (pszFilename != szPath)
5812 {
5813 KSIZE cchSuffix = 0;
5814 KBOOL fNeedSuffix = K_FALSE;
5815 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5816 kHlpAssert(pszCur);
5817 if (pszCur)
5818 {
5819 while (*pszCur != '\0')
5820 {
5821 /* Find the end of the component */
5822 KSIZE cch = 0;
5823 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5824 cch++;
5825
5826 if ( cch > 0 /* wrong, but whatever */
5827 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5828 {
5829 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5830 if ( szPath[cch - 1] != ':'
5831 && szPath[cch - 1] != '/'
5832 && szPath[cch - 1] != '\\')
5833 *pszDst++ = '\\';
5834 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5835 if (fNeedSuffix)
5836 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5837 *pszDst = '\0';
5838
5839 if (kwFsPathExists(szPath))
5840 {
5841 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5842 pszFilename = szPath;
5843 break;
5844 }
5845 }
5846
5847 /* Advance */
5848 pszCur += cch;
5849 while (*pszCur == ';')
5850 pszCur++;
5851 }
5852 }
5853 }
5854 }
5855 }
5856
5857 return LoadLibraryExA(pszFilename, hFile, fFlags);
5858}
5859
5860
5861/** Kernel32 - LoadLibraryExW() for native overloads */
5862static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5863{
5864 char szTmp[4096];
5865 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5866 if (cchTmp < sizeof(szTmp))
5867 return kwSandbox_Kernel32_Native_LoadLibraryExA(szTmp, hFile, fFlags);
5868
5869 KWFS_TODO();
5870 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5871 return NULL;
5872}
5873
5874
5875/** Kernel32 - LoadLibraryExW() */
5876static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5877{
5878 char szTmp[4096];
5879 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5880 if (cchTmp < sizeof(szTmp))
5881 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5882
5883 KWFS_TODO();
5884 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5885 return NULL;
5886}
5887
5888/** Kernel32 - LoadLibraryA() */
5889static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5890{
5891 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5892}
5893
5894
5895/** Kernel32 - LoadLibraryW() */
5896static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5897{
5898 char szTmp[4096];
5899 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5900 if (cchTmp < sizeof(szTmp))
5901 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5902 KWFS_TODO();
5903 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5904 return NULL;
5905}
5906
5907
5908/** Kernel32 - FreeLibrary() */
5909static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5910{
5911 /* Ignored, we like to keep everything loaded. */
5912 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5913 return TRUE;
5914}
5915
5916
5917/** Worker for GetModuleHandleA/W for handling cached modules. */
5918static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5919{
5920 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5921 if (hmod)
5922 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5923 hmod, g_aGetModuleHandleCache[i].pszName));
5924 else
5925 {
5926 /*
5927 * The first time around we have to make sure we have a module table
5928 * entry for it, if not we add one. We need to add it to the tools
5929 * module list to for it to work.
5930 */
5931 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
5932 g_aGetModuleHandleCache[i].fAlwaysPresent);
5933 if (pMod)
5934 {
5935 hmod = pMod->hOurMod;
5936 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5937 {
5938 kwToolAddModule(g_Sandbox.pTool, pMod);
5939 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5940 hmod, g_aGetModuleHandleCache[i].pszName));
5941 }
5942 else
5943 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5944 hmod, g_aGetModuleHandleCache[i].pszName));
5945
5946 }
5947 }
5948 return hmod;
5949}
5950
5951
5952/** Kernel32 - GetModuleHandleA() */
5953static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5954{
5955 KSIZE i;
5956 KSIZE cchModule;
5957 PKWDYNLOAD pDynLoad;
5958 KSIZE cchSuffix;
5959 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5960 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5961
5962 /*
5963 * The executable.
5964 */
5965 if (pszModule == NULL)
5966 {
5967 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5968 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5969 }
5970
5971 /*
5972 * If no path of suffix, pretend it ends with .DLL.
5973 */
5974 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5975
5976 /*
5977 * Cache of system modules we've seen queried.
5978 */
5979 cchModule = kHlpStrLen(pszModule);
5980 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5981 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5982 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5983 || ( cchSuffix > 0
5984 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5985 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5986 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5987 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5988
5989 /*
5990 * Modules we've dynamically loaded.
5991 */
5992 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5993 if (pDynLoad->pMod)
5994 {
5995 const char *pszPath = pDynLoad->pMod->pszPath;
5996 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
5997 if ( stricmp(pszPath, pszModule) == 0
5998 || stricmp(pszName, pszModule) == 0
5999 || ( cchSuffix > 0
6000 && strnicmp(pszName, pszModule, cchModule) == 0
6001 && stricmp(&pszName[cchModule], ".dll") == 0))
6002 {
6003 if ( pDynLoad->pMod->fNative
6004 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6005 {
6006 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
6007 return pDynLoad->hmod;
6008 }
6009 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
6010 SetLastError(ERROR_MOD_NOT_FOUND);
6011 return NULL;
6012 }
6013 }
6014
6015 /*
6016 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6017 * to and go via the g_aGetModuleHandleCache cache.
6018 */
6019/** @todo virtual api DLLs */
6020 if (kHlpStrNICompAscii(pszModule, "api-ms-win-", 11) == 0)
6021 {
6022 HMODULE hmod = GetModuleHandleA(pszModule);
6023 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
6024 if (hmod)
6025 {
6026 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6027 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
6028 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6029 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
6030 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6031 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
6032 if (hmod == GetModuleHandleW(L"UCRTBASE.DLL"))
6033 return kwSandbox_Kernel32_GetModuleHandleA("UCRTBASE.DLL");
6034 }
6035 else
6036 dwErr = GetLastError();
6037 }
6038
6039 kwErrPrintf("pszModule=%s\n", pszModule);
6040 KWFS_TODO();
6041 SetLastError(ERROR_MOD_NOT_FOUND);
6042 return NULL;
6043}
6044
6045
6046/** Kernel32 - GetModuleHandleW() */
6047static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
6048{
6049 KSIZE i;
6050 KSIZE cwcModule;
6051 PKWDYNLOAD pDynLoad;
6052 KSIZE cwcSuffix;
6053 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6054 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6055
6056 /*
6057 * The executable.
6058 */
6059 if (pwszModule == NULL)
6060 {
6061 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
6062 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
6063 }
6064
6065 /*
6066 * If no path of suffix, pretend it ends with .DLL.
6067 */
6068 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
6069
6070 /*
6071 * Cache of system modules we've seen queried.
6072 */
6073 cwcModule = kwUtf16Len(pwszModule);
6074 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6075 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
6076 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
6077 || ( cwcSuffix > 0
6078 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
6079 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
6080 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
6081 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
6082
6083 /*
6084 * Modules we've dynamically loaded.
6085 */
6086 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6087 if (pDynLoad->pMod)
6088 {
6089 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
6090 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
6091 if ( _wcsicmp(pwszPath, pwszModule) == 0
6092 || _wcsicmp(pwszName, pwszModule) == 0
6093 || ( cwcSuffix
6094 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
6095 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
6096 {
6097 if ( pDynLoad->pMod->fNative
6098 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6099 {
6100 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
6101 return pDynLoad->hmod;
6102 }
6103 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
6104 SetLastError(ERROR_MOD_NOT_FOUND);
6105 return NULL;
6106 }
6107 }
6108
6109 /*
6110 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6111 * to and go via the g_aGetModuleHandleCache cache.
6112 */
6113 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
6114 {
6115 HMODULE hmod = GetModuleHandleW(pwszModule);
6116 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
6117 if (hmod)
6118 {
6119 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6120 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
6121 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6122 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
6123 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6124 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
6125 }
6126 else
6127 dwErr = GetLastError();
6128 }
6129
6130 kwErrPrintf("pwszModule=%ls\n", pwszModule);
6131 KWFS_TODO();
6132 SetLastError(dwErr);
6133 return NULL;
6134}
6135
6136
6137/** Used to debug dynamically resolved procedures. */
6138static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
6139{
6140#ifdef _MSC_VER
6141 __debugbreak();
6142#else
6143 KWFS_TODO();
6144#endif
6145 return ~(UINT)0;
6146}
6147
6148
6149#ifndef NDEBUG
6150/*
6151 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
6152 */
6153# if K_ARCH == K_ARCH_X86_32
6154static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
6155# else
6156static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
6157# endif
6158typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
6159typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
6160typedef struct KWCXINTERCEPTORENTRY
6161{
6162 PFNINVOKECOMPILERPASSW pfnOrg;
6163 PKWMODULE pModule;
6164 PFNINVOKECOMPILERPASSW pfnWrap;
6165} KWCXINTERCEPTORENTRY;
6166
6167static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
6168 KWCXINTERCEPTORENTRY *pEntry)
6169{
6170 int i;
6171 KIPTR rcExit;
6172 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
6173 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
6174 for (i = 0; i < cArgs; i++)
6175 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
6176
6177 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
6178
6179 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
6180 return rcExit;
6181}
6182
6183static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
6184static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
6185static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
6186
6187static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
6188{
6189 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
6190 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
6191 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
6192};
6193
6194static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6195{
6196 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
6197}
6198
6199static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6200{
6201 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
6202}
6203
6204static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6205{
6206 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
6207}
6208
6209#endif /* !NDEBUG */
6210
6211
6212/** Kernel32 - GetProcAddress() */
6213static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6214{
6215 KSIZE i;
6216 PKWMODULE pMod;
6217 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6218
6219 /*
6220 * Try locate the module.
6221 */
6222 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6223 if (pMod)
6224 {
6225 KLDRADDR uValue;
6226 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
6227 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
6228 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
6229 KU32_MAX /*iSymbol*/,
6230 pszProc,
6231 kHlpStrLen(pszProc),
6232 NULL /*pszVersion*/,
6233 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
6234 &uValue,
6235 NULL /*pfKind*/);
6236 if (rc == 0)
6237 {
6238 //static int s_cDbgGets = 0;
6239 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
6240 KU32 i = g_cSandboxGetProcReplacements;
6241 while (i-- > 0)
6242 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
6243 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
6244 {
6245 if ( !g_aSandboxGetProcReplacements[i].pszModule
6246 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
6247 {
6248 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
6249 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
6250 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
6251 {
6252 if (!g_aSandboxReplacements[i].fCrtSlotArray)
6253 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
6254 else
6255 {
6256 if (pMod->iCrtSlot == KU8_MAX)
6257 {
6258 int rc = kwLdrModuleCreateCrtSlot(pMod);
6259 if (rc)
6260 {
6261 KW_LOG(("GetProcAddress: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
6262 SetLastError(ERROR_INTERNAL_ERROR);
6263 return NULL;
6264 }
6265 }
6266 uValue = ((KUPTR *)g_aSandboxGetProcReplacements[i].pfnReplacement)[pMod->iCrtSlot];
6267 }
6268
6269 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6270 }
6271 kwLdrModuleRelease(pMod);
6272 return (FARPROC)(KUPTR)uValue;
6273 }
6274 }
6275
6276#ifndef NDEBUG
6277 /* Intercept the compiler pass method, dumping arguments. */
6278 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
6279 {
6280 KU32 i;
6281 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6282 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6283 {
6284 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6285 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6286 break;
6287 }
6288 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6289 while (i-- > 0)
6290 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6291 {
6292 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6293 g_aCxInterceptorEntries[i].pModule = pMod;
6294 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6295 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6296 break;
6297 }
6298 }
6299#endif
6300 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6301 kwLdrModuleRelease(pMod);
6302 //s_cDbgGets++;
6303 //if (s_cGets >= 3)
6304 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6305 return (FARPROC)(KUPTR)uValue;
6306 }
6307
6308 KWFS_TODO();
6309 SetLastError(ERROR_PROC_NOT_FOUND);
6310 kwLdrModuleRelease(pMod);
6311 return NULL;
6312 }
6313
6314 /*
6315 * Hmm... could be a cached module-by-name.
6316 */
6317 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6318 if (g_aGetModuleHandleCache[i].hmod == hmod)
6319 return GetProcAddress(hmod, pszProc);
6320
6321 KWFS_TODO();
6322 return GetProcAddress(hmod, pszProc);
6323}
6324
6325
6326#ifndef NDEBUG
6327/** Kernel32 - GetProcAddress() - native replacement for debugging only. */
6328static FARPROC WINAPI kwSandbox_Kernel32_Native_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6329{
6330 FARPROC pfnRet = GetProcAddress(hmod, pszProc);
6331 KWLDR_LOG(("kwSandbox_Kernel32_Native_GetProcAddress(%p, %s) -> %p\n", hmod, pszProc, pfnRet));
6332 return pfnRet;
6333}
6334#endif
6335
6336
6337/** Kernel32 - GetModuleFileNameA() */
6338static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6339{
6340 PKWMODULE pMod;
6341 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6342
6343 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6344 if (pMod != NULL)
6345 {
6346 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6347 kwLdrModuleRelease(pMod);
6348 return cbRet;
6349 }
6350 KWFS_TODO();
6351 return 0;
6352}
6353
6354
6355/** Kernel32 - GetModuleFileNameW() */
6356static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6357{
6358 PKWMODULE pMod;
6359 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6360
6361 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6362 if (pMod)
6363 {
6364 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6365 kwLdrModuleRelease(pMod);
6366 return cwcRet;
6367 }
6368
6369 KWFS_TODO();
6370 return 0;
6371}
6372
6373
6374/** NtDll - RtlPcToFileHeader
6375 * This is necessary for msvcr100.dll!CxxThrowException. */
6376static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6377{
6378 PVOID pvRet;
6379
6380 /*
6381 * Do a binary lookup of the module table for the current tool.
6382 * This will give us a
6383 */
6384 if (g_Sandbox.fRunning)
6385 {
6386 KUPTR const uPC = (KUPTR)pvPC;
6387 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6388 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6389 KU32 i;
6390 if (iEnd)
6391 {
6392 KU32 iStart = 0;
6393 i = iEnd / 2;
6394 for (;;)
6395 {
6396 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6397 if (uPC < uHModCur)
6398 {
6399 iEnd = i;
6400 if (iStart < i)
6401 { }
6402 else
6403 break;
6404 }
6405 else if (uPC != uHModCur)
6406 {
6407 iStart = ++i;
6408 if (i < iEnd)
6409 { }
6410 else
6411 break;
6412 }
6413 else
6414 {
6415 /* This isn't supposed to happen. */
6416 break;
6417 }
6418
6419 i = iStart + (iEnd - iStart) / 2;
6420 }
6421
6422 /* For reasons of simplicity (= copy & paste), we end up with the
6423 module after the one we're interested in here. */
6424 i--;
6425 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6426 && papMods[i]->pLdrMod)
6427 {
6428 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6429 if (uRvaPC < papMods[i]->cbImage)
6430 {
6431 *ppvImageBase = papMods[i]->hOurMod;
6432 pvRet = papMods[i]->hOurMod;
6433 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6434 return pvRet;
6435 }
6436 }
6437 }
6438 else
6439 i = 0;
6440 }
6441
6442 /*
6443 * Call the regular API.
6444 */
6445 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6446 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6447 return pvRet;
6448}
6449
6450
6451/*
6452 *
6453 * File access APIs (for speeding them up).
6454 * File access APIs (for speeding them up).
6455 * File access APIs (for speeding them up).
6456 *
6457 */
6458
6459
6460/**
6461 * Converts a lookup error to a windows error code.
6462 *
6463 * @returns The windows error code.
6464 * @param enmError The lookup error.
6465 */
6466static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6467{
6468 switch (enmError)
6469 {
6470 case KFSLOOKUPERROR_NOT_FOUND:
6471 case KFSLOOKUPERROR_NOT_DIR:
6472 return ERROR_FILE_NOT_FOUND;
6473
6474 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6475 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6476 case KFSLOOKUPERROR_PATH_TOO_SHORT:
6477 return ERROR_PATH_NOT_FOUND;
6478
6479 case KFSLOOKUPERROR_PATH_TOO_LONG:
6480 return ERROR_FILENAME_EXCED_RANGE;
6481
6482 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6483 return ERROR_NOT_ENOUGH_MEMORY;
6484
6485 default:
6486 return ERROR_PATH_NOT_FOUND;
6487 }
6488}
6489
6490#ifdef WITH_TEMP_MEMORY_FILES
6491
6492/**
6493 * Checks for a cl.exe temporary file.
6494 *
6495 * There are quite a bunch of these. They seems to be passing data between the
6496 * first and second compiler pass. Since they're on disk, they get subjected to
6497 * AV software screening and normal file consistency rules. So, not necessarily
6498 * a very efficient way of handling reasonably small amounts of data.
6499 *
6500 * We make the files live in virtual memory by intercepting their opening,
6501 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6502 *
6503 * @returns K_TRUE / K_FALSE
6504 * @param pwszFilename The file name being accessed.
6505 */
6506static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6507{
6508 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6509 if (pwszName)
6510 {
6511 /* The name starts with _CL_... */
6512 if ( pwszName[0] == '_'
6513 && pwszName[1] == 'C'
6514 && pwszName[2] == 'L'
6515 && pwszName[3] == '_' )
6516 {
6517 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6518 this check by just checking that it's alpha numerical ascii from here on. */
6519 wchar_t wc;
6520 pwszName += 4;
6521 while ((wc = *pwszName++) != '\0')
6522 {
6523 if (wc < 127 && iswalnum(wc))
6524 { /* likely */ }
6525 else
6526 return K_FALSE;
6527 }
6528 return K_TRUE;
6529 }
6530
6531 /* In VC2019 there is also one {UUID} file in temp: */
6532 if (pwszName[0] == '{')
6533 {
6534 KSIZE cwcName = kwUtf16Len(pwszName);
6535 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6536 && pwszName[37] == '}'
6537 && iswalnum(pwszName[1]) // 4
6538 && iswalnum(pwszName[2]) // 4
6539 && iswalnum(pwszName[3]) // 6
6540 && iswalnum(pwszName[4]) // 5
6541 && iswalnum(pwszName[5]) // d
6542 && iswalnum(pwszName[6]) // d
6543 && iswalnum(pwszName[7]) // d
6544 && iswalnum(pwszName[8]) // 9
6545 && pwszName[9] == '-' // -
6546 && iswalnum(pwszName[10]) // e
6547 && iswalnum(pwszName[11]) // 4
6548 && iswalnum(pwszName[12]) // 9
6549 && iswalnum(pwszName[13]) // 4
6550 && pwszName[14] == '-' // -
6551 && iswalnum(pwszName[15]) // 4
6552 && iswalnum(pwszName[16]) // 7
6553 && iswalnum(pwszName[17]) // 1
6554 && iswalnum(pwszName[18]) // b
6555 && pwszName[19] == '-' // -
6556 && iswalnum(pwszName[20]) // 9
6557 && iswalnum(pwszName[21]) // 9
6558 && iswalnum(pwszName[22]) // 6
6559 && iswalnum(pwszName[23]) // b
6560 && pwszName[24] == '-' // -
6561 && iswalnum(pwszName[25]) // 9
6562 && iswalnum(pwszName[26]) // b
6563 && iswalnum(pwszName[27]) // 5
6564 && iswalnum(pwszName[28]) // 5
6565 && iswalnum(pwszName[29]) // 6
6566 && iswalnum(pwszName[30]) // e
6567 && iswalnum(pwszName[31]) // 2
6568 && iswalnum(pwszName[32]) // 5
6569 && iswalnum(pwszName[33]) // a
6570 && iswalnum(pwszName[34]) // 3
6571 && iswalnum(pwszName[35]) // f
6572 && iswalnum(pwszName[36])) // 8
6573 return K_TRUE;
6574 }
6575 }
6576 return K_FALSE;
6577}
6578
6579
6580/**
6581 * Creates a handle to a temporary file.
6582 *
6583 * @returns The handle on success.
6584 * INVALID_HANDLE_VALUE and SetLastError on failure.
6585 * @param pTempFile The temporary file.
6586 * @param dwDesiredAccess The desired access to the handle.
6587 * @param fMapping Whether this is a mapping (K_TRUE) or file
6588 * (K_FALSE) handle type.
6589 */
6590static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6591{
6592 /*
6593 * Create a handle to the temporary file.
6594 */
6595 HANDLE hFile = INVALID_HANDLE_VALUE;
6596 HANDLE hProcSelf = GetCurrentProcess();
6597 if (DuplicateHandle(hProcSelf, hProcSelf,
6598 hProcSelf, &hFile,
6599 SYNCHRONIZE, FALSE,
6600 0 /*dwOptions*/))
6601 {
6602 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6603 if (pHandle)
6604 {
6605 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6606 pHandle->cRefs = 1;
6607 pHandle->offFile = 0;
6608 pHandle->hHandle = hFile;
6609 pHandle->dwDesiredAccess = dwDesiredAccess;
6610 pHandle->tidOwner = KU32_MAX;
6611 pHandle->u.pTempFile = pTempFile;
6612 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6613 {
6614 pTempFile->cActiveHandles++;
6615 kHlpAssert(pTempFile->cActiveHandles >= 1);
6616 kHlpAssert(pTempFile->cActiveHandles <= 2);
6617 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6618 return hFile;
6619 }
6620
6621 kHlpFree(pHandle);
6622 }
6623 else
6624 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6625 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6626 }
6627 else
6628 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6629 return INVALID_HANDLE_VALUE;
6630}
6631
6632
6633static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6634 KBOOL *pfFallback)
6635{
6636 HANDLE hFile;
6637 DWORD dwErr;
6638
6639 /*
6640 * Check if we've got an existing temp file.
6641 * ASSUME exact same path for now.
6642 */
6643 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6644 PKWFSTEMPFILE pTempFile;
6645 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6646 {
6647 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6648 if ( pTempFile->cwcPath == cwcFilename
6649 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6650 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6651 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6652 break;
6653 }
6654
6655 /*
6656 * Create a new temporary file instance if not found.
6657 */
6658 *pfFallback = K_FALSE;
6659 if (pTempFile == NULL)
6660 {
6661 KSIZE cbFilename;
6662
6663 switch (dwCreationDisposition)
6664 {
6665 case CREATE_ALWAYS:
6666 case OPEN_ALWAYS:
6667 case CREATE_NEW:
6668 dwErr = NO_ERROR;
6669 break;
6670
6671 case OPEN_EXISTING:
6672 case TRUNCATE_EXISTING:
6673 *pfFallback = K_TRUE;
6674 kHlpAssertFailed();
6675 SetLastError(ERROR_FILE_NOT_FOUND);
6676 return INVALID_HANDLE_VALUE;
6677
6678 default:
6679 kHlpAssertFailed();
6680 SetLastError(ERROR_INVALID_PARAMETER);
6681 return INVALID_HANDLE_VALUE;
6682 }
6683
6684 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6685 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6686 if (pTempFile)
6687 {
6688 pTempFile->cwcPath = (KU16)cwcFilename;
6689 pTempFile->cbFile = 0;
6690 pTempFile->cbFileAllocated = 0;
6691 pTempFile->cActiveHandles = 0;
6692 pTempFile->cMappings = 0;
6693 pTempFile->cSegs = 0;
6694 pTempFile->paSegs = NULL;
6695 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6696
6697 pTempFile->pNext = g_Sandbox.pTempFileHead;
6698 g_Sandbox.pTempFileHead = pTempFile;
6699 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6700 }
6701 else
6702 {
6703 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6704 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6705 return INVALID_HANDLE_VALUE;
6706 }
6707 }
6708 else
6709 {
6710 switch (dwCreationDisposition)
6711 {
6712 case OPEN_EXISTING:
6713 dwErr = NO_ERROR;
6714 break;
6715 case OPEN_ALWAYS:
6716 dwErr = ERROR_ALREADY_EXISTS;
6717 break;
6718
6719 case TRUNCATE_EXISTING:
6720 case CREATE_ALWAYS:
6721 kHlpAssertFailed();
6722 pTempFile->cbFile = 0;
6723 dwErr = ERROR_ALREADY_EXISTS;
6724 break;
6725
6726 case CREATE_NEW:
6727 kHlpAssertFailed();
6728 SetLastError(ERROR_FILE_EXISTS);
6729 return INVALID_HANDLE_VALUE;
6730
6731 default:
6732 kHlpAssertFailed();
6733 SetLastError(ERROR_INVALID_PARAMETER);
6734 return INVALID_HANDLE_VALUE;
6735 }
6736 }
6737
6738 /*
6739 * Create a handle to the temporary file.
6740 */
6741 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6742 if (hFile != INVALID_HANDLE_VALUE)
6743 SetLastError(dwErr);
6744 return hFile;
6745}
6746
6747#endif /* WITH_TEMP_MEMORY_FILES */
6748
6749/**
6750 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6751 *
6752 * @returns K_TRUE if cacheable, K_FALSE if not.
6753 * @param wcFirst The first extension character.
6754 * @param wcSecond The second extension character.
6755 * @param wcThird The third extension character.
6756 * @param fAttrQuery Set if it's for an attribute query, clear if for
6757 * file creation.
6758 */
6759static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6760{
6761 /* C++ header without an extension or a directory. */
6762 if (wcFirst == '\0')
6763 {
6764 /** @todo exclude temporary files... */
6765 return K_TRUE;
6766 }
6767
6768 /* C Header: .h */
6769 if (wcFirst == 'h' || wcFirst == 'H')
6770 {
6771 if (wcSecond == '\0')
6772 return K_TRUE;
6773
6774 /* C++ Header: .hpp, .hxx */
6775 if ( (wcSecond == 'p' || wcSecond == 'P')
6776 && (wcThird == 'p' || wcThird == 'P'))
6777 return K_TRUE;
6778 if ( (wcSecond == 'x' || wcSecond == 'X')
6779 && (wcThird == 'x' || wcThird == 'X'))
6780 return K_TRUE;
6781 }
6782 /* Misc starting with i. */
6783 else if (wcFirst == 'i' || wcFirst == 'I')
6784 {
6785 if (wcSecond != '\0')
6786 {
6787 if (wcSecond == 'n' || wcSecond == 'N')
6788 {
6789 /* C++ inline header: .inl */
6790 if (wcThird == 'l' || wcThird == 'L')
6791 return K_TRUE;
6792
6793 /* Assembly include file: .inc */
6794 if (wcThird == 'c' || wcThird == 'C')
6795 return K_TRUE;
6796 }
6797 }
6798 }
6799 /* Assembly header: .mac */
6800 else if (wcFirst == 'm' || wcFirst == 'M')
6801 {
6802 if (wcSecond == 'a' || wcSecond == 'A')
6803 {
6804 if (wcThird == 'c' || wcThird == 'C')
6805 return K_TRUE;
6806 }
6807 }
6808#ifdef WITH_PCH_CACHING
6809 /* Precompiled header: .pch */
6810 else if (wcFirst == 'p' || wcFirst == 'P')
6811 {
6812 if (wcSecond == 'c' || wcSecond == 'C')
6813 {
6814 if (wcThird == 'h' || wcThird == 'H')
6815 return !g_Sandbox.fNoPchCaching;
6816 }
6817 }
6818#endif
6819#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6820 /* Linker - Object file: .obj */
6821 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6822 {
6823 if (wcSecond == 'b' || wcSecond == 'B')
6824 {
6825 if (wcThird == 'j' || wcThird == 'J')
6826 return K_TRUE;
6827 }
6828 }
6829#endif
6830 else if (fAttrQuery)
6831 {
6832 /* Dynamic link library: .dll */
6833 if (wcFirst == 'd' || wcFirst == 'D')
6834 {
6835 if (wcSecond == 'l' || wcSecond == 'L')
6836 {
6837 if (wcThird == 'l' || wcThird == 'L')
6838 return K_TRUE;
6839 }
6840 }
6841 /* Executable file: .exe */
6842 else if (wcFirst == 'e' || wcFirst == 'E')
6843 {
6844 if (wcSecond == 'x' || wcSecond == 'X')
6845 {
6846 if (wcThird == 'e' || wcThird == 'E')
6847 return K_TRUE;
6848 }
6849 }
6850 /* Response file: .rsp */
6851 else if (wcFirst == 'r' || wcFirst == 'R')
6852 {
6853 if (wcSecond == 's' || wcSecond == 'S')
6854 {
6855 if (wcThird == 'p' || wcThird == 'P')
6856 return !g_Sandbox.fNoPchCaching;
6857 }
6858 }
6859 /* Linker: */
6860 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6861 {
6862 /* Object file: .obj */
6863 if (wcFirst == 'o' || wcFirst == 'O')
6864 {
6865 if (wcSecond == 'b' || wcSecond == 'B')
6866 {
6867 if (wcThird == 'j' || wcThird == 'J')
6868 return K_TRUE;
6869 }
6870 }
6871 /* Library file: .lib */
6872 else if (wcFirst == 'l' || wcFirst == 'L')
6873 {
6874 if (wcSecond == 'i' || wcSecond == 'I')
6875 {
6876 if (wcThird == 'b' || wcThird == 'B')
6877 return K_TRUE;
6878 }
6879 }
6880 /* Linker definition file: .def */
6881 else if (wcFirst == 'd' || wcFirst == 'D')
6882 {
6883 if (wcSecond == 'e' || wcSecond == 'E')
6884 {
6885 if (wcThird == 'f' || wcThird == 'F')
6886 return K_TRUE;
6887 }
6888 }
6889 }
6890 }
6891
6892 return K_FALSE;
6893}
6894
6895
6896/**
6897 * Checks if the file extension indicates that the file/dir is something we
6898 * ought to cache.
6899 *
6900 * @returns K_TRUE if cachable, K_FALSE if not.
6901 * @param pszExt The kHlpGetExt result.
6902 * @param fAttrQuery Set if it's for an attribute query, clear if for
6903 * file creation.
6904 */
6905static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6906{
6907 wchar_t const wcFirst = *pszExt;
6908 if (wcFirst)
6909 {
6910 wchar_t const wcSecond = pszExt[1];
6911 if (wcSecond)
6912 {
6913 wchar_t const wcThird = pszExt[2];
6914 if (pszExt[3] == '\0')
6915 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6916 return K_FALSE;
6917 }
6918 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6919 }
6920 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6921}
6922
6923
6924/**
6925 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6926 * should be cached.
6927 *
6928 * @returns K_TRUE if cachable, K_FALSE if not.
6929 * @param pwszPath The UTF-16 path to examine.
6930 * @param fAttrQuery Set if it's for an attribute query, clear if for
6931 * file creation.
6932 */
6933static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6934{
6935 KSIZE cwcExt;
6936 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6937 switch (cwcExt)
6938 {
6939 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6940 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6941 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6942 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6943 }
6944 return K_FALSE;
6945}
6946
6947
6948
6949/**
6950 * Creates a new
6951 *
6952 * @returns
6953 * @param pFsObj .
6954 * @param pwszFilename .
6955 */
6956static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6957{
6958 HANDLE hFile;
6959 MY_IO_STATUS_BLOCK Ios;
6960 MY_OBJECT_ATTRIBUTES ObjAttr;
6961 MY_UNICODE_STRING UniStr;
6962 MY_NTSTATUS rcNt;
6963 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6964
6965 /*
6966 * Open the file relative to the parent directory.
6967 */
6968 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6969 kHlpAssert(pFsObj->pParent);
6970 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6971
6972 Ios.Information = ~(ULONG_PTR)0;
6973 Ios.u.Status = -1;
6974
6975 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6976 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6977 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6978
6979 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6980
6981 rcNt = g_pfnNtCreateFile(&hFile,
6982 GENERIC_READ | SYNCHRONIZE,
6983 &ObjAttr,
6984 &Ios,
6985 NULL, /*cbFileInitialAlloc */
6986 FILE_ATTRIBUTE_NORMAL,
6987 FILE_SHARE_READ,
6988 FILE_OPEN,
6989 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6990 NULL, /*pEaBuffer*/
6991 0); /*cbEaBuffer*/
6992 if (MY_NT_SUCCESS(rcNt))
6993 {
6994 /*
6995 * Read the whole file into memory.
6996 */
6997 LARGE_INTEGER cbFile;
6998 if (GetFileSizeEx(hFile, &cbFile))
6999 {
7000 if ( cbFile.QuadPart >= 0
7001#ifdef WITH_PCH_CACHING
7002 && ( cbFile.QuadPart < 16*1024*1024
7003 || ( cbFile.QuadPart < 96*1024*1024
7004 && pFsObj->cchName > 4
7005 && !g_Sandbox.fNoPchCaching
7006 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
7007#endif
7008 )
7009 {
7010 KU32 cbCache = (KU32)cbFile.QuadPart;
7011 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
7012 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
7013 if (hMapping != NULL)
7014 {
7015 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
7016 if (pbCache)
7017 {
7018 /*
7019 * Create the cached file object.
7020 */
7021 PKFSWCACHEDFILE pCachedFile;
7022 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
7023 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
7024 sizeof(*pCachedFile) + cbPath);
7025 if (pCachedFile)
7026 {
7027 pCachedFile->hCached = hFile;
7028 pCachedFile->hSection = hMapping;
7029 pCachedFile->cbCached = cbCache;
7030 pCachedFile->pbCached = pbCache;
7031 pCachedFile->pFsObj = pFsObj;
7032 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
7033 kFsCacheObjRetain(pFsObj);
7034
7035 g_cReadCachedFiles++;
7036 g_cbReadCachedFiles += cbCache;
7037
7038 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
7039 return pCachedFile;
7040 }
7041
7042 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
7043 }
7044 else
7045 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
7046 CloseHandle(hMapping);
7047 }
7048 else
7049 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
7050 }
7051 else
7052 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
7053 }
7054 else
7055 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
7056 g_pfnNtClose(hFile);
7057 }
7058 else
7059 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
7060 return NULL;
7061}
7062
7063
7064/**
7065 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
7066 */
7067static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
7068 KBOOL fIsFileHandle, HANDLE *phFile)
7069{
7070 HANDLE hProcSelf = GetCurrentProcess();
7071 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
7072 hProcSelf, phFile,
7073 dwDesiredAccess, fInheritHandle,
7074 0 /*dwOptions*/))
7075 {
7076 /*
7077 * Create handle table entry for the duplicate handle.
7078 */
7079 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
7080 if (pHandle)
7081 {
7082 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
7083 pHandle->cRefs = 1;
7084 pHandle->offFile = 0;
7085 pHandle->hHandle = *phFile;
7086 pHandle->dwDesiredAccess = dwDesiredAccess;
7087 pHandle->tidOwner = KU32_MAX;
7088 pHandle->u.pCachedFile = pCachedFile;
7089 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
7090 return K_TRUE;
7091
7092 kHlpFree(pHandle);
7093 }
7094 else
7095 KWFS_LOG(("Out of memory for handle!\n"));
7096
7097 CloseHandle(*phFile);
7098 *phFile = INVALID_HANDLE_VALUE;
7099 }
7100 else
7101 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
7102 return K_FALSE;
7103}
7104
7105
7106/**
7107 * Kernel32 - Common code for CreateFileW and CreateFileA.
7108 */
7109static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
7110{
7111 *phFile = INVALID_HANDLE_VALUE;
7112
7113 /*
7114 * At the moment we only handle existing files.
7115 */
7116 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
7117 {
7118 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
7119 kHlpAssert(pFsObj->fHaveStats);
7120 if ( pCachedFile != NULL
7121 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
7122 {
7123 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
7124 return K_TRUE;
7125 }
7126 }
7127 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
7128
7129 /* Do fallback, please. */
7130 return K_FALSE;
7131}
7132
7133
7134/** Kernel32 - CreateFileA */
7135static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7136 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7137 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7138{
7139 HANDLE hFile;
7140
7141 /*
7142 * Check for include files and similar that we do read-only caching of.
7143 */
7144 if (dwCreationDisposition == OPEN_EXISTING)
7145 {
7146 if ( dwDesiredAccess == GENERIC_READ
7147 || dwDesiredAccess == FILE_GENERIC_READ)
7148 {
7149 if (dwShareMode & FILE_SHARE_READ)
7150 {
7151 if ( !pSecAttrs
7152 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7153 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7154 {
7155 const char *pszExt = kHlpGetExt(pszFilename);
7156 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
7157 {
7158 KFSLOOKUPERROR enmError;
7159 PKFSOBJ pFsObj;
7160 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7161
7162 pFsObj = kFsCacheLookupA(g_pFsCache, pszFilename, &enmError);
7163 if (pFsObj)
7164 {
7165 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7166 {
7167 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7168 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7169 kFsCacheObjRelease(g_pFsCache, pFsObj);
7170 if (fRc)
7171 {
7172 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
7173 return hFile;
7174 }
7175 }
7176 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7177 {
7178 KWFS_LOG(("CreateFileA(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7179 SetLastError(ERROR_FILE_NOT_FOUND);
7180 return INVALID_HANDLE_VALUE;
7181 }
7182 /* Always fall back on missing files in volatile areas. */
7183 }
7184 /* These are for nasm and yasm header searching. Cache will already
7185 have checked the directories for the file, no need to call
7186 CreateFile to do it again. */
7187 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7188 {
7189 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7190 SetLastError(ERROR_FILE_NOT_FOUND);
7191 return INVALID_HANDLE_VALUE;
7192 }
7193 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7194 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7195 {
7196 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
7197 SetLastError(ERROR_PATH_NOT_FOUND);
7198 return INVALID_HANDLE_VALUE;
7199 }
7200
7201 /* fallback */
7202 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7203 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7204 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
7205 return hFile;
7206 }
7207 }
7208 }
7209 }
7210 }
7211
7212 /*
7213 * Okay, normal.
7214 */
7215 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7216 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7217 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7218
7219 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7220 return hFile;
7221}
7222
7223
7224/** Kernel32 - CreateFileW */
7225static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7226 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7227 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7228{
7229 HANDLE hFile;
7230
7231#ifdef WITH_TEMP_MEMORY_FILES
7232 /*
7233 * Check for temporary files (cl.exe only).
7234 */
7235 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7236 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7237 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7238 && kwFsIsClTempFileW(pwszFilename))
7239 {
7240 KBOOL fFallback = K_FALSE;
7241 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7242 if (!fFallback)
7243 {
7244 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7245 return hFile;
7246 }
7247 }
7248#endif
7249
7250 /*
7251 * Check for include files and similar that we do read-only caching of.
7252 */
7253 if (dwCreationDisposition == OPEN_EXISTING)
7254 {
7255 if ( dwDesiredAccess == GENERIC_READ
7256 || dwDesiredAccess == FILE_GENERIC_READ)
7257 {
7258 if (dwShareMode & FILE_SHARE_READ)
7259 {
7260 if ( !pSecAttrs
7261 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7262 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7263 {
7264 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7265 {
7266 KFSLOOKUPERROR enmError;
7267 PKFSOBJ pFsObj;
7268 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7269
7270 pFsObj = kFsCacheLookupW(g_pFsCache, pwszFilename, &enmError);
7271 if (pFsObj)
7272 {
7273 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7274 {
7275 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7276 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7277 kFsCacheObjRelease(g_pFsCache, pFsObj);
7278 if (fRc)
7279 {
7280 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7281 return hFile;
7282 }
7283 }
7284 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7285 {
7286 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7287 SetLastError(ERROR_FILE_NOT_FOUND);
7288#if 0
7289 if ( pFsObj->cchName > sizeof("generated.h")
7290 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - sizeof("generated.h") + 1], "generated.h") == 0)
7291 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; pFsObj->fFlags=%#x\n", pwszFilename, pFsObj->fFlags);
7292#endif
7293 return INVALID_HANDLE_VALUE;
7294 }
7295 /* Always fall back on missing files in volatile areas. */
7296 }
7297 /* These are for nasm and yasm style header searching. Cache will
7298 already have checked the directories for the file, no need to call
7299 CreateFile to do it again. */
7300 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7301 {
7302#if 0
7303 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7304 if ( cwcFilename > sizeof("generated.h")
7305 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7306 L"generated.h", sizeof(L"generated.h")) == 0)
7307 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; (KFSLOOKUPERROR_NOT_FOUND)\n", pwszFilename, pFsObj->fFlags);
7308#endif
7309 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7310 SetLastError(ERROR_FILE_NOT_FOUND);
7311 return INVALID_HANDLE_VALUE;
7312 }
7313 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7314 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7315 {
7316#if 0
7317 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7318 if ( cwcFilename > sizeof("generated.h")
7319 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7320 L"generated.h", sizeof(L"generated.h")) == 0)
7321 kwErrPrintf("CreateFileW(%ls) -> ERROR_PATH_NOT_FOUND; (%d)\n", pwszFilename, enmError);
7322#endif
7323 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7324 SetLastError(ERROR_PATH_NOT_FOUND);
7325 return INVALID_HANDLE_VALUE;
7326 }
7327
7328 /* fallback */
7329 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7330 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7331 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7332 return hFile;
7333 }
7334 }
7335 else
7336 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7337 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7338 }
7339 else
7340 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7341 }
7342 else
7343 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7344 }
7345 else
7346 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7347
7348 /*
7349 * Okay, normal.
7350 */
7351 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7352 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7353 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7354
7355 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7356 return hFile;
7357}
7358
7359
7360
7361/** Kernel32 - SetFilePointer */
7362static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7363{
7364 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7365 if (pHandle != NULL)
7366 {
7367 KU32 cbFile;
7368 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7369 switch (pHandle->enmType)
7370 {
7371 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7372 cbFile = pHandle->u.pCachedFile->cbCached;
7373 break;
7374#ifdef WITH_TEMP_MEMORY_FILES
7375 case KWHANDLETYPE_TEMP_FILE:
7376 cbFile = pHandle->u.pTempFile->cbFile;
7377 break;
7378 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7379#endif
7380 case KWHANDLETYPE_OUTPUT_BUF:
7381 default:
7382 kHlpAssertFailed();
7383 kwSandboxHandlePut(pHandle);
7384 SetLastError(ERROR_INVALID_FUNCTION);
7385 return INVALID_SET_FILE_POINTER;
7386 }
7387
7388 switch (dwMoveMethod)
7389 {
7390 case FILE_BEGIN:
7391 break;
7392 case FILE_CURRENT:
7393 offMove += pHandle->offFile;
7394 break;
7395 case FILE_END:
7396 offMove += cbFile;
7397 break;
7398 default:
7399 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7400 kwSandboxHandlePut(pHandle);
7401 SetLastError(ERROR_INVALID_PARAMETER);
7402 return INVALID_SET_FILE_POINTER;
7403 }
7404 if (offMove >= 0)
7405 {
7406 if (offMove >= (KSSIZE)cbFile)
7407 {
7408#ifdef WITH_TEMP_MEMORY_FILES
7409 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7410 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7411#endif
7412 offMove = (KSSIZE)cbFile;
7413#ifdef WITH_TEMP_MEMORY_FILES
7414 /* For writable files, seeking beyond the end is fine, but check that we've got
7415 the type range for the request. */
7416 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7417 {
7418 kHlpAssertMsgFailed(("%#llx\n", offMove));
7419 kwSandboxHandlePut(pHandle);
7420 SetLastError(ERROR_SEEK);
7421 return INVALID_SET_FILE_POINTER;
7422 }
7423#endif
7424 }
7425 pHandle->offFile = (KU32)offMove;
7426 }
7427 else
7428 {
7429 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7430 kwSandboxHandlePut(pHandle);
7431 SetLastError(ERROR_NEGATIVE_SEEK);
7432 return INVALID_SET_FILE_POINTER;
7433 }
7434 if (pcbMoveHi)
7435 *pcbMoveHi = (KU64)offMove >> 32;
7436 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7437 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7438 kwSandboxHandlePut(pHandle);
7439 SetLastError(NO_ERROR);
7440 return (KU32)offMove;
7441 }
7442
7443 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7444 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7445}
7446
7447
7448/** Kernel32 - SetFilePointerEx */
7449static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7450 DWORD dwMoveMethod)
7451{
7452 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7453 if (pHandle != NULL)
7454 {
7455 KI64 offMyMove = offMove.QuadPart;
7456 KU32 cbFile;
7457 switch (pHandle->enmType)
7458 {
7459 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7460 cbFile = pHandle->u.pCachedFile->cbCached;
7461 break;
7462#ifdef WITH_TEMP_MEMORY_FILES
7463 case KWHANDLETYPE_TEMP_FILE:
7464 cbFile = pHandle->u.pTempFile->cbFile;
7465 break;
7466 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7467#endif
7468 case KWHANDLETYPE_OUTPUT_BUF:
7469 default:
7470 kHlpAssertFailed();
7471 kwSandboxHandlePut(pHandle);
7472 SetLastError(ERROR_INVALID_FUNCTION);
7473 return FALSE;
7474 }
7475
7476 switch (dwMoveMethod)
7477 {
7478 case FILE_BEGIN:
7479 break;
7480 case FILE_CURRENT:
7481 offMyMove += pHandle->offFile;
7482 break;
7483 case FILE_END:
7484 offMyMove += cbFile;
7485 break;
7486 default:
7487 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7488 kwSandboxHandlePut(pHandle);
7489 SetLastError(ERROR_INVALID_PARAMETER);
7490 return FALSE;
7491 }
7492 if (offMyMove >= 0)
7493 {
7494 if (offMyMove >= (KSSIZE)cbFile)
7495 {
7496#ifdef WITH_TEMP_MEMORY_FILES
7497 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7498 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7499#endif
7500 offMyMove = (KSSIZE)cbFile;
7501#ifdef WITH_TEMP_MEMORY_FILES
7502 /* For writable files, seeking beyond the end is fine, but check that we've got
7503 the type range for the request. */
7504 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7505 {
7506 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7507 kwSandboxHandlePut(pHandle);
7508 SetLastError(ERROR_SEEK);
7509 return FALSE;
7510 }
7511#endif
7512 }
7513 pHandle->offFile = (KU32)offMyMove;
7514 }
7515 else
7516 {
7517 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7518 kwSandboxHandlePut(pHandle);
7519 SetLastError(ERROR_NEGATIVE_SEEK);
7520 return FALSE;
7521 }
7522 if (poffNew)
7523 poffNew->QuadPart = offMyMove;
7524 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7525 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7526 kwSandboxHandlePut(pHandle);
7527 return TRUE;
7528 }
7529 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7530 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7531}
7532
7533
7534/** Kernel32 - ReadFile */
7535static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7536 LPOVERLAPPED pOverlapped)
7537{
7538 BOOL fRet;
7539 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7540 g_cReadFileCalls++;
7541 if (pHandle != NULL)
7542 {
7543 switch (pHandle->enmType)
7544 {
7545 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7546 {
7547 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7548 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7549 if (cbActually > cbToRead)
7550 cbActually = cbToRead;
7551
7552#ifdef WITH_HASH_MD5_CACHE
7553 if (g_Sandbox.pHashHead)
7554 {
7555 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7556 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7557 g_Sandbox.LastHashRead.cbRead = cbActually;
7558 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7559 }
7560#endif
7561
7562 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7563 pHandle->offFile += cbActually;
7564
7565 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7566 *pcbActuallyRead = cbActually;
7567
7568 g_cbReadFileFromReadCached += cbActually;
7569 g_cbReadFileTotal += cbActually;
7570 g_cReadFileFromReadCached++;
7571
7572 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7573 kwSandboxHandlePut(pHandle);
7574 return TRUE;
7575 }
7576
7577#ifdef WITH_TEMP_MEMORY_FILES
7578 case KWHANDLETYPE_TEMP_FILE:
7579 {
7580 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7581 KU32 cbActually;
7582 if (pHandle->offFile < pTempFile->cbFile)
7583 {
7584 cbActually = pTempFile->cbFile - pHandle->offFile;
7585 if (cbActually > cbToRead)
7586 cbActually = cbToRead;
7587
7588 /* Copy the data. */
7589 if (cbActually > 0)
7590 {
7591 KU32 cbLeft;
7592 KU32 offSeg;
7593 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7594
7595 /* Locate the segment containing the byte at offFile. */
7596 KU32 iSeg = pTempFile->cSegs - 1;
7597 kHlpAssert(pTempFile->cSegs > 0);
7598 while (paSegs[iSeg].offData > pHandle->offFile)
7599 iSeg--;
7600
7601 /* Copy out the data. */
7602 cbLeft = cbActually;
7603 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7604 for (;;)
7605 {
7606 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7607 if (cbAvail >= cbLeft)
7608 {
7609 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7610 break;
7611 }
7612
7613 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7614 cbLeft -= cbAvail;
7615 offSeg = 0;
7616 iSeg++;
7617 kHlpAssert(iSeg < pTempFile->cSegs);
7618 }
7619
7620 /* Update the file offset. */
7621 pHandle->offFile += cbActually;
7622 }
7623 }
7624 /* Read does not commit file space, so return zero bytes. */
7625 else
7626 cbActually = 0;
7627
7628 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7629 *pcbActuallyRead = cbActually;
7630
7631 g_cbReadFileTotal += cbActually;
7632 g_cbReadFileFromInMemTemp += cbActually;
7633 g_cReadFileFromInMemTemp++;
7634
7635 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7636 kwSandboxHandlePut(pHandle);
7637 return TRUE;
7638 }
7639
7640 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7641#endif /* WITH_TEMP_MEMORY_FILES */
7642 case KWHANDLETYPE_OUTPUT_BUF:
7643 default:
7644 kHlpAssertFailed();
7645 kwSandboxHandlePut(pHandle);
7646 SetLastError(ERROR_INVALID_FUNCTION);
7647 *pcbActuallyRead = 0;
7648 return FALSE;
7649 }
7650 }
7651
7652 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7653 if (fRet && pcbActuallyRead)
7654 g_cbReadFileTotal += *pcbActuallyRead;
7655 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7656 return fRet;
7657}
7658
7659
7660/** Kernel32 - ReadFileEx */
7661static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7662 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7663{
7664 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7665
7666 KWFS_LOG(("ReadFile(%p)\n", hFile));
7667 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7668}
7669
7670#ifdef WITH_STD_OUT_ERR_BUFFERING
7671
7672/**
7673 * Write something to a handle, making sure everything is actually written.
7674 *
7675 * @param hHandle Where to write it to.
7676 * @param pchBuf What to write
7677 * @param cchToWrite How much to write.
7678 */
7679static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7680{
7681 if (cchToWrite > 0)
7682 {
7683 DWORD cchWritten = 0;
7684 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7685 {
7686 if (cchWritten == cchToWrite)
7687 { /* likely */ }
7688 else
7689 {
7690 do
7691 {
7692 pchBuf += cchWritten;
7693 cchToWrite -= cchWritten;
7694 cchWritten = 0;
7695 } while ( cchToWrite > 0
7696 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7697 }
7698 }
7699 else
7700 kHlpAssertFailed();
7701 }
7702}
7703
7704
7705/**
7706 * Worker for WriteFile when the output isn't going to the console.
7707 *
7708 * @param pSandbox The sandbox.
7709 * @param pOutBuf The output buffer.
7710 * @param pchBuffer What to write.
7711 * @param cchToWrite How much to write.
7712 */
7713static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7714{
7715 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7716 { /* likely */ }
7717 else
7718 {
7719 /* No realloc, max size is 64KB. */
7720 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7721 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7722 if (!pOutBuf->u.Fully.pchBuf)
7723 {
7724 while ( !pOutBuf->u.Fully.pchBuf
7725 && pOutBuf->u.Fully.cchBufAlloc > 64)
7726 {
7727 pOutBuf->u.Fully.cchBufAlloc /= 2;
7728 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7729 }
7730 if (!pOutBuf->u.Fully.pchBuf)
7731 {
7732 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7733 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7734 }
7735 }
7736 }
7737
7738 /*
7739 * Special case: Output ends with newline and fits in the buffer.
7740 */
7741 if ( cchToWrite > 1
7742 && pchBuffer[cchToWrite - 1] == '\n'
7743 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7744 {
7745 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7746 pOutBuf->u.Fully.cchBuf += cchToWrite;
7747 }
7748 else
7749 {
7750 /*
7751 * Work thru the text line by line, flushing the buffer when
7752 * appropriate. The buffer is not a line buffer here, it's a
7753 * full buffer.
7754 */
7755 while (cchToWrite > 0)
7756 {
7757 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7758 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7759 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7760 {
7761 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7762 pOutBuf->u.Fully.cchBuf += cchLine;
7763 }
7764 /*
7765 * Option one: Flush the buffer and the current line.
7766 *
7767 * We choose this one when the line won't ever fit, or when we have
7768 * an incomplete line in the buffer.
7769 */
7770 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7771 || pOutBuf->u.Fully.cchBuf == 0
7772 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7773 {
7774 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7775 if (pOutBuf->u.Fully.cchBuf > 0)
7776 {
7777 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7778 pOutBuf->u.Fully.cchBuf = 0;
7779 }
7780 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7781 }
7782 /*
7783 * Option two: Only flush the lines in the buffer.
7784 */
7785 else
7786 {
7787 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7788 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7789 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7790 pOutBuf->u.Fully.cchBuf = cchLine;
7791 }
7792
7793 /* advance */
7794 pchBuffer += cchLine;
7795 cchToWrite -= cchLine;
7796 }
7797 }
7798}
7799
7800#endif /* WITH_STD_OUT_ERR_BUFFERING */
7801
7802#ifdef WITH_TEMP_MEMORY_FILES
7803static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7804{
7805 KU32 cbMinFile = offFile + cbNeeded;
7806 if (cbMinFile >= offFile)
7807 {
7808 /* Calc how much space we've already allocated and */
7809 if (cbMinFile <= pTempFile->cbFileAllocated)
7810 return K_TRUE;
7811
7812 /* Grow the file. */
7813 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7814 {
7815 int rc;
7816 KU32 cSegs = pTempFile->cSegs;
7817 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7818 do
7819 {
7820 /* grow the segment array? */
7821 if ((cSegs % 16) == 0)
7822 {
7823 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7824 if (!pvNew)
7825 return K_FALSE;
7826 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7827 }
7828
7829 /* Use page alloc here to simplify mapping later. */
7830 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7831 if (rc == 0)
7832 { /* likely */ }
7833 else
7834 {
7835 cbNewSeg = 64*1024;
7836 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7837 if (rc != 0)
7838 {
7839 kHlpAssertFailed();
7840 return K_FALSE;
7841 }
7842 }
7843 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7844 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7845 pTempFile->cbFileAllocated += cbNewSeg;
7846 pTempFile->cSegs = ++cSegs;
7847
7848 } while (pTempFile->cbFileAllocated < cbMinFile);
7849
7850 return K_TRUE;
7851 }
7852 }
7853
7854 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7855 return K_FALSE;
7856}
7857#endif /* WITH_TEMP_MEMORY_FILES */
7858
7859
7860#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7861/** Kernel32 - WriteFile */
7862static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7863 LPOVERLAPPED pOverlapped)
7864{
7865 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7866 BOOL fRet;
7867 g_cWriteFileCalls++;
7868 if (pHandle != NULL)
7869 {
7870 switch (pHandle->enmType)
7871 {
7872# ifdef WITH_TEMP_MEMORY_FILES
7873 case KWHANDLETYPE_TEMP_FILE:
7874 {
7875 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7876
7877 kHlpAssert(!pOverlapped);
7878 kHlpAssert(pcbActuallyWritten);
7879
7880 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7881 {
7882 KU32 cbLeft;
7883 KU32 offSeg;
7884
7885 /* Locate the segment containing the byte at offFile. */
7886 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7887 KU32 iSeg = pTempFile->cSegs - 1;
7888 kHlpAssert(pTempFile->cSegs > 0);
7889 while (paSegs[iSeg].offData > pHandle->offFile)
7890 iSeg--;
7891
7892 /* Copy in the data. */
7893 cbLeft = cbToWrite;
7894 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7895 for (;;)
7896 {
7897 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7898 if (cbAvail >= cbLeft)
7899 {
7900 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7901 break;
7902 }
7903
7904 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7905 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7906 cbLeft -= cbAvail;
7907 offSeg = 0;
7908 iSeg++;
7909 kHlpAssert(iSeg < pTempFile->cSegs);
7910 }
7911
7912 /* Update the file offset. */
7913 pHandle->offFile += cbToWrite;
7914 if (pHandle->offFile > pTempFile->cbFile)
7915 pTempFile->cbFile = pHandle->offFile;
7916
7917 *pcbActuallyWritten = cbToWrite;
7918
7919 g_cbWriteFileTotal += cbToWrite;
7920 g_cbWriteFileToInMemTemp += cbToWrite;
7921 g_cWriteFileToInMemTemp++;
7922
7923 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7924 kwSandboxHandlePut(pHandle);
7925 return TRUE;
7926 }
7927
7928 kHlpAssertFailed();
7929 kwSandboxHandlePut(pHandle);
7930 *pcbActuallyWritten = 0;
7931 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7932 return FALSE;
7933 }
7934# endif
7935
7936 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7937 kHlpAssertFailed();
7938 kwSandboxHandlePut(pHandle);
7939 SetLastError(ERROR_ACCESS_DENIED);
7940 *pcbActuallyWritten = 0;
7941 return FALSE;
7942
7943# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7944 /*
7945 * Standard output & error.
7946 */
7947 case KWHANDLETYPE_OUTPUT_BUF:
7948 {
7949 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7950 if (pOutBuf->fIsConsole)
7951 {
7952 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7953 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7954 }
7955 else
7956 {
7957# ifdef WITH_STD_OUT_ERR_BUFFERING
7958 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7959 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7960 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7961# else
7962 kHlpAssertFailed();
7963# endif
7964 }
7965 if (pcbActuallyWritten)
7966 *pcbActuallyWritten = cbToWrite;
7967 g_cbWriteFileTotal += cbToWrite;
7968 kwSandboxHandlePut(pHandle);
7969 return TRUE;
7970 }
7971# endif
7972
7973 default:
7974#ifdef WITH_TEMP_MEMORY_FILES
7975 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7976#endif
7977 kHlpAssertFailed();
7978 kwSandboxHandlePut(pHandle);
7979 SetLastError(ERROR_INVALID_FUNCTION);
7980 *pcbActuallyWritten = 0;
7981 return FALSE;
7982 }
7983 }
7984
7985 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7986 if (fRet && pcbActuallyWritten)
7987 g_cbWriteFileTotal += *pcbActuallyWritten;
7988 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7989 return fRet;
7990}
7991
7992
7993/** Kernel32 - WriteFileEx */
7994static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7995 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7996{
7997 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7998
7999 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
8000 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
8001}
8002
8003#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
8004
8005#ifdef WITH_TEMP_MEMORY_FILES
8006
8007/** Kernel32 - SetEndOfFile; */
8008static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
8009{
8010 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8011 if (pHandle != NULL)
8012 {
8013 switch (pHandle->enmType)
8014 {
8015 case KWHANDLETYPE_TEMP_FILE:
8016 {
8017 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8018 if ( pHandle->offFile > pTempFile->cbFile
8019 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
8020 {
8021 kHlpAssertFailed();
8022 kwSandboxHandlePut(pHandle);
8023 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8024 return FALSE;
8025 }
8026
8027 pTempFile->cbFile = pHandle->offFile;
8028 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
8029 kwSandboxHandlePut(pHandle);
8030 return TRUE;
8031 }
8032
8033 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8034 kHlpAssertFailed();
8035 kwSandboxHandlePut(pHandle);
8036 SetLastError(ERROR_ACCESS_DENIED);
8037 return FALSE;
8038
8039# ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8040 case KWHANDLETYPE_OUTPUT_BUF:
8041 kHlpAssertFailed();
8042 kwSandboxHandlePut(pHandle);
8043 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
8044 return FALSE;
8045# endif
8046
8047 default:
8048 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8049 kHlpAssertFailed();
8050 kwSandboxHandlePut(pHandle);
8051 SetLastError(ERROR_INVALID_FUNCTION);
8052 return FALSE;
8053 }
8054 }
8055
8056 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
8057 return SetEndOfFile(hFile);
8058}
8059
8060
8061/** Kernel32 - GetFileType */
8062static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
8063{
8064 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8065 if (pHandle != NULL)
8066 {
8067 switch (pHandle->enmType)
8068 {
8069 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8070 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
8071 kwSandboxHandlePut(pHandle);
8072 return FILE_TYPE_DISK;
8073
8074 case KWHANDLETYPE_TEMP_FILE:
8075 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
8076 kwSandboxHandlePut(pHandle);
8077 return FILE_TYPE_DISK;
8078
8079#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8080 case KWHANDLETYPE_OUTPUT_BUF:
8081 {
8082 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8083 DWORD fRet;
8084 if (pOutBuf->fFileType != KU8_MAX)
8085 {
8086 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
8087 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
8088 }
8089 else
8090 {
8091 fRet = GetFileType(hFile);
8092 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
8093 }
8094 kwSandboxHandlePut(pHandle);
8095 return fRet;
8096 }
8097#endif
8098 }
8099 kwSandboxHandlePut(pHandle);
8100 }
8101
8102 KWFS_LOG(("GetFileType(%p)\n", hFile));
8103 return GetFileType(hFile);
8104}
8105
8106
8107/** Kernel32 - GetFileSize */
8108static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
8109{
8110 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8111 if (pHandle != NULL)
8112 {
8113 if (pcbHighDword)
8114 *pcbHighDword = 0;
8115 SetLastError(NO_ERROR);
8116 switch (pHandle->enmType)
8117 {
8118 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8119 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8120 kwSandboxHandlePut(pHandle);
8121 return pHandle->u.pCachedFile->cbCached;
8122
8123 case KWHANDLETYPE_TEMP_FILE:
8124 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8125 kwSandboxHandlePut(pHandle);
8126 return pHandle->u.pTempFile->cbFile;
8127
8128 case KWHANDLETYPE_OUTPUT_BUF:
8129 /* do default */
8130 break;
8131
8132 default:
8133 kHlpAssertFailed();
8134 kwSandboxHandlePut(pHandle);
8135 SetLastError(ERROR_INVALID_FUNCTION);
8136 return INVALID_FILE_SIZE;
8137 }
8138 kwSandboxHandlePut(pHandle);
8139 }
8140
8141 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
8142 return GetFileSize(hFile, pcbHighDword);
8143}
8144
8145
8146/** Kernel32 - GetFileSizeEx */
8147static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
8148{
8149 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8150 if (pHandle != NULL)
8151 {
8152 switch (pHandle->enmType)
8153 {
8154 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8155 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8156 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
8157 kwSandboxHandlePut(pHandle);
8158 return TRUE;
8159
8160 case KWHANDLETYPE_TEMP_FILE:
8161 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8162 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
8163 kwSandboxHandlePut(pHandle);
8164 return TRUE;
8165
8166 case KWHANDLETYPE_OUTPUT_BUF:
8167 /* do default */
8168 break;
8169
8170 default:
8171 kHlpAssertFailed();
8172 kwSandboxHandlePut(pHandle);
8173 SetLastError(ERROR_INVALID_FUNCTION);
8174 return INVALID_FILE_SIZE;
8175 }
8176 kwSandboxHandlePut(pHandle);
8177 }
8178
8179 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
8180 return GetFileSizeEx(hFile, pcbFile);
8181}
8182
8183
8184/** Kernel32 - CreateFileMappingW */
8185static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
8186 DWORD fProtect, DWORD dwMaximumSizeHigh,
8187 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
8188{
8189 HANDLE hMapping;
8190 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8191 if (pHandle != NULL)
8192 {
8193 switch (pHandle->enmType)
8194 {
8195 case KWHANDLETYPE_TEMP_FILE:
8196 {
8197 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8198 if ( ( fProtect == PAGE_READONLY
8199 || fProtect == PAGE_EXECUTE_READ)
8200 && dwMaximumSizeHigh == 0
8201 && ( dwMaximumSizeLow == 0
8202 || dwMaximumSizeLow == pTempFile->cbFile)
8203 && pwszName == NULL)
8204 {
8205 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
8206 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
8207 kwSandboxHandlePut(pHandle);
8208 return hMapping;
8209 }
8210 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
8211 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8212 kwSandboxHandlePut(pHandle);
8213 SetLastError(ERROR_ACCESS_DENIED);
8214 return INVALID_HANDLE_VALUE;
8215 }
8216
8217 /* moc.exe benefits from this. */
8218 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8219 {
8220 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8221 if ( ( fProtect == PAGE_READONLY
8222 || fProtect == PAGE_EXECUTE_READ)
8223 && dwMaximumSizeHigh == 0
8224 && ( dwMaximumSizeLow == 0
8225 || dwMaximumSizeLow == pCachedFile->cbCached)
8226 && pwszName == NULL)
8227 {
8228 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
8229 K_FALSE /*fIsFileHandle*/, &hMapping))
8230 { /* likely */ }
8231 else
8232 hMapping = NULL;
8233 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8234 kwSandboxHandlePut(pHandle);
8235 return hMapping;
8236 }
8237
8238 /* Do fallback (for .pch). */
8239 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8240 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8241 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8242
8243 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8244 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8245 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8246 kwSandboxHandlePut(pHandle);
8247 return hMapping;
8248 }
8249
8250 /** @todo read cached memory mapped files too for moc. */
8251 }
8252 kwSandboxHandlePut(pHandle);
8253 }
8254
8255 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8256 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8257 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8258 return hMapping;
8259}
8260
8261
8262/** Kernel32 - MapViewOfFile */
8263static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8264 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8265{
8266 PVOID pvRet;
8267 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8268 if (pHandle != NULL)
8269 {
8270 KU32 idxMapping;
8271
8272 /*
8273 * Ensure one free entry in the mapping tracking table first,
8274 * since this is common to both temporary and cached files.
8275 */
8276 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8277 { /* likely */ }
8278 else
8279 {
8280 void *pvNew;
8281 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8282 if (cNew)
8283 cNew *= 2;
8284 else
8285 cNew = 32;
8286 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8287 if (pvNew)
8288 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8289 else
8290 {
8291 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8292 kwSandboxHandlePut(pHandle);
8293 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8294 return NULL;
8295 }
8296 g_Sandbox.cMemMappingsAlloc = cNew;
8297 }
8298
8299 /*
8300 * Type specific work.
8301 */
8302 switch (pHandle->enmType)
8303 {
8304 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8305 case KWHANDLETYPE_TEMP_FILE:
8306 case KWHANDLETYPE_OUTPUT_BUF:
8307 default:
8308 kHlpAssertFailed();
8309 kwSandboxHandlePut(pHandle);
8310 SetLastError(ERROR_INVALID_OPERATION);
8311 return NULL;
8312
8313 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8314 {
8315 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8316 if ( dwDesiredAccess == FILE_MAP_READ
8317 && offFileHigh == 0
8318 && offFileLow == 0
8319 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8320 {
8321 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8322 if (pTempFile->cSegs != 1)
8323 {
8324 KU32 iSeg;
8325 KU32 cbLeft;
8326 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8327 KU8 *pbAll = NULL;
8328 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8329 if (rc != 0)
8330 {
8331 kHlpAssertFailed();
8332 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8333 return NULL;
8334 }
8335
8336 cbLeft = pTempFile->cbFile;
8337 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8338 {
8339 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8340 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8341 cbLeft -= cbToCopy;
8342 }
8343
8344 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8345 {
8346 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8347 pTempFile->paSegs[iSeg].pbData = NULL;
8348 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8349 }
8350
8351 pTempFile->cSegs = 1;
8352 pTempFile->cbFileAllocated = cbAll;
8353 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8354 pTempFile->paSegs[0].pbData = pbAll;
8355 pTempFile->paSegs[0].offData = 0;
8356 }
8357
8358 pTempFile->cMappings++;
8359 kHlpAssert(pTempFile->cMappings == 1);
8360
8361 pvRet = pTempFile->paSegs[0].pbData;
8362 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8363 break;
8364 }
8365
8366 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8367 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8368 kwSandboxHandlePut(pHandle);
8369 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8370 return NULL;
8371 }
8372
8373 /*
8374 * This is simple in comparison to the above temporary file code.
8375 */
8376 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8377 {
8378 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8379 if ( dwDesiredAccess == FILE_MAP_READ
8380 && offFileHigh == 0
8381 && offFileLow == 0
8382 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8383 {
8384 pvRet = pCachedFile->pbCached;
8385 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8386 break;
8387 }
8388 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8389 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8390 kwSandboxHandlePut(pHandle);
8391 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8392 return NULL;
8393 }
8394 }
8395
8396 /*
8397 * Insert into the mapping tracking table. This is common
8398 * and we should only get here with a non-NULL pvRet.
8399 *
8400 * Note! We could look for duplicates and do ref counting, but it's
8401 * easier to just append for now.
8402 */
8403 kHlpAssert(pvRet != NULL);
8404 idxMapping = g_Sandbox.cMemMappings;
8405 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8406
8407 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8408 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8409 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8410 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8411 g_Sandbox.cMemMappings++;
8412
8413 kwSandboxHandlePut(pHandle);
8414 return pvRet;
8415 }
8416
8417 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8418 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8419 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8420 return pvRet;
8421}
8422
8423
8424/** Kernel32 - MapViewOfFileEx */
8425static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8426 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8427{
8428 PVOID pvRet;
8429 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8430 if (pHandle != NULL)
8431 {
8432 switch (pHandle->enmType)
8433 {
8434 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8435 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8436 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8437 if (!pvMapAddr)
8438 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8439 else
8440 {
8441 kHlpAssertFailed();
8442 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8443 }
8444 kwSandboxHandlePut(pHandle);
8445 return NULL;
8446
8447 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8448 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8449 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8450 if (!pvMapAddr)
8451 {
8452 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8453 kwSandboxHandlePut(pHandle);
8454 return pvRet;
8455 }
8456 /* We can use fallback here as the handle is an actual section handle. */
8457 break;
8458
8459 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8460 case KWHANDLETYPE_TEMP_FILE:
8461 case KWHANDLETYPE_OUTPUT_BUF:
8462 default:
8463 kHlpAssertFailed();
8464 kwSandboxHandlePut(pHandle);
8465 SetLastError(ERROR_INVALID_OPERATION);
8466 return NULL;
8467 }
8468 kwSandboxHandlePut(pHandle);
8469 }
8470
8471 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8472 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8473 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8474 return pvRet;
8475
8476}
8477
8478/** Kernel32 - UnmapViewOfFile */
8479static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8480{
8481 /*
8482 * Consult the memory mapping tracker.
8483 */
8484 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8485 KU32 idxMapping = g_Sandbox.cMemMappings;
8486 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8487 while (idxMapping-- > 0)
8488 if (paMemMappings[idxMapping].pvMapping == pvBase)
8489 {
8490 /* Type specific stuff. */
8491 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8492 {
8493 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8494 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8495 }
8496 else
8497 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8498
8499 /* Deref and probably free it. */
8500 if (--paMemMappings[idxMapping].cRefs == 0)
8501 {
8502 g_Sandbox.cMemMappings--;
8503 if (idxMapping != g_Sandbox.cMemMappings)
8504 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8505 }
8506 return TRUE;
8507 }
8508
8509 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8510 return UnmapViewOfFile(pvBase);
8511}
8512
8513/** @todo UnmapViewOfFileEx */
8514
8515#endif /* WITH_TEMP_MEMORY_FILES */
8516
8517
8518/** Kernel32 - DuplicateHandle */
8519static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8520 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8521{
8522 BOOL fRet;
8523
8524 /*
8525 * We must catch our handles being duplicated.
8526 */
8527 if (hSrcProc == GetCurrentProcess())
8528 {
8529 PKWHANDLE pHandle = kwSandboxHandleGet(hSrc);
8530 if (pHandle)
8531 {
8532 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8533 if (fRet)
8534 {
8535 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8536 {
8537 pHandle->cRefs++;
8538 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8539 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8540 pHandle->enmType, pHandle->cRefs));
8541 }
8542 else
8543 {
8544 fRet = FALSE;
8545 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8546 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8547 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8548 }
8549 }
8550 else
8551 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8552 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8553 kwSandboxHandlePut(pHandle);
8554 return fRet;
8555 }
8556 }
8557
8558 /*
8559 * Not one of ours, just do what the caller asks and log it.
8560 */
8561 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8562 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8563 fInheritHandle, dwOptions, fRet, *phNew));
8564 return fRet;
8565}
8566
8567
8568/** Kernel32 - CloseHandle */
8569static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8570{
8571 BOOL fRet;
8572 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8573 PKWHANDLE pHandle = kwSandboxHandleGet(hObject);
8574 if (pHandle)
8575 {
8576 /* Prevent the closing of the standard output and error handles. */
8577 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8578 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) /* why this?!? */)
8579 {
8580 fRet = CloseHandle(hObject);
8581 if (fRet)
8582 {
8583 EnterCriticalSection(&g_Sandbox.HandlesLock);
8584 pHandle = g_Sandbox.papHandles[idxHandle];
8585 g_Sandbox.papHandles[idxHandle] = NULL;
8586 g_Sandbox.cActiveHandles--;
8587 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8588 if (--pHandle->cRefs == 0)
8589 {
8590#ifdef WITH_TEMP_MEMORY_FILES
8591 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8592 {
8593 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8594 pHandle->u.pTempFile->cActiveHandles--;
8595 }
8596#endif
8597 kHlpFree(pHandle);
8598 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8599 }
8600 else
8601 {
8602 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8603 kwSandboxHandlePut(pHandle);
8604 }
8605 LeaveCriticalSection(&g_Sandbox.HandlesLock);
8606 return fRet;
8607 }
8608 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8609 }
8610 else
8611 {
8612#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8613 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8614 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8615#else
8616 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of stdXXX!\n", hObject));
8617#endif
8618 fRet = TRUE;
8619 }
8620 kwSandboxHandlePut(pHandle);
8621 return fRet;
8622 }
8623
8624 fRet = CloseHandle(hObject);
8625 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8626 return fRet;
8627}
8628
8629
8630/** Kernel32 - GetFileAttributesA. */
8631static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8632{
8633 DWORD fRet;
8634 const char *pszExt = kHlpGetExt(pszFilename);
8635 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8636 {
8637 KFSLOOKUPERROR enmError;
8638 PKFSOBJ pFsObj;
8639 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8640
8641 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8642 if (pFsObj)
8643 {
8644 kHlpAssert(pFsObj->fHaveStats);
8645 fRet = pFsObj->Stats.st_attribs;
8646 kFsCacheObjRelease(g_pFsCache, pFsObj);
8647 }
8648 else
8649 {
8650 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8651 fRet = INVALID_FILE_ATTRIBUTES;
8652 }
8653
8654 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8655 return fRet;
8656 }
8657
8658 fRet = GetFileAttributesA(pszFilename);
8659 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8660 return fRet;
8661}
8662
8663
8664/** Kernel32 - GetFileAttributesW. */
8665static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8666{
8667 DWORD fRet;
8668 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8669 {
8670 KFSLOOKUPERROR enmError;
8671 PKFSOBJ pFsObj;
8672 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8673
8674 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8675 if (pFsObj)
8676 {
8677 kHlpAssert(pFsObj->fHaveStats);
8678 fRet = pFsObj->Stats.st_attribs;
8679 kFsCacheObjRelease(g_pFsCache, pFsObj);
8680 }
8681 else
8682 {
8683 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8684 fRet = INVALID_FILE_ATTRIBUTES;
8685 }
8686#ifndef NDEBUG
8687 {
8688 DWORD fCheck = GetFileAttributesW(pwszFilename);
8689 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8690 }
8691#endif
8692 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8693 return fRet;
8694 }
8695
8696 fRet = GetFileAttributesW(pwszFilename);
8697 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8698 return fRet;
8699}
8700
8701
8702/** Kernel32 - GetFileAttributesExA. */
8703static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8704 WIN32_FILE_ATTRIBUTE_DATA *pData)
8705{
8706 BOOL fRet;
8707 const char *pszExt = kHlpGetExt(pszFilename);
8708 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8709 {
8710 KFSLOOKUPERROR enmError;
8711 PKFSOBJ pFsObj;
8712 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8713
8714 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8715 if (pFsObj)
8716 {
8717 kHlpAssert(pFsObj->fHaveStats);
8718 if (enmLevel == GetFileExInfoStandard)
8719 {
8720 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8721 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8722 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8723 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8724 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8725 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8726 kFsCacheObjRelease(g_pFsCache, pFsObj);
8727 fRet = TRUE;
8728 }
8729 else
8730 {
8731 kFsCacheObjRelease(g_pFsCache, pFsObj);
8732 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8733 }
8734 }
8735 else
8736 {
8737 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8738 fRet = FALSE;
8739 }
8740
8741#ifdef K_STRICT
8742 {
8743 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8744 DWORD const dwErrSaved = GetLastError();
8745 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8746 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8747 if (fRetCheck && fRet)
8748 {
8749# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8750 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8751 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8752 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8753 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8754 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8755 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8756 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8757 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8758 }
8759 else
8760 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8761 SetLastError(dwErrSaved);
8762 }
8763#endif
8764 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8765 return fRet;
8766 }
8767
8768 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8769 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8770 return fRet;
8771}
8772
8773
8774/** Kernel32 - GetFileAttributesExW. */
8775static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8776 WIN32_FILE_ATTRIBUTE_DATA *pData)
8777{
8778 BOOL fRet;
8779 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8780 {
8781 KFSLOOKUPERROR enmError;
8782 PKFSOBJ pFsObj;
8783 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8784
8785 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8786 if (pFsObj)
8787 {
8788 kHlpAssert(pFsObj->fHaveStats);
8789 if (enmLevel == GetFileExInfoStandard)
8790 {
8791 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8792 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8793 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8794 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8795 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8796 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8797 kFsCacheObjRelease(g_pFsCache, pFsObj);
8798 fRet = TRUE;
8799 }
8800 else
8801 {
8802 kFsCacheObjRelease(g_pFsCache, pFsObj);
8803 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8804 }
8805 }
8806 else
8807 {
8808 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8809 fRet = FALSE;
8810 }
8811
8812#ifdef K_STRICT
8813 {
8814 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8815 DWORD const dwErrSaved = GetLastError();
8816 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8817 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8818 if (fRetCheck && fRet)
8819 {
8820# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8821 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8822 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8823 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8824 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8825 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8826 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8827 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8828 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8829 }
8830 else
8831 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8832 SetLastError(dwErrSaved);
8833 }
8834#endif
8835 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8836 return fRet;
8837 }
8838
8839 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8840 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8841 return fRet;
8842}
8843
8844
8845/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8846 * directory containing each include file. We cache the result to speed
8847 * things up a little. */
8848static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8849{
8850 DWORD cwcRet;
8851 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8852 {
8853 KFSLOOKUPERROR enmError;
8854 PKFSOBJ pObj;
8855 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8856
8857 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8858 if (pObj)
8859 {
8860 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8861 {
8862 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8863 {
8864 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8865
8866 /* Should preserve trailing slash on directory paths. */
8867 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8868 {
8869 if ( cwcRet + 1 < cwcShortPath
8870 && pwszShortPath[cwcRet - 1] != '\\')
8871 {
8872 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8873 if ( cwcIn > 0
8874 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8875 {
8876 pwszShortPath[cwcRet++] = '\\';
8877 pwszShortPath[cwcRet] = '\0';
8878 }
8879 }
8880 }
8881
8882 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8883 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8884 kFsCacheObjRelease(g_pFsCache, pObj);
8885 return cwcRet;
8886 }
8887
8888 /* fall back for complicated cases. */
8889 }
8890 kFsCacheObjRelease(g_pFsCache, pObj);
8891 }
8892 }
8893 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8894 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8895 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8896 return cwcRet;
8897}
8898
8899
8900#ifdef WITH_TEMP_MEMORY_FILES
8901/** Kernel32 - DeleteFileW
8902 * Skip deleting the in-memory files. */
8903static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8904{
8905 BOOL fRc;
8906 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8907 && kwFsIsClTempFileW(pwszFilename))
8908 {
8909 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8910 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8911 fRc = TRUE;
8912 }
8913 else
8914 {
8915 fRc = DeleteFileW(pwszFilename);
8916 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8917 }
8918 return fRc;
8919}
8920#endif /* WITH_TEMP_MEMORY_FILES */
8921
8922
8923
8924#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8925
8926/*
8927 *
8928 * Console output buffering.
8929 * Console output buffering.
8930 * Console output buffering.
8931 *
8932 */
8933
8934
8935/**
8936 * Write a wide char string to the console.
8937 *
8938 * @param pSandbox The sandbox which output buffer to flush.
8939 */
8940static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8941{
8942 if (cwcToWrite > 0)
8943 {
8944 DWORD cwcWritten = 0;
8945 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8946 {
8947 if (cwcWritten == cwcToWrite)
8948 { /* likely */ }
8949 else
8950 {
8951 DWORD off = 0;
8952 do
8953 {
8954 off += cwcWritten;
8955 cwcWritten = 0;
8956 } while ( off < cwcToWrite
8957 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8958 kHlpAssert(off == cwcWritten);
8959 }
8960 }
8961 else
8962 kHlpAssertFailed();
8963 pSandbox->Combined.cFlushes++;
8964 }
8965}
8966
8967
8968/**
8969 * Flushes the combined console output buffer.
8970 *
8971 * @param pSandbox The sandbox which output buffer to flush.
8972 */
8973static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8974{
8975 if (pSandbox->Combined.cwcBuf > 0)
8976 {
8977 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8978 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8979 pSandbox->Combined.cwcBuf = 0;
8980 }
8981}
8982
8983
8984/**
8985 * For handling combined buffer overflow cases line by line.
8986 *
8987 * @param pSandbox The sandbox.
8988 * @param pwcBuf What to add to the combined buffer. Usually a
8989 * line, unless we're really low on buffer space.
8990 * @param cwcBuf The length of what to add.
8991 * @param fBrokenLine Whether this is a broken line.
8992 */
8993static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8994{
8995 if (fBrokenLine)
8996 kwSandboxConsoleFlushCombined(pSandbox);
8997 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8998 {
8999 kwSandboxConsoleFlushCombined(pSandbox);
9000 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
9001 }
9002 else
9003 {
9004 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
9005 pSandbox->Combined.cwcBuf += cwcBuf;
9006 }
9007}
9008
9009
9010/**
9011 * Called to final flush a line buffer via the combined buffer (if applicable).
9012 *
9013 * @param pSandbox The sandbox.
9014 * @param pLineBuf The line buffer.
9015 * @param pszName The line buffer name (for logging)
9016 */
9017static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
9018{
9019 if (pLineBuf->fIsConsole)
9020 {
9021 if (pLineBuf->u.Con.cwcBuf > 0)
9022 {
9023 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
9024
9025 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
9026 {
9027 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
9028 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
9029 }
9030 else
9031 {
9032 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
9033 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
9034 }
9035 pLineBuf->u.Con.cwcBuf = 0;
9036 }
9037 }
9038#ifdef WITH_STD_OUT_ERR_BUFFERING
9039 else if (pLineBuf->u.Fully.cchBuf > 0)
9040 {
9041 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
9042
9043 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
9044 pLineBuf->u.Fully.cchBuf = 0;
9045 }
9046#endif
9047}
9048
9049
9050/**
9051 * Called at the end of sandboxed execution to flush both stream buffers.
9052 *
9053 * @param pSandbox The sandbox.
9054 */
9055static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
9056{
9057 /*
9058 * First do the cl.exe source file supression trick, if applicable.
9059 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
9060 * handle.
9061 */
9062 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9063 && pSandbox->Combined.cFlushes == 0)
9064 {
9065 if ( pSandbox->StdOut.fIsConsole
9066 || pSandbox->StdErr.fIsConsole)
9067 {
9068 if ( pSandbox->Combined.cwcBuf >= 3
9069 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
9070 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
9071 {
9072 KI32 off = pSandbox->Combined.cwcBuf - 1;
9073 if (pSandbox->Combined.wszBuf[off] == '\n')
9074 {
9075 KBOOL fOk = K_TRUE;
9076 while (off-- > 0)
9077 {
9078 wchar_t const wc = pSandbox->Combined.wszBuf[off];
9079 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
9080 { /* likely */ }
9081 else
9082 {
9083 fOk = K_FALSE;
9084 break;
9085 }
9086 }
9087 if (fOk)
9088 {
9089 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
9090 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9091 pSandbox->Combined.cwcBuf = 0;
9092 return;
9093 }
9094 }
9095 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
9096 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9097 }
9098 }
9099#ifdef WITH_STD_OUT_ERR_BUFFERING
9100 /*
9101 * Otherwise, it goes to standard output (redirected).
9102 */
9103 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
9104 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
9105 {
9106 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
9107 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
9108 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
9109
9110 if (pchBuf[off] == '\n')
9111 {
9112 KBOOL fOk = K_TRUE;
9113 if (pchBuf[off - 1] == '\r')
9114 off--;
9115 while (off-- > 0)
9116 {
9117 char const ch = pchBuf[off];
9118 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
9119 { /* likely */ }
9120 else
9121 {
9122 fOk = K_FALSE;
9123 break;
9124 }
9125 }
9126 if (fOk)
9127 {
9128 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
9129 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9130 pSandbox->StdOut.u.Fully.cchBuf = 0;
9131 return;
9132 }
9133 }
9134 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
9135 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9136 }
9137#endif
9138 }
9139
9140 /*
9141 * Flush the two line buffer, then the combined buffer.
9142 */
9143 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
9144 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
9145 kwSandboxConsoleFlushCombined(pSandbox);
9146}
9147
9148
9149/**
9150 * Writes a string to the given output stream.
9151 *
9152 * @param pSandbox The sandbox.
9153 * @param pLineBuf The line buffer for the output stream.
9154 * @param pwcBuffer The buffer to write.
9155 * @param cwcToWrite The number of wchar_t's in the buffer.
9156 */
9157static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
9158{
9159 kHlpAssert(pLineBuf->fIsConsole);
9160 if (cwcToWrite > 0)
9161 {
9162 /*
9163 * First, find the start of the last incomplete line so we can figure
9164 * out how much line buffering we need to do.
9165 */
9166 KU32 cchLastIncompleteLine;
9167 KU32 offLastIncompleteLine = cwcToWrite;
9168 while ( offLastIncompleteLine > 0
9169 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
9170 offLastIncompleteLine--;
9171 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
9172
9173 /* Was there anything to line buffer? */
9174 if (offLastIncompleteLine < cwcToWrite)
9175 {
9176 /* Need to grow the line buffer? */
9177 KU32 cwcNeeded = offLastIncompleteLine == 0
9178 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
9179 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
9180 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
9181 {
9182 void *pvNew;
9183 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
9184 while (cwcNew < cwcNeeded)
9185 cwcNew *= 2;
9186 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
9187 if (pvNew)
9188 {
9189 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9190 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
9191 }
9192 else
9193 {
9194 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
9195 if (pvNew)
9196 {
9197 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9198 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
9199 }
9200 else
9201 {
9202 /* This isn't perfect, but it will have to do for now. */
9203 if (pLineBuf->u.Con.cwcBuf > 0)
9204 {
9205 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9206 K_TRUE /*fBrokenLine*/);
9207 pLineBuf->u.Con.cwcBuf = 0;
9208 }
9209 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
9210 return;
9211 }
9212 }
9213 }
9214
9215 /*
9216 * Handle the case where we only add to the line buffer.
9217 */
9218 if (offLastIncompleteLine == 0)
9219 {
9220 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
9221 pLineBuf->u.Con.cwcBuf += cwcToWrite;
9222 return;
9223 }
9224 }
9225
9226 /*
9227 * If there is sufficient combined buffer to handle this request, this is rather simple.
9228 */
9229 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
9230 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9231 {
9232 if (pLineBuf->u.Con.cwcBuf > 0)
9233 {
9234 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9235 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9236 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9237 pLineBuf->u.Con.cwcBuf = 0;
9238 }
9239
9240 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9241 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9242 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9243 }
9244 else
9245 {
9246 /*
9247 * Do line-by-line processing of the input, flusing the combined buffer
9248 * when it becomes necessary. We may have to write lines
9249 */
9250 KU32 off = 0;
9251 KU32 offNextLine = 0;
9252
9253 /* If there are buffered chars, we handle the first line outside the
9254 main loop. We must try our best outputting it as a complete line. */
9255 if (pLineBuf->u.Con.cwcBuf > 0)
9256 {
9257 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9258 offNextLine++;
9259 offNextLine++;
9260 kHlpAssert(offNextLine <= offLastIncompleteLine);
9261
9262 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9263 {
9264 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9265 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9266 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9267 pLineBuf->u.Con.cwcBuf = 0;
9268
9269 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9270 pSandbox->Combined.cwcBuf += offNextLine;
9271 }
9272 else
9273 {
9274 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9275 if (cwcLeft > 0)
9276 {
9277 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9278 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9279 pLineBuf->u.Con.cwcBuf += cwcCopy;
9280 off += cwcCopy;
9281 }
9282 if (pLineBuf->u.Con.cwcBuf > 0)
9283 {
9284 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9285 K_TRUE /*fBrokenLine*/);
9286 pLineBuf->u.Con.cwcBuf = 0;
9287 }
9288 if (off < offNextLine)
9289 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9290 }
9291 off = offNextLine;
9292 }
9293
9294 /* Deal with the remaining lines */
9295 while (off < offLastIncompleteLine)
9296 {
9297 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9298 offNextLine++;
9299 offNextLine++;
9300 kHlpAssert(offNextLine <= offLastIncompleteLine);
9301 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9302 off = offNextLine;
9303 }
9304 }
9305
9306 /*
9307 * Buffer any remaining incomplete line chars.
9308 */
9309 if (cchLastIncompleteLine)
9310 {
9311 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9312 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9313 }
9314 }
9315}
9316
9317
9318/**
9319 * Worker for WriteConsoleA and WriteFile.
9320 *
9321 * @param pSandbox The sandbox.
9322 * @param pLineBuf The line buffer.
9323 * @param pchBuffer What to write.
9324 * @param cchToWrite How much to write.
9325 */
9326static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9327{
9328 /*
9329 * Convert it to wide char and use the 'W' to do the work.
9330 */
9331 int cwcRet;
9332 KU32 cwcBuf = cchToWrite * 2 + 1;
9333 wchar_t *pwcBufFree = NULL;
9334 wchar_t *pwcBuf;
9335 kHlpAssert(pLineBuf->fIsConsole);
9336
9337 if (cwcBuf <= 4096)
9338 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9339 else
9340 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9341
9342 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9343 if (cwcRet > 0)
9344 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9345 else
9346 {
9347 DWORD cchWritten;
9348 kHlpAssertFailed();
9349
9350 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9351 if (pLineBuf->u.Con.cwcBuf > 0)
9352 {
9353 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9354 pLineBuf->u.Con.cwcBuf = 0;
9355 }
9356 kwSandboxConsoleFlushCombined(pSandbox);
9357
9358 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9359 {
9360 if (cchWritten >= cchToWrite)
9361 { /* likely */ }
9362 else
9363 {
9364 KU32 off = 0;
9365 do
9366 {
9367 off += cchWritten;
9368 cchWritten = 0;
9369 } while ( off < cchToWrite
9370 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9371 }
9372 }
9373 }
9374
9375 if (pwcBufFree)
9376 kHlpFree(pwcBufFree);
9377}
9378
9379
9380/** Kernel32 - WriteConsoleA */
9381BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9382 PVOID pvReserved)
9383{
9384 BOOL fRc;
9385 PKWOUTPUTSTREAMBUF pLineBuf;
9386 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9387
9388 if (hConOutput == g_Sandbox.StdErr.hOutput)
9389 pLineBuf = &g_Sandbox.StdErr;
9390 else
9391 pLineBuf = &g_Sandbox.StdOut;
9392 if (pLineBuf->fIsConsole)
9393 {
9394 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9395
9396 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9397 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9398 if (pcbWritten)
9399 *pcbWritten = cbToWrite;
9400 fRc = TRUE;
9401 }
9402 else
9403 {
9404 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9405 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9406 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9407 }
9408 return fRc;
9409}
9410
9411
9412/** Kernel32 - WriteConsoleW */
9413BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9414 PVOID pvReserved)
9415{
9416 BOOL fRc;
9417 PKWOUTPUTSTREAMBUF pLineBuf;
9418 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9419
9420 if (hConOutput == g_Sandbox.StdErr.hOutput)
9421 pLineBuf = &g_Sandbox.StdErr;
9422 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9423 pLineBuf = &g_Sandbox.StdOut;
9424 else
9425 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9426 if (pLineBuf->fIsConsole)
9427 {
9428 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9429
9430 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9431 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9432 if (pcwcWritten)
9433 *pcwcWritten = cwcToWrite;
9434 fRc = TRUE;
9435 }
9436 else
9437 {
9438 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9439 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9440 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9441 }
9442 return fRc;
9443}
9444
9445#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9446
9447
9448
9449/*
9450 *
9451 * Virtual memory leak prevension.
9452 * Virtual memory leak prevension.
9453 * Virtual memory leak prevension.
9454 *
9455 */
9456
9457#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9458
9459/** For debug logging. */
9460# ifndef NDEBUG
9461static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9462{
9463 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9464 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9465 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9466 if (cbMemInfo != 0)
9467 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9468 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9469 MemInfo.BaseAddress,
9470 MemInfo.AllocationBase,
9471 MemInfo.RegionSize,
9472 MemInfo.State,
9473 MemInfo.Protect,
9474 MemInfo.Type));
9475}
9476# else
9477# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9478# endif
9479
9480/**
9481 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9482 *
9483 * @param idxFixed The fixed allocation index to "free".
9484 */
9485static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9486{
9487 BOOL fRc;
9488 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9489 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9490 kHlpAssert(fRc); K_NOREF(fRc);
9491 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9492 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9493}
9494
9495#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9496
9497
9498/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9499 * location (~78MB in 32-bit 2010 compiler). */
9500static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9501{
9502 PVOID pvMem;
9503 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9504 {
9505 KU32 idxPreAllocated = KU32_MAX;
9506
9507#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9508 /*
9509 * Look for a pre-reserved CL.exe heap allocation.
9510 */
9511 pvMem = NULL;
9512 if ( pvAddr != 0
9513 && (fAllocType & MEM_RESERVE))
9514 {
9515 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9516 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9517 while (idxFixed-- > 0)
9518 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9519 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9520 {
9521 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9522 {
9523 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9524 {
9525 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9526 pvMem = pvAddr;
9527 idxPreAllocated = idxFixed;
9528 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9529 pvAddr, cb, fAllocType, fProt, pvMem));
9530 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9531 SetLastError(NO_ERROR);
9532 break;
9533 }
9534 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9535 }
9536 else
9537 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9538 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9539 }
9540 }
9541 if (!pvMem)
9542#endif
9543 {
9544 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9545 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9546 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9547 if ( pvAddr
9548 && pvAddr != pvMem
9549 && !( fAllocType == MEM_RESERVE /* After mapping the PCH, VS2019 ends up here (happens */
9550 && fProt == PAGE_READWRITE /* in real cl.exe runs too). Just shut it up to avoid confusion. */
9551#if K_ARCH_BITS >= 64
9552 && cb > 0x10000000 /* seen 67c00000, 33e00000, ++ */
9553#else
9554 && cb > 0x04000000 /* no idea */
9555#endif
9556 )
9557 )
9558 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9559 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9560 }
9561
9562 if (pvMem)
9563 {
9564 /*
9565 * Track it.
9566 */
9567 PKWVIRTALLOC pTracker;
9568
9569 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9570 pTracker = g_Sandbox.pVirtualAllocHead;
9571 while ( pTracker
9572 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9573 pTracker = pTracker->pNext;
9574 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9575 if (!pTracker)
9576 {
9577 DWORD dwErr = GetLastError();
9578 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9579 if (pTracker)
9580 {
9581 pTracker->pvAlloc = pvMem;
9582 pTracker->cbAlloc = cb;
9583 pTracker->idxPreAllocated = idxPreAllocated;
9584 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9585 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9586 g_Sandbox.pVirtualAllocHead = pTracker;
9587 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9588 }
9589 SetLastError(dwErr);
9590 }
9591 }
9592 }
9593 else
9594 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9595 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9596 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9597 return pvMem;
9598}
9599
9600
9601/** Kernel32 - VirtualFree. */
9602static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9603{
9604 BOOL fRc;
9605 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9606 {
9607 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9608 if (dwFreeType & MEM_RELEASE)
9609 {
9610 PKWVIRTALLOC pTracker;
9611 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9612 pTracker = g_Sandbox.pVirtualAllocHead;
9613 if (pTracker)
9614 {
9615 if (pTracker->pvAlloc == pvAddr)
9616 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9617 else
9618 {
9619 PKWVIRTALLOC pPrev;
9620 do
9621 {
9622 pPrev = pTracker;
9623 pTracker = pTracker->pNext;
9624 } while (pTracker && pTracker->pvAlloc != pvAddr);
9625 if (pTracker)
9626 pPrev->pNext = pTracker->pNext;
9627 }
9628 if (pTracker)
9629 {
9630#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9631 if (pTracker->idxPreAllocated != KU32_MAX)
9632 {
9633 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9634 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9635 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9636 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9637 kHlpFree(pTracker);
9638 return TRUE;
9639 }
9640#endif
9641
9642 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9643 if (fRc)
9644 kHlpFree(pTracker);
9645 else
9646 {
9647 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9648 g_Sandbox.pVirtualAllocHead = pTracker;
9649 }
9650 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9651 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9652 return fRc;
9653 }
9654
9655 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9656 }
9657 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9658 }
9659 }
9660
9661#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9662 /*
9663 * Protect our fixed allocations (this isn't just paranoia, btw.).
9664 */
9665 if (dwFreeType & MEM_RELEASE)
9666 {
9667 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9668 while (idxFixed-- > 0)
9669 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9670 {
9671 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9672 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9673 return TRUE;
9674 }
9675 }
9676#endif
9677
9678 /*
9679 * Not tracker or not actually free the virtual range.
9680 */
9681 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9682 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9683 return fRc;
9684}
9685
9686
9687/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9688HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9689{
9690 HANDLE hHeap;
9691 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9692
9693 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9694 if (hHeap != NULL)
9695 {
9696 DWORD dwErr = GetLastError();
9697 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9698 if (pTracker)
9699 {
9700 pTracker->hHeap = hHeap;
9701 pTracker->pNext = g_Sandbox.pHeapHead;
9702 g_Sandbox.pHeapHead = pTracker;
9703 }
9704
9705 SetLastError(dwErr);
9706 }
9707 return hHeap;
9708
9709}
9710
9711
9712/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9713BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9714{
9715 BOOL fRc = HeapDestroy(hHeap);
9716 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9717 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9718 if (fRc)
9719 {
9720 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9721 if (pTracker)
9722 {
9723 if (pTracker->hHeap == hHeap)
9724 g_Sandbox.pHeapHead = pTracker->pNext;
9725 else
9726 {
9727 PKWHEAP pPrev;
9728 do
9729 {
9730 pPrev = pTracker;
9731 pTracker = pTracker->pNext;
9732 } while (pTracker && pTracker->hHeap == hHeap);
9733 if (pTracker)
9734 pPrev->pNext = pTracker->pNext;
9735 }
9736 if (pTracker)
9737 kHlpFree(pTracker);
9738 else
9739 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9740 }
9741 }
9742
9743 return fRc;
9744}
9745
9746
9747
9748/*
9749 *
9750 * Thread/Fiber local storage leak prevention.
9751 * Thread/Fiber local storage leak prevention.
9752 * Thread/Fiber local storage leak prevention.
9753 *
9754 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9755 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9756 * we're leaking these indexes, but more importantely we crash during
9757 * worker exit since the callback is triggered multiple times.
9758 */
9759
9760
9761/** Kernel32 - FlsAlloc */
9762DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9763{
9764 DWORD idxFls = FlsAlloc(pfnCallback);
9765 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9766 if (idxFls != FLS_OUT_OF_INDEXES)
9767 {
9768 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9769 if (pTracker)
9770 {
9771 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9772 pTracker->idx = idxFls;
9773 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9774 g_Sandbox.pFlsAllocHead = pTracker;
9775 }
9776 }
9777
9778 return idxFls;
9779}
9780
9781/** Kernel32 - FlsFree */
9782BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9783{
9784 BOOL fRc = FlsFree(idxFls);
9785 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9786 if (fRc)
9787 {
9788 PKWLOCALSTORAGE pTracker;
9789 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9790
9791 pTracker = g_Sandbox.pFlsAllocHead;
9792 if (pTracker)
9793 {
9794 if (pTracker->idx == idxFls)
9795 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9796 else
9797 {
9798 PKWLOCALSTORAGE pPrev;
9799 do
9800 {
9801 pPrev = pTracker;
9802 pTracker = pTracker->pNext;
9803 } while (pTracker && pTracker->idx != idxFls);
9804 if (pTracker)
9805 pPrev->pNext = pTracker->pNext;
9806 }
9807 if (pTracker)
9808 {
9809 pTracker->idx = FLS_OUT_OF_INDEXES;
9810 pTracker->pNext = NULL;
9811 kHlpFree(pTracker);
9812 }
9813 }
9814 }
9815 return fRc;
9816}
9817
9818
9819/** Kernel32 - TlsAlloc */
9820DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9821{
9822 DWORD idxTls = TlsAlloc();
9823 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9824 if (idxTls != TLS_OUT_OF_INDEXES)
9825 {
9826 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9827 if (pTracker)
9828 {
9829 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9830 pTracker->idx = idxTls;
9831 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9832 g_Sandbox.pTlsAllocHead = pTracker;
9833 }
9834 }
9835
9836 return idxTls;
9837}
9838
9839/** Kernel32 - TlsFree */
9840BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9841{
9842 BOOL fRc = TlsFree(idxTls);
9843 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9844 if (fRc)
9845 {
9846 PKWLOCALSTORAGE pTracker;
9847 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9848
9849 pTracker = g_Sandbox.pTlsAllocHead;
9850 if (pTracker)
9851 {
9852 if (pTracker->idx == idxTls)
9853 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9854 else
9855 {
9856 PKWLOCALSTORAGE pPrev;
9857 do
9858 {
9859 pPrev = pTracker;
9860 pTracker = pTracker->pNext;
9861 } while (pTracker && pTracker->idx != idxTls);
9862 if (pTracker)
9863 pPrev->pNext = pTracker->pNext;
9864 }
9865 if (pTracker)
9866 {
9867 pTracker->idx = TLS_OUT_OF_INDEXES;
9868 pTracker->pNext = NULL;
9869 kHlpFree(pTracker);
9870 }
9871 }
9872 }
9873 return fRc;
9874}
9875
9876
9877
9878/*
9879 *
9880 * Header file hashing.
9881 * Header file hashing.
9882 * Header file hashing.
9883 *
9884 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9885 * indicated that ~12% of the time was spent doing MD5 caluclation when
9886 * rebuiling openssl. The hashing it done right after reading the source
9887 * via ReadFile, same buffers and sizes.
9888 */
9889
9890#ifdef WITH_HASH_MD5_CACHE
9891
9892/** AdvApi32 - CryptCreateHash */
9893static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9894 HCRYPTHASH *phHash)
9895{
9896 BOOL fRc;
9897
9898 /*
9899 * Only do this for cl.exe when it request normal MD5.
9900 */
9901 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9902 {
9903 /** @todo do generic caching of hash results. Need SHA-1 and SHA-256 for
9904 * more recent compilers. */
9905 if (idAlg == CALG_MD5)
9906 {
9907 if (hKey == 0)
9908 {
9909 if (dwFlags == 0)
9910 {
9911 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9912 if (pHash)
9913 {
9914 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9915 pHash->uMagic = KWHASHMD5_MAGIC;
9916 pHash->cbHashed = 0;
9917 pHash->fGoneBad = K_FALSE;
9918 pHash->fFallbackMode = K_FALSE;
9919 pHash->fFinal = K_FALSE;
9920
9921 /* link it. */
9922 pHash->pNext = g_Sandbox.pHashHead;
9923 g_Sandbox.pHashHead = pHash;
9924
9925 *phHash = (KUPTR)pHash;
9926 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9927 hProv, *phHash, TRUE));
9928 return TRUE;
9929 }
9930
9931 kwErrPrintf("CryptCreateHash: out of memory!\n");
9932 }
9933 else
9934 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9935 }
9936 else
9937 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9938 }
9939 //else
9940 // kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9941 }
9942
9943 /*
9944 * Fallback.
9945 */
9946 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9947 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9948 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9949 return fRc;
9950}
9951
9952
9953/** AdvApi32 - CryptHashData */
9954static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9955{
9956 BOOL fRc;
9957 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9958 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9959 while (pHash && (KUPTR)pHash != hHash)
9960 pHash = pHash->pNext;
9961 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9962 hHash, pHash, pbData, cbData, dwFlags));
9963 if (pHash)
9964 {
9965 /*
9966 * Validate the state.
9967 */
9968 if ( pHash->uMagic == KWHASHMD5_MAGIC
9969 && !pHash->fFinal)
9970 {
9971 if (!pHash->fFallbackMode)
9972 {
9973 /*
9974 * Does this match the previous ReadFile call to a cached file?
9975 * If it doesn't, try falling back.
9976 */
9977 if ( g_Sandbox.LastHashRead.cbRead == cbData
9978 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9979 {
9980 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9981 if ( pCachedFile
9982 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9983 {
9984
9985 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9986 {
9987 if ( pHash->pCachedFile == NULL
9988 && pHash->cbHashed == 0)
9989 pHash->pCachedFile = pCachedFile;
9990 if (pHash->pCachedFile == pCachedFile)
9991 {
9992 pHash->cbHashed += cbData;
9993 g_Sandbox.LastHashRead.pCachedFile = NULL;
9994 g_Sandbox.LastHashRead.pvRead = NULL;
9995 g_Sandbox.LastHashRead.cbRead = 0;
9996 g_Sandbox.LastHashRead.offRead = 0;
9997 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9998 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9999 return TRUE;
10000 }
10001
10002 /* Note! it's possible to fall back here too, if necessary. */
10003 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
10004 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
10005 }
10006 else
10007 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
10008 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
10009 }
10010 else if (!pCachedFile)
10011 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
10012 else
10013 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
10014 }
10015 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
10016 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
10017 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
10018 if (pHash->cbHashed == 0)
10019 pHash->fFallbackMode = K_TRUE;
10020 if (pHash->fFallbackMode)
10021 {
10022 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
10023 pHash->fFallbackMode = K_TRUE;
10024 MD5Init(&pHash->Md5Ctx);
10025 MD5Update(&pHash->Md5Ctx, pbData, cbData);
10026 pHash->cbHashed = cbData;
10027 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
10028 hHash, pbData, cbData, dwFlags));
10029 return TRUE;
10030 }
10031 pHash->fGoneBad = K_TRUE;
10032 SetLastError(ERROR_INVALID_PARAMETER);
10033 fRc = FALSE;
10034 }
10035 else
10036 {
10037 /* fallback. */
10038 MD5Update(&pHash->Md5Ctx, pbData, cbData);
10039 pHash->cbHashed += cbData;
10040 fRc = TRUE;
10041 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
10042 hHash, pbData, cbData, dwFlags));
10043 }
10044 }
10045 /*
10046 * Bad handle state.
10047 */
10048 else
10049 {
10050 if (pHash->uMagic != KWHASHMD5_MAGIC)
10051 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
10052 else
10053 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
10054 SetLastError((DWORD)NTE_BAD_HASH);
10055 fRc = FALSE;
10056 }
10057 }
10058 else
10059 {
10060
10061 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
10062 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
10063 }
10064 return fRc;
10065}
10066
10067
10068/** AdvApi32 - CryptGetHashParam */
10069static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
10070 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
10071{
10072 BOOL fRc;
10073 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
10074 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10075 while (pHash && (KUPTR)pHash != hHash)
10076 pHash = pHash->pNext;
10077 if (pHash)
10078 {
10079 if (pHash->uMagic == KWHASHMD5_MAGIC)
10080 {
10081 if (dwFlags == 0)
10082 {
10083 DWORD cbRet;
10084 void *pvRet;
10085 union
10086 {
10087 DWORD dw;
10088 } uBuf;
10089
10090 switch (dwParam)
10091 {
10092 case HP_HASHVAL:
10093 {
10094 /* Check the hash progress. */
10095 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
10096 if (pCachedFile)
10097 {
10098 if ( pCachedFile->cbCached == pHash->cbHashed
10099 && !pHash->fGoneBad)
10100 {
10101 if (pCachedFile->fValidMd5)
10102 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
10103 else
10104 {
10105 MD5Init(&pHash->Md5Ctx);
10106 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
10107 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
10108 pCachedFile->fValidMd5 = K_TRUE;
10109 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
10110 }
10111 pvRet = pCachedFile->abMd5Digest;
10112 }
10113 else
10114 {
10115 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
10116 from what I can tell, so just deal with it. */
10117 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
10118 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
10119 pHash, pCachedFile, pCachedFile->szPath));
10120 pHash->fFallbackMode = K_TRUE;
10121 pHash->pCachedFile = NULL;
10122 MD5Init(&pHash->Md5Ctx);
10123 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
10124 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
10125 pvRet = pHash->abDigest;
10126 }
10127 pHash->fFinal = K_TRUE;
10128 cbRet = 16;
10129 break;
10130 }
10131 else if (pHash->fFallbackMode)
10132 {
10133 if (!pHash->fFinal)
10134 {
10135 pHash->fFinal = K_TRUE;
10136 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
10137 }
10138 pvRet = pHash->abDigest;
10139 cbRet = 16;
10140 break;
10141 }
10142 else
10143 {
10144 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
10145 SetLastError(ERROR_INVALID_SERVER_STATE);
10146 }
10147 return FALSE;
10148 }
10149
10150 case HP_HASHSIZE:
10151 uBuf.dw = 16;
10152 pvRet = &uBuf;
10153 cbRet = sizeof(DWORD);
10154 break;
10155
10156 case HP_ALGID:
10157 uBuf.dw = CALG_MD5;
10158 pvRet = &uBuf;
10159 cbRet = sizeof(DWORD);
10160 break;
10161
10162 default:
10163 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
10164 SetLastError((DWORD)NTE_BAD_TYPE);
10165 return FALSE;
10166 }
10167
10168 /*
10169 * Copy out cbRet from pvRet.
10170 */
10171 if (pbData)
10172 {
10173 if (*pcbData >= cbRet)
10174 {
10175 *pcbData = cbRet;
10176 kHlpMemCopy(pbData, pvRet, cbRet);
10177 if (cbRet == 4)
10178 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
10179 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
10180 else if (cbRet == 16)
10181 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",
10182 dwParam, pHash, pHash->pCachedFile, cbRet,
10183 pbData[0], pbData[1], pbData[2], pbData[3],
10184 pbData[4], pbData[5], pbData[6], pbData[7],
10185 pbData[8], pbData[9], pbData[10], pbData[11],
10186 pbData[12], pbData[13], pbData[14], pbData[15]));
10187 else
10188 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
10189 dwParam, pHash, pHash->pCachedFile, cbRet));
10190 return TRUE;
10191 }
10192
10193 kHlpMemCopy(pbData, pvRet, *pcbData);
10194 }
10195 SetLastError(ERROR_MORE_DATA);
10196 *pcbData = cbRet;
10197 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
10198 }
10199 else
10200 {
10201 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
10202 SetLastError((DWORD)NTE_BAD_FLAGS);
10203 }
10204 }
10205 else
10206 {
10207 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
10208 SetLastError((DWORD)NTE_BAD_HASH);
10209 }
10210 fRc = FALSE;
10211 }
10212 /*
10213 * Regular handle.
10214 */
10215 else
10216 {
10217 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
10218 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
10219 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
10220 }
10221
10222 return fRc;
10223}
10224
10225
10226/** AdvApi32 - CryptDestroyHash */
10227static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
10228{
10229 BOOL fRc;
10230 PKWHASHMD5 pPrev = NULL;
10231 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
10232 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10233 while (pHash && (KUPTR)pHash != hHash)
10234 {
10235 pPrev = pHash;
10236 pHash = pHash->pNext;
10237 }
10238 if (pHash)
10239 {
10240 if (pHash->uMagic == KWHASHMD5_MAGIC)
10241 {
10242 pHash->uMagic = 0;
10243 if (!pPrev)
10244 g_Sandbox.pHashHead = pHash->pNext;
10245 else
10246 pPrev->pNext = pHash->pNext;
10247 kHlpFree(pHash);
10248 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
10249 fRc = TRUE;
10250 }
10251 else
10252 {
10253 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10254 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10255 SetLastError(ERROR_INVALID_HANDLE);
10256 fRc = FALSE;
10257 }
10258 }
10259 /*
10260 * Regular handle.
10261 */
10262 else
10263 {
10264 fRc = CryptDestroyHash(hHash);
10265 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10266 }
10267 return fRc;
10268}
10269
10270#endif /* WITH_HASH_MD5_CACHE */
10271
10272
10273/*
10274 *
10275 * Reuse crypt context.
10276 * Reuse crypt context.
10277 * Reuse crypt context.
10278 *
10279 *
10280 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10281 *
10282 */
10283
10284#ifdef WITH_CRYPT_CTX_REUSE
10285
10286/** AdvApi32 - CryptAcquireContextW. */
10287static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10288 DWORD dwProvType, DWORD dwFlags)
10289{
10290 BOOL fRet;
10291
10292 /*
10293 * Lookup reusable context based on the input.
10294 */
10295 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10296 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10297 KU32 iCtx = g_Sandbox.cCryptCtxs;
10298 while (iCtx-- > 0)
10299 {
10300 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10301 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10302 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10303 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10304 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10305 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10306 {
10307 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10308 {
10309 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10310 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10311 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10312 return TRUE;
10313 }
10314 }
10315 }
10316
10317 /*
10318 * Create it and enter it into the reused array if possible.
10319 */
10320 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10321 if (fRet)
10322 {
10323 iCtx = g_Sandbox.cCryptCtxs;
10324 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10325 {
10326 /* Try duplicate the input strings. */
10327 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10328 (cwcContainer + 1) * sizeof(wchar_t));
10329 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10330 {
10331 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10332 (cwcProvider + 1) * sizeof(wchar_t));
10333 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10334 {
10335 /* Add a couple of references just to be on the safe side and all that. */
10336 HCRYPTPROV hProv = *phProv;
10337 if (CryptContextAddRef(hProv, NULL, 0))
10338 {
10339 if (CryptContextAddRef(hProv, NULL, 0))
10340 {
10341 /* Okay, finish the entry and return success */
10342 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10343 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10344 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10345 g_Sandbox.cCryptCtxs = iCtx + 1;
10346
10347 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10348 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10349 return TRUE;
10350 }
10351 CryptReleaseContext(hProv, 0);
10352 }
10353 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10354
10355 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10356 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10357 }
10358 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10359 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10360 }
10361 }
10362 else
10363 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10364 }
10365
10366 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10367 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10368 return fRet;
10369}
10370
10371
10372/** AdvApi32 - CryptReleaseContext */
10373static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10374{
10375 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10376 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10377 return fRet;
10378}
10379
10380
10381/** AdvApi32 - CryptContextAddRef */
10382static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10383{
10384 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10385 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10386 return fRet;
10387}
10388
10389#endif /* WITH_CRYPT_CTX_REUSE */
10390
10391/*
10392 *
10393 * Structured exception handling.
10394 * Structured exception handling.
10395 * Structured exception handling.
10396 *
10397 */
10398#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10399
10400# define EH_NONCONTINUABLE KU32_C(0x00000001)
10401# define EH_UNWINDING KU32_C(0x00000002)
10402# define EH_EXIT_UNWIND KU32_C(0x00000004)
10403# define EH_STACK_INVALID KU32_C(0x00000008)
10404# define EH_NESTED_CALL KU32_C(0x00000010)
10405
10406typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10407 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10408typedef struct _EXCEPTION_REGISTRATION_RECORD
10409{
10410 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10411 PFNXCPTHANDLER pfnXcptHandler;
10412};
10413
10414
10415/**
10416 * Calls @a pfnHandler.
10417 */
10418static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10419 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10420 PFNXCPTHANDLER pfnHandler)
10421{
10422# if 1
10423 /* This is a more robust version that isn't subject to calling
10424 convension cleanup disputes and such. */
10425 KU32 uSavedEdi;
10426 KU32 uSavedEsi;
10427 KU32 uSavedEbx;
10428 KU32 rcHandler;
10429
10430 __asm
10431 {
10432 mov [uSavedEdi], edi
10433 mov [uSavedEsi], esi
10434 mov [uSavedEbx], ebx
10435 mov esi, esp
10436 mov edi, esp
10437 mov edi, [pXcptRec]
10438 mov edx, [pRegRec]
10439 mov eax, [pXcptCtx]
10440 mov ebx, [ppRegRec]
10441 mov ecx, [pfnHandler]
10442 sub esp, 16
10443 and esp, 0fffffff0h
10444 mov [esp ], edi
10445 mov [esp + 4], edx
10446 mov [esp + 8], eax
10447 mov [esp + 12], ebx
10448 mov edi, esi
10449 call ecx
10450 mov esp, esi
10451 cmp esp, edi
10452 je stack_ok
10453 int 3
10454 stack_ok:
10455 mov edi, [uSavedEdi]
10456 mov esi, [uSavedEsi]
10457 mov ebx, [uSavedEbx]
10458 mov [rcHandler], eax
10459 }
10460 return rcHandler;
10461# else
10462 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10463# endif
10464}
10465
10466
10467/**
10468 * Vectored exception handler that emulates x86 chained exception handler.
10469 *
10470 * This is necessary because the RtlIsValidHandler check fails for self loaded
10471 * code and prevents cl.exe from working. (On AMD64 we can register function
10472 * tables, but on X86 cooking your own handling seems to be the only viabke
10473 * alternative.)
10474 *
10475 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10476 * @param pXcptPtrs The exception details.
10477 */
10478static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10479{
10480 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10481 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10482 if (g_Sandbox.fRunning)
10483 {
10484 HANDLE const hCurProc = GetCurrentProcess();
10485 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10486 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10487 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10488 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10489 {
10490 /* Read the exception record in a safe manner. */
10491 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10492 DWORD cbActuallyRead = 0;
10493 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10494 && cbActuallyRead == sizeof(RegRec))
10495 {
10496 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10497 KU32 rcHandler;
10498 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10499 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10500 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10501 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10502 if (rcHandler == ExceptionContinueExecution)
10503 {
10504 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10505 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10506 return EXCEPTION_CONTINUE_EXECUTION;
10507 }
10508
10509 if (rcHandler == ExceptionContinueSearch)
10510 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10511 else if (rcHandler == ExceptionNestedException)
10512 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10513 else
10514 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10515 }
10516 else
10517 {
10518 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10519 break;
10520 }
10521
10522 /*
10523 * Next.
10524 */
10525 pRegRec = RegRec.pPrevRegRec;
10526 }
10527 }
10528 return EXCEPTION_CONTINUE_SEARCH;
10529}
10530
10531
10532/** NtDll,Kernel32 - RtlUnwind */
10533static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10534 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10535{
10536 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10537 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10538 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10539 if (g_Sandbox.fRunning)
10540 {
10541 HANDLE const hCurProc = GetCurrentProcess();
10542 PCONTEXT pXcptCtx = NULL;
10543 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10544
10545 /*
10546 * Update / create an exception record.
10547 */
10548 if (pXcptRec)
10549 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10550 else
10551 {
10552 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10553 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10554 pXcptRec->ExceptionCode = (DWORD)STATUS_UNWIND;
10555 pXcptRec->ExceptionFlags = EH_UNWINDING;
10556 }
10557 if (!pStopXcptRec)
10558 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10559
10560 /*
10561 * Walk the chain till we find pStopXctpRec.
10562 */
10563 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10564 && pRegRec != NULL
10565 && pRegRec != pStopXcptRec)
10566 {
10567 /* Read the exception record in a safe manner. */
10568 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10569 DWORD cbActuallyRead = 0;
10570 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10571 && cbActuallyRead == sizeof(RegRec))
10572 {
10573 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10574 KU32 rcHandler;
10575 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10576 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10577 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10578 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10579
10580 if (rcHandler == ExceptionContinueSearch)
10581 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10582 else if (rcHandler == ExceptionCollidedUnwind)
10583 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10584 else
10585 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10586 }
10587 else
10588 {
10589 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10590 break;
10591 }
10592
10593 /*
10594 * Pop next.
10595 */
10596 pTib->ExceptionList = RegRec.pPrevRegRec;
10597 pRegRec = RegRec.pPrevRegRec;
10598 }
10599 return;
10600 }
10601
10602 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10603}
10604
10605#endif /* WINDOWS + X86 */
10606
10607
10608/*
10609 *
10610 * Misc function only intercepted while debugging.
10611 * Misc function only intercepted while debugging.
10612 * Misc function only intercepted while debugging.
10613 *
10614 */
10615
10616#ifndef NDEBUG
10617
10618/** CRT - memcpy */
10619static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10620{
10621 KU8 const *pbSrc = (KU8 const *)pvSrc;
10622 KU8 *pbDst = (KU8 *)pvDst;
10623 KSIZE cbLeft = cb;
10624 while (cbLeft-- > 0)
10625 *pbDst++ = *pbSrc++;
10626 return pvDst;
10627}
10628
10629
10630/** CRT - memset */
10631static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10632{
10633 KU8 *pbDst = (KU8 *)pvDst;
10634 KSIZE cbLeft = cb;
10635 while (cbLeft-- > 0)
10636 *pbDst++ = (KU8)bFiller;
10637 return pvDst;
10638}
10639
10640#endif /* NDEBUG */
10641
10642
10643/** @todo consider hooking NtQueryDirectoryFile as c1xx.dll/c1.dll in 2019
10644 * uses it directly to read the content of include directories, however
10645 * they do it one file at the time. We already have the info in the
10646 * cache (where we do bulk reads). There are a lot of calls for the
10647 * SDK include directories, as one can imagine. */
10648
10649/**
10650 * Functions that needs replacing for sandboxed execution.
10651 */
10652KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10653{
10654 /*
10655 * Kernel32.dll and friends.
10656 */
10657 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10658 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10659
10660 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10661 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10662 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10663 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10664 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10665 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10666 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10667 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10668 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10669 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10670 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10671
10672 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10673 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10674 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10675 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10676
10677 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10678
10679 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10680 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10681 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10682 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10683 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10684 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10685 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10686 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10687 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10688 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10689 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10690
10691 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10692 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10693 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10694 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10695#ifdef WITH_TEMP_MEMORY_FILES
10696 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10697 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10698 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10699 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10700 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10701 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10702 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10703 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10704 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10705 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10706#endif
10707 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10708 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10709 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10710 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10711 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10712 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10713 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10714 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10715 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10716#ifdef WITH_TEMP_MEMORY_FILES
10717 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10718#endif
10719
10720#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10721 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10722 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10723#endif
10724
10725 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10726 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10727
10728 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10729 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10730
10731 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10732 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10733 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10734 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10735
10736 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10737
10738#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10739 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10740#endif
10741
10742#ifdef WITH_HASH_MD5_CACHE
10743 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10744 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10745 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10746 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10747#endif
10748
10749#ifdef WITH_CRYPT_CTX_REUSE
10750 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10751 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10752 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10753#endif
10754
10755 /*
10756 * MS Visual C++ CRTs.
10757 */
10758 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10759 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10760 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10761 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10762 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10763 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10764
10765 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10766 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10767 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10768
10769 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10770 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10771 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
10772
10773 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
10774 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
10775 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
10776 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
10777 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
10778 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
10779 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
10780 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
10781 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
10782 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
10783 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
10784 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
10785 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
10786 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
10787 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
10788 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
10789 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
10790 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
10791 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
10792 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
10793
10794 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
10795 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
10796 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
10797 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
10798 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
10799 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
10800 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
10801 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
10802 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
10803 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
10804 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
10805 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
10806 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
10807 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
10808
10809#ifndef NDEBUG
10810 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
10811 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
10812#endif
10813};
10814/** Number of entries in g_aReplacements. */
10815KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
10816
10817
10818/**
10819 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
10820 * execution.
10821 */
10822KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
10823{
10824 /*
10825 * Kernel32.dll and friends.
10826 */
10827 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10828 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10829
10830 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10831 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10832
10833#if 0
10834 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10835#endif
10836
10837 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10838 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10839 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10840 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10841#ifdef WITH_TEMP_MEMORY_FILES
10842 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10843 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10844 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10845 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10846 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10847 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10848 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10849 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10850 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10851 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10852#endif
10853 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10854 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10855 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10856 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10857 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10858 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10859 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10860 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10861 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10862#ifdef WITH_TEMP_MEMORY_FILES
10863 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10864#endif
10865 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10866 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10867 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExW },
10868#ifndef NDEBUG
10869 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_Native_GetProcAddress },
10870#endif
10871
10872#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10873 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10874 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10875#endif
10876
10877#ifdef WITH_HASH_MD5_CACHE
10878 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10879 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10880 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10881 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10882#endif
10883
10884 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10885
10886#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10887 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10888#endif
10889
10890 /*
10891 * MS Visual C++ CRTs.
10892 */
10893 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10894 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10895 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10896 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10897 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10898 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10899 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10900
10901#if 0 /* used by mspdbXXX.dll */
10902 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10903 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10904#endif
10905};
10906/** Number of entries in g_aSandboxNativeReplacements. */
10907KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10908
10909
10910/**
10911 * Functions that needs replacing when queried by GetProcAddress.
10912 */
10913KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10914{
10915 /*
10916 * Kernel32.dll and friends.
10917 */
10918 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10919 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10920 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10921 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10922};
10923/** Number of entries in g_aSandboxGetProcReplacements. */
10924KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10925
10926
10927/**
10928 * Control handler.
10929 *
10930 * @returns TRUE if handled, FALSE if not.
10931 * @param dwCtrlType The signal.
10932 */
10933static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10934{
10935 DWORD cbIgn;
10936 int volatile rc; /* volatile for debugging */
10937 int volatile rcPrev;
10938 const char *pszMsg;
10939 switch (dwCtrlType)
10940 {
10941 case CTRL_C_EVENT:
10942 rc = 9;
10943 pszMsg = "kWorker: Ctrl-C\r\n";
10944 break;
10945
10946 case CTRL_BREAK_EVENT:
10947 rc = 10;
10948 pszMsg = "kWorker: Ctrl-Break\r\n";
10949 break;
10950
10951 case CTRL_CLOSE_EVENT:
10952 rc = 11;
10953 pszMsg = "kWorker: console closed\r\n";
10954 break;
10955
10956 case CTRL_LOGOFF_EVENT:
10957 rc = 11;
10958 pszMsg = "kWorker: logoff event\r\n";
10959 break;
10960
10961 case CTRL_SHUTDOWN_EVENT:
10962 rc = 11;
10963 pszMsg = "kWorker: shutdown event\r\n";
10964 break;
10965
10966 default:
10967 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10968 return TRUE;
10969 }
10970
10971 /*
10972 * Terminate the process after 5 seconds.
10973 * If we get here a second time we just terminate the process ourselves.
10974 *
10975 * Note! We do no try call exit() here as it turned out to deadlock a lot
10976 * flusing file descriptors (stderr back when we first wrote to it).
10977 */
10978 rcPrev = g_rcCtrlC;
10979 g_rcCtrlC = rc;
10980 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10981 if (rcPrev == 0)
10982 {
10983 int i;
10984 for (i = 0; i < 10; i++)
10985 {
10986 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10987 Sleep(500);
10988 }
10989 }
10990 TerminateProcess(GetCurrentProcess(), rc);
10991 return TRUE;
10992}
10993
10994
10995#if 0
10996/**
10997 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10998 */
10999static void kwSandboxResetModuleVisited(void)
11000{
11001 PKWMODULE pMod = g_pModuleHead;
11002 while (pMod)
11003 {
11004 pMod->fVisited = K_FALSE;
11005 pMod = pMod->pNextList;
11006 }
11007}
11008
11009
11010/**
11011 * Used by kwSandboxExec to reset the state of the module tree.
11012 *
11013 * This is done recursively.
11014 *
11015 * @param pMod The root of the tree to consider.
11016 */
11017static void kwSandboxResetModuleState(PKWMODULE pMod)
11018{
11019 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
11020 if (!pMod->fNative)
11021 {
11022 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
11023 if (!pMod->fVisited) /* Avoid loops. */
11024 {
11025 KSIZE iImp;
11026 pMod->fVisited = K_TRUE;
11027 iImp = pMod->u.Manual.cImpMods;
11028 while (iImp-- > 0)
11029 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
11030 }
11031 }
11032 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11033 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
11034 {
11035 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11036 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11037 {
11038 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11039 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11040 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11041 pMod->pszPath, pszValue ? pszValue : "<null>"));
11042 }
11043 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11044 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11045 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11046 pMod->pszPath, pszValue ? pszValue : "<null>"));
11047 else
11048 {
11049 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11050 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11051 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11052 if (pszValue != NULL)
11053 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11054 else
11055 pMod->pszMsPdbSrvEndpoint = NULL;
11056 pMod->fNeedReInit = K_TRUE;
11057 }
11058 }
11059}
11060#else
11061/**
11062 * Used by kwSandboxExec to reset the state of the module tree.
11063 */
11064static void kwSandboxResetModuleState(void)
11065{
11066 PKWMODULE pMod = g_pModuleHead;
11067 while (pMod)
11068 {
11069 if (!pMod->fNative)
11070 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
11071 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11072 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
11073 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
11074 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
11075 {
11076 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11077 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11078 {
11079 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11080 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11081 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11082 pMod->pszPath, pszValue ? pszValue : "<null>"));
11083 }
11084 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11085 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11086 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11087 pMod->pszPath, pszValue ? pszValue : "<null>"));
11088 else
11089 {
11090 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11091 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11092 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11093 if (pszValue != NULL)
11094 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11095 else
11096 pMod->pszMsPdbSrvEndpoint = NULL;
11097 pMod->fNeedReInit = K_TRUE;
11098 }
11099 }
11100
11101 pMod = pMod->pNextList;
11102 }
11103}
11104#endif
11105
11106static PPEB kwSandboxGetProcessEnvironmentBlock(void)
11107{
11108#if K_ARCH == K_ARCH_X86_32
11109 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
11110#elif K_ARCH == K_ARCH_AMD64
11111 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
11112#else
11113# error "Port me!"
11114#endif
11115}
11116
11117
11118/**
11119 * Enters the given handle into the handle table.
11120 *
11121 * @returns K_TRUE on success, K_FALSE on failure.
11122 * @param pSandbox The sandbox.
11123 * @param pHandle The handle.
11124 * @param hHandle The handle value to enter it under (for the
11125 * duplicate handle API).
11126 */
11127static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
11128{
11129 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
11130 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
11131
11132 EnterCriticalSection(&g_Sandbox.HandlesLock);
11133
11134 /*
11135 * Grow handle table.
11136 */
11137 if (idxHandle >= pSandbox->cHandles)
11138 {
11139 void *pvNew;
11140 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
11141 while (cHandles <= idxHandle)
11142 cHandles *= 2;
11143 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
11144 if (!pvNew)
11145 {
11146 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11147 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
11148 return K_FALSE;
11149 }
11150 pSandbox->papHandles = (PKWHANDLE *)pvNew;
11151 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
11152 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
11153 pSandbox->cHandles = cHandles;
11154 }
11155
11156 /*
11157 * Check that the entry is unused then insert it.
11158 */
11159 kHlpAssertStmtReturn(pSandbox->papHandles[idxHandle] == NULL, LeaveCriticalSection(&g_Sandbox.HandlesLock), K_FALSE);
11160 pSandbox->papHandles[idxHandle] = pHandle;
11161 pSandbox->cActiveHandles++;
11162 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11163 return K_TRUE;
11164}
11165
11166
11167/**
11168 * Safely looks up a handle, does not get it and it must not be 'put'.
11169 *
11170 * @returns Pointer to the handle structure if found, otherwise NULL.
11171 * @param hFile The handle to resolve.
11172 */
11173static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile)
11174{
11175 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11176 EnterCriticalSection(&g_Sandbox.HandlesLock);
11177 if (idxHandle < g_Sandbox.cHandles)
11178 {
11179 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11180 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11181 return pHandle;
11182 }
11183 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11184 return NULL;
11185}
11186
11187
11188/**
11189 * Safely gets a handle, must be "put" when done with it.
11190 *
11191 * @returns Pointer to the handle structure if found, otherwise NULL.
11192 * @param hFile The handle to resolve.
11193 */
11194static PKWHANDLE kwSandboxHandleGet(HANDLE hFile)
11195{
11196 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11197 EnterCriticalSection(&g_Sandbox.HandlesLock);
11198 if (idxHandle < g_Sandbox.cHandles)
11199 {
11200 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11201 if (pHandle)
11202 {
11203 kHlpAssertMsg(pHandle->tidOwner == KU32_MAX, ("hFile=%p tidOwner=%#x\n", hFile, pHandle->tidOwner));
11204 pHandle->tidOwner = GetCurrentThreadId();
11205 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11206 return pHandle;
11207 }
11208 }
11209 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11210 return NULL;
11211}
11212
11213
11214/**
11215 * Puts a handle returned by kwSandboxHandleGet.
11216 *
11217 * @param pHandle The handle to "put".
11218 */
11219K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle)
11220{
11221 kHlpAssertMsg(pHandle->tidOwner == GetCurrentThreadId(),
11222 ("hFile tidOwner=%#x tidMe=%#x\n", pHandle->hHandle, pHandle->tidOwner, GetCurrentThreadId()));
11223 pHandle->tidOwner = KU32_MAX;
11224}
11225
11226
11227/**
11228 * Creates a correctly quoted ANSI command line string from the given argv.
11229 *
11230 * @returns Pointer to the command line.
11231 * @param cArgs Number of arguments.
11232 * @param papszArgs The argument vector.
11233 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11234 * @param pcbCmdLine Where to return the command line length,
11235 * including one terminator.
11236 */
11237static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
11238{
11239 KU32 i;
11240 KSIZE cbCmdLine;
11241 char *pszCmdLine;
11242
11243 /* Make a copy of the argument vector that we'll be quoting. */
11244 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
11245 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
11246
11247 /* Quote the arguments that need it. */
11248 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
11249
11250 /* figure out cmd line length. */
11251 cbCmdLine = 0;
11252 for (i = 0; i < cArgs; i++)
11253 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
11254 *pcbCmdLine = cbCmdLine;
11255
11256 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
11257 if (pszCmdLine)
11258 {
11259 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
11260 if (papszQuotedArgs[0] != papszArgs[0])
11261 free(papszQuotedArgs[0]);
11262
11263 for (i = 1; i < cArgs; i++)
11264 {
11265 *psz++ = ' ';
11266 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
11267 if (papszQuotedArgs[i] != papszArgs[i])
11268 free(papszQuotedArgs[i]);
11269 }
11270 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
11271
11272 *psz++ = '\0';
11273 *psz++ = '\0';
11274 }
11275
11276 return pszCmdLine;
11277}
11278
11279
11280
11281static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
11282 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11283 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11284{
11285 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11286 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11287 wchar_t *pwcPool;
11288 KSIZE cbStrings;
11289 KSIZE cwc;
11290 KSIZE cbCmdLine;
11291 KU32 i;
11292
11293 /* Simple stuff. */
11294 pSandbox->rcExitCode = 256;
11295 pSandbox->pTool = pTool;
11296 pSandbox->idMainThread = GetCurrentThreadId();
11297 pSandbox->pgmptr = (char *)pTool->pszPath;
11298 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
11299#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11300 if (pSandbox->StdOut.fIsConsole)
11301 pSandbox->StdOut.u.Con.cwcBuf = 0;
11302 else
11303 pSandbox->StdOut.u.Fully.cchBuf = 0;
11304 if (pSandbox->StdErr.fIsConsole)
11305 pSandbox->StdErr.u.Con.cwcBuf = 0;
11306 else
11307 pSandbox->StdErr.u.Fully.cchBuf = 0;
11308 pSandbox->Combined.cwcBuf = 0;
11309 pSandbox->Combined.cFlushes = 0;
11310#endif
11311 pSandbox->fNoPchCaching = fNoPchCaching;
11312 pSandbox->cArgs = cArgs;
11313 pSandbox->papszArgs = (char **)papszArgs;
11314 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
11315 if (!pSandbox->pszCmdLine)
11316 return KERR_NO_MEMORY;
11317
11318 /*
11319 * Convert command line and argv to UTF-16.
11320 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
11321 */
11322 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
11323 if (!pSandbox->papwszArgs)
11324 return KERR_NO_MEMORY;
11325 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
11326 for (i = 0; i < cArgs; i++)
11327 {
11328 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
11329 pSandbox->papwszArgs[i] = pwcPool;
11330 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11331 pwcPool++;
11332 }
11333 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11334 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11335
11336 /*
11337 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11338 */
11339 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11340 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11341 if (!pSandbox->pwszCmdLine)
11342 return KERR_NO_MEMORY;
11343 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11344
11345 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11346 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11347 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11348
11349 /*
11350 * Setup the environment.
11351 */
11352 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11353 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11354 {
11355 KU32 iDst = 0;
11356 for (i = 0; i < cEnvVars; i++)
11357 {
11358 const char *pszVar = papszEnvVars[i];
11359 KSIZE cchVar = kHlpStrLen(pszVar);
11360 const char *pszEqual;
11361 if ( cchVar > 0
11362 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11363 {
11364 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11365 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11366 if (pszCopy && pwszCopy)
11367 {
11368 pSandbox->papszEnvVars[iDst] = pszCopy;
11369 pSandbox->environ[iDst] = pszCopy;
11370 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11371 pSandbox->wenviron[iDst] = pwszCopy;
11372
11373 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11374 if ( (pszEqual - pszVar) == 4
11375 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11376 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11377 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11378 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11379 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11380 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11381
11382 iDst++;
11383 }
11384 else
11385 {
11386 kHlpFree(pszCopy);
11387 kHlpFree(pwszCopy);
11388 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11389 }
11390 }
11391 else
11392 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11393 }
11394 pSandbox->papszEnvVars[iDst] = NULL;
11395 pSandbox->environ[iDst] = NULL;
11396 pSandbox->papwszEnvVars[iDst] = NULL;
11397 pSandbox->wenviron[iDst] = NULL;
11398 }
11399 else
11400 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11401
11402 /*
11403 * Invalidate the volatile parts of cache (kBuild output directory,
11404 * temporary directory, whatever).
11405 */
11406 kFsCacheInvalidateCustomBoth(g_pFsCache);
11407
11408#ifdef WITH_HISTORY
11409 /*
11410 * Record command line in debug history.
11411 */
11412 kHlpFree(g_apszHistory[g_iHistoryNext]);
11413 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11414 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11415#endif
11416
11417 return 0;
11418}
11419
11420
11421/**
11422 * Does sandbox cleanup between jobs.
11423 *
11424 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11425 * influence the result, so that kmk can get on with things ASAP.
11426 *
11427 * @param pSandbox The sandbox.
11428 */
11429static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11430{
11431 PROCESS_MEMORY_COUNTERS MemInfo;
11432 PKWVIRTALLOC pTracker;
11433 PKWHEAP pHeap;
11434 PKWLOCALSTORAGE pLocalStorage;
11435#ifdef WITH_HASH_MD5_CACHE
11436 PKWHASHMD5 pHash;
11437#endif
11438#ifdef WITH_TEMP_MEMORY_FILES
11439 PKWFSTEMPFILE pTempFile;
11440#endif
11441 PKWEXITCALLACK pExitCallback;
11442
11443 /*
11444 * First stuff that may cause code to run.
11445 */
11446
11447 /* Do exit callback first. */
11448 pExitCallback = g_Sandbox.pExitCallbackHead;
11449 g_Sandbox.pExitCallbackHead = NULL;
11450 while (pExitCallback)
11451 {
11452 PKWEXITCALLACK pNext = pExitCallback->pNext;
11453 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11454 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11455 __try
11456 {
11457 pExitCallback->pfnCallback();
11458 }
11459 __except (EXCEPTION_EXECUTE_HANDLER)
11460 {
11461 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11462 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11463 kHlpAssertFailed();
11464 }
11465 kHlpFree(pExitCallback);
11466 pExitCallback = pNext;
11467 }
11468
11469 /* Free left behind FlsAlloc leaks. */
11470 pLocalStorage = g_Sandbox.pFlsAllocHead;
11471 g_Sandbox.pFlsAllocHead = NULL;
11472 while (pLocalStorage)
11473 {
11474 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11475 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11476 FlsFree(pLocalStorage->idx);
11477 kHlpFree(pLocalStorage);
11478 pLocalStorage = pNext;
11479 }
11480
11481 /* Free left behind TlsAlloc leaks. */
11482 pLocalStorage = g_Sandbox.pTlsAllocHead;
11483 g_Sandbox.pTlsAllocHead = NULL;
11484 while (pLocalStorage)
11485 {
11486 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11487 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11488 TlsFree(pLocalStorage->idx);
11489 kHlpFree(pLocalStorage);
11490 pLocalStorage = pNext;
11491 }
11492
11493
11494 /*
11495 * Then free resources associated with the sandbox run.
11496 */
11497
11498 /* Open handles, except fixed handles (stdout and stderr). */
11499 EnterCriticalSection(&pSandbox->HandlesLock);
11500 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11501 {
11502 KU32 idxHandle = pSandbox->cHandles;
11503 while (idxHandle-- > 0)
11504 if (pSandbox->papHandles[idxHandle] == NULL)
11505 { /* likely */ }
11506 else
11507 {
11508 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11509 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11510 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11511 {
11512 pSandbox->papHandles[idxHandle] = NULL;
11513 pSandbox->cLeakedHandles++;
11514
11515 switch (pHandle->enmType)
11516 {
11517 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11518 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11519 idxHandle, pHandle->hHandle, pHandle->cRefs));
11520 break;
11521 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11522 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11523 idxHandle, pHandle->hHandle, pHandle->cRefs));
11524 break;
11525 case KWHANDLETYPE_OUTPUT_BUF:
11526 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11527 idxHandle, pHandle->hHandle, pHandle->cRefs));
11528 break;
11529#ifdef WITH_TEMP_MEMORY_FILES
11530 case KWHANDLETYPE_TEMP_FILE:
11531 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11532 idxHandle, pHandle->hHandle, pHandle->cRefs));
11533 pHandle->u.pTempFile->cActiveHandles--;
11534 break;
11535 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11536 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11537 idxHandle, pHandle->hHandle, pHandle->cRefs));
11538 pHandle->u.pTempFile->cActiveHandles--;
11539 break;
11540#endif
11541 default:
11542 kHlpAssertFailed();
11543 }
11544 if (--pHandle->cRefs == 0)
11545 kHlpFree(pHandle);
11546 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11547 break;
11548 }
11549 }
11550 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11551 }
11552 LeaveCriticalSection(&pSandbox->HandlesLock);
11553
11554 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11555 g_Sandbox.cMemMappings = 0;
11556
11557#ifdef WITH_TEMP_MEMORY_FILES
11558 /* The temporary files aren't externally visible, they're all in memory. */
11559 pTempFile = pSandbox->pTempFileHead;
11560 pSandbox->pTempFileHead = NULL;
11561 while (pTempFile)
11562 {
11563 PKWFSTEMPFILE pNext = pTempFile->pNext;
11564 KU32 iSeg = pTempFile->cSegs;
11565 while (iSeg-- > 0)
11566 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11567 kHlpFree(pTempFile->paSegs);
11568 pTempFile->pNext = NULL;
11569 kHlpFree(pTempFile);
11570
11571 pTempFile = pNext;
11572 }
11573#endif
11574
11575 /* Free left behind HeapCreate leaks. */
11576 pHeap = g_Sandbox.pHeapHead;
11577 g_Sandbox.pHeapHead = NULL;
11578 while (pHeap != NULL)
11579 {
11580 PKWHEAP pNext = pHeap->pNext;
11581 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11582 HeapDestroy(pHeap->hHeap);
11583 pHeap = pNext;
11584 }
11585
11586 /* Free left behind VirtualAlloc leaks. */
11587 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11588 pTracker = g_Sandbox.pVirtualAllocHead;
11589 g_Sandbox.pVirtualAllocHead = NULL;
11590 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11591 while (pTracker)
11592 {
11593 PKWVIRTALLOC pNext = pTracker->pNext;
11594 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11595
11596#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11597 if (pTracker->idxPreAllocated != KU32_MAX)
11598 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11599 else
11600#endif
11601 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11602 kHlpFree(pTracker);
11603 pTracker = pNext;
11604 }
11605
11606 /* Free the environment. */
11607 if (pSandbox->papszEnvVars)
11608 {
11609 KU32 i;
11610 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11611 kHlpFree(pSandbox->papszEnvVars[i]);
11612 pSandbox->environ[0] = NULL;
11613 pSandbox->papszEnvVars[0] = NULL;
11614
11615 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11616 kHlpFree(pSandbox->papwszEnvVars[i]);
11617 pSandbox->wenviron[0] = NULL;
11618 pSandbox->papwszEnvVars[0] = NULL;
11619 }
11620
11621#ifdef WITH_HASH_MD5_CACHE
11622 /*
11623 * Hash handles.
11624 */
11625 pHash = pSandbox->pHashHead;
11626 pSandbox->pHashHead = NULL;
11627 while (pHash)
11628 {
11629 PKWHASHMD5 pNext = pHash->pNext;
11630 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11631 kHlpFree(pHash);
11632 pHash = pNext;
11633 }
11634#endif
11635
11636 /*
11637 * Check the memory usage. If it's getting high, trigger a respawn
11638 * after the next job.
11639 */
11640 MemInfo.WorkingSetSize = 0;
11641 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11642 {
11643 /* The first time thru, we figure out approximately when to restart
11644 based on installed RAM and CPU threads. */
11645 static KU64 s_cbMaxWorkingSet = 0;
11646 if (s_cbMaxWorkingSet != 0)
11647 { /* likely */ }
11648 else
11649 {
11650 SYSTEM_INFO SysInfo;
11651 MEMORYSTATUSEX GlobalMemInfo;
11652 const char *pszValue;
11653
11654 /* Calculate a reasonable estimate. */
11655 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11656 GetNativeSystemInfo(&SysInfo);
11657
11658 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11659 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11660 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11661#if K_ARCH_BITS >= 64
11662 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11663#else
11664 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11665#endif
11666 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11667 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11668
11669 /* User limit. */
11670 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11671 if (pszValue != NULL)
11672 {
11673 char *pszNext;
11674 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11675 if (*pszNext == '\0' || *pszNext == 'M')
11676 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11677 else if (*pszNext == 'K')
11678 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11679 else if (*pszNext == 'G')
11680 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11681 else
11682 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11683 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11684 }
11685
11686 /* Clamp it a little. */
11687 if (s_cbMaxWorkingSet < 168*1024*1024)
11688 s_cbMaxWorkingSet = 168*1024*1024;
11689#if K_ARCH_BITS < 64
11690 else
11691 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11692 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11693 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11694 : 1536*1024*1024 /* got 4GB VA */);
11695#endif
11696 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11697 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11698 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11699 }
11700
11701 /* Finally the check. */
11702 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11703 {
11704 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11705 g_fRestart = K_TRUE;
11706 }
11707 }
11708
11709 /*
11710 * The CRT has a max of 8192 handles, so we better restart after a while if
11711 * someone is leaking handles or we risk running out of descriptors.
11712 *
11713 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11714 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11715 */
11716 if (pSandbox->cLeakedHandles > 6000)
11717 {
11718 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11719 g_fRestart = K_TRUE;
11720 }
11721}
11722
11723
11724/**
11725 * Does essential cleanups and restoring, anything externally visible.
11726 *
11727 * All cleanups that aren't externally visible are postponed till after we've
11728 * informed kmk of the result, so it can be done in the dead time between jobs.
11729 *
11730 * @param pSandbox The sandbox.
11731 */
11732static void kwSandboxCleanup(PKWSANDBOX pSandbox)
11733{
11734 /*
11735 * Restore the parent command line string.
11736 */
11737 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11738 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11739 pProcParams->CommandLine = pSandbox->SavedCommandLine;
11740#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11741 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
11742 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
11743#endif
11744}
11745
11746
11747static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11748 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11749{
11750 int rcExit = 42;
11751 int rc;
11752
11753 /*
11754 * Initialize the sandbox environment.
11755 */
11756 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
11757 if (rc == 0)
11758 {
11759 if (g_cVerbose > 2)
11760 fprintf(stderr, "kWorker: info: Executing (sandboxed): %s\n", g_Sandbox.pszCmdLine);
11761
11762 /*
11763 * Do module initialization.
11764 */
11765#if 0
11766 //kwSandboxResetModuleVisited();
11767 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
11768#else
11769 kwSandboxResetModuleState();
11770#endif
11771 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
11772 if (rc == 0)
11773 {
11774 /*
11775 * Call the main function.
11776 */
11777#if K_ARCH == K_ARCH_AMD64
11778 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
11779#elif K_ARCH == K_ARCH_X86_32
11780 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
11781#else
11782# error "Port me!"
11783#endif
11784
11785 /* Save the NT TIB first (should do that here, not in some other function). */
11786 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
11787 pSandbox->TibMainThread = *pTib;
11788
11789 /* Make the call in a guarded fashion. */
11790#if K_ARCH == K_ARCH_AMD64
11791 /* AMD64 */
11792 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
11793 __try
11794 {
11795 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11796 if (setjmp(pSandbox->JmpBuf) == 0)
11797 {
11798 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11799 pSandbox->fRunning = K_TRUE;
11800 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
11801 pSandbox->fRunning = K_FALSE;
11802 }
11803 else
11804 rcExit = pSandbox->rcExitCode;
11805 }
11806#elif K_ARCH == K_ARCH_X86_32
11807 /* x86 (see _tmainCRTStartup) */
11808 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
11809 __try
11810 {
11811 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11812 if (setjmp(pSandbox->JmpBuf) == 0)
11813 {
11814 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11815 pSandbox->fRunning = K_TRUE;
11816 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
11817 pSandbox->fRunning = K_FALSE;
11818 }
11819 else
11820 rcExit = pSandbox->rcExitCode;
11821 }
11822#endif
11823 __except (EXCEPTION_EXECUTE_HANDLER)
11824 {
11825 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
11826#ifdef WITH_HISTORY
11827 {
11828 KU32 cPrinted = 0;
11829 while (cPrinted++ < 5)
11830 {
11831 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
11832 if (g_apszHistory[idx])
11833 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
11834 }
11835 }
11836#endif
11837 rcExit = 512;
11838 }
11839 pSandbox->fRunning = K_FALSE;
11840
11841 /* Now, restore the NT TIB. */
11842 *pTib = pSandbox->TibMainThread;
11843 }
11844 else
11845 rcExit = 42 + 4;
11846
11847 /*
11848 * Flush and clean up the essential bits only, postpone whatever we
11849 * can till after we've replied to kmk.
11850 */
11851#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11852 kwSandboxConsoleFlushAll(&g_Sandbox);
11853#endif
11854 kwSandboxCleanup(&g_Sandbox);
11855 /** @todo Flush sandboxed native CRTs too. */
11856 }
11857 else
11858 rcExit = 42 + 3;
11859
11860 return rcExit;
11861}
11862
11863
11864/**
11865 * Does the post command part of a job (optional).
11866 *
11867 * @returns The exit code of the job.
11868 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11869 * @param papszPostCmdArgs The post command and its argument.
11870 */
11871static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11872{
11873 const char *pszCmd = papszPostCmdArgs[0];
11874
11875 /* Allow the kmk builtin prefix. */
11876 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
11877 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
11878 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
11879
11880 /* Command switch. */
11881 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
11882 {
11883 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
11884 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
11885 }
11886
11887 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
11888}
11889
11890
11891/**
11892 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11893 */
11894static unsigned kwGetCurrentProcessorGroup(void)
11895{
11896 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
11897 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
11898 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
11899 if (pfnGetter)
11900 {
11901 GROUP_AFFINITY GroupAffinity;
11902 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
11903 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
11904 return GroupAffinity.Group;
11905 }
11906 return 0;
11907}
11908
11909
11910/**
11911 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11912 */
11913static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
11914{
11915 KSIZE cchRet = 0;
11916 HANDLE hToken;
11917 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
11918 {
11919 DWORD cbRet;
11920 TOKEN_STATISTICS TokenStats;
11921 memset(&TokenStats, 0, sizeof(TokenStats));
11922 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
11923 cchRet = sprintf(pszValue, "%" KX64_PRI,
11924 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
11925 else
11926 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
11927 CloseHandle(hToken);
11928 }
11929 else
11930 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
11931 return cchRet;
11932}
11933
11934
11935/**
11936 * Look for and expand the special environment variable.
11937 *
11938 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
11939 * couldn't accuratly determine. Currently the following variables are
11940 * implemented:
11941 * - "@@PROCESSOR_GROUP@@" - The processor group number.
11942 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
11943 * - "@@PID@@" - The kWorker process ID.
11944 * - "@@@@" - Escaped "@@".
11945 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
11946 */
11947static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
11948{
11949 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
11950 KU32 i = cEnvVars;
11951 while (i-- > 0)
11952 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
11953 && papszEnvVars[i][cchSpecialEnv] == '=')
11954 {
11955 /* We will expand stuff like @@NAME@@ */
11956 const char *pszValue = papszEnvVars[i];
11957 KSIZE offDst = 0;
11958 char szTmp[1024];
11959 for (;;)
11960 {
11961 const char *pszAt = kHlpStrChr(pszValue, '@');
11962 while (pszAt && pszAt[1] != '@')
11963 pszAt = kHlpStrChr(pszAt + 1, '@');
11964 if (pszAt)
11965 {
11966 KSIZE cchSrc = pszAt - pszValue;
11967 if (offDst + cchSrc < sizeof(szTmp))
11968 {
11969 char szSrc[64];
11970
11971 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11972 offDst += cchSrc;
11973 pszValue = pszAt + 2;
11974
11975 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11976 {
11977 pszValue += 15;
11978 if (g_iProcessGroup == -1)
11979 g_iProcessGroup = kwGetCurrentProcessorGroup();
11980 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11981 }
11982 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11983 {
11984 pszValue += 19;
11985 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11986 }
11987 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11988 {
11989 pszValue += 5;
11990 cchSrc = sprintf(szSrc, "%d", getpid());
11991 }
11992 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
11993 {
11994 pszValue += 2;
11995 szSrc[0] = '@';
11996 szSrc[1] = '@';
11997 szSrc[2] = '\0';
11998 cchSrc = 2;
11999 }
12000 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
12001 {
12002 static unsigned int s_iCounter = 0;
12003 pszValue += 15;
12004 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
12005 }
12006 else
12007 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
12008 pszValue - 2);
12009 if (offDst + cchSrc < sizeof(szTmp))
12010 {
12011 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
12012 offDst += cchSrc;
12013 continue;
12014 }
12015 }
12016 }
12017 else
12018 {
12019 KSIZE cchSrc = kHlpStrLen(pszValue);
12020 if (offDst + cchSrc < sizeof(szTmp))
12021 {
12022 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12023 offDst += cchSrc;
12024 break;
12025 }
12026 }
12027 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
12028 }
12029 szTmp[offDst] = '\0';
12030
12031 /* Return a copy of it: */
12032 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
12033 if (papszEnvVars[i])
12034 {
12035 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
12036 return 0;
12037 }
12038 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
12039 }
12040
12041 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
12042}
12043
12044
12045/**
12046 * Part 2 of the "JOB" command handler.
12047 *
12048 * @returns The exit code of the job.
12049 * @param pszExecutable The executable to execute.
12050 * @param pszCwd The current working directory of the job.
12051 * @param cArgs The number of arguments.
12052 * @param papszArgs The argument vector.
12053 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
12054 * @param cEnvVars The number of environment variables.
12055 * @param papszEnvVars The environment vector.
12056 * @param pszSpecialEnv Name of special environment variable that
12057 * requires selective expansion here.
12058 * @param fNoPchCaching Whether to disable precompiled header file
12059 * caching. Avoid trouble when creating them.
12060 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12061 * @param papszPostCmdArgs The post command and its argument.
12062 */
12063static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
12064 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12065 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
12066 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12067{
12068 int rcExit;
12069 PKWTOOL pTool;
12070 char *pszSpecialEnvFree = NULL;
12071
12072 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
12073 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
12074#ifdef KW_LOG_ENABLED
12075 {
12076 KU32 i;
12077 for (i = 0; i < cArgs; i++)
12078 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
12079 for (i = 0; i < cPostCmdArgs; i++)
12080 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
12081 }
12082#endif
12083 g_cJobs++;
12084
12085 /*
12086 * Expand pszSpecialEnv if present.
12087 */
12088 if (pszSpecialEnv && *pszSpecialEnv)
12089 {
12090 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
12091 if (!rcExit)
12092 { /* likely */ }
12093 else
12094 return rcExit;
12095 }
12096
12097 /*
12098 * Lookup the tool.
12099 */
12100 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
12101 if (pTool)
12102 {
12103 /*
12104 * Change the directory if we're going to execute the job inside
12105 * this process. Then invoke the tool type specific handler.
12106 */
12107 switch (pTool->enmType)
12108 {
12109 case KWTOOLTYPE_SANDBOXED:
12110 case KWTOOLTYPE_WATCOM:
12111 {
12112 /* Change dir. */
12113 KFSLOOKUPERROR enmError;
12114 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
12115 if ( pNewCurDir == g_pCurDirObj
12116 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
12117 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12118 else if (SetCurrentDirectoryA(pszCwd))
12119 {
12120 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
12121 g_pCurDirObj = pNewCurDir;
12122 }
12123 else
12124 {
12125 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
12126 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12127 rcExit = 42 + 1;
12128 break;
12129 }
12130
12131 /* Call specific handler. */
12132 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
12133 {
12134 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
12135 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
12136 cEnvVars, papszEnvVars, fNoPchCaching);
12137 }
12138 else
12139 {
12140 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
12141 rcExit = 42 + 2;
12142 }
12143 break;
12144 }
12145
12146 case KWTOOLTYPE_EXEC:
12147 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
12148 rcExit = 42 + 2;
12149 break;
12150
12151 default:
12152 kHlpAssertFailed();
12153 kwErrPrintf("Internal tool type corruption!!\n");
12154 rcExit = 42 + 2;
12155 g_fRestart = K_TRUE;
12156 break;
12157 }
12158
12159 /*
12160 * Do the post command, if present.
12161 */
12162 if (cPostCmdArgs && rcExit == 0)
12163 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
12164 }
12165 else
12166 rcExit = 42 + 1;
12167 if (pszSpecialEnvFree)
12168 {
12169 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
12170 kHlpFree(pszSpecialEnvFree);
12171 }
12172 return rcExit;
12173}
12174
12175
12176/**
12177 * Handles a "JOB" command.
12178 *
12179 * @returns The exit code of the job.
12180 * @param pszMsg Points to the "JOB" command part of the message.
12181 * @param cbMsg Number of message bytes at @a pszMsg. There are
12182 * 4 more zero bytes after the message body to
12183 * simplify parsing.
12184 */
12185static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
12186{
12187 int rcExit = 42;
12188
12189 /*
12190 * Unpack the message.
12191 */
12192 const char *pszExecutable;
12193 KSIZE cbTmp;
12194
12195 pszMsg += sizeof("JOB");
12196 cbMsg -= sizeof("JOB");
12197
12198 /* Executable name. */
12199 pszExecutable = pszMsg;
12200 cbTmp = kHlpStrLen(pszMsg) + 1;
12201 pszMsg += cbTmp;
12202 if ( cbTmp < cbMsg
12203 && cbTmp > 2)
12204 {
12205 const char *pszCwd;
12206 cbMsg -= cbTmp;
12207
12208 /* Current working directory. */
12209 pszCwd = pszMsg;
12210 cbTmp = kHlpStrLen(pszMsg) + 1;
12211 pszMsg += cbTmp;
12212 if ( cbTmp + sizeof(KU32) < cbMsg
12213 && cbTmp >= 2)
12214 {
12215 KU32 cArgs;
12216 cbMsg -= cbTmp;
12217
12218 /* Argument count. */
12219 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
12220 pszMsg += sizeof(cArgs);
12221 cbMsg -= sizeof(cArgs);
12222
12223 if (cArgs > 0 && cArgs < 4096)
12224 {
12225 /* The argument vector. */
12226 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
12227 if (papszArgs)
12228 {
12229 KU32 i;
12230 for (i = 0; i < cArgs; i++)
12231 {
12232 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
12233 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
12234 pszMsg += cbTmp;
12235 if (cbTmp < cbMsg)
12236 cbMsg -= cbTmp;
12237 else
12238 {
12239 cbMsg = 0;
12240 break;
12241 }
12242
12243 }
12244 papszArgs[cArgs] = 0;
12245
12246 /* Environment variable count. */
12247 if (cbMsg > sizeof(KU32))
12248 {
12249 KU32 cEnvVars;
12250 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
12251 pszMsg += sizeof(cEnvVars);
12252 cbMsg -= sizeof(cEnvVars);
12253
12254 if (cEnvVars >= 0 && cEnvVars < 4096)
12255 {
12256 /* The argument vector. */
12257 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
12258 if (papszEnvVars)
12259 {
12260 for (i = 0; i < cEnvVars; i++)
12261 {
12262 papszEnvVars[i] = pszMsg;
12263 cbTmp = kHlpStrLen(pszMsg) + 1;
12264 pszMsg += cbTmp;
12265 if (cbTmp < cbMsg)
12266 cbMsg -= cbTmp;
12267 else
12268 {
12269 cbMsg = 0;
12270 break;
12271 }
12272 }
12273 papszEnvVars[cEnvVars] = 0;
12274
12275 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
12276 if (cbMsg >= sizeof(KU8) * 2)
12277 {
12278 KBOOL fWatcomBrainDamange = *pszMsg++;
12279 KBOOL fNoPchCaching = *pszMsg++;
12280 cbMsg -= 2;
12281
12282 /* Name of special enviornment variable requiring selective expansion. */
12283 if (cbMsg >= 1)
12284 {
12285 const char *pszSpecialEnv = pszMsg;
12286 cbTmp = kHlpStrLen(pszMsg);
12287 pszMsg += cbTmp + 1;
12288 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
12289
12290 /* Post command argument count (can be zero). */
12291 if (cbMsg >= sizeof(KU32))
12292 {
12293 KU32 cPostCmdArgs;
12294 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
12295 pszMsg += sizeof(cPostCmdArgs);
12296 cbMsg -= sizeof(cPostCmdArgs);
12297
12298 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
12299 {
12300 char const *apszPostCmdArgs[32+1];
12301 for (i = 0; i < cPostCmdArgs; i++)
12302 {
12303 apszPostCmdArgs[i] = pszMsg;
12304 cbTmp = kHlpStrLen(pszMsg) + 1;
12305 pszMsg += cbTmp;
12306 if ( cbTmp < cbMsg
12307 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
12308 cbMsg -= cbTmp;
12309 else
12310 {
12311 cbMsg = KSIZE_MAX;
12312 break;
12313 }
12314 }
12315 if (cbMsg == 0)
12316 {
12317 apszPostCmdArgs[cPostCmdArgs] = NULL;
12318
12319 /*
12320 * The next step.
12321 */
12322 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
12323 cArgs, papszArgs, fWatcomBrainDamange,
12324 cEnvVars, papszEnvVars, pszSpecialEnv,
12325 fNoPchCaching,
12326 cPostCmdArgs, apszPostCmdArgs);
12327 }
12328 else if (cbMsg == KSIZE_MAX)
12329 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
12330 else
12331 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
12332 }
12333 else
12334 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
12335 }
12336 else
12337 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
12338 }
12339 else
12340 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12341 }
12342 else
12343 kwErrPrintf("Detected bogus message unpacking flags!\n");
12344 kHlpFree((void *)papszEnvVars);
12345 }
12346 else
12347 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12348 }
12349 else
12350 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12351 }
12352 else
12353 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12354 kHlpFree((void *)papszArgs);
12355 }
12356 else
12357 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12358 }
12359 else
12360 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12361 }
12362 else
12363 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12364 }
12365 else
12366 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12367 return rcExit;
12368}
12369
12370
12371/**
12372 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12373 *
12374 * @retval 0 on success.
12375 * @retval -1 on error (fully bitched).
12376 *
12377 * @param hPipe The pipe handle.
12378 * @param pvBuf The buffer to write out out.
12379 * @param cbToWrite The number of bytes to write.
12380 */
12381static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12382{
12383 KU8 const *pbBuf = (KU8 const *)pvBuf;
12384 KU32 cbLeft = cbToWrite;
12385 while (g_rcCtrlC == 0)
12386 {
12387 DWORD cbActuallyWritten = 0;
12388 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12389 {
12390 cbLeft -= cbActuallyWritten;
12391 if (!cbLeft)
12392 return 0;
12393 pbBuf += cbActuallyWritten;
12394 }
12395 else
12396 {
12397 DWORD dwErr = GetLastError();
12398 if (cbLeft == cbToWrite)
12399 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12400 else
12401 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12402 return -1;
12403 }
12404 }
12405 return -1;
12406}
12407
12408
12409/**
12410 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12411 *
12412 * @retval 0 on success.
12413 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12414 * @retval -1 on error (fully bitched).
12415 * @param hPipe The pipe handle.
12416 * @param pvBuf The buffer to read into.
12417 * @param cbToRead The number of bytes to read.
12418 * @param fShutdownOkay Whether connection shutdown while reading the
12419 * first byte is okay or not.
12420 */
12421static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12422{
12423 KU8 *pbBuf = (KU8 *)pvBuf;
12424 KU32 cbLeft = cbToRead;
12425 while (g_rcCtrlC == 0)
12426 {
12427 DWORD cbActuallyRead = 0;
12428 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12429 {
12430 cbLeft -= cbActuallyRead;
12431 if (!cbLeft)
12432 return 0;
12433 pbBuf += cbActuallyRead;
12434 }
12435 else
12436 {
12437 DWORD dwErr = GetLastError();
12438 if (cbLeft == cbToRead)
12439 {
12440 if ( fMayShutdown
12441 && dwErr == ERROR_BROKEN_PIPE)
12442 return 1;
12443 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12444 }
12445 else
12446 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12447 return -1;
12448 }
12449 }
12450 return -1;
12451}
12452
12453
12454/**
12455 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12456 *
12457 * @returns pszBuf
12458 * @param pszBuf The buffer (sufficiently large).
12459 * @param uValue The value.
12460 */
12461static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12462{
12463 char szTmp[64];
12464 char *psz = &szTmp[63];
12465 int cch = 4;
12466
12467 *psz-- = '\0';
12468 do
12469 {
12470 if (--cch == 0)
12471 {
12472 *psz-- = ' ';
12473 cch = 3;
12474 }
12475 *psz-- = (uValue % 10) + '0';
12476 uValue /= 10;
12477 } while (uValue != 0);
12478
12479 return strcpy(pszBuf, psz + 1);
12480}
12481
12482
12483/**
12484 * Prints statistics.
12485 */
12486static void kwPrintStats(void)
12487{
12488 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12489 MEMORYSTATUSEX MemStatus;
12490 IO_COUNTERS IoCounters;
12491 DWORD cHandles;
12492 KSIZE cMisses;
12493 char szBuf[16*1024];
12494 int off = 0;
12495 char szPrf[24];
12496 char sz1[64];
12497 char sz2[64];
12498 char sz3[64];
12499 char sz4[64];
12500
12501 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12502
12503 szBuf[off++] = '\n';
12504
12505 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12506 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12507 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12508 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12509 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12510
12511 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12512 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12513
12514 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12515 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12516 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12517
12518 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12519 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12520 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12521
12522 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12523 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12524 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12525 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12526 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12527 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12528 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12529
12530 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12531 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12532 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12533 kwFmtU64(sz1, g_pFsCache->cObjects),
12534 kwFmtU64(sz2, g_pFsCache->cbObjects),
12535 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12536 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12537 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12538 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12539 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12540 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12541#ifdef KFSCACHE_CFG_UTF16
12542 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12543 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12544 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12545 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12546 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12547#endif
12548 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12549 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12550 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12551 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12552 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12553
12554 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12555 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12556 kwFmtU64(sz1, g_pFsCache->cLookups),
12557 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12558 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12559 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12560 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12561 kwFmtU64(sz4, cMisses),
12562 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12563
12564 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12565 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12566 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12567 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12568 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12569 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12570 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12571 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12572
12573
12574 /*
12575 * Process & Memory details.
12576 */
12577 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12578 cHandles = 0;
12579 MemInfo.cb = sizeof(MemInfo);
12580 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12581 memset(&MemInfo, 0, sizeof(MemInfo));
12582 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12583 kwFmtU64(sz1, cHandles),
12584 kwFmtU64(sz2, MemInfo.PageFaultCount),
12585 kwFmtU64(sz3, MemInfo.PagefileUsage),
12586 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12587 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12588 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12589 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12590 kwFmtU64(sz3, MemInfo.PrivateUsage));
12591 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12592 szPrf,
12593 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12594 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12595 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12596 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12597
12598 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12599 memset(&IoCounters, 0, sizeof(IoCounters));
12600 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12601 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12602 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12603 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12604 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12605 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12606 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12607 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12608 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12609
12610 MemStatus.dwLength = sizeof(MemStatus);
12611 if (!GlobalMemoryStatusEx(&MemStatus))
12612 memset(&MemStatus, 0, sizeof(MemStatus));
12613 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12614 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12615 MemStatus.ullAvailVirtual);
12616 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12617
12618 maybe_con_fwrite(szBuf, off, 1, stdout);
12619 fflush(stdout);
12620}
12621
12622
12623/**
12624 * Handles what comes after --test.
12625 *
12626 * @returns Exit code.
12627 * @param argc Number of arguments after --test.
12628 * @param argv Arguments after --test.
12629 */
12630static int kwTestRun(int argc, char **argv)
12631{
12632 int i;
12633 int j;
12634 int rcExit;
12635 int cRepeats;
12636 char szCwd[MAX_PATH];
12637 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12638 KU32 cEnvVars;
12639 char **papszEnvVars;
12640 const char *pszSpecialEnv = "";
12641 const char *pszSpecialEnvFull = NULL;
12642 KBOOL fWatcomBrainDamange = K_FALSE;
12643 KBOOL fNoPchCaching = K_FALSE;
12644
12645 /*
12646 * Parse arguments.
12647 */
12648 /* Repeat count. */
12649 i = 0;
12650 if (i >= argc)
12651 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12652 if (strcmp(argv[i], "--") != 0)
12653 {
12654 cRepeats = atoi(argv[i]);
12655 if (cRepeats <= 0)
12656 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12657 i++;
12658
12659 /* Optional directory change. */
12660 if ( i < argc
12661 && ( strcmp(argv[i], "--chdir") == 0
12662 || strcmp(argv[i], "-C") == 0 ) )
12663 {
12664 i++;
12665 if (i >= argc)
12666 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12667 pszCwd = argv[i++];
12668 }
12669
12670 /* Optional watcom flag directory change. */
12671 if ( i < argc
12672 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12673 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12674 {
12675 fWatcomBrainDamange = K_TRUE;
12676 i++;
12677 }
12678
12679 /* Optional watcom flag directory change. */
12680 if ( i < argc
12681 && strcmp(argv[i], "--no-pch-caching") == 0)
12682 {
12683 fNoPchCaching = K_TRUE;
12684 i++;
12685 }
12686
12687 /* Optional directory change. */
12688 if ( i < argc
12689 && ( strcmp(argv[i], "--set-special") == 0
12690 || strcmp(argv[i], "-s") == 0 ) )
12691 {
12692 i++;
12693 if (i >= argc)
12694 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12695 pszSpecialEnvFull = argv[i++];
12696 putenv(pszSpecialEnvFull);
12697 pszSpecialEnv = strdup(pszSpecialEnvFull);
12698 *strchr(pszSpecialEnv, '=') = '\0';
12699 }
12700
12701 /* Trigger breakpoint */
12702 if ( i < argc
12703 && strcmp(argv[i], "--breakpoint") == 0)
12704 {
12705 __debugbreak();
12706 i++;
12707 }
12708
12709 /* Check for '--'. */
12710 if (i >= argc)
12711 return kwErrPrintfRc(2, "Missing '--'\n");
12712 if (strcmp(argv[i], "--") != 0)
12713 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
12714 i++;
12715 }
12716 else
12717 {
12718 cRepeats = 1;
12719 i++;
12720 }
12721 if (i >= argc)
12722 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
12723
12724 /*
12725 * Duplicate the environment.
12726 */
12727 cEnvVars = 0;
12728 while (environ[cEnvVars] != NULL)
12729 cEnvVars++;
12730 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
12731
12732 /*
12733 * Do the job.
12734 */
12735 rcExit = 0;
12736 for (j = 0; j < cRepeats; j++)
12737 {
12738 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
12739 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
12740 argc - i, &argv[i], fWatcomBrainDamange,
12741 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
12742 0, NULL);
12743 KW_LOG(("rcExit=%d\n", rcExit));
12744 kwSandboxCleanupLate(&g_Sandbox);
12745 }
12746
12747 if (getenv("KWORKER_STATS") != NULL)
12748 kwPrintStats();
12749
12750# ifdef WITH_LOG_FILE
12751 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12752 CloseHandle(g_hLogFile);
12753# endif
12754 return rcExit;
12755}
12756
12757
12758/**
12759 * Reads @a pszFile into memory and chops it up into an argument vector.
12760 *
12761 * @returns Pointer to the argument vector on success, NULL on failure.
12762 * @param pszFile The file to load.
12763 * @param pcArgs Where to return the number of arguments.
12764 * @param ppszFileContent Where to return the allocation.
12765 */
12766static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
12767{
12768 char **papszArgs = NULL;
12769 FILE *pFile = fopen(pszFile, "r");
12770 if (pFile)
12771 {
12772 long cbFile;
12773 if ( fseek(pFile, 0, SEEK_END) == 0
12774 && (cbFile = ftell(pFile)) >= 0
12775 && fseek(pFile, 0, SEEK_SET) == 0)
12776 {
12777 char *pszFile = kHlpAllocZ(cbFile + 3);
12778 if (pszFile)
12779 {
12780 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
12781 if ( feof(pFile)
12782 && !ferror(pFile))
12783 {
12784 size_t off = 0;
12785 int cArgs = 0;
12786 int cAllocated = 0;
12787 char ch;
12788
12789 pszFile[cbRead] = '\0';
12790 pszFile[cbRead + 1] = '\0';
12791 pszFile[cbRead + 2] = '\0';
12792
12793 while ((ch = pszFile[off]) != '\0')
12794 {
12795 char *pszArg;
12796 switch (ch)
12797 {
12798 case ' ':
12799 case '\t':
12800 case '\n':
12801 case '\r':
12802 off++;
12803 continue;
12804
12805 case '\\':
12806 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
12807 {
12808 off += 2;
12809 continue;
12810 }
12811 /* fall thru */
12812 default:
12813 pszArg = &pszFile[off];
12814 do
12815 ch = pszFile[++off];
12816 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
12817 pszFile[off++] = '\0';
12818 break;
12819
12820 case '\'':
12821 pszArg = &pszFile[++off];
12822 while ((ch = pszFile[off]) != '\0' && ch != '\'')
12823 off++;
12824 pszFile[off++] = '\0';
12825 break;
12826
12827 case '\"': /** @todo escape sequences */
12828 pszArg = &pszFile[++off];
12829 while ((ch = pszFile[off]) != '\0' && ch != '"')
12830 off++;
12831 pszFile[off++] = '\0';
12832 break;
12833 }
12834 if (cArgs + 1 >= cAllocated)
12835 {
12836 void *pvNew;
12837 cAllocated = cAllocated ? cAllocated * 2 : 16;
12838 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
12839 if (pvNew)
12840 papszArgs = (char **)pvNew;
12841 else
12842 {
12843 kHlpFree(papszArgs);
12844 papszArgs = NULL;
12845 break;
12846 }
12847 }
12848 papszArgs[cArgs] = pszArg;
12849 papszArgs[++cArgs] = NULL;
12850 }
12851 *pcArgs = cArgs;
12852 }
12853 else
12854 kwErrPrintf("Error reading '%s'!\n", pszFile);
12855 }
12856 else
12857 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
12858 }
12859 else
12860 kwErrPrintf("Error seeking '%s'!\n", pszFile);
12861 fclose(pFile);
12862 }
12863 else
12864 kwErrPrintf("Error opening '%s'!\n", pszFile);
12865 return papszArgs;
12866}
12867
12868/**
12869 * Appends a string to an string vector (arguments or enviornment).
12870 *
12871 * @returns 0 on success, non-zero on failure (exit code).
12872 * @param ppapszVector Pointer to the string pointer array.
12873 * @param pcEntries Pointer to the array size.
12874 * @param pszAppend The string to append.
12875 */
12876static int kwFullTestVectorAppend(const char ***ppapszVector, KU32 *pcEntries, char const *pszAppend)
12877{
12878 KU32 cEntries = *pcEntries;
12879 if (!(cEntries & 15))
12880 {
12881 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
12882 if (pvNew)
12883 *ppapszVector = (const char **)pvNew;
12884 else
12885 return kwErrPrintfRc(2, "Out of memory!\n");
12886 }
12887 (*ppapszVector)[cEntries] = pszAppend;
12888 (*ppapszVector)[++cEntries] = NULL;
12889 *pcEntries = cEntries;
12890 return 0;
12891}
12892
12893
12894/**
12895 * Parses arguments for --full-test.
12896 *
12897 * @returns 0 on success, non-zero on failure (exit code).
12898 */
12899static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
12900 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
12901{
12902 PKWONETEST pCur = *ppHead;
12903 int i;
12904 for (i = 0; i < argc; i++)
12905 {
12906 int rc = 0;
12907 const char *pszArg = argv[i];
12908 if (*pszArg == 'k')
12909 {
12910 if (kHlpStrComp(pszArg, "kSubmit") == 0)
12911 {
12912 if (*piState != 0)
12913 {
12914 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
12915 if (!pCur)
12916 return kwErrPrintfRc(2, "Out of memory!\n");
12917 pCur->fVirgin = K_TRUE;
12918 pCur->pszCwd = pszDefaultCwd;
12919 pCur->cRuns = 1;
12920 pCur->pNext = *ppHead;
12921 *ppHead = pCur;
12922 *piState = 0;
12923 }
12924 else if (!pCur->fVirgin)
12925 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
12926 pCur->pszJobSrc = pszJobSrc;
12927 pCur->iJobSrc = i;
12928 continue; /* (to stay virgin) */
12929 }
12930
12931 /* Ignore "kWorker 378172/62:" sequences that kmk/kSubmit spews out on failure. */
12932 if ( kHlpStrComp(pszArg, "kWorker") == 0
12933 && i + 1 < argc
12934 && (unsigned)(argv[i + 1][0] - '0') <= 9)
12935 {
12936 i++;
12937 continue;
12938 }
12939 }
12940
12941 if ( *pszArg == '-'
12942 && ( *piState == 0
12943 || pszArg[1] == '@'))
12944 {
12945 const char *pszValue = NULL;
12946 char ch = *++pszArg;
12947 pszArg++;
12948 if (ch == '-')
12949 {
12950 ch = '\0';
12951 if (*pszArg == '\0') /* -- */
12952 *piState = 2;
12953 /* Translate or handle long options: */
12954 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
12955 ch = 'E';
12956 else if (kHlpStrComp(pszArg, "special-env") == 0)
12957 ch = 's';
12958 else if (kHlpStrComp(pszArg, "default-env") == 0)
12959 {
12960 unsigned i;
12961 pCur->cEnvVars = 0;
12962 for (i = 0; environ[i] && rc == 0; i++)
12963 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
12964 }
12965 else if (kHlpStrComp(pszArg, "chdir") == 0)
12966 ch = 'C';
12967 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
12968 ch = 'P';
12969 else if (kHlpStrComp(pszArg, "response-file") == 0)
12970 ch = '@';
12971 else if (kHlpStrComp(pszArg, "runs") == 0)
12972 ch = 'R';
12973 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
12974 pCur->fWatcomBrainDamange = K_TRUE;
12975 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
12976 pCur->fNoPchCaching = K_TRUE;
12977 else if (kHlpStrComp(pszArg, "executable") == 0)
12978 ch = 'e';
12979 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12980 {
12981 __debugbreak();
12982 continue; /* (to stay virgin) */
12983 }
12984 else
12985 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12986 pszArg = "";
12987 }
12988
12989 while (ch != '\0' && rc == 0)
12990 {
12991 /* Fetch value if needed: */
12992 switch (ch)
12993 {
12994 case '@':
12995 case 'e':
12996 case 'E':
12997 case 's':
12998 case 'C':
12999 case 'R':
13000 if (*pszArg == ':' || *pszArg == '=')
13001 pszValue = &pszArg[1];
13002 else if (*pszArg)
13003 pszValue = pszArg;
13004 else if (i + 1 < argc)
13005 pszValue = argv[++i];
13006 else
13007 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
13008 pszArg = "";
13009 break;
13010 }
13011
13012 /* Handle the option: */
13013 switch (ch)
13014 {
13015 case 'E':
13016 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
13017 break;
13018 case 'C':
13019 pCur->pszCwd = pszValue;
13020 break;
13021 case 's':
13022 pCur->pszSpecialEnv = pszValue;
13023 break;
13024 case 'e':
13025 pCur->pszExecutable = pszValue;
13026 break;
13027 case 'P':
13028 *piState = 1;
13029 if (*pszArg)
13030 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
13031 break;
13032 case 'R':
13033 pCur->cRuns = atoi(pszValue);
13034 if ((int)pCur->cRuns < 0)
13035 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
13036 break;
13037 case '@':
13038 if (cRecursions < 5)
13039 {
13040 char *pszLeaked = NULL;
13041 int cArgs = 0;
13042 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
13043 if (papszArgsLeaked)
13044 {
13045 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
13046 cRecursions + 1, pszValue);
13047 pCur = *ppHead;
13048 }
13049 else
13050 return 2;
13051 }
13052 else
13053 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
13054 break;
13055 }
13056
13057 /* next */
13058 ch = *pszArg++;
13059 }
13060 }
13061 else if (*piState == 2)
13062 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
13063 else if (*piState == 1)
13064 {
13065 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
13066 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
13067 else
13068 *piState = 2;
13069 }
13070 else
13071 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
13072 if (rc)
13073 return rc;
13074 pCur->fVirgin = K_FALSE;
13075 }
13076 return 0;
13077}
13078
13079
13080/**
13081 * Handles what comes after --full-test.
13082 *
13083 * @returns Exit code.
13084 * @param argc Number of arguments after --full-test.
13085 * @param argv Arguments after --full-test.
13086 */
13087static int kwFullTestRun(int argc, char **argv)
13088{
13089 char szDefaultCwd[MAX_PATH];
13090 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
13091 KWONETEST FirstTest;
13092 PKWONETEST pHead = &FirstTest;
13093 PKWONETEST pCur;
13094 int iState = 0;
13095 int rcExit;
13096
13097 /*
13098 * Parse arguments.
13099 */
13100 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
13101 FirstTest.pszJobSrc = "command-line";
13102 FirstTest.iJobSrc = 1;
13103 FirstTest.fVirgin = K_TRUE;
13104 FirstTest.pszCwd = pszDefaultCwd;
13105 FirstTest.cRuns = 1;
13106
13107 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
13108 if (rcExit)
13109 return rcExit;
13110
13111 /*
13112 * Do the job. LIFO ordering (see kSubmit).
13113 */
13114 for (pCur = pHead; pCur; pCur = pCur->pNext)
13115 {
13116 if (!pCur->pszExecutable && pCur->papszArgs)
13117 pCur->pszExecutable = pCur->papszArgs[0];
13118 if ( pCur->pszExecutable
13119 && pCur->cArgs > 0
13120 && pCur->cEnvVars > 0)
13121 {
13122 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
13123 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
13124 unsigned iRun;
13125
13126 for (iRun = 0; iRun < pCur->cRuns; iRun++)
13127 {
13128 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
13129 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
13130 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
13131 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
13132
13133 KW_LOG(("rcExit=%d\n", rcExit));
13134 kwSandboxCleanupLate(&g_Sandbox);
13135
13136 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
13137 }
13138 kHlpFree(papszEnvVarsCopy);
13139 }
13140 else
13141 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
13142 pCur->pszExecutable ? "" : " No executable!",
13143 pCur->cArgs < 1 ? " No arguments!" : "",
13144 pCur->cEnvVars < 1 ? " No environment!" : "",
13145 pCur->iJobSrc, pCur->pszJobSrc);
13146 }
13147
13148 if (getenv("KWORKER_STATS") != NULL)
13149 kwPrintStats();
13150
13151# ifdef WITH_LOG_FILE
13152 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13153 CloseHandle(g_hLogFile);
13154# endif
13155 return rcExit;
13156}
13157
13158
13159/**
13160 * Helper for main() argument handling that sets the processor group if
13161 * possible.
13162 */
13163static void kwSetProcessorGroup(unsigned long uGroup)
13164{
13165 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
13166 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
13167 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
13168
13169 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
13170 if (pfnSetThreadGroupAffinity)
13171 {
13172 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
13173 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
13174 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
13175 if (NewAff.Mask && (WORD)uGroup == uGroup)
13176 {
13177 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
13178 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
13179 }
13180 else if (GetLastError() == NO_ERROR)
13181 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
13182 else
13183 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
13184 uGroup, GetLastError());
13185 }
13186 else
13187 {
13188 OSVERSIONINFOA VerInfo = {0};
13189 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
13190 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
13191 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
13192 }
13193}
13194
13195
13196int main(int argc, char **argv)
13197{
13198 KSIZE cbMsgBuf = 0;
13199 KU8 *pbMsgBuf = NULL;
13200 int i;
13201 HANDLE hPipe = INVALID_HANDLE_VALUE;
13202 const char *pszTmp;
13203 KFSLOOKUPERROR enmIgnored;
13204 DWORD dwType;
13205#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
13206 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
13207 kwSandboxVecXcptEmulateChained);
13208#endif
13209#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13210 HANDLE hCurProc = GetCurrentProcess();
13211 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
13212 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
13213#endif
13214
13215
13216#ifdef WITH_FIXED_VIRTUAL_ALLOCS
13217 /*
13218 * Reserve memory for cl.exe
13219 */
13220 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
13221 {
13222 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
13223 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
13224 g_aFixedVirtualAllocs[i].cbFixed,
13225 MEM_RESERVE, PAGE_READWRITE);
13226 if ( !g_aFixedVirtualAllocs[i].pvReserved
13227 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
13228 {
13229 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
13230 GetLastError());
13231 if (g_aFixedVirtualAllocs[i].pvReserved)
13232 {
13233 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
13234 g_aFixedVirtualAllocs[i].pvReserved = NULL;
13235 }
13236 }
13237 }
13238#endif
13239
13240 /*
13241 * Register our Control-C and Control-Break handlers.
13242 */
13243 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
13244 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
13245
13246 /*
13247 * Create the cache and mark the temporary directory as using the custom revision.
13248 */
13249 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
13250 if (!g_pFsCache)
13251 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
13252
13253 pszTmp = getenv("TEMP");
13254 if (pszTmp && *pszTmp != '\0')
13255 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13256 pszTmp = getenv("TMP");
13257 if (pszTmp && *pszTmp != '\0')
13258 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13259 pszTmp = getenv("TMPDIR");
13260 if (pszTmp && *pszTmp != '\0')
13261 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13262
13263 /*
13264 * Make g_abDefLdBuf executable.
13265 */
13266 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
13267 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
13268 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
13269 InitializeCriticalSection(&g_Sandbox.HandlesLock);
13270 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
13271
13272#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13273 /*
13274 * Get and duplicate the console handles.
13275 */
13276 /* Standard output. */
13277 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
13278 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
13279 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13280 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
13281 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
13282 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
13283 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13284 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13285 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
13286 g_Sandbox.HandleStdOut.cRefs = 0x10001;
13287 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
13288 g_Sandbox.HandleStdOut.tidOwner = KU32_MAX;
13289 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
13290 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
13291 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
13292 {
13293 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
13294 g_Sandbox.cFixedHandles++;
13295 else
13296 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
13297 }
13298 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13299 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
13300
13301 /* Standard error. */
13302 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
13303 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
13304 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13305 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
13306 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
13307 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
13308 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13309 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13310 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
13311 g_Sandbox.HandleStdErr.cRefs = 0x10001;
13312 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
13313 g_Sandbox.HandleStdErr.tidOwner = KU32_MAX;
13314 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
13315 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
13316 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
13317 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
13318 {
13319 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
13320 g_Sandbox.cFixedHandles++;
13321 else
13322 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
13323 }
13324 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13325 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
13326
13327 /* Combined console buffer. */
13328 if (g_Sandbox.StdErr.fIsConsole)
13329 {
13330 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
13331 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13332 }
13333 else if (g_Sandbox.StdOut.fIsConsole)
13334 {
13335 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
13336 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13337 }
13338 else
13339 {
13340 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
13341 g_Sandbox.Combined.uCodepage = CP_ACP;
13342 }
13343 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
13344#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
13345
13346
13347 /*
13348 * Parse arguments.
13349 */
13350 for (i = 1; i < argc; i++)
13351 {
13352 if (strcmp(argv[i], "--pipe") == 0)
13353 {
13354 i++;
13355 if (i < argc)
13356 {
13357 char *pszEnd = NULL;
13358 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13359 if ( *argv[i]
13360 && pszEnd != NULL
13361 && *pszEnd == '\0'
13362 && u64Value != 0
13363 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13364 && (uintptr_t)u64Value == u64Value)
13365 hPipe = (HANDLE)(uintptr_t)u64Value;
13366 else
13367 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13368 }
13369 else
13370 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13371 }
13372 else if (strcmp(argv[i], "--volatile") == 0)
13373 {
13374 i++;
13375 if (i < argc)
13376 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13377 else
13378 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13379 }
13380 else if (strcmp(argv[i], "--test") == 0)
13381 return kwTestRun(argc - i - 1, &argv[i + 1]);
13382 else if (strcmp(argv[i], "--full-test") == 0)
13383 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13384 else if (strcmp(argv[i], "--priority") == 0)
13385 {
13386 i++;
13387 if (i < argc)
13388 {
13389 char *pszEnd = NULL;
13390 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13391 if ( *argv[i]
13392 && pszEnd != NULL
13393 && *pszEnd == '\0'
13394 && uValue >= 1
13395 && uValue <= 5)
13396 {
13397 DWORD dwClass;
13398 int dwPriority;
13399 switch (uValue)
13400 {
13401 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13402 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13403 default:
13404 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13405 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13406 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13407 }
13408 SetPriorityClass(GetCurrentProcess(), dwClass);
13409 if (dwPriority != INT_MAX)
13410 SetThreadPriority(GetCurrentThread(), dwPriority);
13411 }
13412 else
13413 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13414 }
13415 else
13416 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13417 }
13418 else if (strcmp(argv[i], "--group") == 0)
13419 {
13420 i++;
13421 if (i < argc)
13422 {
13423 char *pszEnd = NULL;
13424 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13425 if ( *argv[i]
13426 && pszEnd != NULL
13427 && *pszEnd == '\0'
13428 && uValue == (WORD)uValue)
13429 kwSetProcessorGroup(uValue);
13430 else
13431 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13432 }
13433 else
13434 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13435 }
13436 else if ( strcmp(argv[i], "--verbose") == 0
13437 || strcmp(argv[i], "-v") == 0)
13438 g_cVerbose++;
13439 else if ( strcmp(argv[i], "--help") == 0
13440 || strcmp(argv[i], "-h") == 0
13441 || strcmp(argv[i], "-?") == 0)
13442 {
13443 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13444 "usage: kWorker <--help|-h>\n"
13445 "usage: kWorker <--version|-V>\n"
13446 "usage: kWorker [--volatile dir] --full-test kSubmit ...\n"
13447 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13448 "\n"
13449 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13450 return 0;
13451 }
13452 else if ( strcmp(argv[i], "--version") == 0
13453 || strcmp(argv[i], "-V") == 0)
13454 return kbuild_version(argv[0]);
13455 else
13456 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13457 }
13458
13459 /*
13460 * If no --pipe argument, then assume its standard input.
13461 * We need to carefully replace the CRT stdin with a handle to "nul".
13462 */
13463 if (hPipe == INVALID_HANDLE_VALUE)
13464 {
13465 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13466 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13467 {
13468 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13469 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13470 {
13471 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13472 if (fdNul >= 0)
13473 {
13474 if (_dup2(fdNul, 0) >= 0)
13475 {
13476 close(fdNul);
13477 hPipe = hDuplicate;
13478 }
13479 else
13480 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13481 }
13482 else
13483 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13484 }
13485 else
13486 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13487 }
13488 else
13489 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13490 GetFileType(hPipe), GetLastError());
13491 }
13492 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13493 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13494 GetFileType(hPipe), GetLastError());
13495 g_hPipe = hPipe;
13496
13497 /*
13498 * Serve the pipe.
13499 */
13500 for (;;)
13501 {
13502 KU32 cbMsg = 0;
13503 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13504 if (rc == 0)
13505 {
13506 /* Make sure the message length is within sane bounds. */
13507 if ( cbMsg > 4
13508 && cbMsg <= 256*1024*1024)
13509 {
13510 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13511 if (cbMsg + 4 <= cbMsgBuf)
13512 { /* likely */ }
13513 else
13514 {
13515 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13516 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13517 if (!pbMsgBuf)
13518 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13519 }
13520
13521 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13522 *(KU32 *)pbMsgBuf = cbMsg;
13523 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13524 if (rc == 0)
13525 {
13526 const char *psz;
13527
13528 pbMsgBuf[cbMsg] = '\0';
13529 pbMsgBuf[cbMsg + 1] = '\0';
13530 pbMsgBuf[cbMsg + 2] = '\0';
13531 pbMsgBuf[cbMsg + 3] = '\0';
13532
13533 /* The first string after the header is the command. */
13534 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13535 if ( strcmp(psz, "JOB") == 0
13536 && g_rcCtrlC == 0)
13537 {
13538 struct
13539 {
13540 KI32 rcExitCode;
13541 KU8 bExiting;
13542 KU8 abZero[3];
13543 } Reply;
13544 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13545 Reply.bExiting = g_fRestart;
13546 Reply.abZero[0] = 0;
13547 Reply.abZero[1] = 0;
13548 Reply.abZero[2] = 0;
13549 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13550 if ( rc == 0
13551 && !g_fRestart)
13552 {
13553 kwSandboxCleanupLate(&g_Sandbox);
13554 if (g_rcCtrlC == 0)
13555 continue;
13556 }
13557 }
13558 else
13559 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13560 }
13561 }
13562 else
13563 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13564 }
13565
13566 /*
13567 * If we're exitting because we're restarting, we need to delay till
13568 * kmk/kSubmit has read the result. Windows documentation says it
13569 * immediately discards pipe buffers once the pipe is broken by the
13570 * server (us). So, We flush the buffer and queues a 1 byte read
13571 * waiting for kSubmit to close the pipe when it receives the
13572 * bExiting = K_TRUE result.
13573 */
13574 if (g_fRestart)
13575 {
13576 DWORD cbIgnored = 1;
13577 KU8 b;
13578 FlushFileBuffers(hPipe);
13579 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13580 }
13581
13582 CloseHandle(hPipe);
13583#ifdef WITH_LOG_FILE
13584 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13585 CloseHandle(g_hLogFile);
13586#endif
13587 if (getenv("KWORKER_STATS") != NULL)
13588 kwPrintStats();
13589 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13590 }
13591}
13592
13593
13594/** @page pg_kWorker kSubmit / kWorker
13595 *
13596 * @section sec_kWorker_Motivation Motivation / Inspiration
13597 *
13598 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13599 * builds on machines "infested" by Anti Virus protection and disk encryption
13600 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13601 * got "infected".
13602 *
13603 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13604 * mainly a bunch of tiny assembly and C files being compiler a million times.
13605 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13606 * own toolchain from within the same process, saving a lot of process creation
13607 * and teardown overhead.
13608 *
13609 *
13610 * @section sec_kWorker_kSubmit About kSubmit
13611 *
13612 * When wanting to execute a job in a kWorker instance, it must be submitted
13613 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13614 * built into kmk and does not exist as an external program. The reason for
13615 * this is that it keep track of the kWorker instances.
13616 *
13617 * The kSubmit command has the --32-bit and --64-bit options for selecting
13618 * between 32-bit and 64-bit worker instance. We generally assume the user of
13619 * the command knows which bit count the executable has, so kSubmit is spared
13620 * the extra work of finding out.
13621 *
13622 * The kSubmit command shares a environment and current directory manipulation
13623 * with the kRedirect command, but not the file redirection. So long no file
13624 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13625 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13626 * and/or current directory changes to work.
13627 *
13628 * Unlike the kRedirect command, the kSubmit command can also specify an
13629 * internall post command to be executed after the main command succeeds.
13630 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13631 * information from Microsoft COFF object files and Watcom OMF object files and
13632 * is scheduled to replace kDepIDB.
13633 *
13634 *
13635 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13636 *
13637 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13638 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13639 * it. When the job is completed, kWorker writes a short reply with the exit
13640 * code and an internal status indicating whether it is going to restart.
13641 *
13642 * The kWorker intance will reply to kSubmit before completing all the internal
13643 * cleanup work, so as not to delay the next job execution unnecessarily. This
13644 * includes checking its own memory consumption and checking whether it needs
13645 * restarting. So, a decision to restart unfortunately have to wait till after
13646 * the next job has completed. This is a little bit unfortunate if the next job
13647 * requires a lot of memory and kWorker has already leaked/used a lot.
13648 *
13649 *
13650 * @section sec_kWorker_How_Works How kWorker Works
13651 *
13652 * kWorker will load the executable specified by kSubmit into memory and call
13653 * it's entrypoint in a lightly sandbox'ed environment.
13654 *
13655 *
13656 * @subsection ssec_kWorker_Loaing Image loading
13657 *
13658 * kWorker will manually load all the executable images into memory, fix them
13659 * up, and make a copy of the virgin image so it can be restored using memcpy
13660 * the next time it is used.
13661 *
13662 * Imported functions are monitored and replacements used for a few of them.
13663 * These replacements are serve the following purposes:
13664 * - Provide a different command line.
13665 * - Provide a different environment.
13666 * - Intercept process termination.
13667 * - Intercept thread creation (only linker is allowed to create threads).
13668 * - Intercept file reading for caching (header files, ++) as file system
13669 * access is made even slower by anti-virus software.
13670 * - Intercept crypto hash APIs to cache MD5 digests of header files
13671 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13672 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13673 * in memory as writing files grows expensive with encryption and
13674 * anti-virus software active.
13675 * - Intercept some file system queries to use the kFsCache instead of
13676 * going to the kernel and slowly worm thru the AV filter driver.
13677 * - Intercept standard output/error and console writes to aggressivly
13678 * buffer the output. The MS CRT does not buffer either when it goes to
13679 * the console, resulting in terrible performance and mixing up output
13680 * with other compile jobs.
13681 * This also allows us to filter out the annoying source file announcements
13682 * by cl.exe.
13683 * - Intercept VirtualAlloc and VirtualFree to prevent
13684 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13685 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13686 * the callbacks run after each job.
13687 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13688 * executables and tools using custom heaps (like the microsoft linker).
13689 * [exectuable images only]
13690 * - Intercept atexit and _onexit registration to be able run them after
13691 * each job instead of crashing as kWorker exits. This also helps avoid
13692 * some leaks. [executable image only]
13693 *
13694 * DLLs falls into two categories, system DLLs which we always load using the
13695 * native loader, and tool DLLs which can be handled like the executable or
13696 * optionally using the native loader. We maintain a hardcoded white listing of
13697 * tool DLLs we trust to load using the native loader.
13698 *
13699 * Imports of natively loaded DLLs are processed too, but we only replace a
13700 * subset of the functions compared to natively loaded excutable and DLL images.
13701 *
13702 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13703 * This is to speed up job execution.
13704 *
13705 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13706 * for each job run, but so far this hasn't been necessary.
13707 *
13708 *
13709 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
13710 *
13711 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
13712 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
13713 * intermediate representation between the first (c1/c1xx.dll) and second pass
13714 * (c2.dll).
13715 *
13716 * kWorker helps the compiler as best as it can. Given a little knowledge about
13717 * stable and volatile file system areas, it can do a lot of caching that a
13718 * normal compiler driver cannot easily do when given a single file.
13719 *
13720 *
13721 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
13722 *
13723 * The preprocessor part will open and process header files exactly as they are
13724 * encountered in the source files. If string.h is included by the main source
13725 * and five other header files, it will be searched for (include path), opened,
13726 * read, MD5-summed, and pre-processed six times. The last five times is just a
13727 * waste of time because of the guards or \#pragma once. A smart compiler would
13728 * make a little extra effort and realize this.
13729 *
13730 * kWorker will cache help the preprocessor by remembering places where the
13731 * header was not found with help of kFsCache, and cache the file in memory when
13732 * found. The first part is taken care of by intercepting GetFileAttributesW,
13733 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
13734 * cached, the file is kept open and the CreateFileW call returns a duplicate of
13735 * that handle. An internal handle table is used by ReadFile and CloseFile to
13736 * keep track of intercepted handles (also used for temporary file, temporary
13737 * file mappings, console buffering, and standard out/err buffering).
13738 *
13739 * PS. The header search optimization also comes in handy when cl.exe goes on
13740 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
13741 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
13742 * optionally compile the three pass DLLs as executables during development
13743 * and problem analysis.
13744 *
13745 *
13746 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
13747 *
13748 * The issues of the temporary files is pretty severe on the Dell machine used
13749 * for benchmarking with full AV and encryption. The synthetic benchmark
13750 * improved by 30% when kWorker implemented measures to keep them entirely in
13751 * memory.
13752 *
13753 * kWorker implement these by recognizing the filename pattern in CreateFileW
13754 * and creating/opening the given file as needed. The handle returned is a
13755 * duplicate of the current process, thus giving us a good chance of catching
13756 * API calls we're not intercepting.
13757 *
13758 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
13759 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
13760 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
13761 * UnmapViewOfFile.
13762 *
13763 *
13764 * @section sec_kWorker_Numbers Some measurements.
13765 *
13766 * - r2881 building src/VBox/Runtime:
13767 * - without: 2m01.016388s = 120.016388 s
13768 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
13769 * - r2884 building vbox/debug (r110512):
13770 * - without: 11m14.446609s = 674.446609 s
13771 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
13772 * - r2896 building vbox/debug (r110577):
13773 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
13774 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
13775 * MS Defender as AV):
13776 * - without: 10m24.990389s = 624.990389s
13777 * - with: 08m04.738184s = 484.738184s
13778 * - delta: 624.99s - 484.74s = 140.25s
13779 * - saved: 140.25/624.99 = 22% faster
13780 *
13781 *
13782 * @subsection subsec_kWorker_Early_Numbers Early Experiments
13783 *
13784 * These are some early experiments doing 1024 compilations of
13785 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
13786 * main function:
13787 *
13788 * Skylake (W10/amd64, only stdandard MS defender):
13789 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
13790 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
13791 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
13792 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
13793 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
13794 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
13795 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
13796 *
13797 * Dell (W7/amd64, infected by mcafee):
13798 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
13799 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
13800 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
13801 *
13802 * The command line:
13803 * @code{.cpp}
13804 "\"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"
13805 * @endcode
13806 */
13807
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