VirtualBox

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

Last change on this file since 3634 was 3634, checked in by bird, 7 months ago

kWorker: VC++ 2022 build fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 534.3 KB
Line 
1/* $Id: kWorker.c 3634 2024-11-02 01:51:30Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <[email protected]>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33//#undef NDEBUG
34//#define K_STRICT 1
35//#define KW_LOG_ENABLED
36
37#define PSAPI_VERSION 1
38#include <k/kHlp.h>
39#include <k/kLdr.h>
40
41#include <stdio.h>
42#include <intrin.h>
43#include <setjmp.h>
44#include <ctype.h>
45#include <errno.h>
46#include <process.h>
47
48#include "nt/ntstat.h"
49#include "kbuild_version.h"
50
51#include "nt/ntstuff.h"
52#include "nt/nthlp.h"
53#include <psapi.h>
54
55#include "nt/kFsCache.h"
56#include "nt_fullpath.h"
57#include "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_CACHE
73 * Enables caching of MD5, SHA-1, SHA-256 and SHA-512 hashes for cl.exe.
74 * This prevents wasting time on rehashing common headers each time
75 * they are included. */
76#define WITH_HASH_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_CACHE
495 /** Set if we've got a valid MD5 hash in abMd5Digest. */
496 KBOOL fValidMd5;
497 /** Set if we've got a valid SHA-1 hash in abMd5Digest. */
498 KBOOL fValidSha1;
499 /** Set if we've got a valid SHA-256 hash in abMd5Digest. */
500 KBOOL fValidSha256;
501 /** Set if we've got a valid SHA-512 hash in abMd5Digest. */
502 KBOOL fValidSha512;
503 /** The MD5 digest if fValidMd5 is set. */
504 KU8 abMd5Digest[16];
505 /** The SHA-1 digest if fValidSha1 is set. */
506 KU8 abSha1Digest[20];
507 /** The SHA-256 digest if fValidSha256 is set. */
508 KU8 abSha256Digest[32];
509 /** The SHA-512 digest if fValidSha256 is set. */
510 KU8 abSha512Digest[64];
511#endif
512
513 /** Circular self reference. Prevents the object from ever going away and
514 * keeps it handy for debugging. */
515 PKFSOBJ pFsObj;
516 /** The file path (for debugging). */
517 char szPath[1];
518} KFSWCACHEDFILE;
519/** Pointer to a cached filed. */
520typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
521
522#ifdef WITH_HASH_CACHE
523
524/** Pointer to a MD5 hash instance. */
525typedef struct KWCRYPTHASH *PKWCRYPTHASH;
526/**
527 * A MD5 hash instance.
528 */
529typedef struct KWCRYPTHASH
530{
531 /** The magic value. */
532 KUPTR uMagic;
533 /** Pointer to the next hash handle. */
534 PKWCRYPTHASH pNext;
535 /** The cached file we've associated this handle with. */
536 PKFSWCACHEDFILE pCachedFile;
537 /** The number of bytes we've hashed. */
538 KU32 cbHashed;
539 /** Set if this has gone wrong. */
540 KBOOL fGoneBad;
541 /** Set if we've already finalized the digest. */
542 KBOOL fFinal;
543 /** If in fallback mode. */
544 HCRYPTHASH hFallback;
545 /** The algorithm. */
546 ALG_ID idAlg;
547 /** The hash name. */
548 const char *pszAlgName;
549 /** The digest size. */
550 KU32 cbDigest;
551 /** The finalized digest. */
552 KU8 abDigest[64];
553} KWCRYPTHASH;
554/** Magic value for KWCRYPTHASH::uMagic (Les McCann). */
555# define KWCRYPTHASH_MAGIC KUPTR_C(0x19350923)
556
557#endif /* WITH_HASH_CACHE */
558#ifdef WITH_TEMP_MEMORY_FILES
559
560typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
561typedef struct KWFSTEMPFILESEG
562{
563 /** File offset of data. */
564 KU32 offData;
565 /** The size of the buffer pbData points to. */
566 KU32 cbDataAlloc;
567 /** The segment data. */
568 KU8 *pbData;
569} KWFSTEMPFILESEG;
570
571typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
572typedef struct KWFSTEMPFILE
573{
574 /** Pointer to the next temporary file for this run. */
575 PKWFSTEMPFILE pNext;
576 /** The UTF-16 path. (Allocated after this structure.) */
577 const wchar_t *pwszPath;
578 /** The path length. */
579 KU16 cwcPath;
580 /** Number of active handles using this file/mapping (<= 2). */
581 KU8 cActiveHandles;
582 /** Number of active mappings (mapped views) (0 or 1). */
583 KU8 cMappings;
584 /** The amount of space allocated in the segments. */
585 KU32 cbFileAllocated;
586 /** The current file size. */
587 KU32 cbFile;
588 /** The number of segments. */
589 KU32 cSegs;
590 /** Segments making up the file. */
591 PKWFSTEMPFILESEG paSegs;
592} KWFSTEMPFILE;
593
594#endif /* WITH_TEMP_MEMORY_FILES */
595#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
596
597/**
598 * Console line buffer or output full buffer.
599 */
600typedef struct KWOUTPUTSTREAMBUF
601{
602 /** The main output handle. */
603 HANDLE hOutput;
604 /** Our backup handle. */
605 HANDLE hBackup;
606 /** Set if this is a console handle and we're in line buffered mode.
607 * When clear, we may buffer multiple lines, though try flush on line
608 * boundraries when ever possible. */
609 KBOOL fIsConsole;
610 /** Compressed GetFileType result. */
611 KU8 fFileType;
612 KU8 abPadding[2];
613 union
614 {
615 /** Line buffer mode (fIsConsole == K_TRUE). */
616 struct
617 {
618 /** Amount of pending console output in wchar_t's. */
619 KU32 cwcBuf;
620 /** The allocated buffer size. */
621 KU32 cwcBufAlloc;
622 /** Pending console output. */
623 wchar_t *pwcBuf;
624 } Con;
625 /** Fully buffered mode (fIsConsole == K_FALSE). */
626 struct
627 {
628 /** Amount of pending output (in chars). */
629 KU32 cchBuf;
630#ifdef WITH_STD_OUT_ERR_BUFFERING
631 /** The allocated buffer size (in chars). */
632 KU32 cchBufAlloc;
633 /** Pending output. */
634 char *pchBuf;
635#endif
636 } Fully;
637 } u;
638} KWOUTPUTSTREAMBUF;
639/** Pointer to a console line buffer. */
640typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
641
642/**
643 * Combined console buffer of complete lines.
644 */
645typedef struct KWCONSOLEOUTPUT
646{
647 /** The console output handle.
648 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
649 * combined output buffering. */
650 HANDLE hOutput;
651 /** The current code page for the console. */
652 KU32 uCodepage;
653 /** Amount of pending console output in wchar_t's. */
654 KU32 cwcBuf;
655 /** Number of times we've flushed it in any way (for cl.exe hack). */
656 KU32 cFlushes;
657 /** Pending console output. */
658 wchar_t wszBuf[8192];
659} KWCONSOLEOUTPUT;
660/** Pointer to a combined console buffer. */
661typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
662
663#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
664
665/** Handle type. */
666typedef enum KWHANDLETYPE
667{
668 KWHANDLETYPE_INVALID = 0,
669 KWHANDLETYPE_FSOBJ_READ_CACHE,
670 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
671#ifdef WITH_TEMP_MEMORY_FILES
672 KWHANDLETYPE_TEMP_FILE,
673 KWHANDLETYPE_TEMP_FILE_MAPPING,
674#endif
675 KWHANDLETYPE_OUTPUT_BUF
676} KWHANDLETYPE;
677
678/** Handle data. */
679typedef struct KWHANDLE
680{
681 KWHANDLETYPE enmType;
682 /** Number of references */
683 KU32 cRefs;
684 /** The current file offset. */
685 KU32 offFile;
686 /** Handle access. */
687 KU32 dwDesiredAccess;
688 /** The handle. */
689 HANDLE hHandle;
690 /** The current owner (GetCurrentThreadId). */
691 KU32 tidOwner;
692
693 /** Type specific data. */
694 union
695 {
696 /** The file system object. */
697 PKFSWCACHEDFILE pCachedFile;
698#ifdef WITH_TEMP_MEMORY_FILES
699 /** Temporary file handle or mapping handle. */
700 PKWFSTEMPFILE pTempFile;
701#endif
702#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
703 /** Buffered output stream. */
704 PKWOUTPUTSTREAMBUF pOutBuf;
705#endif
706 } u;
707} KWHANDLE;
708typedef KWHANDLE *PKWHANDLE;
709
710/**
711 * Tracking one of our memory mappings.
712 */
713typedef struct KWMEMMAPPING
714{
715 /** Number of references. */
716 KU32 cRefs;
717 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
718 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
719 KWHANDLETYPE enmType;
720 /** The mapping address. */
721 PVOID pvMapping;
722 /** Type specific data. */
723 union
724 {
725 /** The file system object. */
726 PKFSWCACHEDFILE pCachedFile;
727#ifdef WITH_TEMP_MEMORY_FILES
728 /** Temporary file handle or mapping handle. */
729 PKWFSTEMPFILE pTempFile;
730#endif
731 } u;
732} KWMEMMAPPING;
733/** Pointer to a memory mapping tracker. */
734typedef KWMEMMAPPING *PKWMEMMAPPING;
735
736
737/** Pointer to a VirtualAlloc tracker entry. */
738typedef struct KWVIRTALLOC *PKWVIRTALLOC;
739/**
740 * Tracking an VirtualAlloc allocation.
741 */
742typedef struct KWVIRTALLOC
743{
744 PKWVIRTALLOC pNext;
745 void *pvAlloc;
746 KSIZE cbAlloc;
747 /** This is KU32_MAX if not a preallocated chunk. */
748 KU32 idxPreAllocated;
749} KWVIRTALLOC;
750
751
752/** Pointer to a heap (HeapCreate) tracker entry. */
753typedef struct KWHEAP *PKWHEAP;
754/**
755 * Tracking an heap (HeapCreate)
756 */
757typedef struct KWHEAP
758{
759 PKWHEAP pNext;
760 HANDLE hHeap;
761} KWHEAP;
762
763
764/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
765typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
766/**
767 * Tracking an FlsAlloc/TlsAlloc index.
768 */
769typedef struct KWLOCALSTORAGE
770{
771 PKWLOCALSTORAGE pNext;
772 KU32 idx;
773} KWLOCALSTORAGE;
774
775
776/** Pointer to an at exit callback record */
777typedef struct KWEXITCALLACK *PKWEXITCALLACK;
778/**
779 * At exit callback record.
780 */
781typedef struct KWEXITCALLACK
782{
783 PKWEXITCALLACK pNext;
784 _onexit_t pfnCallback;
785 /** At exit doesn't have an exit code. */
786 KBOOL fAtExit;
787} KWEXITCALLACK;
788
789
790typedef enum KWTOOLTYPE
791{
792 KWTOOLTYPE_INVALID = 0,
793 KWTOOLTYPE_SANDBOXED,
794 KWTOOLTYPE_WATCOM,
795 KWTOOLTYPE_EXEC,
796 KWTOOLTYPE_END
797} KWTOOLTYPE;
798
799typedef enum KWTOOLHINT
800{
801 KWTOOLHINT_INVALID = 0,
802 KWTOOLHINT_NONE,
803 KWTOOLHINT_VISUAL_CPP_CL,
804 KWTOOLHINT_VISUAL_CPP_LINK,
805 KWTOOLHINT_END
806} KWTOOLHINT;
807
808
809/**
810 * A kWorker tool.
811 */
812typedef struct KWTOOL
813{
814 /** The user data core structure. */
815 KFSUSERDATA Core;
816
817 /** The normalized path to the program. */
818 const char *pszPath;
819 /** UTF-16 version of pszPath. */
820 wchar_t const *pwszPath;
821 /** The kind of tool. */
822 KWTOOLTYPE enmType;
823
824 union
825 {
826 struct
827 {
828 /** The main entry point. */
829 KUPTR uMainAddr;
830 /** The executable. */
831 PKWMODULE pExe;
832 /** List of dynamically loaded modules.
833 * These will be kept loaded till the tool is destroyed (if we ever do that). */
834 PKWDYNLOAD pDynLoadHead;
835 /** Module array sorted by hOurMod. */
836 PKWMODULE *papModules;
837 /** Number of entries in papModules. */
838 KU32 cModules;
839
840 /** Tool hint (for hacks and such). */
841 KWTOOLHINT enmHint;
842 } Sandboxed;
843 } u;
844} KWTOOL;
845/** Pointer to a tool. */
846typedef struct KWTOOL *PKWTOOL;
847
848
849typedef struct KWSANDBOX *PKWSANDBOX;
850typedef struct KWSANDBOX
851{
852 /** Jump buffer (first for alignment reasons). */
853 jmp_buf JmpBuf;
854 /** The tool currently running in the sandbox. */
855 PKWTOOL pTool;
856 /** The thread ID of the main thread (owner of JmpBuf). */
857 DWORD idMainThread;
858 /** Copy of the NT TIB of the main thread. */
859 NT_TIB TibMainThread;
860 /** The NT_TIB::ExceptionList value inside the try case.
861 * We restore this prior to the longjmp. */
862 void *pOutXcptListHead;
863 /** The exit code in case of longjmp. */
864 int rcExitCode;
865 /** Set if we're running. */
866 KBOOL fRunning;
867 /** Whether to disable caching of ".pch" files. */
868 KBOOL fNoPchCaching;
869
870 /** The command line. */
871 char *pszCmdLine;
872 /** The UTF-16 command line. */
873 wchar_t *pwszCmdLine;
874 /** Number of arguments in papszArgs. */
875 int cArgs;
876 /** The argument vector. */
877 char **papszArgs;
878 /** The argument vector. */
879 wchar_t **papwszArgs;
880
881 /** The _pgmptr msvcrt variable. */
882 char *pgmptr;
883 /** The _wpgmptr msvcrt variable. */
884 wchar_t *wpgmptr;
885
886 /** The _initenv msvcrt variable. */
887 char **initenv;
888 /** The _winitenv msvcrt variable. */
889 wchar_t **winitenv;
890
891 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
892 KSIZE cEnvVarsAllocated;
893 /** The _environ msvcrt variable. */
894 char **environC;
895 /** The _wenviron msvcrt variable. */
896 wchar_t **wenvironC;
897 /** The shadow _environ msvcrt variable. */
898 char **papszEnvVars;
899 /** The shadow _wenviron msvcrt variable. */
900 wchar_t **papwszEnvVars;
901
902
903 /** Critical section protecting the below handle members below.
904 * @note Does not protect the individual handles. */
905 CRITICAL_SECTION HandlesLock;
906 /** Handle table. */
907 PKWHANDLE *papHandles;
908 /** Size of the handle table. */
909 KU32 cHandles;
910 /** Number of active handles in the table. */
911 KU32 cActiveHandles;
912 /** Number of handles in the handle table that will not be freed. */
913 KU32 cFixedHandles;
914 /** Total number of leaked handles. */
915 KU32 cLeakedHandles;
916
917 /** Number of active memory mappings in paMemMappings. */
918 KU32 cMemMappings;
919 /** The allocated size of paMemMappings. */
920 KU32 cMemMappingsAlloc;
921 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
922 PKWMEMMAPPING paMemMappings;
923
924#ifdef WITH_TEMP_MEMORY_FILES
925 /** Head of the list of temporary file. */
926 PKWFSTEMPFILE pTempFileHead;
927#endif
928
929 /** Critical section protecting pVirtualAllocHead. */
930 CRITICAL_SECTION VirtualAllocLock;
931 /** Head of the virtual alloc allocations. */
932 PKWVIRTALLOC pVirtualAllocHead;
933 /** Head of the heap list (HeapCreate).
934 * This is only done from images we forcibly restore. */
935 PKWHEAP pHeapHead;
936 /** Head of the FlsAlloc indexes. */
937 PKWLOCALSTORAGE pFlsAllocHead;
938 /** Head of the TlsAlloc indexes. */
939 PKWLOCALSTORAGE pTlsAllocHead;
940
941 /** The at exit callback head.
942 * This is only done from images we forcibly restore. */
943 PKWEXITCALLACK pExitCallbackHead;
944
945 MY_UNICODE_STRING SavedCommandLine;
946
947#ifdef WITH_HASH_CACHE
948 /** The crypto provider instance we use for hashes. */
949 HCRYPTPROV hCryptProvRsa;
950 /** The crypto provider instance we use for hashes. */
951 HCRYPTPROV hCryptProvAes;
952 /** List of crypto hash instances. */
953 PKWCRYPTHASH pHashHead;
954 /** ReadFile sets these while CryptHashData claims and clears them.
955 *
956 * This is part of the heuristics we use for MD5/SHA1/SHA256 caching for header
957 * files. The observed pattern is that c1.dll/c1xx.dll first reads a chunk of a
958 * source or header, then passes the same buffer and read byte count to
959 * CryptHashData.
960 */
961 struct
962 {
963 /** The cached file last read from. */
964 PKFSWCACHEDFILE pCachedFile;
965 /** The file offset of the last cached read. */
966 KU32 offRead;
967 /** The number of bytes read last. */
968 KU32 cbRead;
969 /** The buffer pointer of the last read. */
970 void *pvRead;
971 } LastHashRead;
972#endif
973
974#ifdef WITH_CRYPT_CTX_REUSE
975 /** Reusable crypt contexts. */
976 struct
977 {
978 /** The creation provider type. */
979 KU32 dwProvType;
980 /** The creation flags. */
981 KU32 dwFlags;
982 /** The length of the container name. */
983 KU32 cwcContainer;
984 /** The length of the provider name. */
985 KU32 cwcProvider;
986 /** The container name string. */
987 wchar_t *pwszContainer;
988 /** The provider name string. */
989 wchar_t *pwszProvider;
990 /** The context handle. */
991 HCRYPTPROV hProv;
992 } aCryptCtxs[4];
993 /** Number of reusable crypt conexts in aCryptCtxs. */
994 KU32 cCryptCtxs;
995#endif
996
997
998#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
999 /** The internal standard output handle. */
1000 KWHANDLE HandleStdOut;
1001 /** The internal standard error handle. */
1002 KWHANDLE HandleStdErr;
1003 /** Standard output (and whatever else) buffer. */
1004 KWOUTPUTSTREAMBUF StdOut;
1005 /** Standard error buffer. */
1006 KWOUTPUTSTREAMBUF StdErr;
1007 /** Combined buffer of completed lines. */
1008 KWCONSOLEOUTPUT Combined;
1009#endif
1010} KWSANDBOX;
1011
1012
1013/** A CRT slot. */
1014typedef struct KWCRTSLOT
1015{
1016 KU32 iSlot;
1017
1018 /** The CRT module data. */
1019 PKWMODULE pModule;
1020 /** Pointer to the malloc function. */
1021 void * (__cdecl *pfnMalloc)(size_t);
1022 /** Pointer to the beginthreadex function. */
1023 uintptr_t (__cdecl *pfnBeginThreadEx)(void *, unsigned, unsigned (__stdcall *)(void *), void *, unsigned, unsigned *);
1024
1025} KWCRTSLOT;
1026typedef KWCRTSLOT *PKWCRTSLOT;
1027
1028
1029/** Replacement function entry. */
1030typedef struct KWREPLACEMENTFUNCTION
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 /** The replacement function, data address or CRT slot function array. */
1039 KUPTR pfnReplacement;
1040 /** Only replace in the executable.
1041 * @todo fix the reinitialization of non-native DLLs! */
1042 KBOOL fOnlyExe;
1043 /** Set if pfnReplacement points to a CRT slot function array. */
1044 KBOOL fCrtSlotArray;
1045} KWREPLACEMENTFUNCTION;
1046typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
1047
1048#if 0
1049/** Replacement function entry. */
1050typedef struct KWREPLACEMENTDATA
1051{
1052 /** The function name. */
1053 const char *pszFunction;
1054 /** The length of the function name. */
1055 KSIZE cchFunction;
1056 /** The module name (optional). */
1057 const char *pszModule;
1058 /** Function providing the replacement. */
1059 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
1060} KWREPLACEMENTDATA;
1061typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
1062#endif
1063
1064/**
1065 * One test job (--full-test).
1066 */
1067typedef struct KWONETEST
1068{
1069 /** Where this job originated. */
1070 const char *pszJobSrc;
1071 /** The argument number it started with. */
1072 unsigned iJobSrc;
1073 /** Set if virgin, clear if modified. */
1074 KBOOL fVirgin;
1075
1076 /** Number of runs to give it. */
1077 unsigned cRuns;
1078
1079 /** @name kSubmitHandleJobUnpacked arguments
1080 * @{ */
1081 const char *pszExecutable;
1082 const char *pszCwd;
1083 KU32 cArgs;
1084 const char **papszArgs;
1085 KU32 cEnvVars;
1086 const char **papszEnvVars;
1087 const char *pszSpecialEnv;
1088 KBOOL fWatcomBrainDamange;
1089 KBOOL fNoPchCaching;
1090 KU32 cPostCmdArgs;
1091 const char **papszPostCmdArgs;
1092 /** @} */
1093
1094 /** Pointer to the next one. */
1095 struct KWONETEST *pNext;
1096} KWONETEST;
1097/** Pointer to one test job. */
1098typedef KWONETEST *PKWONETEST;
1099
1100
1101/*********************************************************************************************************************************
1102* Global Variables *
1103*********************************************************************************************************************************/
1104/** The sandbox data. */
1105static KWSANDBOX g_Sandbox;
1106
1107/** The module currently occupying g_abDefLdBuf. */
1108static PKWMODULE g_pModInLdBuf = NULL;
1109
1110/** The module that previuosly occupied g_abDefLdBuf. */
1111static PKWMODULE g_pModPrevInLdBuf = NULL;
1112
1113/** Module list head. */
1114static PKWMODULE g_pModuleHead = NULL;
1115/** Where to insert the next module. */
1116static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1117
1118/** Module hash table. */
1119static PKWMODULE g_apModules[127];
1120
1121/** GetModuleHandle cache. */
1122static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1123{
1124#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1125 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1126#if 1
1127 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1128 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1129#endif
1130 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1131};
1132
1133/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1134static PKWMODULE g_pModPendingTlsAlloc = NULL;
1135
1136/** The 1KB TLS DLLs. */
1137static KWTLSDLL g_aTls1KDlls[] =
1138{
1139 { L"kWorkerTls1K.dll", K_FALSE },
1140 { L"kWorkerTls1K01.dll", K_FALSE },
1141 { L"kWorkerTls1K02.dll", K_FALSE },
1142 { L"kWorkerTls1K03.dll", K_FALSE },
1143 { L"kWorkerTls1K04.dll", K_FALSE },
1144 { L"kWorkerTls1K05.dll", K_FALSE },
1145 { L"kWorkerTls1K06.dll", K_FALSE },
1146 { L"kWorkerTls1K07.dll", K_FALSE },
1147 { L"kWorkerTls1K08.dll", K_FALSE },
1148 { L"kWorkerTls1K09.dll", K_FALSE },
1149 { L"kWorkerTls1K10.dll", K_FALSE },
1150 { L"kWorkerTls1K11.dll", K_FALSE },
1151 { L"kWorkerTls1K12.dll", K_FALSE },
1152 { L"kWorkerTls1K13.dll", K_FALSE },
1153 { L"kWorkerTls1K14.dll", K_FALSE },
1154 { L"kWorkerTls1K15.dll", K_FALSE },
1155};
1156
1157/** The 64KB TLS DLLs. */
1158static KWTLSDLL g_aTls64KDlls[] =
1159{
1160 { L"kWorkerTls64K.dll", K_FALSE },
1161 { L"kWorkerTls64K01.dll", K_FALSE },
1162 { L"kWorkerTls64K02.dll", K_FALSE },
1163 { L"kWorkerTls64K03.dll", K_FALSE },
1164 { L"kWorkerTls64K04.dll", K_FALSE },
1165 { L"kWorkerTls64K05.dll", K_FALSE },
1166 { L"kWorkerTls64K06.dll", K_FALSE },
1167 { L"kWorkerTls64K07.dll", K_FALSE },
1168};
1169
1170/** The 128KB TLS DLLs. */
1171static KWTLSDLL g_aTls128KDlls[] =
1172{
1173 { L"kWorkerTls128K.dll", K_FALSE },
1174 { L"kWorkerTls128K01.dll", K_FALSE },
1175 { L"kWorkerTls128K02.dll", K_FALSE },
1176 { L"kWorkerTls128K03.dll", K_FALSE },
1177 { L"kWorkerTls128K04.dll", K_FALSE },
1178 { L"kWorkerTls128K05.dll", K_FALSE },
1179 { L"kWorkerTls128K06.dll", K_FALSE },
1180 { L"kWorkerTls128K07.dll", K_FALSE },
1181};
1182
1183/** The 512KB TLS DLLs. */
1184static KWTLSDLL g_aTls512KDlls[] =
1185{
1186 { L"kWorkerTls512K.dll", K_FALSE },
1187 { L"kWorkerTls512K01.dll", K_FALSE },
1188 { L"kWorkerTls512K02.dll", K_FALSE },
1189 { L"kWorkerTls512K03.dll", K_FALSE },
1190 { L"kWorkerTls512K04.dll", K_FALSE },
1191 { L"kWorkerTls512K05.dll", K_FALSE },
1192 { L"kWorkerTls512K06.dll", K_FALSE },
1193 { L"kWorkerTls512K07.dll", K_FALSE },
1194};
1195
1196/** The TLS DLLs grouped by size. */
1197static KWTLSDLLENTRY const g_aTlsDlls[] =
1198{
1199 { 1024, K_ELEMENTS(g_aTls1KDlls), g_aTls1KDlls },
1200 { 64*1024, K_ELEMENTS(g_aTls64KDlls), g_aTls64KDlls },
1201 { 128*1024, K_ELEMENTS(g_aTls128KDlls), g_aTls128KDlls },
1202 { 512*1024, K_ELEMENTS(g_aTls512KDlls), g_aTls512KDlls },
1203};
1204
1205/** CRT slots.
1206 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1207static KWCRTSLOT g_aCrtSlots[32];
1208
1209/** windbg .reload statements. vs */
1210char g_szReloads[4096];
1211/** Current offset into g_szReloads. */
1212KU32 volatile g_cchReloads;
1213
1214/** The file system cache. */
1215static PKFSCACHE g_pFsCache;
1216/** The current directory (referenced). */
1217static PKFSOBJ g_pCurDirObj = NULL;
1218#ifdef KBUILD_OS_WINDOWS
1219/** The windows system32 directory (referenced). */
1220static PKFSDIR g_pWinSys32 = NULL;
1221#endif
1222
1223/** Verbosity level. */
1224static int g_cVerbose = 2;
1225
1226/** Whether we should restart the worker. */
1227static KBOOL g_fRestart = K_FALSE;
1228
1229/** The process group this worker is tied to (--group option), -1 if none. */
1230static KI32 g_iProcessGroup = -1;
1231
1232/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1233static int volatile g_rcCtrlC = 0;
1234
1235/** The communication pipe handle. We break this when we see Ctrl-C such. */
1236#ifdef KBUILD_OS_WINDOWS
1237static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1238#else
1239static int g_hPipe = -1;
1240#endif
1241
1242
1243/* Further down. */
1244extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1245extern KU32 const g_cSandboxReplacements;
1246
1247extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1248extern KU32 const g_cSandboxNativeReplacements;
1249
1250extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1251extern KU32 const g_cSandboxGetProcReplacements;
1252
1253
1254/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1255 * cover the default executable link address of 0x400000.
1256 * @remarks Early main() makes it read+write+executable. Attempts as having
1257 * it as a separate section failed because the linker insists on
1258 * writing out every zero in the uninitialized section, resulting in
1259 * really big binaries. */
1260__declspec(align(0x1000))
1261static KU8 g_abDefLdBuf[16*1024*1024];
1262
1263#ifdef WITH_LOG_FILE
1264/** Log file handle. */
1265static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1266#endif
1267
1268
1269#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1270/** Virtual address space reserved for CL.EXE heap manager.
1271 *
1272 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1273 * address. It's among other things used for precompiled headers, which
1274 * seemingly have addresses hardcoded into them and won't work if mapped
1275 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1276 * for it. (The /Zm option may affect this allocation.)
1277 */
1278static struct
1279{
1280 /** The memory address we need. */
1281 KUPTR const uFixed;
1282 /** How much we need to fix. */
1283 KSIZE const cbFixed;
1284 /** What we actually got, NULL if given back. */
1285 void *pvReserved;
1286 /** Whether it is in use or not. */
1287 KBOOL fInUse;
1288} g_aFixedVirtualAllocs[] =
1289{
1290# if K_ARCH == K_ARCH_X86_32
1291 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1292 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1293 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1294# else
1295 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1296# endif
1297};
1298#endif
1299
1300
1301#ifdef WITH_HISTORY
1302/** The job history. */
1303static char *g_apszHistory[32];
1304/** Index of the next history entry. */
1305static unsigned g_iHistoryNext = 0;
1306#endif
1307
1308
1309/** Number of jobs executed. */
1310static KU32 g_cJobs;
1311/** Number of tools. */
1312static KU32 g_cTools;
1313/** Number of modules. */
1314static KU32 g_cModules;
1315/** Number of non-native modules. */
1316static KU32 g_cNonNativeModules;
1317/** Number of read-cached files. */
1318static KU32 g_cReadCachedFiles;
1319/** Total size of read-cached files. */
1320static KSIZE g_cbReadCachedFiles;
1321
1322/** Total number of ReadFile calls. */
1323static KSIZE g_cReadFileCalls;
1324/** Total bytes read via ReadFile. */
1325static KSIZE g_cbReadFileTotal;
1326/** Total number of read from read-cached files. */
1327static KSIZE g_cReadFileFromReadCached;
1328/** Total bytes read from read-cached files. */
1329static KSIZE g_cbReadFileFromReadCached;
1330/** Total number of read from in-memory temporary files. */
1331static KSIZE g_cReadFileFromInMemTemp;
1332/** Total bytes read from in-memory temporary files. */
1333static KSIZE g_cbReadFileFromInMemTemp;
1334
1335/** Total number of WriteFile calls. */
1336static KSIZE g_cWriteFileCalls;
1337/** Total bytes written via WriteFile. */
1338static KSIZE g_cbWriteFileTotal;
1339/** Total number of written to from in-memory temporary files. */
1340static KSIZE g_cWriteFileToInMemTemp;
1341/** Total bytes written to in-memory temporary files. */
1342static KSIZE g_cbWriteFileToInMemTemp;
1343
1344#ifdef WITH_HASH_CACHE
1345/** Total number of hashes. */
1346static KSIZE g_cHashes;
1347/** Number of cached hash hits. */
1348static KSIZE g_cHashesCached;
1349/** Number of fallbacks. */
1350static KSIZE g_cHashesFallbacks;
1351/** Number of partial cached file hashes. */
1352static KSIZE g_cHashesPartial;
1353/** Total number of MD5 hashes. */
1354static KSIZE g_cHashesMd5;
1355/** Total number of SHA-1 hashes. */
1356static KSIZE g_cHashesSha1;
1357/** Total number of SHA-256 hashes. */
1358static KSIZE g_cHashesSha256;
1359/** Total number of SHA-512 hashes. */
1360static KSIZE g_cHashesSha512;
1361#endif
1362
1363
1364/*********************************************************************************************************************************
1365* Internal Functions *
1366*********************************************************************************************************************************/
1367static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1368static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1369 const char *pszSearchPath, PKWMODULE *ppMod);
1370static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1371static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName);
1372static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule);
1373static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod);
1374static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1375static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1376static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile);
1377static PKWHANDLE kwSandboxHandleGet(HANDLE hFile);
1378K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle);
1379#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1380static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1381#endif
1382static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1383
1384
1385
1386
1387/**
1388 * Debug printing.
1389 * @param pszFormat Debug format string.
1390 * @param ... Format argument.
1391 */
1392static void kwDbgPrintfV(const char *pszFormat, va_list va)
1393{
1394 if (g_cVerbose >= 2)
1395 {
1396 DWORD const dwSavedErr = GetLastError();
1397#ifdef WITH_LOG_FILE
1398 DWORD dwIgnored;
1399 char szTmp[2048];
1400 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1401 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1402 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1403 cch += cchPrefix;
1404 else
1405 {
1406 cch = sizeof(szTmp) - 1;
1407 szTmp[cch] = '\0';
1408 }
1409
1410 if (g_hLogFile == INVALID_HANDLE_VALUE)
1411 {
1412 wchar_t wszFilename[128];
1413 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1414 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1415 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1416 }
1417
1418 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1419#else
1420 fprintf(stderr, "debug: ");
1421 vfprintf(stderr, pszFormat, va);
1422#endif
1423
1424 SetLastError(dwSavedErr);
1425 }
1426}
1427
1428
1429/**
1430 * Debug printing.
1431 * @param pszFormat Debug format string.
1432 * @param ... Format argument.
1433 */
1434static void kwDbgPrintf(const char *pszFormat, ...)
1435{
1436 if (g_cVerbose >= 2)
1437 {
1438 va_list va;
1439 va_start(va, pszFormat);
1440 kwDbgPrintfV(pszFormat, va);
1441 va_end(va);
1442 }
1443}
1444
1445
1446/**
1447 * Debugger printing.
1448 * @param pszFormat Debug format string.
1449 * @param ... Format argument.
1450 */
1451static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1452{
1453 if (IsDebuggerPresent())
1454 {
1455 DWORD const dwSavedErr = GetLastError();
1456 char szTmp[2048];
1457
1458 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1459 OutputDebugStringA(szTmp);
1460
1461 SetLastError(dwSavedErr);
1462 }
1463}
1464
1465
1466/**
1467 * Debugger printing.
1468 * @param pszFormat Debug format string.
1469 * @param ... Format argument.
1470 */
1471static void kwDebuggerPrintf(const char *pszFormat, ...)
1472{
1473 va_list va;
1474 va_start(va, pszFormat);
1475 kwDebuggerPrintfV(pszFormat, va);
1476 va_end(va);
1477}
1478
1479
1480
1481/**
1482 * Error printing.
1483 * @param pszFormat Message format string.
1484 * @param ... Format argument.
1485 */
1486static void kwErrPrintfV(const char *pszFormat, va_list va)
1487{
1488 DWORD const dwSavedErr = GetLastError();
1489
1490#if defined(KW_LOG_ENABLED) && defined(WITH_LOG_FILE)
1491 va_list vaCopy;
1492# if defined(va_copy) || !defined(_MSC_VER) || _MSC_VER >= 1700 /*??*/
1493 va_copy(vaCopy, va);
1494# else
1495 vaCopy = va;
1496# endif
1497 kwDebuggerPrintf("kWorker: error: ");
1498 kwDebuggerPrintfV(pszFormat, vaCopy);
1499#endif
1500
1501 fprintf(stderr, "kWorker: error: ");
1502 vfprintf(stderr, pszFormat, va);
1503 fflush(stderr); /* In case it's a pipe. */
1504
1505 SetLastError(dwSavedErr);
1506}
1507
1508
1509/**
1510 * Error printing.
1511 * @param pszFormat Message format string.
1512 * @param ... Format argument.
1513 */
1514static void kwErrPrintf(const char *pszFormat, ...)
1515{
1516 va_list va;
1517 va_start(va, pszFormat);
1518 kwErrPrintfV(pszFormat, va);
1519 va_end(va);
1520}
1521
1522
1523/**
1524 * Error printing.
1525 * @return rc;
1526 * @param rc Return value
1527 * @param pszFormat Message format string.
1528 * @param ... Format argument.
1529 */
1530static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1531{
1532 va_list va;
1533 va_start(va, pszFormat);
1534 kwErrPrintfV(pszFormat, va);
1535 va_end(va);
1536 return rc;
1537}
1538
1539
1540#ifdef K_STRICT
1541
1542KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1543{
1544 DWORD const dwSavedErr = GetLastError();
1545
1546 fprintf(stderr,
1547 "\n"
1548 "!!Assertion failed!!\n"
1549 "Expression: %s\n"
1550 "Function : %s\n"
1551 "File: %s\n"
1552 "Line: %d\n"
1553 , pszExpr, pszFunction, pszFile, iLine);
1554
1555 SetLastError(dwSavedErr);
1556}
1557
1558
1559KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1560{
1561 DWORD const dwSavedErr = GetLastError();
1562 va_list va;
1563
1564 va_start(va, pszFormat);
1565 fprintf(stderr, pszFormat, va);
1566 va_end(va);
1567
1568 SetLastError(dwSavedErr);
1569}
1570
1571#endif /* K_STRICT */
1572
1573
1574/**
1575 * Hashes a string.
1576 *
1577 * @returns 32-bit string hash.
1578 * @param pszString String to hash.
1579 */
1580static KU32 kwStrHash(const char *pszString)
1581{
1582 /* This algorithm was created for sdbm (a public-domain reimplementation of
1583 ndbm) database library. it was found to do well in scrambling bits,
1584 causing better distribution of the keys and fewer splits. it also happens
1585 to be a good general hashing function with good distribution. the actual
1586 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1587 is the faster version used in gawk. [there is even a faster, duff-device
1588 version] the magic constant 65599 was picked out of thin air while
1589 experimenting with different constants, and turns out to be a prime.
1590 this is one of the algorithms used in berkeley db (see sleepycat) and
1591 elsewhere. */
1592 KU32 uHash = 0;
1593 KU32 uChar;
1594 while ((uChar = (unsigned char)*pszString++) != 0)
1595 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1596 return uHash;
1597}
1598
1599
1600/**
1601 * Hashes a string.
1602 *
1603 * @returns The string length.
1604 * @param pszString String to hash.
1605 * @param puHash Where to return the 32-bit string hash.
1606 */
1607static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1608{
1609 const char * const pszStart = pszString;
1610 KU32 uHash = 0;
1611 KU32 uChar;
1612 while ((uChar = (unsigned char)*pszString) != 0)
1613 {
1614 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1615 pszString++;
1616 }
1617 *puHash = uHash;
1618 return pszString - pszStart;
1619}
1620
1621
1622/**
1623 * Hashes a string.
1624 *
1625 * @returns The string length in wchar_t units.
1626 * @param pwszString String to hash.
1627 * @param puHash Where to return the 32-bit string hash.
1628 */
1629static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1630{
1631 const wchar_t * const pwszStart = pwszString;
1632 KU32 uHash = 0;
1633 KU32 uChar;
1634 while ((uChar = *pwszString) != 0)
1635 {
1636 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1637 pwszString++;
1638 }
1639 *puHash = uHash;
1640 return pwszString - pwszStart;
1641}
1642
1643
1644/**
1645 * Converts the given string to unicode.
1646 *
1647 * @returns Length of the resulting string in wchar_t's.
1648 * @param pszSrc The source string.
1649 * @param pwszDst The destination buffer.
1650 * @param cwcDst The size of the destination buffer in wchar_t's.
1651 */
1652static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1653{
1654 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1655 KSIZE offDst = 0;
1656 while (offDst < cwcDst)
1657 {
1658 char ch = *pszSrc++;
1659 pwszDst[offDst++] = ch;
1660 if (!ch)
1661 return offDst - 1;
1662 kHlpAssert((unsigned)ch < 127);
1663 }
1664
1665 pwszDst[offDst - 1] = '\0';
1666 return offDst;
1667}
1668
1669
1670/**
1671 * Converts the given string to UTF-16, allocating the buffer.
1672 *
1673 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1674 * the source string.
1675 * @param pchSrc The source string.
1676 * @param cchSrc The length of the source string.
1677 */
1678static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1679{
1680 DWORD const dwErrSaved = GetLastError();
1681 KSIZE cwcBuf = cchSrc + 1;
1682 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1683 if (pwszBuf)
1684 {
1685 if (cchSrc > 0)
1686 {
1687 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1688 if (cwcRet > 0)
1689 {
1690 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1691 pwszBuf[cwcRet] = '\0';
1692 }
1693 else
1694 {
1695 kHlpFree(pwszBuf);
1696
1697 /* Figure the length and allocate the right buffer size. */
1698 SetLastError(NO_ERROR);
1699 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1700 if (cwcRet)
1701 {
1702 cwcBuf = cwcRet + 2;
1703 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1704 if (pwszBuf)
1705 {
1706 SetLastError(NO_ERROR);
1707 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1708 if (cwcRet)
1709 {
1710 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1711 pwszBuf[cwcRet] = '\0';
1712 }
1713 else
1714 {
1715 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1716 kHlpFree(pwszBuf);
1717 pwszBuf = NULL;
1718 }
1719 }
1720 }
1721 else
1722 {
1723 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1724 pwszBuf = NULL;
1725 }
1726 }
1727 }
1728 else
1729 pwszBuf[0] = '\0';
1730 }
1731 SetLastError(dwErrSaved);
1732 return pwszBuf;
1733}
1734
1735
1736/**
1737 * Converts the given UTF-16 to a normal string.
1738 *
1739 * @returns Length of the resulting string.
1740 * @param pwszSrc The source UTF-16 string.
1741 * @param pszDst The destination buffer.
1742 * @param cbDst The size of the destination buffer in bytes.
1743 */
1744static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1745{
1746 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1747 KSIZE offDst = 0;
1748 while (offDst < cbDst)
1749 {
1750 wchar_t wc = *pwszSrc++;
1751 pszDst[offDst++] = (char)wc;
1752 if (!wc)
1753 return offDst - 1;
1754 kHlpAssert((unsigned)wc < 127);
1755 }
1756
1757 pszDst[offDst - 1] = '\0';
1758 return offDst;
1759}
1760
1761
1762/**
1763 * Converts the given UTF-16 to ASSI, allocating the buffer.
1764 *
1765 * @returns Pointer to the new heap allocation containing the ANSI version of
1766 * the source string.
1767 * @param pwcSrc The source string.
1768 * @param cwcSrc The length of the source string.
1769 */
1770static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1771{
1772 DWORD const dwErrSaved = GetLastError();
1773 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1774 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1775 if (pszBuf)
1776 {
1777 if (cwcSrc > 0)
1778 {
1779 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1780 if (cchRet > 0)
1781 {
1782 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1783 pszBuf[cchRet] = '\0';
1784 }
1785 else
1786 {
1787 kHlpFree(pszBuf);
1788
1789 /* Figure the length and allocate the right buffer size. */
1790 SetLastError(NO_ERROR);
1791 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1792 if (cchRet)
1793 {
1794 cbBuf = cchRet + 2;
1795 pszBuf = (char *)kHlpAlloc(cbBuf);
1796 if (pszBuf)
1797 {
1798 SetLastError(NO_ERROR);
1799 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1800 if (cchRet)
1801 {
1802 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1803 pszBuf[cchRet] = '\0';
1804 }
1805 else
1806 {
1807 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1808 kHlpFree(pszBuf);
1809 pszBuf = NULL;
1810 }
1811 }
1812 }
1813 else
1814 {
1815 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1816 pszBuf = NULL;
1817 }
1818 }
1819 }
1820 else
1821 pszBuf[0] = '\0';
1822 }
1823 SetLastError(dwErrSaved);
1824 return pszBuf;
1825}
1826
1827
1828
1829/** UTF-16 string length. */
1830static KSIZE kwUtf16Len(wchar_t const *pwsz)
1831{
1832 KSIZE cwc = 0;
1833 while (*pwsz != '\0')
1834 cwc++, pwsz++;
1835 return cwc;
1836}
1837
1838/**
1839 * Copy out the UTF-16 string following the convension of GetModuleFileName
1840 */
1841static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1842{
1843 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1844 if (cwcSrc + 1 <= cwcDst)
1845 {
1846 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1847 return (DWORD)cwcSrc;
1848 }
1849 if (cwcDst > 0)
1850 {
1851 KSIZE cwcDstTmp = cwcDst - 1;
1852 pwszDst[cwcDstTmp] = '\0';
1853 if (cwcDstTmp > 0)
1854 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1855 }
1856 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1857 return (DWORD)cwcDst;
1858}
1859
1860
1861/**
1862 * Copy out the ANSI string following the convension of GetModuleFileName
1863 */
1864static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1865{
1866 KSIZE cchSrc = kHlpStrLen(pszSrc);
1867 if (cchSrc + 1 <= cbDst)
1868 {
1869 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1870 return (DWORD)cchSrc;
1871 }
1872 if (cbDst > 0)
1873 {
1874 KSIZE cbDstTmp = cbDst - 1;
1875 pszDst[cbDstTmp] = '\0';
1876 if (cbDstTmp > 0)
1877 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1878 }
1879 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1880 return (DWORD)cbDst;
1881}
1882
1883
1884/**
1885 * Normalizes the path so we get a consistent hash.
1886 *
1887 * @returns status code.
1888 * @param pszPath The path.
1889 * @param pszNormPath The output buffer.
1890 * @param cbNormPath The size of the output buffer.
1891 */
1892static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1893{
1894 KFSLOOKUPERROR enmError;
1895 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1896 if (pFsObj)
1897 {
1898 KBOOL fRc;
1899 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1900 kFsCacheObjRelease(g_pFsCache, pFsObj);
1901 if (fRc)
1902 return 0;
1903 return KERR_BUFFER_OVERFLOW;
1904 }
1905 return KERR_FILE_NOT_FOUND;
1906}
1907
1908
1909/**
1910 * Get the pointer to the filename part of the path.
1911 *
1912 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1913 * @returns Pointer to the terminator char if no filename.
1914 * @param pszPath The path to parse.
1915 */
1916static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1917{
1918 const wchar_t *pwszLast = NULL;
1919 for (;;)
1920 {
1921 wchar_t wc = *pwszPath;
1922#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1923 if (wc == '/' || wc == '\\' || wc == ':')
1924 {
1925 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1926 /* nothing */;
1927 pwszLast = pwszPath;
1928 }
1929#else
1930 if (wc == '/')
1931 {
1932 while ((wc = *++pszFilename) == '/')
1933 /* betsuni */;
1934 pwszLast = pwszPath;
1935 }
1936#endif
1937 if (!wc)
1938 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1939 pwszPath++;
1940 }
1941}
1942
1943
1944
1945/**
1946 * Retains a new reference to the given module
1947 * @returns pMod
1948 * @param pMod The module to retain.
1949 */
1950static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1951{
1952 kHlpAssert(pMod->cRefs > 0);
1953 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1954 pMod->cRefs++;
1955 return pMod;
1956}
1957
1958
1959/**
1960 * Releases a module reference.
1961 *
1962 * @param pMod The module to release.
1963 */
1964static void kwLdrModuleRelease(PKWMODULE pMod)
1965{
1966 if (--pMod->cRefs == 0)
1967 {
1968 /* Make sure it doesn't receive any more native TLS callbacks.if non-native. */
1969 if (!pMod->fNative && pMod->u.Manual.ppTlsWorkerModuleVar)
1970 {
1971 *pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1972 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1973 }
1974
1975 /* Unlink it from the hash table. */
1976 if (!pMod->fExe)
1977 {
1978 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1979 if (g_apModules[idx] == pMod)
1980 g_apModules[idx] = pMod->pNextHash;
1981 else
1982 {
1983 PKWMODULE pPrev = g_apModules[idx];
1984 kHlpAssert(pPrev != NULL);
1985 while (pPrev->pNextHash != pMod)
1986 {
1987 pPrev = pPrev->pNextHash;
1988 kHlpAssert(pPrev != NULL);
1989 }
1990 pPrev->pNextHash = pMod->pNextHash;
1991 }
1992 }
1993
1994 /* Unlink it from the list. */
1995 if (pMod != g_pModuleHead)
1996 {
1997 PKWMODULE pPrev = g_pModuleHead;
1998 while (pPrev)
1999 {
2000 if (pPrev->pNextList == pMod)
2001 {
2002 pPrev->pNextList = pMod->pNextList;
2003 if (!pMod->pNextList)
2004 g_ppModuleNext = &pPrev->pNextList;
2005 break;
2006 }
2007 pPrev = pPrev->pNextList;
2008 }
2009 kHlpAssert(pPrev != NULL);
2010 }
2011 else
2012 {
2013 g_pModuleHead = pMod->pNextList;
2014 if (!pMod->pNextList)
2015 g_ppModuleNext = &g_pModuleHead;
2016 }
2017
2018 /* Release import modules. */
2019 if (!pMod->fNative)
2020 {
2021 KSIZE idx = pMod->u.Manual.cImpMods;
2022 while (idx-- > 0)
2023 if (pMod->u.Manual.apImpMods[idx])
2024 {
2025 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
2026 pMod->u.Manual.apImpMods[idx] = NULL;
2027 }
2028 }
2029
2030 /* Free our resources. */
2031 kLdrModClose(pMod->pLdrMod);
2032 pMod->pLdrMod = NULL;
2033
2034 if (!pMod->fNative)
2035 {
2036 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
2037 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2038 }
2039
2040 if (pMod->iCrtSlot != KU8_MAX)
2041 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
2042
2043 if (pMod->pszMsPdbSrvEndpoint)
2044 {
2045 kHlpFree(pMod->pszMsPdbSrvEndpoint);
2046 pMod->pszMsPdbSrvEndpoint = NULL;
2047 }
2048
2049 kHlpFree(pMod);
2050 }
2051 else
2052 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
2053}
2054
2055
2056/**
2057 * Links the module into the module hash table.
2058 *
2059 * @returns pMod
2060 * @param pMod The module to link.
2061 */
2062static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
2063{
2064 if (!pMod->fExe)
2065 {
2066 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
2067 pMod->pNextHash = g_apModules[idx];
2068 g_apModules[idx] = pMod;
2069 }
2070
2071 pMod->pNextList = NULL;
2072 *g_ppModuleNext = pMod;
2073 g_ppModuleNext = &pMod->pNextList;
2074
2075 return pMod;
2076}
2077
2078
2079/**
2080 * Replaces imports for this module according to g_aSandboxNativeReplacements.
2081 *
2082 * @param pMod The natively loaded module to process.
2083 */
2084static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
2085{
2086 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
2087 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
2088 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
2089 IMAGE_NT_HEADERS const *pNtHdrs;
2090 IMAGE_DATA_DIRECTORY const *pDirEnt;
2091
2092 kHlpAssert(pMod->fNative);
2093
2094 /*
2095 * Locate the export descriptors.
2096 */
2097 /* MZ header. */
2098 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
2099 {
2100 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
2101 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
2102 }
2103 else
2104 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
2105
2106 /* Check PE header. */
2107 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2108 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
2109
2110 /* Locate the import descriptor array. */
2111 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2112 if ( pDirEnt->Size > 0
2113 && pDirEnt->VirtualAddress != 0)
2114 {
2115 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
2116 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
2117 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
2118 KU8 *pbProtRange = NULL;
2119 SIZE_T cbProtRange = 0;
2120 DWORD fOldProt = 0;
2121 KU32 const cbPage = 0x1000;
2122 BOOL fRc;
2123
2124
2125 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
2126 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
2127 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
2128
2129 /*
2130 * Walk the import descriptor array.
2131 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
2132 */
2133 while ( cLeft-- > 0
2134 && pImpDesc->Name > 0
2135 && pImpDesc->FirstThunk > 0)
2136 {
2137 KU32 iThunk;
2138 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
2139 PKWMODULE pImportMod = NULL;
2140 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
2141 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
2142 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
2143 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
2144 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
2145 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
2146 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
2147
2148 /* Iterate the thunks. */
2149 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
2150 {
2151 KUPTR const off = paOrgThunks[iThunk].u1.Function;
2152 kHlpAssertReturnVoid(off < cbImage);
2153 if (!IMAGE_SNAP_BY_ORDINAL(off))
2154 {
2155 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
2156 KSIZE const cchSymbol = kHlpStrLen((const char *)&pName->Name[0]);
2157 KU32 i = g_cSandboxNativeReplacements;
2158 while (i-- > 0)
2159 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
2160 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
2161 {
2162 if ( !g_aSandboxNativeReplacements[i].pszModule
2163 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
2164 {
2165 KWLDR_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
2166
2167 /* The .rdata section is normally read-only, so we need to make it writable first. */
2168 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
2169 {
2170 /* Restore previous .rdata page. */
2171 if (fOldProt)
2172 {
2173 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
2174 kHlpAssert(fRc || GetLastError() == ERROR_NOACCESS /*tinderwin2*/);
2175 fOldProt = 0;
2176 }
2177
2178 /* Query attributes for the current .rdata page. */
2179 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
2180 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
2181 kHlpAssert(cbProtRange);
2182 if (cbProtRange)
2183 {
2184 switch (ProtInfo.Protect)
2185 {
2186 case PAGE_READWRITE:
2187 case PAGE_WRITECOPY:
2188 case PAGE_EXECUTE_READWRITE:
2189 case PAGE_EXECUTE_WRITECOPY:
2190 /* Already writable, nothing to do. */
2191 fRc = TRUE;
2192 break;
2193
2194 default:
2195 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2196 case PAGE_READONLY:
2197 cbProtRange = cbPage;
2198 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2199 break;
2200
2201 case PAGE_EXECUTE:
2202 case PAGE_EXECUTE_READ:
2203 cbProtRange = cbPage;
2204 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2205 break;
2206 }
2207 kHlpAssertStmt(fRc, fOldProt = 0);
2208 }
2209 }
2210
2211 /*
2212 * Unslotted replacements are simple.
2213 */
2214 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2215 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2216 else
2217 {
2218 /*
2219 * Must find our module entry for this module, possibly creating one.
2220 */
2221 if (!pImportMod)
2222 {
2223 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2224 K_TRUE /*fAlwaysPresent*/);
2225 if (!pImportMod)
2226 {
2227 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2228 pszImport, pMod->pszPath);
2229 break;
2230 }
2231 }
2232 paThunks[iThunk].u1.AddressOfData
2233 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2234 }
2235 break;
2236 }
2237 }
2238 }
2239 }
2240
2241
2242 /* Next import descriptor. */
2243 pImpDesc++;
2244 }
2245
2246
2247 if (fOldProt)
2248 {
2249 DWORD fIgnore = 0;
2250 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2251 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2252 }
2253 }
2254
2255}
2256
2257
2258/**
2259 * Creates a module from a native kLdr module handle.
2260 *
2261 * @returns Module w/ 1 reference on success, NULL on failure.
2262 * @param pLdrMod The native kLdr module.
2263 * @param pszPath The normalized path to the module.
2264 * @param cbPath The module path length with terminator.
2265 * @param uHashPath The module path hash.
2266 * @param fDoReplacements Whether to do import replacements on this
2267 * module.
2268 */
2269static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2270 KBOOL fDoReplacements, PKWMODULE pVirtualApiMod)
2271{
2272 /*
2273 * Create the entry.
2274 */
2275 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2276 if (pMod)
2277 {
2278 pMod->pwszPath = (wchar_t *)(pMod + 1);
2279 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2280 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2281 pMod->uHashPath = uHashPath;
2282 pMod->cRefs = 1;
2283 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2284 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2285 pMod->fExe = K_FALSE;
2286 pMod->fNative = K_TRUE;
2287 pMod->pLdrMod = pLdrMod;
2288 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2289 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2290 pMod->iCrtSlot = KU8_MAX;
2291 pMod->fNeedReInit = K_FALSE;
2292 pMod->pszMsPdbSrvEndpoint = NULL;
2293 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2294 pMod->pVirtualApiMod = pVirtualApiMod;
2295 if (pVirtualApiMod)
2296 kwLdrModuleRetain(pVirtualApiMod);
2297
2298 if (fDoReplacements)
2299 {
2300 DWORD const dwSavedErr = GetLastError();
2301 kwLdrModuleDoNativeImportReplacements(pMod);
2302 SetLastError(dwSavedErr);
2303 }
2304
2305 KWLDR_LOG(("New module: %p LB %#010x %s (native%s%s)\n",
2306 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath,
2307 pVirtualApiMod ? ", virtual api => " : "", pVirtualApiMod ? pVirtualApiMod->pszPath : ""));
2308 g_cModules++;
2309 return kwLdrModuleLink(pMod);
2310 }
2311 return NULL;
2312}
2313
2314
2315
2316/**
2317 * Creates a module using the native loader.
2318 *
2319 * @returns Module w/ 1 reference on success, NULL on failure.
2320 * @param pszPath The normalized path to the module.
2321 * @param uHashPath The module path hash.
2322 * @param fDoReplacements Whether to do import replacements on this
2323 * module.
2324 */
2325static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2326{
2327 PKLDRMOD pLdrMod;
2328 int rc;
2329
2330 /*
2331 * HACK ALERT! Make sure the application path is searched when looking for
2332 * imports in the module we're loading.
2333 */
2334 /** @todo improve on this hack! */
2335 PKWMODULE pExe = g_Sandbox.pTool ? g_Sandbox.pTool->u.Sandboxed.pExe : NULL;
2336 if (pExe)
2337 {
2338 /* HACK ALERT! */
2339 wchar_t *pwzFilename = (wchar_t *)&pExe->pwszPath[pExe->offFilenameW];
2340 wchar_t wcSaved = pExe->pwszPath[pExe->offFilenameW];
2341 *pwzFilename = '\0';
2342 if (!SetDllDirectoryW(pExe->pwszPath))
2343 kwErrPrintf("SetDllDirectoryW failed: %u\n", GetLastError());
2344 KW_LOG(("kwLdrModuleCreateNative: Applied SetDllDirectoryW hack (%ls)\n", pExe->pwszPath));
2345 *pwzFilename = wcSaved;
2346 }
2347 else
2348 KW_LOG(("kwLdrModuleCreateNative: Warning! Too early for SetDllDirectoryW hack\n"));
2349
2350
2351 /*
2352 * Load the library and create a module structure for it.
2353 */
2354 rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2355 if (rc == 0)
2356 {
2357 KSIZE cchPath = kHlpStrLen(pszPath);
2358 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, cchPath + 1, uHashPath,
2359 fDoReplacements, NULL /*pVirtualApiMod*/);
2360 if (pMod)
2361 return pMod;
2362 kLdrModClose(pLdrMod);
2363 }
2364 return NULL;
2365}
2366
2367
2368/**
2369 * Checks if the given name could be a virtual API module or not.
2370 */
2371static KBOOL kwLdrIsVirtualApiModule(const char *pszName, KSIZE cchName)
2372{
2373 if (cchName <= 7)
2374 return K_FALSE;
2375 switch (*pszName)
2376 {
2377 default:
2378 return K_FALSE;
2379 case 'a':
2380 case 'A':
2381 if (pszName[1] != 'p' && pszName[1] != 'P')
2382 return K_FALSE;
2383 if (pszName[2] != 'i' && pszName[2] != 'I')
2384 return K_FALSE;
2385 break;
2386 case 'e':
2387 case 'E':
2388 if (pszName[1] != 'x' && pszName[1] != 'X')
2389 return K_FALSE;
2390 if (pszName[2] != 't' && pszName[2] != 'T')
2391 return K_FALSE;
2392 break;
2393 }
2394 if (pszName[3] != '-')
2395 return K_FALSE;
2396 if (pszName[4] != 'm' && pszName[4] != 'M')
2397 return K_FALSE;
2398 if (pszName[5] != 's' && pszName[5] != 'S')
2399 return K_FALSE;
2400 if (pszName[6] != '-')
2401 return K_FALSE;
2402 return K_TRUE;
2403}
2404
2405
2406/**
2407 * Try load what seems to be a virtual API DLL.
2408 *
2409 * This is a worker for kwLdrModuleResolveAndLookup and
2410 * kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule.
2411 *
2412 * @returns Pointer to module on success, NULL on failure.
2413 * @param pszName The name of the module. This must be
2414 * normalized already!
2415 * @param cchName The length of the name.
2416 */
2417static PKWMODULE kwLdrModuleTryLoadVirtualDll(const char *pszName, KSIZE cchName)
2418{
2419 HMODULE hModule;
2420
2421 /*
2422 * Look it up in the hash table.
2423 */
2424 KU32 const uHashPath = kwStrHash(pszName);
2425 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2426 PKWMODULE pMod = g_apModules[idxHash];
2427 if (pMod)
2428 {
2429 do
2430 {
2431 if ( pMod->uHashPath == uHashPath
2432 && kHlpStrComp(pMod->pszPath, pszName) == 0)
2433 return kwLdrModuleRetain(pMod);
2434 pMod = pMod->pNextHash;
2435 } while (pMod);
2436 }
2437
2438 /*
2439 * Not found. Try load it.
2440 */
2441 hModule = LoadLibraryA(pszName);
2442 if (!hModule)
2443 {
2444 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s failed (%u)\n", pszName, GetLastError()));
2445 return NULL;
2446 }
2447
2448 /*
2449 * Loaded successfully. Create a module for the real module.
2450 */
2451 pMod = kwLdrModuleForLoadedNativeByHandle(hModule, K_FALSE /*fEnsureCrtSlot*/, pszName);
2452 if (pMod)
2453 {
2454 /* Create a module for the virtual API name too, unless it is actually a real DLL. */
2455 if (stricmp(&pMod->pszPath[pMod->offFilename], pszName) != 0)
2456 {
2457 PKLDRMOD pLdrMod;
2458 int rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2459 if (rc == 0)
2460 {
2461 PKWMODULE pVirtMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszName, cchName + 1, kwStrHash(pszName),
2462 K_FALSE /*fDoReplacements*/, pMod /*pVirtualApiMod*/);
2463 if (pVirtMod)
2464 {
2465 kwLdrModuleRelease(pMod);
2466 pMod = pVirtMod;
2467 }
2468 else
2469 {
2470 kLdrModClose(pLdrMod);
2471 kwErrPrintf("out of memory\n");
2472 }
2473 }
2474 else
2475 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
2476 }
2477 else
2478 {
2479 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s -> %s - A real DLL!\n", pszName, pMod->pszPath));
2480 /* HACK ALERT! If api-ms-win-crt-* find ucrtbase.dll and attach it as the
2481 real module as we cannot make replacements in the virtual
2482 API set forward DLLs. */
2483 /** @todo Find a way of scanning the exports and collect forwarder DLLs and
2484 * imported DLLs. kLdrModEnumSymbols()? */
2485 if ( pMod->pVirtualApiMod == NULL
2486 && kHlpStrNICompAscii(pszName, TUPLE("api-ms-win-crt-")) == 0)
2487 {
2488 HMODULE hModReal = GetModuleHandleW(L"ucrtbase.dll");
2489 if (hModReal)
2490 {
2491 PKWMODULE pRealMod = kwLdrModuleForLoadedNativeByHandle(hModReal, K_TRUE /*fEnsureCrtSlot*/, "ucrtbase.dll");
2492 if (pRealMod)
2493 {
2494 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: Linking %s to '%s'.\n", pszName, pRealMod->pszPath));
2495 pMod->pVirtualApiMod = pRealMod;
2496 }
2497 else
2498 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: kwLdrModuleForLoadedNativeByHandle failed for ucrtbase.dll/%s!\n", pszName));
2499 }
2500 else
2501 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: no ucrtbase.dll found for %s!\n", pszName));
2502 }
2503 }
2504 }
2505
2506 return pMod;
2507}
2508
2509
2510/**
2511 * Sets up the quick zero & copy tables for the non-native module.
2512 *
2513 * This is a worker for kwLdrModuleCreateNonNative.
2514 *
2515 * @param pMod The module.
2516 */
2517static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2518{
2519 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2520 KU32 cSegs = pMod->pLdrMod->cSegments;
2521 KU32 iSeg;
2522
2523 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2524 pMod->u.Manual.cQuickCopyChunks = 0;
2525 pMod->u.Manual.cQuickZeroChunks = 0;
2526
2527 for (iSeg = 0; iSeg < cSegs; iSeg++)
2528 switch (paSegs[iSeg].enmProt)
2529 {
2530 case KPROT_READWRITE:
2531 case KPROT_WRITECOPY:
2532 case KPROT_EXECUTE_READWRITE:
2533 case KPROT_EXECUTE_WRITECOPY:
2534 if (paSegs[iSeg].cbMapped)
2535 {
2536 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2537 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2538 {
2539 /*
2540 * Check for trailing zero words.
2541 */
2542 KSIZE cbTrailingZeros;
2543 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2544 && (paSegs[iSeg].cbMapped & 7) == 0
2545 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2546 {
2547 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2548 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2549 KSIZE idxFirstZero = cNatural;
2550 while (idxFirstZero > 0)
2551 if (pauNatural[--idxFirstZero] == 0)
2552 { /* likely */ }
2553 else
2554 {
2555 idxFirstZero++;
2556 break;
2557 }
2558 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2559 if (cbTrailingZeros < 128)
2560 cbTrailingZeros = 0;
2561 }
2562 else
2563 cbTrailingZeros = 0;
2564
2565 /*
2566 * Add quick copy entry.
2567 */
2568 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2569 {
2570 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2571 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2572 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2573 pMod->u.Manual.cQuickCopyChunks = (KU8)(iChunk + 1);
2574 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2575 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2576 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2577 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2578 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2579 }
2580
2581 /*
2582 * Add quick zero entry.
2583 */
2584 if (cbTrailingZeros)
2585 {
2586 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2587 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2588 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2589 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2590 pMod->u.Manual.cQuickZeroChunks = (KU8)(iZero + 1);
2591 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2592 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2593 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2594 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2595 }
2596 }
2597 else
2598 {
2599 /*
2600 * We're out of quick copy table entries, so just copy the whole darn thing.
2601 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2602 */
2603 kHlpAssertFailed();
2604 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2605 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2606 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2607 pMod->u.Manual.cQuickCopyChunks = 1;
2608 KWLDR_LOG(("Quick copy not possible!\n"));
2609 return;
2610 }
2611 }
2612 break;
2613
2614 default:
2615 break;
2616 }
2617}
2618
2619
2620/**
2621 * Called from the TLS allocation DLL when ever the native loader wants to issue
2622 * a TLS callback after the initial kwLdrTlsAllocationHook callout.
2623 *
2624 * @param hDll The DLL handle.
2625 * @param dwReason The callback reason.
2626 * @param pvContext Some context value that seems to always be NULL.
2627 * @param pMod Out internal module.
2628 */
2629static void kwLdrTlsNativeLoaderCallback(void *hDll, DWORD dwReason, void *pvContext, PKWMODULE pMod)
2630{
2631 if ( pMod
2632 && pMod->u.Manual.enmState == KWMODSTATE_READY)
2633 {
2634 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p\n",
2635 hDll, dwReason, pvContext, pMod));
2636 if (pMod->u.Manual.cTlsCallbacks)
2637 {
2638 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
2639 do
2640 {
2641 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: Calling TLS callback %p(%p, %#x, %p) - %s\n",
2642 *ppfnCallback, pMod->hOurMod, dwReason, pvContext, pMod->pszPath));
2643 (*ppfnCallback)(pMod->hOurMod, dwReason, pvContext);
2644 ppfnCallback++;
2645 } while (*ppfnCallback);
2646 }
2647 }
2648 else
2649 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p - skipped\n",
2650 hDll, dwReason, pvContext, pMod));
2651}
2652
2653
2654/**
2655 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2656 *
2657 * @returns Address of the callback function (kwLdrTlsNativeLoaderCallback).
2658 * @param hDll The DLL handle.
2659 * @param idxTls The allocated TLS index.
2660 * @param pabInitData The init data in the TLS allocation DLL
2661 * (g_abInitData).
2662 * @param ppWorkerModuleVar Pointer to the variable holding the pMod
2663 * callback parameter value (g_pvWorkerModule).
2664 *
2665 * @see KWLDRTLSALLOCATIONHOOK in kWorkerTlsXxxxK.c
2666 */
2667__declspec(dllexport) KUPTR kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, KU8 *pabInitData, PKWMODULE *ppWorkerModuleVar)
2668{
2669 /*
2670 * Do the module initialization thing first.
2671 */
2672 PKWMODULE pMod = g_pModPendingTlsAlloc;
2673 if (pMod)
2674 {
2675 if ( pMod->u.Manual.idxTls == KU32_MAX
2676 && pMod->u.Manual.pabTlsInitData == NULL)
2677 {
2678 pMod->u.Manual.idxTls = idxTls;
2679 pMod->u.Manual.pabTlsInitData = pabInitData;
2680 pMod->u.Manual.ppTlsWorkerModuleVar = ppWorkerModuleVar;
2681 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2682
2683#if 0 /** @todo this doesn't work W10 18363 */
2684 {
2685 /*
2686 * Try sabotage the DLL name so we can load this module again.
2687 */
2688 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2689 LIST_ENTRY *pHead;
2690 LIST_ENTRY *pCur;
2691
2692 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2693 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2694 {
2695 LDR_DATA_TABLE_ENTRY *pMte;
2696 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2697 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2698 {
2699 PUNICODE_STRING pStr = &pMte->FullDllName;
2700 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2701 pStr->Buffer[--off]++;
2702 pStr->Buffer[--off]++;
2703 pStr->Buffer[--off]++;
2704 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2705 break;
2706 }
2707 }
2708 }
2709#endif
2710
2711 /*
2712 * Don't return a callback function unless the module has callbacks to service.
2713 */
2714 if (pMod->u.Manual.cTlsCallbacks > 0)
2715 {
2716 *ppWorkerModuleVar = pMod;
2717 return (KUPTR)kwLdrTlsNativeLoaderCallback;
2718 }
2719 return 0;
2720 }
2721 KWLDR_LOG(("kwLdrTlsAllocationHook: WTF? pMod=%p: idxTls=%#x pabTlsInitData=%p\n",
2722 pMod, pMod->u.Manual.idxTls, pMod->u.Manual.pabTlsInitData));
2723 }
2724 return 0;
2725}
2726
2727
2728/**
2729 * Allocates and initializes TLS variables.
2730 *
2731 * @returns 0 on success, non-zero failure.
2732 * @param pMod The module.
2733 */
2734static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2735{
2736 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2737 IMAGE_NT_HEADERS const *pNtHdrs;
2738 IMAGE_DATA_DIRECTORY const *pTlsDir;
2739
2740 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2741 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2742 else
2743 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2744 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2745
2746 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2747 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2748 {
2749 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2750 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2751 KU32 iEntry;
2752 KU32 iTlsDll;
2753 KU32 iTlsDllSub;
2754 KUPTR offIndex;
2755 KUPTR offCallbacks;
2756 KUPTR const *puCallbacks;
2757 KSIZE cbData;
2758 const wchar_t *pwszTlsDll;
2759 HMODULE hmodTlsDll;
2760
2761 /*
2762 * Check and log.
2763 */
2764 for (iEntry = 0; iEntry < cEntries; iEntry++)
2765 {
2766 offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2767 offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2768 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2769 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2770 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2771 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2772 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2773
2774 if (offIndex >= pMod->cbImage)
2775 {
2776 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2777 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2778 return -1;
2779 }
2780 if (offCallbacks >= pMod->cbImage)
2781 {
2782 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2783 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2784 return -1;
2785 }
2786 while (*puCallbacks != 0)
2787 {
2788 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2789 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2790 puCallbacks++;
2791 }
2792 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2793 {
2794 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2795 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2796 return -1;
2797 }
2798 }
2799
2800 if (cEntries > 1)
2801 {
2802 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2803 return -1;
2804 }
2805
2806 /*
2807 * Make the allocation by loading a new instance of one of the TLS dlls.
2808 * The DLL will make a call to kwLdrTlsAllocationHook.
2809 */
2810 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2811 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2812 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2813 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2814
2815 /** @todo find better strategy here. Like temporary copy or whatever when
2816 * there is more than a single user. */
2817 for (iTlsDll = 0; cbData > g_aTlsDlls[iTlsDll].cbTls;)
2818 if (++iTlsDll >= K_ELEMENTS(g_aTlsDlls))
2819 {
2820 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2821 return -1;
2822 }
2823 for (iTlsDllSub = 0; g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed;)
2824 if (++iTlsDllSub >= g_aTlsDlls[iTlsDll].cDlls)
2825 {
2826 kwErrPrintf("No unused TLS DLLs for %s of size %u!\n", pMod->pszPath, (unsigned)cbData);
2827 return -1;
2828 }
2829
2830 g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed = K_TRUE;
2831 pwszTlsDll = g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].pwszName;
2832
2833 pMod->u.Manual.pabTlsInitData = NULL;
2834 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2835 pMod->u.Manual.idxTls = KU32_MAX;
2836
2837 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2838 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2839 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2840 pMod->u.Manual.cTlsCallbacks = 0;
2841 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2842 pMod->u.Manual.cTlsCallbacks++;
2843 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2844
2845 g_pModPendingTlsAlloc = pMod;
2846 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2847 g_pModPendingTlsAlloc = NULL;
2848 if (hmodTlsDll == NULL)
2849 {
2850 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2851 return -1;
2852 }
2853 if (pMod->u.Manual.idxTls == KU32_MAX)
2854 {
2855 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2856 return -1;
2857 }
2858
2859 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2860 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x pabTlsInitData=%p\n",
2861 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData, pMod->u.Manual.pabTlsInitData));
2862
2863 kHlpAssert(pMod->u.Manual.pabTlsInitData);
2864 if (pMod->u.Manual.pabTlsInitData && pMod->u.Manual.cbTlsInitData)
2865 kHlpMemCopy(pMod->u.Manual.pabTlsInitData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData],
2866 pMod->u.Manual.cbTlsInitData);
2867 }
2868 return 0;
2869}
2870
2871
2872/**
2873 * Creates a module using the our own loader.
2874 *
2875 * @returns Module w/ 1 reference on success, NULL on failure.
2876 * @param pszPath The normalized path to the module.
2877 * @param uHashPath The module path hash.
2878 * @param fExe K_TRUE if this is an executable image, K_FALSE
2879 * if not. Executable images does not get entered
2880 * into the global module table.
2881 * @param pExeMod The executable module of the process (for
2882 * resolving imports). NULL if fExe is set.
2883 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2884 */
2885static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2886 PKWMODULE pExeMod, const char *pszSearchPath)
2887{
2888 /*
2889 * Open the module and check the type.
2890 */
2891 PKLDRMOD pLdrMod;
2892 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2893 if (rc == 0)
2894 {
2895 switch (pLdrMod->enmType)
2896 {
2897 case KLDRTYPE_EXECUTABLE_FIXED:
2898 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2899 case KLDRTYPE_EXECUTABLE_PIC:
2900 if (!fExe)
2901 rc = KERR_GENERAL_FAILURE;
2902 break;
2903
2904 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2905 case KLDRTYPE_SHARED_LIBRARY_PIC:
2906 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2907 if (fExe)
2908 rc = KERR_GENERAL_FAILURE;
2909 break;
2910
2911 default:
2912 rc = KERR_GENERAL_FAILURE;
2913 kwErrPrintf("kwLdrModuleCreateNonNative: Unsupported module type %d (%s)!\n", pLdrMod->enmType, pszPath);
2914 break;
2915 }
2916 if (rc == 0)
2917 {
2918 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2919 if (cImports >= 0)
2920 {
2921 /*
2922 * Create the entry.
2923 */
2924 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2925 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2926 + sizeof(pMod) * cImports
2927 + cbPath
2928 + cbPath * 2 * sizeof(wchar_t));
2929 if (pMod)
2930 {
2931 KBOOL fFixed;
2932
2933 pMod->cRefs = 1;
2934 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2935 pMod->uHashPath = uHashPath;
2936 pMod->fExe = fExe;
2937 pMod->fNative = K_FALSE;
2938 pMod->pLdrMod = pLdrMod;
2939 pMod->iCrtSlot = KU8_MAX;
2940 pMod->fNeedReInit = K_FALSE;
2941 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2942 pMod->pszMsPdbSrvEndpoint = NULL;
2943 pMod->u.Manual.cImpMods = (KU32)cImports;
2944#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2945 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2946#endif
2947 pMod->u.Manual.fUseLdBuf = K_FALSE;
2948 pMod->u.Manual.fCanDoQuick = K_FALSE;
2949 pMod->u.Manual.cQuickZeroChunks = 0;
2950 pMod->u.Manual.cQuickCopyChunks = 0;
2951 pMod->u.Manual.pabTlsInitData = NULL;
2952 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2953 pMod->u.Manual.idxTls = KU32_MAX;
2954 pMod->u.Manual.offTlsInitData = KU32_MAX;
2955 pMod->u.Manual.cbTlsInitData = 0;
2956 pMod->u.Manual.cbTlsAlloc = 0;
2957 pMod->u.Manual.cTlsCallbacks = 0;
2958 pMod->u.Manual.offTlsCallbacks = 0;
2959 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2960 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2961 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2962 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2963
2964 /*
2965 * Figure out where to load it and get memory there.
2966 */
2967 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2968 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2969 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2970 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2971 if ( !fFixed
2972 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2973 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2974 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2975 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2976 else
2977 pMod->u.Manual.fUseLdBuf = K_TRUE;
2978 if (rc == 0)
2979 {
2980 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2981 if (rc == 0)
2982 {
2983 KI32 iImp;
2984 KU32 cchReloads;
2985
2986 /*
2987 * Link the module (unless it's an executable image) and process the imports.
2988 */
2989 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2990 kwLdrModuleLink(pMod);
2991 KWLDR_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2992 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2993 KWLDR_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2994 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2995 cchReloads = g_cchReloads;
2996 if (cchReloads + 80 < sizeof(g_szReloads))
2997 {
2998 cchReloads += _snprintf(&g_szReloads[cchReloads], sizeof(g_szReloads) - cchReloads,
2999 "%s.reload /f %s=%p\n", cchReloads ? "; " : "",
3000 pMod->pszPath, pMod->u.Manual.pbLoad);
3001 g_cchReloads = cchReloads;
3002 }
3003
3004 for (iImp = 0; iImp < cImports; iImp++)
3005 {
3006 char szName[1024];
3007 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
3008 if (rc == 0)
3009 {
3010 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
3011 &pMod->u.Manual.apImpMods[iImp]);
3012 if (rc == 0)
3013 continue;
3014 }
3015 kwErrPrintf("Error getting import '%s' for '%s': %d (%u)\n",
3016 szName, pMod->pszPath, rc, GetLastError());
3017 break;
3018 }
3019
3020 if (rc == 0)
3021 {
3022 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
3023 kwLdrModuleGetImportCallback, pMod);
3024 if (rc == 0)
3025 {
3026#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3027 /*
3028 * Find the function table. No validation here because the
3029 * loader did that already, right...
3030 */
3031 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
3032 IMAGE_NT_HEADERS const *pNtHdrs;
3033 IMAGE_DATA_DIRECTORY const *pXcptDir;
3034 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
3035 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
3036 else
3037 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
3038 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
3039 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
3040 if (pXcptDir->Size > 0)
3041 {
3042 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
3043 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
3044 == pXcptDir->Size);
3045 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
3046 }
3047 else
3048 {
3049 pMod->u.Manual.cFunctions = 0;
3050 pMod->u.Manual.paFunctions = NULL;
3051 }
3052#endif
3053
3054 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
3055
3056 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
3057 if (rc == 0)
3058 {
3059 /*
3060 * Final finish.
3061 */
3062 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
3063 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
3064 pMod->u.Manual.enmReInitState = KWMODSTATE_NEEDS_BITS;
3065 if ( g_Sandbox.pTool
3066 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
3067 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3068 && !pMod->fExe)
3069 pMod->u.Manual.enmReInitState = KWMODSTATE_READY;
3070 g_cModules++;
3071 g_cNonNativeModules++;
3072 return pMod;
3073 }
3074 kwErrPrintf("kwLdrModuleCreateNonNativeSetupTls failed with %d for %s\n", rc, pMod->pszPath);
3075 }
3076 else
3077 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
3078 }
3079
3080 kwLdrModuleRelease(pMod);
3081 return NULL;
3082 }
3083
3084 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
3085 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3086 }
3087 else if (fFixed)
3088 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
3089 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
3090 else
3091 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3092 }
3093 else
3094 kwErrPrintf("kwLdrModuleCreateNonNative: out of memory!\n");
3095 }
3096 else
3097 kwErrPrintf("kwLdrModuleCreateNonNative: kLdrModNumberOfImports failed for '%s'\n", pszPath);
3098 }
3099 kLdrModClose(pLdrMod);
3100 }
3101 else
3102 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
3103 return NULL;
3104}
3105
3106
3107/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
3108static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
3109 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
3110{
3111 PKWMODULE pCurMod = (PKWMODULE)pvUser;
3112 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
3113 int rc;
3114 K_NOREF(pMod);
3115
3116 if (pImpMod->fNative)
3117 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
3118 iSymbol, pchSymbol, cchSymbol, pszVersion,
3119 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3120 puValue, pfKind);
3121 else
3122 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
3123 iSymbol, pchSymbol, cchSymbol, pszVersion,
3124 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3125 puValue, pfKind);
3126 if (rc == 0)
3127 {
3128 KU32 i = g_cSandboxReplacements;
3129 while (i-- > 0)
3130 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
3131 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
3132 {
3133 if ( !g_aSandboxReplacements[i].pszModule
3134 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
3135 {
3136 if ( pCurMod->fExe
3137 || !g_aSandboxReplacements[i].fOnlyExe)
3138 {
3139 KWLDR_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
3140 if (!g_aSandboxReplacements[i].fCrtSlotArray)
3141 *puValue = g_aSandboxReplacements[i].pfnReplacement;
3142 else
3143 {
3144 if (pImpMod->iCrtSlot == KU8_MAX)
3145 {
3146 rc = kwLdrModuleCreateCrtSlot(pImpMod);
3147 if (rc)
3148 KWLDR_LOG(("kwLdrModuleGetImportCallback: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
3149 }
3150 *puValue = ((KUPTR *)g_aSandboxReplacements[i].pfnReplacement)[pImpMod->iCrtSlot];
3151 }
3152 }
3153 break;
3154 }
3155 }
3156 }
3157
3158 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
3159 KWLDR_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
3160 return rc;
3161
3162}
3163
3164
3165/**
3166 * Gets the main entrypoint for a module.
3167 *
3168 * @returns 0 on success, KERR on failure
3169 * @param pMod The module.
3170 * @param puAddrMain Where to return the address.
3171 */
3172static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
3173{
3174 KLDRADDR uLdrAddrMain;
3175 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
3176 if (rc == 0)
3177 {
3178 *puAddrMain = (KUPTR)uLdrAddrMain;
3179 return 0;
3180 }
3181 return rc;
3182}
3183
3184
3185/**
3186 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
3187 *
3188 * @returns K_TRUE/K_FALSE.
3189 * @param pszFilename The filename (no path).
3190 * @param enmLocation The location.
3191 */
3192static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
3193{
3194 if (enmLocation != KWLOCATION_SYSTEM32)
3195 return K_TRUE;
3196 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3197 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3198 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3199 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3200 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3201 || kHlpStrNICompAscii(pszFilename, TUPLE("api-ms-win-crt-")) == 0
3202#if 0 /* for debugging, only for debugging. */
3203 || kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3204 || kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3205 || kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3206#endif
3207 ;
3208}
3209
3210
3211/**
3212 * Lazily initializes the g_pWinSys32 variable.
3213 */
3214static PKFSDIR kwLdrResolveWinSys32(void)
3215{
3216 KFSLOOKUPERROR enmError;
3217 PKFSDIR pWinSys32;
3218
3219 /* Get the path first. */
3220 char szSystem32[MAX_PATH];
3221 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
3222 {
3223 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
3224 strcpy(szSystem32, "C:\\Windows\\System32");
3225 }
3226
3227 /* Look it up and verify it. */
3228 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
3229 if (pWinSys32)
3230 {
3231 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
3232 {
3233 g_pWinSys32 = pWinSys32;
3234 return pWinSys32;
3235 }
3236
3237 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
3238 }
3239 else
3240 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
3241 return NULL;
3242}
3243
3244
3245/**
3246 * Whether we can load this DLL natively or not.
3247 *
3248 * @returns K_TRUE/K_FALSE.
3249 * @param pszFilename The filename (no path).
3250 * @param enmLocation The location.
3251 * @param pszFullPath The full filename and path.
3252 */
3253static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
3254{
3255 if (enmLocation == KWLOCATION_SYSTEM32)
3256 return K_TRUE;
3257 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
3258 return K_TRUE;
3259 if ( enmLocation == KWLOCATION_UNKNOWN
3260 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3261 return K_TRUE;
3262
3263 /* If the location is unknown, we must check if it's some dynamic loading
3264 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
3265 if (enmLocation == KWLOCATION_UNKNOWN)
3266 {
3267 PKFSDIR pWinSys32 = g_pWinSys32;
3268 if (!pWinSys32)
3269 pWinSys32 = kwLdrResolveWinSys32();
3270 if (pWinSys32)
3271 {
3272 KFSLOOKUPERROR enmError;
3273 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
3274 if (pFsObj)
3275 {
3276 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
3277 kFsCacheObjRelease(g_pFsCache, pFsObj);
3278 if (fInWinSys32)
3279 return K_TRUE;
3280 }
3281 }
3282 }
3283
3284 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3285 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3286 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3287 || kHlpStrNICompAscii(pszFilename, TUPLE("tbbmalloc")) == 0
3288 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3289 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3290 || ( enmLocation != KWLOCATION_UNKNOWN
3291 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3292#if 0 /* for debugging, only for debugging. */
3293 //|| kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3294 //|| kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3295 //|| kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3296#endif
3297 ;
3298}
3299
3300
3301/**
3302 * Check if the path leads to a regular file (that exists).
3303 *
3304 * @returns K_TRUE / K_FALSE
3305 * @param pszPath Path to the file to check out.
3306 */
3307static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
3308{
3309 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
3310 KSIZE cchPath = kHlpStrLen(pszPath);
3311 if ( cchPath > 3
3312 && pszPath[cchPath - 4] == '.'
3313 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
3314 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
3315 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
3316 {
3317 KFSLOOKUPERROR enmError;
3318 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3319 if (pFsObj)
3320 {
3321 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
3322 kFsCacheObjRelease(g_pFsCache, pFsObj);
3323 return fRc;
3324 }
3325 }
3326 else
3327 {
3328 BirdStat_T Stat;
3329 int rc = birdStatFollowLink(pszPath, &Stat);
3330 if (rc == 0)
3331 {
3332 if (S_ISREG(Stat.st_mode))
3333 return K_TRUE;
3334 }
3335 }
3336 return K_FALSE;
3337}
3338
3339
3340/**
3341 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
3342 *
3343 * If the file exists, we consult the module hash table before trying to load it
3344 * off the disk.
3345 *
3346 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
3347 * failure.
3348 * @param pszPath The name of the import module.
3349 * @param enmLocation The location we're searching. This is used in
3350 * the heuristics for determining if we can use the
3351 * native loader or need to sandbox the DLL.
3352 * @param pExe The executable (optional).
3353 * @param pszSearchPath The PATH to search (optional).
3354 */
3355static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
3356{
3357 /*
3358 * Does the file exists and is it a regular file?
3359 */
3360 if (kwLdrModuleIsRegularFile(pszPath))
3361 {
3362 /*
3363 * Yes! Normalize it and look it up in the hash table.
3364 */
3365 char szNormPath[1024];
3366 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
3367 if (rc == 0)
3368 {
3369 const char *pszName;
3370 KU32 const uHashPath = kwStrHash(szNormPath);
3371 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3372 PKWMODULE pMod = g_apModules[idxHash];
3373 if (pMod)
3374 {
3375 do
3376 {
3377 if ( pMod->uHashPath == uHashPath
3378 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3379 return kwLdrModuleRetain(pMod);
3380 pMod = pMod->pNextHash;
3381 } while (pMod);
3382 }
3383
3384 /*
3385 * Not in the hash table, so we have to load it from scratch.
3386 */
3387 pszName = kHlpGetFilename(szNormPath);
3388 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
3389 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
3390 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
3391 else
3392 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
3393 if (pMod)
3394 return pMod;
3395 return (PKWMODULE)~(KUPTR)0;
3396 }
3397 }
3398 return NULL;
3399}
3400
3401
3402/**
3403 * Gets a reference to the module by the given name.
3404 *
3405 * We must do the search path thing, as our hash table may multiple DLLs with
3406 * the same base name due to different tools version and similar. We'll use a
3407 * modified search sequence, though. No point in searching the current
3408 * directory for instance.
3409 *
3410 * @returns 0 on success, KERR on failure.
3411 * @param pszName The name of the import module.
3412 * @param pExe The executable (optional).
3413 * @param pImporter The module doing the importing (optional).
3414 * @param pszSearchPath The PATH to search (optional).
3415 * @param ppMod Where to return the module pointer w/ reference.
3416 */
3417static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
3418 const char *pszSearchPath, PKWMODULE *ppMod)
3419{
3420 KSIZE const cchName = kHlpStrLen(pszName);
3421 char szPath[1024];
3422 char *psz;
3423 PKWMODULE pMod = NULL;
3424 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
3425 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
3426
3427 /* Virtual API module. Normalize and try load it. */
3428 if (pMod == NULL && cchName > 7 && kwLdrIsVirtualApiModule(pszName, cchName))
3429 {
3430 if (cchName + cchSuffix >= sizeof(szPath))
3431 return KERR_BUFFER_OVERFLOW;
3432 kHlpMemCopy(szPath, pszName, cchName);
3433 if (fNeedSuffix)
3434 kHlpMemCopy(&szPath[cchName], ".dll", sizeof(".dll"));
3435 szPath[cchName + cchSuffix] = '\0';
3436 _strlwr(szPath);
3437 pMod = kwLdrModuleTryLoadVirtualDll(szPath, cchName + cchSuffix);
3438 }
3439
3440 /* The import path. */
3441 if (pMod == NULL && pImporter != NULL)
3442 {
3443 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
3444 return KERR_BUFFER_OVERFLOW;
3445
3446 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
3447 if (fNeedSuffix)
3448 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3449 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
3450 }
3451
3452 /* Application directory first. */
3453 if (pMod == NULL && pExe != NULL && pExe != pImporter)
3454 {
3455 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
3456 return KERR_BUFFER_OVERFLOW;
3457 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
3458 if (fNeedSuffix)
3459 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3460 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
3461 }
3462
3463 /* The windows directory. */
3464 if (pMod == NULL)
3465 {
3466 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
3467 if ( cchDir <= 2
3468 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
3469 return KERR_BUFFER_OVERFLOW;
3470 szPath[cchDir++] = '\\';
3471 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
3472 if (fNeedSuffix)
3473 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3474 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3475 }
3476
3477 /* The path. */
3478 if ( pMod == NULL
3479 && pszSearchPath)
3480 {
3481 const char *pszCur = pszSearchPath;
3482 while (*pszCur != '\0')
3483 {
3484 /* Find the end of the component */
3485 KSIZE cch = 0;
3486 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
3487 cch++;
3488
3489 if ( cch > 0 /* wrong, but whatever */
3490 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
3491 {
3492 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
3493 if ( szPath[cch - 1] != ':'
3494 && szPath[cch - 1] != '/'
3495 && szPath[cch - 1] != '\\')
3496 *pszDst++ = '\\';
3497 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
3498 if (fNeedSuffix)
3499 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
3500 *pszDst = '\0';
3501
3502 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3503 if (pMod)
3504 break;
3505 }
3506
3507 /* Advance */
3508 pszCur += cch;
3509 while (*pszCur == ';')
3510 pszCur++;
3511 }
3512 }
3513
3514 /* Return. */
3515 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3516 {
3517 *ppMod = pMod;
3518 return 0;
3519 }
3520 *ppMod = NULL;
3521 return KERR_GENERAL_FAILURE;
3522}
3523
3524
3525/**
3526 * Creates a CRT slot for the given module.
3527 *
3528 * @returns 0 on success, non-zero on failure.
3529 * @param pModule The module.
3530 */
3531static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3532{
3533 KSIZE iSlot;
3534 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3535 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3536 if (g_aCrtSlots[iSlot].pModule == NULL)
3537 {
3538 KLDRADDR uAddr;
3539 int rc;
3540
3541 /* Do the linking: */
3542 g_aCrtSlots[iSlot].pModule = pModule;
3543 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3544 pModule->iCrtSlot = (KU8)iSlot;
3545
3546 /* resolve symbols: */
3547 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3548 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3549 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3550 if (rc != 0)
3551 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3552
3553 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "_beginthreadex", 14,
3554 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3555 *(KUPTR *)&g_aCrtSlots[iSlot].pfnBeginThreadEx = rc == 0 ? (KUPTR)uAddr : 0;
3556 //if (rc != 0)
3557 // kwErrPrintf("Failed to resolved '_beginthreadex' in '%s': %d\n", pModule->pszPath, rc);
3558
3559 return 0;
3560 }
3561 kwErrPrintf("Out of CRT slots!\n");
3562 return KERR_NO_MEMORY;
3563}
3564
3565
3566/**
3567 * Locates the module structure for an already loaded native module.
3568 *
3569 * This will create a module structure if needed.
3570 *
3571 * @returns Pointer to the module structure on success, NULL on failure.
3572 * @param hModule The native module handle.
3573 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3574 * @param pszLogName The name to use for logging/errors.
3575 */
3576static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName)
3577{
3578 /*
3579 * Get a normalized path for it.
3580 */
3581 char szModPath[1024];
3582 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3583 {
3584 char szNormPath[1024];
3585 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3586 if (rc == 0)
3587 {
3588 /*
3589 * Hash the path and look it up.
3590 */
3591 KU32 uHashPath;
3592 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3593 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3594 PKWMODULE pMod = g_apModules[idxHash];
3595 if (pMod)
3596 {
3597 do
3598 {
3599 if ( pMod->uHashPath == uHashPath
3600 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3601 {
3602 kwLdrModuleRetain(pMod);
3603 break;
3604 }
3605 pMod = pMod->pNextHash;
3606 } while (pMod);
3607 }
3608
3609 /*
3610 * If not in the hash table, so create a module entry.
3611 */
3612 if (!pMod)
3613 {
3614 PKLDRMOD pLdrMod;
3615 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3616 if (rc == 0)
3617 {
3618 /** @todo more accurately determine location */
3619 const char *pszFilename = kHlpGetFilename(szNormPath);
3620 KBOOL fDoReplacements = kwLdrModuleShouldDoNativeReplacements(pszFilename, KWLOCATION_SYSTEM32);
3621 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3622 fDoReplacements, NULL /*pVirtualApiMod*/);
3623 if (!pMod)
3624 {
3625 kLdrModClose(pLdrMod);
3626 kwErrPrintf("out of memory\n");
3627 }
3628 }
3629 else
3630 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszLogName, rc);
3631 }
3632 if (pMod)
3633 {
3634 /*
3635 * Create a CRT slot for the module if necessary.
3636 */
3637 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3638 return pMod;
3639 rc = kwLdrModuleCreateCrtSlot(pMod);
3640 if (rc == 0)
3641 return pMod;
3642 kwLdrModuleRelease(pMod);
3643 }
3644 }
3645 else
3646 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszLogName, GetLastError());
3647 }
3648 else
3649 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszLogName, GetLastError());
3650 return NULL;
3651}
3652
3653
3654/**
3655 * Locates the module structure for an already loaded native module.
3656 *
3657 * This will create a module structure if needed.
3658 *
3659 * @returns Pointer to the module structure on success, NULL on failure.
3660 * @param pszName The name of the module.
3661 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3662 * @param fAlwaysPresent Whether the module is expected to always be present,
3663 * or not. If not, complain less.
3664 */
3665static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3666{
3667 /*
3668 * Locate the module handle and pass it to kwLdrModuleForLoadedNativeByHandle.
3669 */
3670 HANDLE hModule = GetModuleHandleA(pszName);
3671 if (hModule)
3672 return kwLdrModuleForLoadedNativeByHandle(hModule, fEnsureCrtSlot, pszName);
3673 if (fAlwaysPresent)
3674 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3675 return NULL;
3676}
3677
3678
3679/**
3680 * Does the TLS memory initialization for a module on the current thread.
3681 *
3682 * @returns 0 on success, error on failure.
3683 * @param pMod The module.
3684 */
3685static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3686{
3687 if (pMod->u.Manual.idxTls != KU32_MAX)
3688 {
3689 PTEB pTeb = NtCurrentTeb();
3690 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3691 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3692 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3693 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3694 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3695 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3696 if (pMod->u.Manual.cbTlsInitData)
3697 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3698 }
3699 return 0;
3700}
3701
3702
3703/**
3704 * Does the TLS callbacks for a module.
3705 *
3706 * @param pMod The module.
3707 * @param dwReason The callback reason.
3708 */
3709static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3710{
3711 if (pMod->u.Manual.cTlsCallbacks)
3712 {
3713 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3714 do
3715 {
3716 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *ppfnCallback, pMod->hOurMod, dwReason));
3717 (*ppfnCallback)(pMod->hOurMod, dwReason, 0);
3718 } while (*++ppfnCallback);
3719 }
3720}
3721
3722
3723/**
3724 * Does module initialization starting at @a pMod.
3725 *
3726 * This is initially used on the executable. Later it is used by the
3727 * LoadLibrary interceptor.
3728 *
3729 * @returns 0 on success, error on failure.
3730 * @param pMod The module to initialize.
3731 */
3732static int kwLdrModuleInitTree(PKWMODULE pMod)
3733{
3734 int rc = 0;
3735 if (!pMod->fNative)
3736 {
3737 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3738 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3739
3740 /*
3741 * Need to copy bits?
3742 */
3743 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3744 {
3745 if (pMod->u.Manual.fUseLdBuf)
3746 {
3747#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3748 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3749 {
3750 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3751 kHlpAssert(fRc); K_NOREF(fRc);
3752 }
3753#endif
3754 g_pModPrevInLdBuf = g_pModInLdBuf;
3755 g_pModInLdBuf = pMod;
3756 }
3757
3758 /* Do quick zeroing and copying when we can. */
3759 pMod->u.Manual.fCanDoQuick = K_FALSE;
3760 if ( pMod->u.Manual.fCanDoQuick
3761 && ( !pMod->u.Manual.fUseLdBuf
3762 || g_pModPrevInLdBuf == pMod))
3763 {
3764 /* Zero first. */
3765 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3766 switch (pMod->u.Manual.cQuickZeroChunks)
3767 {
3768 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3769 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3770 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3771 case 0: break;
3772 }
3773
3774 /* Then copy. */
3775 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3776 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3777 switch (pMod->u.Manual.cQuickCopyChunks)
3778 {
3779 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3780 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3781 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3782 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3783 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3784 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3785 case 0: break;
3786 }
3787 }
3788 /* Must copy the whole image. */
3789 else
3790 {
3791 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3792 pMod->u.Manual.fCanDoQuick = K_TRUE;
3793 }
3794 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3795 }
3796
3797#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3798 /*
3799 * Need to register function table?
3800 */
3801 if ( !pMod->u.Manual.fRegisteredFunctionTable
3802 && pMod->u.Manual.cFunctions > 0)
3803 {
3804 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3805 pMod->u.Manual.cFunctions,
3806 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3807 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3808 }
3809#endif
3810
3811
3812 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3813 {
3814 /*
3815 * Must do imports first, but mark our module as being initialized to avoid
3816 * endless recursion should there be a dependency loop.
3817 */
3818 KSIZE iImp;
3819 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3820
3821 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3822 {
3823 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3824 if (rc != 0)
3825 return rc;
3826 }
3827
3828 /* Do TLS allocations for module init? */
3829 rc = kwLdrCallTlsAllocateAndInit(pMod);
3830 if (rc != 0)
3831 return rc;
3832 if (pMod->u.Manual.cTlsCallbacks > 0)
3833 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3834
3835 /* Finally call the entry point. */
3836 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3837 if (rc == 0)
3838 pMod->u.Manual.enmState = KWMODSTATE_READY;
3839 else
3840 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3841 }
3842 }
3843 /*
3844 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3845 * _MSPDBSRV_ENDPOINT_ changes value.
3846 */
3847 else if (pMod->fNeedReInit)
3848 {
3849 int rc2;
3850 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3851 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3852 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3853 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3854 if (!rc && !rc2)
3855 { /* likely */ }
3856 else
3857 {
3858 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3859 if (rc2 && !rc)
3860 rc = rc2;
3861 }
3862 pMod->fNeedReInit = K_FALSE;
3863 }
3864 return rc;
3865}
3866
3867
3868/**
3869 * Looks up a module handle for a tool.
3870 *
3871 * @returns Referenced loader module on success, NULL on if not found.
3872 * @param pTool The tool.
3873 * @param hmod The module handle.
3874 */
3875static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3876{
3877 KUPTR const uHMod = (KUPTR)hmod;
3878 PKWMODULE *papMods;
3879 KU32 iEnd;
3880 KU32 i;
3881 PKWDYNLOAD pDynLoad;
3882
3883 if (pTool)
3884 { /* likely */ }
3885 else
3886 return NULL;
3887
3888 /* The executable. */
3889 if ( hmod == NULL
3890 || (pTool->u.Sandboxed.pExe && pTool->u.Sandboxed.pExe->hOurMod == hmod))
3891 {
3892 if (pTool->u.Sandboxed.pExe)
3893 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3894 return NULL;
3895 }
3896
3897 /*
3898 * Binary lookup using the module table.
3899 */
3900 papMods = pTool->u.Sandboxed.papModules;
3901 iEnd = pTool->u.Sandboxed.cModules;
3902 if (iEnd)
3903 {
3904 KU32 iStart = 0;
3905 i = iEnd / 2;
3906 for (;;)
3907 {
3908 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
3909 if (uHMod < uHModCur)
3910 {
3911 iEnd = i--;
3912 if (iStart <= i)
3913 { }
3914 else
3915 break;
3916 }
3917 else if (uHMod != uHModCur)
3918 {
3919 iStart = ++i;
3920 if (i < iEnd)
3921 { }
3922 else
3923 break;
3924 }
3925 /* We've got a match. Always return the non-virtual module (first) when there is one. */
3926 else if (!papMods[i]->pVirtualApiMod)
3927 return kwLdrModuleRetain(papMods[i]);
3928 else
3929 {
3930 while (i > 0 && papMods[i - 1]->pVirtualApiMod && papMods[i - 1]->hOurMod == hmod)
3931 i--;
3932 return kwLdrModuleRetain(papMods[i]);
3933 }
3934
3935 i = iStart + (iEnd - iStart) / 2;
3936 }
3937
3938#ifndef NDEBUG
3939 iStart = pTool->u.Sandboxed.cModules;
3940 while (--iStart > 0)
3941 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3942 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3943#endif
3944 }
3945
3946 /*
3947 * Dynamically loaded images.
3948 */
3949 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3950 if (pDynLoad->hmod == hmod)
3951 {
3952 if (pDynLoad->pMod)
3953 return kwLdrModuleRetain(pDynLoad->pMod);
3954 KWFS_TODO();
3955 return NULL;
3956 }
3957
3958 return NULL;
3959}
3960
3961/**
3962 * Adds the given module to the tool import table.
3963 *
3964 * @returns 0 on success, non-zero on failure.
3965 * @param pTool The tool.
3966 * @param pMod The module.
3967 */
3968static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3969{
3970 /*
3971 * Binary lookup. Locating the right slot for it, return if already there.
3972 */
3973 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3974 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3975 KU32 iEnd = pTool->u.Sandboxed.cModules;
3976 KU32 i;
3977 if (iEnd)
3978 {
3979 KU32 iStart = 0;
3980 i = iEnd / 2;
3981 for (;;)
3982 {
3983 PKWMODULE pCurMod = papMods[i];
3984 KUPTR const uHModCur = (KUPTR)pCurMod->hOurMod;
3985 if (uHMod < uHModCur)
3986 {
3987 iEnd = i;
3988 if (iStart < i)
3989 { }
3990 else
3991 break;
3992 }
3993 else if (uHMod != uHModCur)
3994 {
3995 iStart = ++i;
3996 if (i < iEnd)
3997 { }
3998 else
3999 break;
4000 }
4001 else
4002 {
4003 /* Already there in the table. The non-virtual module must be the first
4004 entry if we've got duplicate hmod values because of virtual modules. */
4005 if (pMod != pCurMod)
4006 {
4007 /* Skip to the last module with the same hmod. */
4008 while (i + 1 < iEnd && (KUPTR)(pCurMod = papMods[i + 1])->hOurMod == uHMod)
4009 {
4010 if (pMod == pCurMod)
4011 return 0;
4012 i++;
4013 }
4014
4015 /* Then scan backwards till the first one. */
4016 while (i > 0 && (KUPTR)(pCurMod = papMods[i - 1])->hOurMod == uHMod)
4017 {
4018 if (pMod == pCurMod)
4019 return 0;
4020 i--;
4021 }
4022 pCurMod = papMods[i];
4023 if (pMod != pCurMod)
4024 {
4025 if (pMod->pVirtualApiMod && !pCurMod->pVirtualApiMod)
4026 i++;
4027 break;
4028 }
4029 }
4030 return 0;
4031 }
4032
4033 i = iStart + (iEnd - iStart) / 2;
4034 }
4035#ifndef NDEBUG
4036 iStart = pTool->u.Sandboxed.cModules;
4037 while (--iStart > 0)
4038 {
4039 kHlpAssert(papMods[iStart] != pMod);
4040 kHlpAssert( (KUPTR)papMods[iStart]->hOurMod != uHMod
4041 || pMod->pVirtualApiMod
4042 || papMods[iStart]->pVirtualApiMod);
4043 }
4044 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod <= uHMod);
4045 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod >= uHMod);
4046#endif
4047 }
4048 else
4049 i = 0;
4050
4051 /*
4052 * Grow the table?
4053 */
4054 if ((pTool->u.Sandboxed.cModules % 16) == 0)
4055 {
4056 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
4057 if (!pvNew)
4058 return KERR_NO_MEMORY;
4059 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
4060 }
4061
4062 /* Insert it. */
4063 if (i != pTool->u.Sandboxed.cModules)
4064 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
4065 papMods[i] = kwLdrModuleRetain(pMod);
4066 pTool->u.Sandboxed.cModules++;
4067 KWLDR_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
4068 return 0;
4069}
4070
4071
4072/**
4073 * Adds the given module and all its imports to the
4074 *
4075 * @returns 0 on success, non-zero on failure.
4076 * @param pTool The tool.
4077 * @param pMod The module.
4078 */
4079static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
4080{
4081 int rc = kwToolAddModule(pTool, pMod);
4082 if (pMod->pVirtualApiMod && rc == 0)
4083 rc = kwToolAddModule(pTool, pMod->pVirtualApiMod);
4084 if (!pMod->fNative && rc == 0)
4085 {
4086 KSIZE iImp = pMod->u.Manual.cImpMods;
4087 while (iImp-- > 0)
4088 {
4089 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
4090 if (rc == 0)
4091 { }
4092 else
4093 break;
4094 }
4095 }
4096
4097 return 0;
4098}
4099
4100
4101/**
4102 * Creates a tool entry and inserts it.
4103 *
4104 * @returns Pointer to the tool entry. NULL on failure.
4105 * @param pToolFsObj The file object of the tool. The created tool
4106 * will be associated with it.
4107 *
4108 * A reference is donated by the caller and must be
4109 * released.
4110 * @param pszSearchPath The PATH environment variable value, or NULL.
4111 */
4112static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
4113{
4114 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
4115 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
4116 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
4117 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
4118 if (pTool)
4119 {
4120 KBOOL fRc;
4121 wchar_t wcSaved;
4122 wchar_t *pwcEnd;
4123 pTool->pwszPath = (wchar_t const *)(pTool + 1);
4124 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
4125 kHlpAssert(fRc); K_NOREF(fRc);
4126
4127 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
4128 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
4129 kHlpAssert(fRc);
4130
4131 /* HACK ALERT! This is to help the loader search the application directory. */
4132 pwcEnd = (wchar_t *)&pTool->pwszPath[pToolFsObj->cwcParent];
4133 wcSaved = *pwcEnd;
4134 *pwcEnd = '\0';
4135 if (!SetDllDirectoryW(pTool->pwszPath))
4136 kwErrPrintf("SetDllDirectoryW(tool) failed: %u\n", GetLastError());
4137 *pwcEnd = wcSaved;
4138
4139 pTool->enmType = KWTOOLTYPE_SANDBOXED;
4140 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
4141 NULL /*pExeMod*/, pszSearchPath);
4142 if (pTool->u.Sandboxed.pExe)
4143 {
4144 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
4145 if (rc == 0)
4146 {
4147 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
4148 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
4149 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
4150 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
4151 else
4152 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
4153 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
4154 }
4155 else
4156 {
4157 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
4158 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
4159 pTool->u.Sandboxed.pExe = NULL;
4160 pTool->enmType = KWTOOLTYPE_EXEC;
4161 }
4162 }
4163 else
4164 {
4165 kwErrPrintf("kwLdrModuleCreateNonNative failed!\n");
4166 pTool->enmType = KWTOOLTYPE_EXEC;
4167 }
4168
4169 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4170 g_cTools++;
4171 return pTool;
4172 }
4173 kwErrPrintf("kFsCacheObjAddUserData failed!\n");
4174 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4175 return NULL;
4176}
4177
4178
4179/**
4180 * Looks up the given tool, creating a new tool table entry if necessary.
4181 *
4182 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
4183 * @param pszExe The executable for the tool (not normalized).
4184 * @param cEnvVars Number of environment varibles.
4185 * @param papszEnvVars Environment variables. For getting the PATH.
4186 */
4187static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
4188{
4189 /*
4190 * We associate the tools instances with the file system objects.
4191 *
4192 * We'd like to do the lookup without invaliding the volatile parts of the
4193 * cache, thus the double lookup here. The cache gets invalidate later on.
4194 */
4195 KFSLOOKUPERROR enmError;
4196 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4197 if ( !pToolFsObj
4198 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
4199 {
4200 kFsCacheInvalidateCustomBoth(g_pFsCache);
4201 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4202 }
4203 if (pToolFsObj)
4204 {
4205 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
4206 {
4207 const char *pszSearchPath;
4208 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
4209 if (pTool)
4210 {
4211 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4212 return pTool;
4213 }
4214
4215 /*
4216 * Need to create a new tool.
4217 */
4218 pszSearchPath = NULL;
4219 while (cEnvVars-- > 0)
4220 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
4221 {
4222 pszSearchPath = &papszEnvVars[cEnvVars][5];
4223 break;
4224 }
4225
4226 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
4227 if (pTool)
4228 return pTool;
4229
4230 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
4231 }
4232 else
4233 {
4234 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4235 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
4236 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
4237 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
4238 }
4239 }
4240 else
4241 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
4242 return NULL;
4243}
4244
4245
4246
4247/*
4248 *
4249 * File system cache.
4250 * File system cache.
4251 * File system cache.
4252 *
4253 */
4254
4255
4256/**
4257 * This is for kDep.
4258 */
4259int kwFsPathExists(const char *pszPath)
4260{
4261 BirdTimeSpec_T TsIgnored;
4262 KFSLOOKUPERROR enmError;
4263 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
4264 if (pFsObj)
4265 {
4266 kFsCacheObjRelease(g_pFsCache, pFsObj);
4267 return 1;
4268 }
4269 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
4270}
4271
4272
4273/* duplicated in dir-nt-bird.c */
4274void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
4275{
4276 KFSLOOKUPERROR enmError;
4277 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
4278 if (pPathObj)
4279 {
4280 KSIZE off = pPathObj->cchParent;
4281 if (off > 0)
4282 {
4283 KSIZE offEnd = off + pPathObj->cchName;
4284 if (offEnd < cbFull)
4285 {
4286 PKFSDIR pAncestor;
4287
4288 pszFull[off + pPathObj->cchName] = '\0';
4289 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
4290
4291 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4292 {
4293 kHlpAssert(off > 1);
4294 kHlpAssert(pAncestor != NULL);
4295 kHlpAssert(pAncestor->Obj.cchName > 0);
4296 pszFull[--off] = '/';
4297 off -= pAncestor->Obj.cchName;
4298 kHlpAssert(pAncestor->Obj.cchParent == off);
4299 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4300 }
4301 kFsCacheObjRelease(g_pFsCache, pPathObj);
4302 return;
4303 }
4304 }
4305 else
4306 {
4307 if ((size_t)pPathObj->cchName + 1 < cbFull)
4308 {
4309 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
4310 pszFull[pPathObj->cchName] = '/';
4311 pszFull[pPathObj->cchName + 1] = '\0';
4312
4313 kFsCacheObjRelease(g_pFsCache, pPathObj);
4314 return;
4315 }
4316 }
4317
4318 /* do fallback. */
4319 kHlpAssertFailed();
4320 kFsCacheObjRelease(g_pFsCache, pPathObj);
4321 }
4322
4323 nt_fullpath(pszPath, pszFull, cbFull);
4324}
4325
4326
4327/**
4328 * Helper for getting the extension of a UTF-16 path.
4329 *
4330 * @returns Pointer to the extension or the terminator.
4331 * @param pwszPath The path.
4332 * @param pcwcExt Where to return the length of the extension.
4333 */
4334static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
4335{
4336 wchar_t const *pwszName = pwszPath;
4337 wchar_t const *pwszExt = NULL;
4338 for (;;)
4339 {
4340 wchar_t const wc = *pwszPath++;
4341 if (wc == '.')
4342 pwszExt = pwszPath;
4343 else if (wc == '/' || wc == '\\' || wc == ':')
4344 {
4345 pwszName = pwszPath;
4346 pwszExt = NULL;
4347 }
4348 else if (wc == '\0')
4349 {
4350 if (pwszExt)
4351 {
4352 *pcwcExt = pwszPath - pwszExt - 1;
4353 return pwszExt;
4354 }
4355 *pcwcExt = 0;
4356 return pwszPath - 1;
4357 }
4358 }
4359}
4360
4361
4362
4363/**
4364 * Parses the argument string passed in as pszSrc.
4365 *
4366 * @returns size of the processed arguments.
4367 * @param pszSrc Pointer to the commandline that's to be parsed.
4368 * @param pcArgs Where to return the number of arguments.
4369 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
4370 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
4371 *
4372 * @remarks Lifted from startuphacks-win.c
4373 */
4374static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
4375{
4376 int bs;
4377 char chQuote;
4378 char *pfFlags;
4379 int cbArgs;
4380 int cArgs;
4381
4382#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
4383#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
4384#define WHITE(c) ((c) == ' ' || (c) == '\t')
4385
4386#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
4387#define _ARG_RESPONSE 0x02 /* Argument read from response file */
4388#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
4389#define _ARG_ENV 0x08 /* Argument from environment */
4390#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
4391
4392 cArgs = 0;
4393 cbArgs = 0;
4394
4395#if 0
4396 /* argv[0] */
4397 PUTC((char)_ARG_NONZERO);
4398 PUTV;
4399 for (;;)
4400 {
4401 PUTC(*pszSrc);
4402 if (*pszSrc == 0)
4403 break;
4404 ++pszSrc;
4405 }
4406 ++pszSrc;
4407#endif
4408
4409 for (;;)
4410 {
4411 while (WHITE(*pszSrc))
4412 ++pszSrc;
4413 if (*pszSrc == 0)
4414 break;
4415 pfFlags = pchPool;
4416 PUTC((unsigned char)_ARG_NONZERO);
4417 PUTV;
4418 bs = 0; chQuote = 0;
4419 for (;;)
4420 {
4421 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
4422 {
4423 while (bs >= 2)
4424 {
4425 PUTC('\\');
4426 bs -= 2;
4427 }
4428 if (bs & 1)
4429 PUTC(*pszSrc);
4430 else
4431 {
4432 chQuote = chQuote ? 0 : *pszSrc;
4433 if (pfFlags != NULL)
4434 *pfFlags |= _ARG_DQUOTE;
4435 }
4436 bs = 0;
4437 }
4438 else if (*pszSrc == '\\')
4439 ++bs;
4440 else
4441 {
4442 while (bs != 0)
4443 {
4444 PUTC('\\');
4445 --bs;
4446 }
4447 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
4448 break;
4449 PUTC(*pszSrc);
4450 }
4451 ++pszSrc;
4452 }
4453 PUTC(0);
4454 }
4455
4456 *pcArgs = cArgs;
4457 return cbArgs;
4458}
4459
4460
4461
4462
4463/*
4464 *
4465 * Process and thread related APIs.
4466 * Process and thread related APIs.
4467 * Process and thread related APIs.
4468 *
4469 */
4470
4471/** Common worker for ExitProcess(), exit() and friends. */
4472static void WINAPI kwSandboxDoExit(int uExitCode)
4473{
4474 if (g_Sandbox.idMainThread == GetCurrentThreadId())
4475 {
4476 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
4477
4478 g_Sandbox.rcExitCode = (int)uExitCode;
4479
4480 /* Before we jump, restore the TIB as we're not interested in any
4481 exception chain stuff installed by the sandboxed executable. */
4482 *pTib = g_Sandbox.TibMainThread;
4483 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
4484
4485 longjmp(g_Sandbox.JmpBuf, 1);
4486 }
4487 KWFS_TODO();
4488}
4489
4490
4491/** ExitProcess replacement. */
4492static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
4493{
4494 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
4495 kwSandboxDoExit((int)uExitCode);
4496}
4497
4498
4499/** ExitProcess replacement. */
4500static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
4501{
4502 if (hProcess == GetCurrentProcess())
4503 kwSandboxDoExit(uExitCode);
4504 KWFS_TODO();
4505 return TerminateProcess(hProcess, uExitCode);
4506}
4507
4508
4509/** Normal CRT exit(). */
4510static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
4511{
4512 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
4513 kwSandboxDoExit(rcExitCode);
4514}
4515
4516
4517/** Quick CRT _exit(). */
4518static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
4519{
4520 /* Quick. */
4521 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
4522 kwSandboxDoExit(rcExitCode);
4523}
4524
4525
4526/** Return to caller CRT _cexit(). */
4527static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
4528{
4529 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
4530 kwSandboxDoExit(rcExitCode);
4531}
4532
4533
4534/** Quick return to caller CRT _c_exit(). */
4535static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
4536{
4537 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
4538 kwSandboxDoExit(rcExitCode);
4539}
4540
4541
4542/** Runtime error and exit _amsg_exit(). */
4543static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
4544{
4545 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
4546 kwSandboxDoExit(255);
4547}
4548
4549
4550/** CRT - terminate(). */
4551static void __cdecl kwSandbox_msvcrt_terminate(void)
4552{
4553 KW_LOG(("\nRuntime - terminate!\n"));
4554 kwSandboxDoExit(254);
4555}
4556
4557
4558/** CRT - _onexit */
4559static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
4560{
4561 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4562 {
4563 PKWEXITCALLACK pCallback;
4564 KW_LOG(("_onexit(%p)\n", pfnFunc));
4565 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4566
4567 pCallback = kHlpAlloc(sizeof(*pCallback));
4568 if (pCallback)
4569 {
4570 pCallback->pfnCallback = pfnFunc;
4571 pCallback->fAtExit = K_FALSE;
4572 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4573 g_Sandbox.pExitCallbackHead = pCallback;
4574 return pfnFunc;
4575 }
4576 return NULL;
4577 }
4578 //KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
4579 //return pfnFunc;
4580}
4581
4582
4583/** CRT - atexit */
4584static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
4585{
4586 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4587 {
4588 PKWEXITCALLACK pCallback;
4589 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4590 KW_LOG(("atexit(%p)\n", pfnFunc));
4591
4592 pCallback = kHlpAlloc(sizeof(*pCallback));
4593 if (pCallback)
4594 {
4595 pCallback->pfnCallback = (_onexit_t)pfnFunc;
4596 pCallback->fAtExit = K_TRUE;
4597 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4598 g_Sandbox.pExitCallbackHead = pCallback;
4599 return 0;
4600 }
4601 return -1;
4602 }
4603 //KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4604 //return 0;
4605}
4606
4607
4608/** Kernel32 - SetConsoleCtrlHandler(). */
4609static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4610{
4611 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4612 return TRUE;
4613}
4614
4615
4616/** The CRT internal __getmainargs() API. */
4617static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4618 int dowildcard, int const *piNewMode)
4619{
4620 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4621 *pargc = g_Sandbox.cArgs;
4622 *pargv = g_Sandbox.papszArgs;
4623 *penvp = g_Sandbox.environC;
4624
4625 /** @todo startinfo points at a newmode (setmode) value. */
4626 return 0;
4627}
4628
4629
4630/** The CRT internal __wgetmainargs() API. */
4631static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4632 int dowildcard, int const *piNewMode)
4633{
4634 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4635 *pargc = g_Sandbox.cArgs;
4636 *pargv = g_Sandbox.papwszArgs;
4637 *penvp = g_Sandbox.wenvironC;
4638
4639 /** @todo startinfo points at a newmode (setmode) value. */
4640 return 0;
4641}
4642
4643
4644
4645/** Kernel32 - GetCommandLineA() */
4646static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4647{
4648 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4649 return g_Sandbox.pszCmdLine;
4650}
4651
4652
4653/** Kernel32 - GetCommandLineW() */
4654static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4655{
4656 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4657 return g_Sandbox.pwszCmdLine;
4658}
4659
4660
4661/** Kernel32 - GetStartupInfoA() */
4662static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4663{
4664 KW_LOG(("GetStartupInfoA\n"));
4665 GetStartupInfoA(pStartupInfo);
4666 pStartupInfo->lpReserved = NULL;
4667 pStartupInfo->lpTitle = NULL;
4668 pStartupInfo->lpReserved2 = NULL;
4669 pStartupInfo->cbReserved2 = 0;
4670}
4671
4672
4673/** Kernel32 - GetStartupInfoW() */
4674static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4675{
4676 KW_LOG(("GetStartupInfoW\n"));
4677 GetStartupInfoW(pStartupInfo);
4678 pStartupInfo->lpReserved = NULL;
4679 pStartupInfo->lpTitle = NULL;
4680 pStartupInfo->lpReserved2 = NULL;
4681 pStartupInfo->cbReserved2 = 0;
4682}
4683
4684
4685/** CRT - __p___argc(). */
4686static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4687{
4688 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4689 return &g_Sandbox.cArgs;
4690}
4691
4692
4693/** CRT - __p___argv(). */
4694static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4695{
4696 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4697 return &g_Sandbox.papszArgs;
4698}
4699
4700
4701/** CRT - __p___sargv(). */
4702static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4703{
4704 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4705 return &g_Sandbox.papwszArgs;
4706}
4707
4708
4709/** CRT - __p__acmdln(). */
4710static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4711{
4712 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4713 return (char **)&g_Sandbox.pszCmdLine;
4714}
4715
4716
4717/** CRT - __p__acmdln(). */
4718static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4719{
4720 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4721 return &g_Sandbox.pwszCmdLine;
4722}
4723
4724
4725/** CRT - __p__pgmptr(). */
4726static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4727{
4728 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4729 return &g_Sandbox.pgmptr;
4730}
4731
4732
4733/** CRT - __p__wpgmptr(). */
4734static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4735{
4736 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4737 return &g_Sandbox.wpgmptr;
4738}
4739
4740
4741/** CRT - _get_pgmptr(). */
4742static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4743{
4744 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4745 *ppszValue = g_Sandbox.pgmptr;
4746 return 0;
4747}
4748
4749
4750/** CRT - _get_wpgmptr(). */
4751static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4752{
4753 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4754 *ppwszValue = g_Sandbox.wpgmptr;
4755 return 0;
4756}
4757
4758/** Just in case. */
4759static void kwSandbox_msvcrt__wincmdln(void)
4760{
4761 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4762 KWFS_TODO();
4763}
4764
4765
4766/** Just in case. */
4767static void kwSandbox_msvcrt__wwincmdln(void)
4768{
4769 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4770 KWFS_TODO();
4771}
4772
4773/** CreateThread interceptor. */
4774static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4775 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4776 DWORD fFlags, PDWORD pidThread)
4777{
4778 HANDLE hThread = NULL;
4779 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4780 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4781 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4782 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4783 {
4784 /* Allow link::DbgThread. */
4785 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4786 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4787 }
4788 else
4789 KWFS_TODO();
4790 return hThread;
4791}
4792
4793
4794/** _beginthread - create a new thread. */
4795static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4796{
4797 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4798 KWFS_TODO();
4799 return 0;
4800}
4801
4802
4803/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4804static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4805 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4806 unsigned fCreate, unsigned *pidThread)
4807{
4808 /*
4809 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4810 * whatever it needs to.
4811 */
4812 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4813 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4814 pfnThreadProc, pvUser, fCreate, pidThread));
4815 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4816 {
4817 uintptr_t rcRet;
4818 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4819 if (!s_pfnReal)
4820 {
4821 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4822 if (!s_pfnReal)
4823 {
4824 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4825 __debugbreak();
4826 }
4827 }
4828 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4829 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4830 return rcRet;
4831 }
4832
4833 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4834 KWFS_TODO();
4835 return 0;
4836}
4837
4838
4839/** _beginthreadex - create a new thread. */
4840static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex_wrapped(void *pvSecAttr, unsigned cbStack,
4841 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4842 unsigned fCreate, unsigned *pidThread, PKWCRTSLOT pSlot)
4843{
4844 /*
4845 * Since the VC++ 12 (VS 2013) compiler, the 2nd pass is now threaded.
4846 * Let it do whatever it needs to.
4847 */
4848 KW_LOG(("kwSandbox_msvcrt__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4849 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4850 pfnThreadProc, pvUser, fCreate, pidThread));
4851 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4852 && pSlot->pfnBeginThreadEx)
4853 {
4854 uintptr_t rcRet = pSlot->pfnBeginThreadEx(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4855 KW_LOG(("kwSandbox_msvcrt__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4856 return rcRet;
4857 }
4858
4859 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4860 KWFS_TODO();
4861 return 0;
4862}
4863
4864CRT_SLOT_FUNCTION_WRAPPER(uintptr_t __cdecl, kwSandbox_msvcrt__beginthreadex,
4865 (void *pvSecAttr, unsigned cbStack, unsigned (__stdcall *pfnThreadProc)(void *),
4866 void *pvUser, unsigned fCreate, unsigned *pidThread),
4867 (pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread, &g_aCrtSlots[iCrtSlot]));
4868
4869
4870
4871/*
4872 *
4873 * Environment related APIs.
4874 * Environment related APIs.
4875 * Environment related APIs.
4876 *
4877 */
4878
4879/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4880static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4881{
4882 char *pszzEnv;
4883 char *pszCur;
4884 KSIZE cbNeeded = 1;
4885 KSIZE iVar = 0;
4886
4887 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4888
4889 /* Figure how space much we need first. */
4890 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4891 cbNeeded += kHlpStrLen(pszCur) + 1;
4892
4893 /* Allocate it. */
4894 pszzEnv = kHlpAlloc(cbNeeded);
4895 if (pszzEnv)
4896 {
4897 char *psz = pszzEnv;
4898 iVar = 0;
4899 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4900 {
4901 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4902 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4903 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4904 }
4905 *psz++ = '\0';
4906 kHlpAssert((KUPTR)(psz - pszzEnv) == cbNeeded);
4907 }
4908
4909 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4910#if 0
4911 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4912 pszCur = pszzEnv;
4913 iVar = 0;
4914 while (*pszCur)
4915 {
4916 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4917 iVar++;
4918 pszCur += kHlpStrLen(pszCur) + 1;
4919 }
4920 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4921 pszCur++;
4922 fprintf(stderr, "ended at %p, after %u bytes (expected %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4923#endif
4924 return pszzEnv;
4925}
4926
4927
4928/** Kernel32 - GetEnvironmentStrings */
4929static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4930{
4931 KW_LOG(("GetEnvironmentStrings!\n"));
4932 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4933}
4934
4935
4936/** Kernel32 - GetEnvironmentStringsW */
4937static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4938{
4939 wchar_t *pwszzEnv;
4940 wchar_t *pwszCur;
4941 KSIZE cwcNeeded = 1;
4942 KSIZE iVar = 0;
4943
4944 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4945
4946 /* Figure how space much we need first. */
4947 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4948 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4949
4950 /* Allocate it. */
4951 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4952 if (pwszzEnv)
4953 {
4954 wchar_t *pwsz = pwszzEnv;
4955 iVar = 0;
4956 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4957 {
4958 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4959 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4960 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4961 }
4962 *pwsz++ = '\0';
4963 kHlpAssert((KUPTR)(pwsz - pwszzEnv) == cwcNeeded);
4964 }
4965
4966 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4967 return pwszzEnv;
4968}
4969
4970
4971/** Kernel32 - FreeEnvironmentStringsA */
4972static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4973{
4974 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4975 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4976 kHlpFree(pszzEnv);
4977 return TRUE;
4978}
4979
4980
4981/** Kernel32 - FreeEnvironmentStringsW */
4982static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4983{
4984 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4985 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4986 kHlpFree(pwszzEnv);
4987 return TRUE;
4988}
4989
4990
4991/**
4992 * Grows the environment vectors (KWSANDBOX::environC, KWSANDBOX::papszEnvVars,
4993 * KWSANDBOX::wenvironC, and KWSANDBOX::papwszEnvVars).
4994 *
4995 * @returns 0 on success, non-zero on failure.
4996 * @param pSandbox The sandbox.
4997 * @param cMin Minimum size, including terminator.
4998 */
4999static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
5000{
5001 void *pvNew;
5002 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
5003 KSIZE cNew = cOld + 256;
5004 while (cNew < cMin)
5005 cNew += 256;
5006
5007 pvNew = kHlpRealloc(pSandbox->environC, cNew * sizeof(pSandbox->environC[0]));
5008 if (pvNew)
5009 {
5010 pSandbox->environC = (char **)pvNew;
5011 pSandbox->environC[cOld] = NULL;
5012
5013 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
5014 if (pvNew)
5015 {
5016 pSandbox->papszEnvVars = (char **)pvNew;
5017 pSandbox->papszEnvVars[cOld] = NULL;
5018
5019 pvNew = kHlpRealloc(pSandbox->wenvironC, cNew * sizeof(pSandbox->wenvironC[0]));
5020 if (pvNew)
5021 {
5022 pSandbox->wenvironC = (wchar_t **)pvNew;
5023 pSandbox->wenvironC[cOld] = NULL;
5024
5025 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
5026 if (pvNew)
5027 {
5028 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
5029 pSandbox->papwszEnvVars[cOld] = NULL;
5030
5031 pSandbox->cEnvVarsAllocated = cNew;
5032 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
5033 cNew, pSandbox->environC, pSandbox->wenvironC, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
5034 return 0;
5035 }
5036 }
5037 }
5038 }
5039 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
5040 return KERR_NO_MEMORY;
5041}
5042
5043
5044/**
5045 * Sets an environment variable, ANSI style.
5046 *
5047 * @returns 0 on success, non-zero on failure.
5048 * @param pSandbox The sandbox.
5049 * @param pchVar The variable name.
5050 * @param cchVar The length of the name.
5051 * @param pszValue The value.
5052 */
5053static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
5054{
5055 /* Allocate and construct the new strings. */
5056 KSIZE cchTmp = kHlpStrLen(pszValue);
5057 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
5058 if (pszNew)
5059 {
5060 wchar_t *pwszNew;
5061 kHlpMemCopy(pszNew, pchVar, cchVar);
5062 pszNew[cchVar] = '=';
5063 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
5064 cchTmp += cchVar + 1;
5065 pszNew[cchTmp] = '\0';
5066
5067 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
5068 if (pwszNew)
5069 {
5070 /* Look it up. */
5071 KSIZE iVar = 0;
5072 char *pszEnv;
5073 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5074 {
5075 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5076 && pszEnv[cchVar] == '=')
5077 {
5078 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5079 " iVar=%d: %p='%s' and %p='%ls'\n",
5080 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5081 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5082 iVar, pszNew, pszNew, pwszNew, pwszNew));
5083
5084 kHlpFree(pSandbox->papszEnvVars[iVar]);
5085 pSandbox->papszEnvVars[iVar] = pszNew;
5086 pSandbox->environC[iVar] = pszNew;
5087
5088 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5089 pSandbox->papwszEnvVars[iVar] = pwszNew;
5090 pSandbox->wenvironC[iVar] = pwszNew;
5091 return 0;
5092 }
5093 iVar++;
5094 }
5095
5096 /* Not found, do we need to grow the table first? */
5097 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5098 kwSandboxGrowEnv(pSandbox, iVar + 2);
5099 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5100 {
5101 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5102
5103 pSandbox->papszEnvVars[iVar + 1] = NULL;
5104 pSandbox->papszEnvVars[iVar] = pszNew;
5105 pSandbox->environC[iVar + 1] = NULL;
5106 pSandbox->environC[iVar] = pszNew;
5107
5108 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5109 pSandbox->papwszEnvVars[iVar] = pwszNew;
5110 pSandbox->wenvironC[iVar + 1] = NULL;
5111 pSandbox->wenvironC[iVar] = pwszNew;
5112 return 0;
5113 }
5114
5115 kHlpFree(pwszNew);
5116 }
5117 kHlpFree(pszNew);
5118 }
5119 KW_LOG(("Out of memory!\n"));
5120 return 0;
5121}
5122
5123
5124/**
5125 * Sets an environment variable, UTF-16 style.
5126 *
5127 * @returns 0 on success, non-zero on failure.
5128 * @param pSandbox The sandbox.
5129 * @param pwcVar The variable name.
5130 * @param cwcVar The length of the name.
5131 * @param pwszValue The value.
5132 */
5133static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
5134{
5135 /* Allocate and construct the new strings. */
5136 KSIZE cwcTmp = kwUtf16Len(pwszValue);
5137 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
5138 if (pwszNew)
5139 {
5140 char *pszNew;
5141 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
5142 pwszNew[cwcVar] = '=';
5143 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
5144 cwcTmp += cwcVar + 1;
5145 pwszNew[cwcVar] = '\0';
5146
5147 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
5148 if (pszNew)
5149 {
5150 /* Look it up. */
5151 KSIZE iVar = 0;
5152 wchar_t *pwszEnv;
5153 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5154 {
5155 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
5156 && pwszEnv[cwcVar] == '=')
5157 {
5158 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5159 " iVar=%d: %p='%s' and %p='%ls'\n",
5160 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5161 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5162 iVar, pszNew, pszNew, pwszNew, pwszNew));
5163
5164 kHlpFree(pSandbox->papszEnvVars[iVar]);
5165 pSandbox->papszEnvVars[iVar] = pszNew;
5166 pSandbox->environC[iVar] = pszNew;
5167
5168 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5169 pSandbox->papwszEnvVars[iVar] = pwszNew;
5170 pSandbox->wenvironC[iVar] = pwszNew;
5171 return 0;
5172 }
5173 iVar++;
5174 }
5175
5176 /* Not found, do we need to grow the table first? */
5177 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5178 kwSandboxGrowEnv(pSandbox, iVar + 2);
5179 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5180 {
5181 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5182
5183 pSandbox->papszEnvVars[iVar + 1] = NULL;
5184 pSandbox->papszEnvVars[iVar] = pszNew;
5185 pSandbox->environC[iVar + 1] = NULL;
5186 pSandbox->environC[iVar] = pszNew;
5187
5188 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5189 pSandbox->papwszEnvVars[iVar] = pwszNew;
5190 pSandbox->wenvironC[iVar + 1] = NULL;
5191 pSandbox->wenvironC[iVar] = pwszNew;
5192 return 0;
5193 }
5194
5195 kHlpFree(pwszNew);
5196 }
5197 kHlpFree(pszNew);
5198 }
5199 KW_LOG(("Out of memory!\n"));
5200 return 0;
5201}
5202
5203
5204/** ANSI unsetenv worker. */
5205static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5206{
5207 KSIZE iVar = 0;
5208 char *pszEnv;
5209 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5210 {
5211 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5212 && pszEnv[cchVar] == '=')
5213 {
5214 KSIZE cVars = iVar;
5215 while (pSandbox->papszEnvVars[cVars])
5216 cVars++;
5217 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
5218 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
5219
5220 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5221 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5222 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5223
5224 kHlpFree(pSandbox->papszEnvVars[iVar]);
5225 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5226 pSandbox->environC[iVar] = pSandbox->papszEnvVars[cVars];
5227 pSandbox->papszEnvVars[cVars] = NULL;
5228 pSandbox->environC[cVars] = NULL;
5229
5230 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5231 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5232 pSandbox->wenvironC[iVar] = pSandbox->papwszEnvVars[cVars];
5233 pSandbox->papwszEnvVars[cVars] = NULL;
5234 pSandbox->wenvironC[cVars] = NULL;
5235 return 0;
5236 }
5237 iVar++;
5238 }
5239 return KERR_ENVVAR_NOT_FOUND;
5240}
5241
5242
5243/** UTF-16 unsetenv worker. */
5244static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5245{
5246 KSIZE iVar = 0;
5247 wchar_t *pwszEnv;
5248 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5249 {
5250 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5251 && pwszEnv[cwcVar] == '=')
5252 {
5253 KSIZE cVars = iVar;
5254 while (pSandbox->papwszEnvVars[cVars])
5255 cVars++;
5256 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
5257 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
5258
5259 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5260 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5261 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5262
5263 kHlpFree(pSandbox->papszEnvVars[iVar]);
5264 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5265 pSandbox->environC[iVar] = pSandbox->papszEnvVars[cVars];
5266 pSandbox->papszEnvVars[cVars] = NULL;
5267 pSandbox->environC[cVars] = NULL;
5268
5269 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5270 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5271 pSandbox->wenvironC[iVar] = pSandbox->papwszEnvVars[cVars];
5272 pSandbox->papwszEnvVars[cVars] = NULL;
5273 pSandbox->wenvironC[cVars] = NULL;
5274 return 0;
5275 }
5276 iVar++;
5277 }
5278 return KERR_ENVVAR_NOT_FOUND;
5279}
5280
5281
5282
5283/** ANSI getenv worker. */
5284static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5285{
5286 KSIZE iVar = 0;
5287 char *pszEnv;
5288 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
5289 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5290 && pszEnv[cchVar] == '=')
5291 return &pszEnv[cchVar + 1];
5292 return NULL;
5293}
5294
5295
5296/** UTF-16 getenv worker. */
5297static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5298{
5299 KSIZE iVar = 0;
5300 wchar_t *pwszEnv;
5301 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
5302 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5303 && pwszEnv[cwcVar] == '=')
5304 return &pwszEnv[cwcVar + 1];
5305 return NULL;
5306}
5307
5308
5309/** Kernel32 - GetEnvironmentVariableA() */
5310static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
5311{
5312 char *pszFoundValue;
5313 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5314
5315 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5316 if (pszFoundValue)
5317 {
5318 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
5319 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
5320 return cchRet;
5321 }
5322 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
5323 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5324 return 0;
5325}
5326
5327
5328/** Kernel32 - GetEnvironmentVariableW() */
5329static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
5330{
5331 wchar_t *pwszFoundValue;
5332 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5333
5334 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5335 if (pwszFoundValue)
5336 {
5337 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
5338 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
5339 return cchRet;
5340 }
5341 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
5342 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5343 return 0;
5344}
5345
5346
5347/** Kernel32 - SetEnvironmentVariableA() */
5348static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
5349{
5350 int rc;
5351 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5352
5353 if (pszValue)
5354 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5355 else
5356 {
5357 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5358 rc = 0; //??
5359 }
5360 if (rc == 0)
5361 {
5362 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
5363 return TRUE;
5364 }
5365 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5366 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
5367 return FALSE;
5368}
5369
5370
5371/** Kernel32 - SetEnvironmentVariableW() */
5372static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
5373{
5374 int rc;
5375 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5376
5377 if (pwszValue)
5378 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5379 else
5380 {
5381 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5382 rc = 0; //??
5383 }
5384 if (rc == 0)
5385 {
5386 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
5387 return TRUE;
5388 }
5389 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5390 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
5391 return FALSE;
5392}
5393
5394
5395/** Kernel32 - ExpandEnvironmentStringsA() */
5396static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
5397{
5398 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5399 KWFS_TODO();
5400 return 0;
5401}
5402
5403
5404/** Kernel32 - ExpandEnvironmentStringsW() */
5405static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
5406{
5407 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5408 KWFS_TODO();
5409 return 0;
5410}
5411
5412
5413/** CRT - _putenv(). */
5414static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
5415{
5416 int rc;
5417 char const *pszEqual;
5418 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5419
5420 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
5421 if (pszEqual)
5422 {
5423 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
5424 if (rc == 0)
5425 { }
5426 else
5427 rc = -1;
5428 }
5429 else
5430 {
5431 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
5432 rc = 0;
5433 }
5434 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
5435 return rc;
5436}
5437
5438
5439/** CRT - _wputenv(). */
5440static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
5441{
5442 int rc;
5443 wchar_t const *pwszEqual;
5444 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5445
5446 pwszEqual = wcschr(pwszVarEqualValue, '=');
5447 if (pwszEqual)
5448 {
5449 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
5450 if (rc == 0)
5451 { }
5452 else
5453 rc = -1;
5454 }
5455 else
5456 {
5457 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
5458 rc = 0;
5459 }
5460 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
5461 return rc;
5462}
5463
5464
5465/** CRT - _putenv_s(). */
5466static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
5467{
5468 char const *pszEqual;
5469 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5470
5471 pszEqual = kHlpStrChr(pszVar, '=');
5472 if (pszEqual == NULL)
5473 {
5474 if (pszValue)
5475 {
5476 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5477 if (rc == 0)
5478 {
5479 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
5480 return 0;
5481 }
5482 }
5483 else
5484 {
5485 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5486 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
5487 return 0;
5488 }
5489 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
5490 return ENOMEM;
5491 }
5492 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
5493 return EINVAL;
5494}
5495
5496
5497/** CRT - _wputenv_s(). */
5498static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
5499{
5500 wchar_t const *pwszEqual;
5501 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5502
5503 pwszEqual = wcschr(pwszVar, '=');
5504 if (pwszEqual == NULL)
5505 {
5506 if (pwszValue)
5507 {
5508 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5509 if (rc == 0)
5510 {
5511 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
5512 return 0;
5513 }
5514 }
5515 else
5516 {
5517 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5518 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
5519 return 0;
5520 }
5521 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
5522 return ENOMEM;
5523 }
5524 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
5525 return EINVAL;
5526}
5527
5528
5529/** CRT - get pointer to the __initenv variable (initial environment). */
5530static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
5531{
5532 KW_LOG(("__p___initenv\n"));
5533 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5534 KWFS_TODO();
5535 return &g_Sandbox.initenv;
5536}
5537
5538
5539/** CRT - get pointer to the __winitenv variable (initial environment). */
5540static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
5541{
5542 KW_LOG(("__p___winitenv\n"));
5543 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5544 KWFS_TODO();
5545 return &g_Sandbox.winitenv;
5546}
5547
5548
5549/** CRT - get pointer to the _environ variable (current environment). */
5550static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
5551{
5552 KW_LOG(("__p__environ\n"));
5553 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5554 return &g_Sandbox.environC;
5555}
5556
5557
5558/** CRT - get pointer to the _wenviron variable (current environment). */
5559static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
5560{
5561 KW_LOG(("__p__wenviron\n"));
5562 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5563 return &g_Sandbox.wenvironC;
5564}
5565
5566
5567/** CRT - get the _environ variable (current environment).
5568 * @remarks Not documented or prototyped? */
5569static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
5570{
5571 KWFS_TODO(); /** @todo check the callers expectations! */
5572 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5573 *ppapszEnviron = g_Sandbox.environC;
5574 return 0;
5575}
5576
5577
5578/** CRT - get the _wenviron variable (current environment).
5579 * @remarks Not documented or prototyped? */
5580static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
5581{
5582 KWFS_TODO(); /** @todo check the callers expectations! */
5583 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5584 *ppapwszEnviron = g_Sandbox.wenvironC;
5585 return 0;
5586}
5587
5588
5589/** CRT - _wdupenv_s() (see _tdupenv_s(). */
5590static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
5591 PKWCRTSLOT pSlot)
5592{
5593 errno_t rc;
5594 wchar_t *pwszValue;
5595 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5596
5597 if (ppwszValue)
5598 {
5599 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
5600 if (pwszValue)
5601 {
5602 size_t cwcValue = wcslen(pwszValue);
5603 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
5604 if (pwszDst)
5605 {
5606 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
5607 pwszDst[cwcValue] = '\0';
5608 *ppwszValue = pwszDst;
5609 if (pcwcValue)
5610 *pcwcValue = cwcValue;
5611 rc = 0;
5612 }
5613 else
5614 {
5615 *ppwszValue = NULL;
5616 if (pcwcValue)
5617 *pcwcValue = 0;
5618 rc = ENOMEM;
5619 }
5620 }
5621 else
5622 {
5623 *ppwszValue = NULL;
5624 if (pcwcValue)
5625 *pcwcValue = 0;
5626 rc = 0;
5627 }
5628 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5629 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5630 }
5631 else
5632 {
5633 /*
5634 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5635 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5636 * was set to a value.
5637 */
5638 if (pcwcValue)
5639 *pcwcValue = 0;
5640 rc = EINVAL;
5641 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5642 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5643 }
5644 return rc;
5645}
5646CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5647 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5648 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5649
5650
5651
5652/*
5653 *
5654 * Loader related APIs
5655 * Loader related APIs
5656 * Loader related APIs
5657 *
5658 */
5659
5660/**
5661 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5662 */
5663static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5664{
5665 /* Load it first. */
5666 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5667 if (hmod)
5668 {
5669 pDynLoad->hmod = hmod;
5670 pDynLoad->pMod = NULL; /* indicates special */
5671
5672 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5673 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5674 KWLDR_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5675 }
5676 else
5677 kHlpFree(pDynLoad);
5678 return hmod;
5679}
5680
5681
5682/**
5683 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5684 */
5685static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5686{
5687 static const char s_szDll[] = ".dll";
5688 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5689 PKWMODULE pMod;
5690 char szNormPath[256];
5691
5692 /*
5693 * Lower case it and make sure it ends with .dll.
5694 */
5695 if (cbFilename > sizeof(szNormPath))
5696 {
5697 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5698 return NULL;
5699 }
5700 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5701 _strlwr(szNormPath);
5702 kHlpAssert(cbFilename > 7 /* api-ms- */ );
5703 if (strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5704 {
5705 if (cbFilename + sizeof(s_szDll) - 1 > sizeof(szNormPath))
5706 {
5707 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5708 return NULL;
5709 }
5710
5711 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5712 cbFilename += sizeof(s_szDll) - 1;
5713 }
5714
5715 /*
5716 * Try load it.
5717 */
5718 pMod = kwLdrModuleTryLoadVirtualDll(szNormPath, cbFilename - 1);
5719 if (pMod)
5720 {
5721 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5722
5723 pDynLoad->pMod = pMod;
5724 pDynLoad->hmod = pMod->hOurMod;
5725
5726 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5727 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5728 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5729 return pDynLoad->hmod;
5730 }
5731 kHlpFree(pDynLoad);
5732 return NULL;
5733}
5734
5735
5736/** Kernel32 - LoadLibraryExA() */
5737static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5738{
5739 KSIZE cchFilename = kHlpStrLen(pszFilename);
5740 const char *pszSearchPath;
5741 PKWDYNLOAD pDynLoad;
5742 PKWMODULE pMod;
5743 int rc;
5744 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5745 //fprintf(stderr, "LoadLibraryExA: %s, %#x\n", pszFilename, fFlags);
5746
5747 /*
5748 * Deal with a couple of extremely unlikely special cases right away.
5749 */
5750 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5751 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5752 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5753 { /* likely */ }
5754 else
5755 {
5756 KWFS_TODO();
5757 return LoadLibraryExA(pszFilename, hFile, fFlags);
5758 }
5759
5760 /*
5761 * Check if we've already got a dynload entry for this one.
5762 */
5763 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5764 if ( pDynLoad->cchRequest == cchFilename
5765 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5766 {
5767 if (pDynLoad->pMod)
5768 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5769 else
5770 rc = 0;
5771 if (rc == 0)
5772 {
5773 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5774 return pDynLoad->hmod;
5775 }
5776 SetLastError(ERROR_DLL_INIT_FAILED);
5777 return NULL;
5778 }
5779
5780 /*
5781 * Allocate a dynload entry for the request.
5782 */
5783 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5784 if (pDynLoad)
5785 {
5786 pDynLoad->cchRequest = cchFilename;
5787 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5788 }
5789 else
5790 {
5791 KWLDR_LOG(("LoadLibraryExA: Out of memory!\n"));
5792 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5793 return NULL;
5794 }
5795
5796 /*
5797 * Deal with resource / data DLLs.
5798 */
5799 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5800 | LOAD_LIBRARY_AS_DATAFILE
5801 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5802 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5803 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5804
5805 /*
5806 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5807 */
5808 if ( kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5809 && kHlpIsFilenameOnly(pszFilename))
5810 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5811
5812 /*
5813 * Normal library loading.
5814 * We start by being very lazy and reusing the code for resolving imports.
5815 */
5816 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5817 if (!kHlpIsFilenameOnly(pszFilename))
5818 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5819#if 1 /* HACK ALERT! We run into trouble with a 2nd mspdb140.dll instance (x64 + x86), so use the one already loaded. A call
5820 * to NdrClientCall2 at ConnectToServer+0x426 fails with E_INVALIDARG. Problems with multiple connections from same PID? */
5821 else if ( strcmp(pszFilename, "mspdb140.dll") == 0
5822 && GetModuleHandleA(pszFilename) != NULL)
5823 {
5824 pMod = kwLdrModuleForLoadedNativeByHandle(GetModuleHandleA(pszFilename), K_FALSE, pszFilename);
5825 KWLDR_LOG(("LoadLibraryExA: mspdb140 hack: pMod=%p\n", pMod));
5826 }
5827#endif
5828 else
5829 {
5830 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5831 if (rc != 0)
5832 pMod = NULL;
5833 }
5834 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5835 {
5836 /* Enter it into the tool module table and dynamic link request cache. */
5837 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5838
5839 pDynLoad->pMod = pMod;
5840 pDynLoad->hmod = pMod->hOurMod;
5841
5842 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5843 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5844
5845 /*
5846 * Make sure it's initialized (need to link it first since DllMain may
5847 * use loader APIs).
5848 */
5849 rc = kwLdrModuleInitTree(pMod);
5850 if (rc == 0)
5851 {
5852 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5853 return pDynLoad->hmod;
5854 }
5855
5856 SetLastError(ERROR_DLL_INIT_FAILED);
5857 }
5858 else
5859 {
5860 KWFS_TODO();
5861 kHlpFree(pDynLoad);
5862 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5863 }
5864 return NULL;
5865}
5866
5867
5868/** Kernel32 - LoadLibraryExA() for native overloads */
5869static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5870{
5871 char szPath[1024];
5872 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5873
5874 /*
5875 * We may have to help resolved unqualified DLLs living in the executable directory.
5876 */
5877 if ( kHlpIsFilenameOnly(pszFilename)
5878 && g_Sandbox.pTool
5879 && g_Sandbox.pTool->u.Sandboxed.pExe)
5880 {
5881 KSIZE const cchFilename = kHlpStrLen(pszFilename);
5882#define MY_IMATCH(a_szName) (cchFilename == sizeof(a_szName) - 1 && kHlpStrICompAscii(pszFilename, a_szName) == 0)
5883 if ( !kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5884 && !MY_IMATCH("ntdll")
5885 && !MY_IMATCH("kernel32")
5886 && !MY_IMATCH("ntdll.dll")
5887 && !MY_IMATCH("kernelbase")
5888 && !MY_IMATCH("kernel32.dll")
5889 && !MY_IMATCH("kernelbase.dll")
5890 )
5891#undef MY_IMATCH
5892 {
5893 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5894 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5895 {
5896 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5897 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5898 if (kwFsPathExists(szPath))
5899 {
5900 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5901 pszFilename = szPath;
5902 }
5903 }
5904
5905 if (pszFilename != szPath)
5906 {
5907 KSIZE cchSuffix = 0;
5908 KBOOL fNeedSuffix = K_FALSE;
5909 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5910 kHlpAssert(pszCur);
5911 if (pszCur)
5912 {
5913 while (*pszCur != '\0')
5914 {
5915 /* Find the end of the component */
5916 KSIZE cch = 0;
5917 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5918 cch++;
5919
5920 if ( cch > 0 /* wrong, but whatever */
5921 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5922 {
5923 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5924 if ( szPath[cch - 1] != ':'
5925 && szPath[cch - 1] != '/'
5926 && szPath[cch - 1] != '\\')
5927 *pszDst++ = '\\';
5928 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5929 if (fNeedSuffix)
5930 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5931 *pszDst = '\0';
5932
5933 if (kwFsPathExists(szPath))
5934 {
5935 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5936 pszFilename = szPath;
5937 break;
5938 }
5939 }
5940
5941 /* Advance */
5942 pszCur += cch;
5943 while (*pszCur == ';')
5944 pszCur++;
5945 }
5946 }
5947 }
5948 }
5949 }
5950
5951 return LoadLibraryExA(pszFilename, hFile, fFlags);
5952}
5953
5954
5955/** Kernel32 - LoadLibraryExW() for native overloads */
5956static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5957{
5958 char szTmp[4096];
5959 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5960 if (cchTmp < sizeof(szTmp))
5961 return kwSandbox_Kernel32_Native_LoadLibraryExA(szTmp, hFile, fFlags);
5962
5963 KWFS_TODO();
5964 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5965 return NULL;
5966}
5967
5968
5969/** Kernel32 - LoadLibraryExW() */
5970static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5971{
5972 char szTmp[4096];
5973 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5974 if (cchTmp < sizeof(szTmp))
5975 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5976
5977 KWFS_TODO();
5978 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5979 return NULL;
5980}
5981
5982/** Kernel32 - LoadLibraryA() */
5983static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5984{
5985 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5986}
5987
5988
5989/** Kernel32 - LoadLibraryW() */
5990static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5991{
5992 char szTmp[4096];
5993 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5994 if (cchTmp < sizeof(szTmp))
5995 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5996 KWFS_TODO();
5997 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5998 return NULL;
5999}
6000
6001
6002/** Kernel32 - FreeLibrary() */
6003static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
6004{
6005 /* Ignored, we like to keep everything loaded. */
6006 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6007 return TRUE;
6008}
6009
6010
6011/** Worker for GetModuleHandleA/W for handling cached modules. */
6012static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
6013{
6014 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
6015 if (hmod)
6016 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
6017 hmod, g_aGetModuleHandleCache[i].pszName));
6018 else
6019 {
6020 /*
6021 * The first time around we have to make sure we have a module table
6022 * entry for it, if not we add one. We need to add it to the tools
6023 * module list to for it to work.
6024 */
6025 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
6026 g_aGetModuleHandleCache[i].fAlwaysPresent);
6027 if (pMod)
6028 {
6029 hmod = pMod->hOurMod;
6030 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
6031 {
6032 kwToolAddModule(g_Sandbox.pTool, pMod);
6033 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
6034 hmod, g_aGetModuleHandleCache[i].pszName));
6035 }
6036 else
6037 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
6038 hmod, g_aGetModuleHandleCache[i].pszName));
6039
6040 }
6041 }
6042 return hmod;
6043}
6044
6045
6046/** Kernel32 - GetModuleHandleA() */
6047static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
6048{
6049 KSIZE i;
6050 KSIZE cchModule;
6051 PKWDYNLOAD pDynLoad;
6052 KSIZE cchSuffix;
6053 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6054 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6055
6056 /*
6057 * The executable.
6058 */
6059 if (pszModule == NULL)
6060 {
6061 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(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 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
6069
6070 /*
6071 * Cache of system modules we've seen queried.
6072 */
6073 cchModule = kHlpStrLen(pszModule);
6074 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6075 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
6076 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
6077 || ( cchSuffix > 0
6078 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
6079 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
6080 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".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 char *pszPath = pDynLoad->pMod->pszPath;
6090 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
6091 if ( stricmp(pszPath, pszModule) == 0
6092 || stricmp(pszName, pszModule) == 0
6093 || ( cchSuffix > 0
6094 && strnicmp(pszName, pszModule, cchModule) == 0
6095 && stricmp(&pszName[cchModule], ".dll") == 0))
6096 {
6097 if ( pDynLoad->pMod->fNative
6098 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6099 {
6100 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
6101 return pDynLoad->hmod;
6102 }
6103 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
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/** @todo virtual api DLLs */
6114 if (kHlpStrNICompAscii(pszModule, "api-ms-win-", 11) == 0)
6115 {
6116 HMODULE hmod = GetModuleHandleA(pszModule);
6117 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
6118 if (hmod)
6119 {
6120 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6121 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
6122 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6123 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
6124 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6125 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
6126 if (hmod == GetModuleHandleW(L"UCRTBASE.DLL"))
6127 return kwSandbox_Kernel32_GetModuleHandleA("UCRTBASE.DLL");
6128 }
6129 else
6130 dwErr = GetLastError();
6131 }
6132
6133 kwErrPrintf("pszModule=%s\n", pszModule);
6134 KWFS_TODO();
6135 SetLastError(ERROR_MOD_NOT_FOUND);
6136 return NULL;
6137}
6138
6139
6140/** Kernel32 - GetModuleHandleW() */
6141static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
6142{
6143 KSIZE i;
6144 KSIZE cwcModule;
6145 PKWDYNLOAD pDynLoad;
6146 KSIZE cwcSuffix;
6147 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6148 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6149
6150 /*
6151 * The executable.
6152 */
6153 if (pwszModule == NULL)
6154 {
6155 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
6156 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
6157 }
6158
6159 /*
6160 * If no path of suffix, pretend it ends with .DLL.
6161 */
6162 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
6163
6164 /*
6165 * Cache of system modules we've seen queried.
6166 */
6167 cwcModule = kwUtf16Len(pwszModule);
6168 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6169 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
6170 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
6171 || ( cwcSuffix > 0
6172 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
6173 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
6174 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
6175 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
6176
6177 /*
6178 * Modules we've dynamically loaded.
6179 */
6180 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6181 if (pDynLoad->pMod)
6182 {
6183 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
6184 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
6185 if ( _wcsicmp(pwszPath, pwszModule) == 0
6186 || _wcsicmp(pwszName, pwszModule) == 0
6187 || ( cwcSuffix
6188 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
6189 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
6190 {
6191 if ( pDynLoad->pMod->fNative
6192 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6193 {
6194 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
6195 return pDynLoad->hmod;
6196 }
6197 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
6198 SetLastError(ERROR_MOD_NOT_FOUND);
6199 return NULL;
6200 }
6201 }
6202
6203 /*
6204 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6205 * to and go via the g_aGetModuleHandleCache cache.
6206 */
6207 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
6208 {
6209 HMODULE hmod = GetModuleHandleW(pwszModule);
6210 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
6211 if (hmod)
6212 {
6213 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6214 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
6215 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6216 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
6217 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6218 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
6219 }
6220 else
6221 dwErr = GetLastError();
6222 }
6223
6224 kwErrPrintf("pwszModule=%ls\n", pwszModule);
6225 KWFS_TODO();
6226 SetLastError(dwErr);
6227 return NULL;
6228}
6229
6230
6231/** Used to debug dynamically resolved procedures. */
6232static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
6233{
6234#ifdef _MSC_VER
6235 __debugbreak();
6236#else
6237 KWFS_TODO();
6238#endif
6239 return ~(UINT)0;
6240}
6241
6242
6243#ifndef NDEBUG
6244/*
6245 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
6246 */
6247# if K_ARCH == K_ARCH_X86_32
6248static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
6249# else
6250static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
6251# endif
6252typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
6253typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
6254typedef struct KWCXINTERCEPTORENTRY
6255{
6256 PFNINVOKECOMPILERPASSW pfnOrg;
6257 PKWMODULE pModule;
6258 PFNINVOKECOMPILERPASSW pfnWrap;
6259} KWCXINTERCEPTORENTRY;
6260
6261static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
6262 KWCXINTERCEPTORENTRY *pEntry)
6263{
6264 int i;
6265 KIPTR rcExit;
6266 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
6267 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
6268 for (i = 0; i < cArgs; i++)
6269 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
6270
6271 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
6272
6273 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
6274 return rcExit;
6275}
6276
6277static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
6278static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
6279static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
6280
6281static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
6282{
6283 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
6284 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
6285 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
6286};
6287
6288static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6289{
6290 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
6291}
6292
6293static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6294{
6295 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
6296}
6297
6298static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6299{
6300 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
6301}
6302
6303#endif /* !NDEBUG */
6304
6305
6306/** Kernel32 - GetProcAddress() */
6307static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6308{
6309 KSIZE i;
6310 PKWMODULE pMod;
6311 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6312
6313 /*
6314 * Try locate the module.
6315 */
6316 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6317 if (pMod)
6318 {
6319 KLDRADDR uValue;
6320 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
6321 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
6322 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
6323 KU32_MAX /*iSymbol*/,
6324 pszProc,
6325 kHlpStrLen(pszProc),
6326 NULL /*pszVersion*/,
6327 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
6328 &uValue,
6329 NULL /*pfKind*/);
6330 if (rc == 0)
6331 {
6332 //static int s_cDbgGets = 0;
6333 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
6334 i = g_cSandboxGetProcReplacements;
6335 while (i-- > 0)
6336 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
6337 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
6338 {
6339 if ( !g_aSandboxGetProcReplacements[i].pszModule
6340 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
6341 {
6342 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
6343 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
6344 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
6345 {
6346 if (!g_aSandboxReplacements[i].fCrtSlotArray)
6347 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
6348 else
6349 {
6350 if (pMod->iCrtSlot == KU8_MAX)
6351 {
6352 rc = kwLdrModuleCreateCrtSlot(pMod);
6353 if (rc)
6354 {
6355 KW_LOG(("GetProcAddress: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
6356 SetLastError(ERROR_INTERNAL_ERROR);
6357 return NULL;
6358 }
6359 }
6360 uValue = ((KUPTR *)g_aSandboxGetProcReplacements[i].pfnReplacement)[pMod->iCrtSlot];
6361 }
6362
6363 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6364 }
6365 kwLdrModuleRelease(pMod);
6366 return (FARPROC)(KUPTR)uValue;
6367 }
6368 }
6369
6370#ifndef NDEBUG
6371 /* Intercept the compiler pass method, dumping arguments. */
6372 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
6373 {
6374 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6375 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6376 {
6377 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6378 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6379 break;
6380 }
6381 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6382 while (i-- > 0)
6383 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6384 {
6385 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6386 g_aCxInterceptorEntries[i].pModule = pMod;
6387 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6388 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6389 break;
6390 }
6391 }
6392#endif
6393 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6394 kwLdrModuleRelease(pMod);
6395 //s_cDbgGets++;
6396 //if (s_cGets >= 3)
6397 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6398 return (FARPROC)(KUPTR)uValue;
6399 }
6400
6401 KWFS_TODO();
6402 SetLastError(ERROR_PROC_NOT_FOUND);
6403 kwLdrModuleRelease(pMod);
6404 return NULL;
6405 }
6406
6407 /*
6408 * Hmm... could be a cached module-by-name.
6409 */
6410 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6411 if (g_aGetModuleHandleCache[i].hmod == hmod)
6412 return GetProcAddress(hmod, pszProc);
6413
6414 KWFS_TODO();
6415 return GetProcAddress(hmod, pszProc);
6416}
6417
6418
6419#ifndef NDEBUG
6420/** Kernel32 - GetProcAddress() - native replacement for debugging only. */
6421static FARPROC WINAPI kwSandbox_Kernel32_Native_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6422{
6423 FARPROC pfnRet = GetProcAddress(hmod, pszProc);
6424 KWLDR_LOG(("kwSandbox_Kernel32_Native_GetProcAddress(%p, %s) -> %p\n", hmod, pszProc, pfnRet));
6425 return pfnRet;
6426}
6427#endif
6428
6429
6430/** Kernel32 - GetModuleFileNameA() */
6431static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6432{
6433 PKWMODULE pMod;
6434 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6435
6436 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6437 if (pMod != NULL)
6438 {
6439 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6440 kwLdrModuleRelease(pMod);
6441 return cbRet;
6442 }
6443 KWFS_TODO();
6444 return 0;
6445}
6446
6447
6448/** Kernel32 - GetModuleFileNameW() */
6449static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6450{
6451 PKWMODULE pMod;
6452 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6453
6454 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6455 if (pMod)
6456 {
6457 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6458 kwLdrModuleRelease(pMod);
6459 return cwcRet;
6460 }
6461
6462 KWFS_TODO();
6463 return 0;
6464}
6465
6466
6467/** NtDll - RtlPcToFileHeader
6468 * This is necessary for msvcr100.dll!CxxThrowException. */
6469static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6470{
6471 PVOID pvRet;
6472
6473 /*
6474 * Do a binary lookup of the module table for the current tool.
6475 * This will give us a
6476 */
6477 if (g_Sandbox.fRunning)
6478 {
6479 KUPTR const uPC = (KUPTR)pvPC;
6480 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6481 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6482 KU32 i;
6483 if (iEnd)
6484 {
6485 KU32 iStart = 0;
6486 i = iEnd / 2;
6487 for (;;)
6488 {
6489 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6490 if (uPC < uHModCur)
6491 {
6492 iEnd = i;
6493 if (iStart < i)
6494 { }
6495 else
6496 break;
6497 }
6498 else if (uPC != uHModCur)
6499 {
6500 iStart = ++i;
6501 if (i < iEnd)
6502 { }
6503 else
6504 break;
6505 }
6506 else
6507 {
6508 /* This isn't supposed to happen. */
6509 break;
6510 }
6511
6512 i = iStart + (iEnd - iStart) / 2;
6513 }
6514
6515 /* For reasons of simplicity (= copy & paste), we end up with the
6516 module after the one we're interested in here. */
6517 i--;
6518 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6519 && papMods[i]->pLdrMod)
6520 {
6521 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6522 if (uRvaPC < papMods[i]->cbImage)
6523 {
6524 *ppvImageBase = papMods[i]->hOurMod;
6525 pvRet = papMods[i]->hOurMod;
6526 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6527 return pvRet;
6528 }
6529 }
6530 }
6531 else
6532 i = 0;
6533 }
6534
6535 /*
6536 * Call the regular API.
6537 */
6538 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6539 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6540 return pvRet;
6541}
6542
6543
6544/*
6545 *
6546 * File access APIs (for speeding them up).
6547 * File access APIs (for speeding them up).
6548 * File access APIs (for speeding them up).
6549 *
6550 */
6551
6552
6553/**
6554 * Converts a lookup error to a windows error code.
6555 *
6556 * @returns The windows error code.
6557 * @param enmError The lookup error.
6558 */
6559static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6560{
6561 switch (enmError)
6562 {
6563 case KFSLOOKUPERROR_NOT_FOUND:
6564 case KFSLOOKUPERROR_NOT_DIR:
6565 return ERROR_FILE_NOT_FOUND;
6566
6567 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6568 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6569 case KFSLOOKUPERROR_PATH_TOO_SHORT:
6570 return ERROR_PATH_NOT_FOUND;
6571
6572 case KFSLOOKUPERROR_PATH_TOO_LONG:
6573 return ERROR_FILENAME_EXCED_RANGE;
6574
6575 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6576 return ERROR_NOT_ENOUGH_MEMORY;
6577
6578 default:
6579 return ERROR_PATH_NOT_FOUND;
6580 }
6581}
6582
6583#ifdef WITH_TEMP_MEMORY_FILES
6584
6585/**
6586 * Checks for a cl.exe temporary file.
6587 *
6588 * There are quite a bunch of these. They seems to be passing data between the
6589 * first and second compiler pass. Since they're on disk, they get subjected to
6590 * AV software screening and normal file consistency rules. So, not necessarily
6591 * a very efficient way of handling reasonably small amounts of data.
6592 *
6593 * We make the files live in virtual memory by intercepting their opening,
6594 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6595 *
6596 * @returns K_TRUE / K_FALSE
6597 * @param pwszFilename The file name being accessed.
6598 */
6599static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6600{
6601 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6602 if (pwszName)
6603 {
6604 /* The name starts with _CL_... */
6605 if ( pwszName[0] == '_'
6606 && pwszName[1] == 'C'
6607 && pwszName[2] == 'L'
6608 && pwszName[3] == '_' )
6609 {
6610 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6611 this check by just checking that it's alpha numerical ascii from here on. */
6612 wchar_t wc;
6613 pwszName += 4;
6614 while ((wc = *pwszName++) != '\0')
6615 {
6616 if (wc < 127 && iswalnum(wc))
6617 { /* likely */ }
6618 else
6619 return K_FALSE;
6620 }
6621 return K_TRUE;
6622 }
6623
6624 /* In VC2019 there is also one {UUID} file in temp: */
6625 if (pwszName[0] == '{')
6626 {
6627 KSIZE cwcName = kwUtf16Len(pwszName);
6628 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6629 && pwszName[37] == '}'
6630 && iswalnum(pwszName[1]) // 4
6631 && iswalnum(pwszName[2]) // 4
6632 && iswalnum(pwszName[3]) // 6
6633 && iswalnum(pwszName[4]) // 5
6634 && iswalnum(pwszName[5]) // d
6635 && iswalnum(pwszName[6]) // d
6636 && iswalnum(pwszName[7]) // d
6637 && iswalnum(pwszName[8]) // 9
6638 && pwszName[9] == '-' // -
6639 && iswalnum(pwszName[10]) // e
6640 && iswalnum(pwszName[11]) // 4
6641 && iswalnum(pwszName[12]) // 9
6642 && iswalnum(pwszName[13]) // 4
6643 && pwszName[14] == '-' // -
6644 && iswalnum(pwszName[15]) // 4
6645 && iswalnum(pwszName[16]) // 7
6646 && iswalnum(pwszName[17]) // 1
6647 && iswalnum(pwszName[18]) // b
6648 && pwszName[19] == '-' // -
6649 && iswalnum(pwszName[20]) // 9
6650 && iswalnum(pwszName[21]) // 9
6651 && iswalnum(pwszName[22]) // 6
6652 && iswalnum(pwszName[23]) // b
6653 && pwszName[24] == '-' // -
6654 && iswalnum(pwszName[25]) // 9
6655 && iswalnum(pwszName[26]) // b
6656 && iswalnum(pwszName[27]) // 5
6657 && iswalnum(pwszName[28]) // 5
6658 && iswalnum(pwszName[29]) // 6
6659 && iswalnum(pwszName[30]) // e
6660 && iswalnum(pwszName[31]) // 2
6661 && iswalnum(pwszName[32]) // 5
6662 && iswalnum(pwszName[33]) // a
6663 && iswalnum(pwszName[34]) // 3
6664 && iswalnum(pwszName[35]) // f
6665 && iswalnum(pwszName[36])) // 8
6666 return K_TRUE;
6667 }
6668 }
6669 return K_FALSE;
6670}
6671
6672
6673/**
6674 * Creates a handle to a temporary file.
6675 *
6676 * @returns The handle on success.
6677 * INVALID_HANDLE_VALUE and SetLastError on failure.
6678 * @param pTempFile The temporary file.
6679 * @param dwDesiredAccess The desired access to the handle.
6680 * @param fMapping Whether this is a mapping (K_TRUE) or file
6681 * (K_FALSE) handle type.
6682 */
6683static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6684{
6685 /*
6686 * Create a handle to the temporary file.
6687 */
6688 HANDLE hFile = INVALID_HANDLE_VALUE;
6689 HANDLE hProcSelf = GetCurrentProcess();
6690 if (DuplicateHandle(hProcSelf, hProcSelf,
6691 hProcSelf, &hFile,
6692 SYNCHRONIZE, FALSE,
6693 0 /*dwOptions*/))
6694 {
6695 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6696 if (pHandle)
6697 {
6698 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6699 pHandle->cRefs = 1;
6700 pHandle->offFile = 0;
6701 pHandle->hHandle = hFile;
6702 pHandle->dwDesiredAccess = dwDesiredAccess;
6703 pHandle->tidOwner = KU32_MAX;
6704 pHandle->u.pTempFile = pTempFile;
6705 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6706 {
6707 pTempFile->cActiveHandles++;
6708 kHlpAssert(pTempFile->cActiveHandles >= 1);
6709 kHlpAssert(pTempFile->cActiveHandles <= 2);
6710 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6711 return hFile;
6712 }
6713
6714 kHlpFree(pHandle);
6715 }
6716 else
6717 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6718 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6719 }
6720 else
6721 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6722 return INVALID_HANDLE_VALUE;
6723}
6724
6725
6726static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6727 KBOOL *pfFallback)
6728{
6729 HANDLE hFile;
6730 DWORD dwErr;
6731
6732 /*
6733 * Check if we've got an existing temp file.
6734 * ASSUME exact same path for now.
6735 */
6736 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6737 PKWFSTEMPFILE pTempFile;
6738 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6739 {
6740 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6741 if ( pTempFile->cwcPath == cwcFilename
6742 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6743 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6744 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6745 break;
6746 }
6747
6748 /*
6749 * Create a new temporary file instance if not found.
6750 */
6751 *pfFallback = K_FALSE;
6752 if (pTempFile == NULL)
6753 {
6754 KSIZE cbFilename;
6755
6756 switch (dwCreationDisposition)
6757 {
6758 case CREATE_ALWAYS:
6759 case OPEN_ALWAYS:
6760 case CREATE_NEW:
6761 dwErr = NO_ERROR;
6762 break;
6763
6764 case OPEN_EXISTING:
6765 case TRUNCATE_EXISTING:
6766 *pfFallback = K_TRUE;
6767 kHlpAssertFailed();
6768 SetLastError(ERROR_FILE_NOT_FOUND);
6769 return INVALID_HANDLE_VALUE;
6770
6771 default:
6772 kHlpAssertFailed();
6773 SetLastError(ERROR_INVALID_PARAMETER);
6774 return INVALID_HANDLE_VALUE;
6775 }
6776
6777 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6778 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6779 if (pTempFile)
6780 {
6781 pTempFile->cwcPath = (KU16)cwcFilename;
6782 pTempFile->cbFile = 0;
6783 pTempFile->cbFileAllocated = 0;
6784 pTempFile->cActiveHandles = 0;
6785 pTempFile->cMappings = 0;
6786 pTempFile->cSegs = 0;
6787 pTempFile->paSegs = NULL;
6788 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6789
6790 pTempFile->pNext = g_Sandbox.pTempFileHead;
6791 g_Sandbox.pTempFileHead = pTempFile;
6792 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6793 }
6794 else
6795 {
6796 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6797 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6798 return INVALID_HANDLE_VALUE;
6799 }
6800 }
6801 else
6802 {
6803 switch (dwCreationDisposition)
6804 {
6805 case OPEN_EXISTING:
6806 dwErr = NO_ERROR;
6807 break;
6808 case OPEN_ALWAYS:
6809 dwErr = ERROR_ALREADY_EXISTS;
6810 break;
6811
6812 case TRUNCATE_EXISTING:
6813 case CREATE_ALWAYS:
6814 kHlpAssertFailed();
6815 pTempFile->cbFile = 0;
6816 dwErr = ERROR_ALREADY_EXISTS;
6817 break;
6818
6819 case CREATE_NEW:
6820 kHlpAssertFailed();
6821 SetLastError(ERROR_FILE_EXISTS);
6822 return INVALID_HANDLE_VALUE;
6823
6824 default:
6825 kHlpAssertFailed();
6826 SetLastError(ERROR_INVALID_PARAMETER);
6827 return INVALID_HANDLE_VALUE;
6828 }
6829 }
6830
6831 /*
6832 * Create a handle to the temporary file.
6833 */
6834 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6835 if (hFile != INVALID_HANDLE_VALUE)
6836 SetLastError(dwErr);
6837 return hFile;
6838}
6839
6840#endif /* WITH_TEMP_MEMORY_FILES */
6841
6842/**
6843 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6844 *
6845 * @returns K_TRUE if cacheable, K_FALSE if not.
6846 * @param wcFirst The first extension character.
6847 * @param wcSecond The second extension character.
6848 * @param wcThird The third extension character.
6849 * @param fAttrQuery Set if it's for an attribute query, clear if for
6850 * file creation.
6851 */
6852static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6853{
6854 /* C++ header without an extension or a directory. */
6855 if (wcFirst == '\0')
6856 {
6857 /** @todo exclude temporary files... */
6858 return K_TRUE;
6859 }
6860
6861 /* C Header: .h */
6862 if (wcFirst == 'h' || wcFirst == 'H')
6863 {
6864 if (wcSecond == '\0')
6865 return K_TRUE;
6866
6867 /* C++ Header: .hpp, .hxx */
6868 if ( (wcSecond == 'p' || wcSecond == 'P')
6869 && (wcThird == 'p' || wcThird == 'P'))
6870 return K_TRUE;
6871 if ( (wcSecond == 'x' || wcSecond == 'X')
6872 && (wcThird == 'x' || wcThird == 'X'))
6873 return K_TRUE;
6874 }
6875 /* Misc starting with i. */
6876 else if (wcFirst == 'i' || wcFirst == 'I')
6877 {
6878 if (wcSecond != '\0')
6879 {
6880 if (wcSecond == 'n' || wcSecond == 'N')
6881 {
6882 /* C++ inline header: .inl */
6883 if (wcThird == 'l' || wcThird == 'L')
6884 return K_TRUE;
6885
6886 /* Assembly include file: .inc */
6887 if (wcThird == 'c' || wcThird == 'C')
6888 return K_TRUE;
6889 }
6890 }
6891 }
6892 /* Assembly header: .mac */
6893 else if (wcFirst == 'm' || wcFirst == 'M')
6894 {
6895 if (wcSecond == 'a' || wcSecond == 'A')
6896 {
6897 if (wcThird == 'c' || wcThird == 'C')
6898 return K_TRUE;
6899 }
6900 }
6901#ifdef WITH_PCH_CACHING
6902 /* Precompiled header: .pch */
6903 else if (wcFirst == 'p' || wcFirst == 'P')
6904 {
6905 if (wcSecond == 'c' || wcSecond == 'C')
6906 {
6907 if (wcThird == 'h' || wcThird == 'H')
6908 return !g_Sandbox.fNoPchCaching;
6909 }
6910 }
6911#endif
6912#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6913 /* Linker - Object file: .obj */
6914 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6915 {
6916 if (wcSecond == 'b' || wcSecond == 'B')
6917 {
6918 if (wcThird == 'j' || wcThird == 'J')
6919 return K_TRUE;
6920 }
6921 }
6922#endif
6923 else if (fAttrQuery)
6924 {
6925 /* Dynamic link library: .dll */
6926 if (wcFirst == 'd' || wcFirst == 'D')
6927 {
6928 if (wcSecond == 'l' || wcSecond == 'L')
6929 {
6930 if (wcThird == 'l' || wcThird == 'L')
6931 return K_TRUE;
6932 }
6933 }
6934 /* Executable file: .exe */
6935 else if (wcFirst == 'e' || wcFirst == 'E')
6936 {
6937 if (wcSecond == 'x' || wcSecond == 'X')
6938 {
6939 if (wcThird == 'e' || wcThird == 'E')
6940 return K_TRUE;
6941 }
6942 }
6943 /* Response file: .rsp */
6944 else if (wcFirst == 'r' || wcFirst == 'R')
6945 {
6946 if (wcSecond == 's' || wcSecond == 'S')
6947 {
6948 if (wcThird == 'p' || wcThird == 'P')
6949 return !g_Sandbox.fNoPchCaching;
6950 }
6951 }
6952 /* Linker: */
6953 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6954 {
6955 /* Object file: .obj */
6956 if (wcFirst == 'o' || wcFirst == 'O')
6957 {
6958 if (wcSecond == 'b' || wcSecond == 'B')
6959 {
6960 if (wcThird == 'j' || wcThird == 'J')
6961 return K_TRUE;
6962 }
6963 }
6964 /* Library file: .lib */
6965 else if (wcFirst == 'l' || wcFirst == 'L')
6966 {
6967 if (wcSecond == 'i' || wcSecond == 'I')
6968 {
6969 if (wcThird == 'b' || wcThird == 'B')
6970 return K_TRUE;
6971 }
6972 }
6973 /* Linker definition file: .def */
6974 else if (wcFirst == 'd' || wcFirst == 'D')
6975 {
6976 if (wcSecond == 'e' || wcSecond == 'E')
6977 {
6978 if (wcThird == 'f' || wcThird == 'F')
6979 return K_TRUE;
6980 }
6981 }
6982 }
6983 }
6984
6985 return K_FALSE;
6986}
6987
6988
6989/**
6990 * Checks if the file extension indicates that the file/dir is something we
6991 * ought to cache.
6992 *
6993 * @returns K_TRUE if cachable, K_FALSE if not.
6994 * @param pszExt The kHlpGetExt result.
6995 * @param fAttrQuery Set if it's for an attribute query, clear if for
6996 * file creation.
6997 */
6998static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6999{
7000 wchar_t const wcFirst = *pszExt;
7001 if (wcFirst)
7002 {
7003 wchar_t const wcSecond = pszExt[1];
7004 if (wcSecond)
7005 {
7006 wchar_t const wcThird = pszExt[2];
7007 if (pszExt[3] == '\0')
7008 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
7009 return K_FALSE;
7010 }
7011 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
7012 }
7013 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
7014}
7015
7016
7017/**
7018 * Checks if the extension of the given UTF-16 path indicates that the file/dir
7019 * should be cached.
7020 *
7021 * @returns K_TRUE if cachable, K_FALSE if not.
7022 * @param pwszPath The UTF-16 path to examine.
7023 * @param fAttrQuery Set if it's for an attribute query, clear if for
7024 * file creation.
7025 */
7026static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
7027{
7028 KSIZE cwcExt;
7029 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
7030 switch (cwcExt)
7031 {
7032 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
7033 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
7034 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
7035 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
7036 }
7037 return K_FALSE;
7038}
7039
7040
7041
7042/**
7043 * Creates a new
7044 *
7045 * @returns
7046 * @param pFsObj .
7047 * @param pwszFilename .
7048 */
7049static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
7050{
7051 HANDLE hFile;
7052 MY_IO_STATUS_BLOCK Ios;
7053 MY_OBJECT_ATTRIBUTES ObjAttr;
7054 MY_UNICODE_STRING UniStr;
7055 MY_NTSTATUS rcNt;
7056 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7057
7058 /*
7059 * Open the file relative to the parent directory.
7060 */
7061 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
7062 kHlpAssert(pFsObj->pParent);
7063 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
7064
7065 Ios.Information = ~(ULONG_PTR)0;
7066 Ios.u.Status = -1;
7067
7068 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
7069 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
7070 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
7071
7072 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
7073
7074 rcNt = g_pfnNtCreateFile(&hFile,
7075 GENERIC_READ | SYNCHRONIZE,
7076 &ObjAttr,
7077 &Ios,
7078 NULL, /*cbFileInitialAlloc */
7079 FILE_ATTRIBUTE_NORMAL,
7080 FILE_SHARE_READ,
7081 FILE_OPEN,
7082 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
7083 NULL, /*pEaBuffer*/
7084 0); /*cbEaBuffer*/
7085 if (MY_NT_SUCCESS(rcNt))
7086 {
7087 /*
7088 * Read the whole file into memory.
7089 */
7090 LARGE_INTEGER cbFile;
7091 if (GetFileSizeEx(hFile, &cbFile))
7092 {
7093 if ( cbFile.QuadPart >= 0
7094#ifdef WITH_PCH_CACHING
7095 && ( cbFile.QuadPart < 16*1024*1024
7096 || ( cbFile.QuadPart < 96*1024*1024
7097 && pFsObj->cchName > 4
7098 && !g_Sandbox.fNoPchCaching
7099 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
7100#endif
7101 )
7102 {
7103 KU32 cbCache = (KU32)cbFile.QuadPart;
7104 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
7105 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
7106 if (hMapping != NULL)
7107 {
7108 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
7109 if (pbCache)
7110 {
7111 /*
7112 * Create the cached file object.
7113 */
7114 PKFSWCACHEDFILE pCachedFile;
7115 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
7116 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
7117 sizeof(*pCachedFile) + cbPath);
7118 if (pCachedFile)
7119 {
7120 pCachedFile->hCached = hFile;
7121 pCachedFile->hSection = hMapping;
7122 pCachedFile->cbCached = cbCache;
7123 pCachedFile->pbCached = pbCache;
7124 pCachedFile->pFsObj = pFsObj;
7125 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
7126 kFsCacheObjRetain(pFsObj);
7127
7128 g_cReadCachedFiles++;
7129 g_cbReadCachedFiles += cbCache;
7130
7131 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
7132 return pCachedFile;
7133 }
7134
7135 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
7136 }
7137 else
7138 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
7139 CloseHandle(hMapping);
7140 }
7141 else
7142 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
7143 }
7144 else
7145 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
7146 }
7147 else
7148 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
7149 g_pfnNtClose(hFile);
7150 }
7151 else
7152 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
7153 return NULL;
7154}
7155
7156
7157/**
7158 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
7159 */
7160static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
7161 KBOOL fIsFileHandle, HANDLE *phFile)
7162{
7163 HANDLE hProcSelf = GetCurrentProcess();
7164 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
7165 hProcSelf, phFile,
7166 dwDesiredAccess, fInheritHandle,
7167 0 /*dwOptions*/))
7168 {
7169 /*
7170 * Create handle table entry for the duplicate handle.
7171 */
7172 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
7173 if (pHandle)
7174 {
7175 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
7176 pHandle->cRefs = 1;
7177 pHandle->offFile = 0;
7178 pHandle->hHandle = *phFile;
7179 pHandle->dwDesiredAccess = dwDesiredAccess;
7180 pHandle->tidOwner = KU32_MAX;
7181 pHandle->u.pCachedFile = pCachedFile;
7182 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
7183 return K_TRUE;
7184
7185 kHlpFree(pHandle);
7186 }
7187 else
7188 KWFS_LOG(("Out of memory for handle!\n"));
7189
7190 CloseHandle(*phFile);
7191 *phFile = INVALID_HANDLE_VALUE;
7192 }
7193 else
7194 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
7195 return K_FALSE;
7196}
7197
7198
7199/**
7200 * Kernel32 - Common code for CreateFileW and CreateFileA.
7201 */
7202static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
7203{
7204 *phFile = INVALID_HANDLE_VALUE;
7205
7206 /*
7207 * At the moment we only handle existing files.
7208 */
7209 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
7210 {
7211 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
7212 kHlpAssert(pFsObj->fHaveStats);
7213 if ( pCachedFile != NULL
7214 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
7215 {
7216 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
7217 return K_TRUE;
7218 }
7219 }
7220 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
7221
7222 /* Do fallback, please. */
7223 return K_FALSE;
7224}
7225
7226
7227/** Kernel32 - CreateFileA */
7228static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7229 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7230 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7231{
7232 HANDLE hFile;
7233
7234 /*
7235 * Check for include files and similar that we do read-only caching of.
7236 */
7237 if (dwCreationDisposition == OPEN_EXISTING)
7238 {
7239 if ( dwDesiredAccess == GENERIC_READ
7240 || dwDesiredAccess == FILE_GENERIC_READ)
7241 {
7242 if (dwShareMode & FILE_SHARE_READ)
7243 {
7244 if ( !pSecAttrs
7245 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7246 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7247 {
7248 const char *pszExt = kHlpGetExt(pszFilename);
7249 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
7250 {
7251 KFSLOOKUPERROR enmError;
7252 PKFSOBJ pFsObj;
7253 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7254
7255 pFsObj = kFsCacheLookupA(g_pFsCache, pszFilename, &enmError);
7256 if (pFsObj)
7257 {
7258 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7259 {
7260 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7261 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7262 kFsCacheObjRelease(g_pFsCache, pFsObj);
7263 if (fRc)
7264 {
7265 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
7266 return hFile;
7267 }
7268 }
7269 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7270 {
7271 KWFS_LOG(("CreateFileA(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7272 SetLastError(ERROR_FILE_NOT_FOUND);
7273 return INVALID_HANDLE_VALUE;
7274 }
7275 /* Always fall back on missing files in volatile areas. */
7276 }
7277 /* These are for nasm and yasm header searching. Cache will already
7278 have checked the directories for the file, no need to call
7279 CreateFile to do it again. */
7280 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7281 {
7282 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7283 SetLastError(ERROR_FILE_NOT_FOUND);
7284 return INVALID_HANDLE_VALUE;
7285 }
7286 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7287 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7288 {
7289 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
7290 SetLastError(ERROR_PATH_NOT_FOUND);
7291 return INVALID_HANDLE_VALUE;
7292 }
7293
7294 /* fallback */
7295 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7296 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7297 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
7298 return hFile;
7299 }
7300 }
7301 }
7302 }
7303 }
7304
7305 /*
7306 * Okay, normal.
7307 */
7308 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7309 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7310 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7311
7312 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7313 return hFile;
7314}
7315
7316
7317/** Kernel32 - CreateFileW */
7318static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7319 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7320 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7321{
7322 HANDLE hFile;
7323
7324#ifdef WITH_TEMP_MEMORY_FILES
7325 /*
7326 * Check for temporary files (cl.exe only).
7327 */
7328 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7329 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7330 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7331 && kwFsIsClTempFileW(pwszFilename))
7332 {
7333 KBOOL fFallback = K_FALSE;
7334 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7335 if (!fFallback)
7336 {
7337 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7338 return hFile;
7339 }
7340 }
7341#endif
7342
7343 /*
7344 * Check for include files and similar that we do read-only caching of.
7345 */
7346 if (dwCreationDisposition == OPEN_EXISTING)
7347 {
7348 if ( dwDesiredAccess == GENERIC_READ
7349 || dwDesiredAccess == FILE_GENERIC_READ)
7350 {
7351 if (dwShareMode & FILE_SHARE_READ)
7352 {
7353 if ( !pSecAttrs
7354 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7355 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7356 {
7357 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7358 {
7359 KFSLOOKUPERROR enmError;
7360 PKFSOBJ pFsObj;
7361 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7362
7363 pFsObj = kFsCacheLookupW(g_pFsCache, pwszFilename, &enmError);
7364 if (pFsObj)
7365 {
7366 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7367 {
7368 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7369 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7370 kFsCacheObjRelease(g_pFsCache, pFsObj);
7371 if (fRc)
7372 {
7373 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7374 return hFile;
7375 }
7376 }
7377 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7378 {
7379 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7380 SetLastError(ERROR_FILE_NOT_FOUND);
7381#if 0
7382 if ( pFsObj->cchName > sizeof("generated.h")
7383 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - sizeof("generated.h") + 1], "generated.h") == 0)
7384 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; pFsObj->fFlags=%#x\n", pwszFilename, pFsObj->fFlags);
7385#endif
7386 return INVALID_HANDLE_VALUE;
7387 }
7388 /* Always fall back on missing files in volatile areas. */
7389 }
7390 /* These are for nasm and yasm style header searching. Cache will
7391 already have checked the directories for the file, no need to call
7392 CreateFile to do it again. */
7393 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7394 {
7395#if 0
7396 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7397 if ( cwcFilename > sizeof("generated.h")
7398 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7399 L"generated.h", sizeof(L"generated.h")) == 0)
7400 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; (KFSLOOKUPERROR_NOT_FOUND)\n", pwszFilename, pFsObj->fFlags);
7401#endif
7402 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7403 SetLastError(ERROR_FILE_NOT_FOUND);
7404 return INVALID_HANDLE_VALUE;
7405 }
7406 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7407 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7408 {
7409#if 0
7410 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7411 if ( cwcFilename > sizeof("generated.h")
7412 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7413 L"generated.h", sizeof(L"generated.h")) == 0)
7414 kwErrPrintf("CreateFileW(%ls) -> ERROR_PATH_NOT_FOUND; (%d)\n", pwszFilename, enmError);
7415#endif
7416 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7417 SetLastError(ERROR_PATH_NOT_FOUND);
7418 return INVALID_HANDLE_VALUE;
7419 }
7420
7421 /* fallback */
7422 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7423 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7424 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7425 return hFile;
7426 }
7427 }
7428 else
7429 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7430 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7431 }
7432 else
7433 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7434 }
7435 else
7436 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7437 }
7438 else
7439 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7440
7441 /*
7442 * Okay, normal.
7443 */
7444 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7445 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7446 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7447
7448 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7449 return hFile;
7450}
7451
7452
7453
7454/** Kernel32 - SetFilePointer */
7455static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7456{
7457 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7458 if (pHandle != NULL)
7459 {
7460 KU32 cbFile;
7461 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7462 switch (pHandle->enmType)
7463 {
7464 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7465 cbFile = pHandle->u.pCachedFile->cbCached;
7466 break;
7467#ifdef WITH_TEMP_MEMORY_FILES
7468 case KWHANDLETYPE_TEMP_FILE:
7469 cbFile = pHandle->u.pTempFile->cbFile;
7470 break;
7471 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7472#endif
7473 case KWHANDLETYPE_OUTPUT_BUF:
7474 default:
7475 kHlpAssertFailed();
7476 kwSandboxHandlePut(pHandle);
7477 SetLastError(ERROR_INVALID_FUNCTION);
7478 return INVALID_SET_FILE_POINTER;
7479 }
7480
7481 switch (dwMoveMethod)
7482 {
7483 case FILE_BEGIN:
7484 break;
7485 case FILE_CURRENT:
7486 offMove += pHandle->offFile;
7487 break;
7488 case FILE_END:
7489 offMove += cbFile;
7490 break;
7491 default:
7492 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7493 kwSandboxHandlePut(pHandle);
7494 SetLastError(ERROR_INVALID_PARAMETER);
7495 return INVALID_SET_FILE_POINTER;
7496 }
7497 if (offMove >= 0)
7498 {
7499 if (offMove >= (KSSIZE)cbFile)
7500 {
7501#ifdef WITH_TEMP_MEMORY_FILES
7502 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7503 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7504#endif
7505 offMove = (KSSIZE)cbFile;
7506#ifdef WITH_TEMP_MEMORY_FILES
7507 /* For writable files, seeking beyond the end is fine, but check that we've got
7508 the type range for the request. */
7509 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7510 {
7511 kHlpAssertMsgFailed(("%#llx\n", offMove));
7512 kwSandboxHandlePut(pHandle);
7513 SetLastError(ERROR_SEEK);
7514 return INVALID_SET_FILE_POINTER;
7515 }
7516#endif
7517 }
7518 pHandle->offFile = (KU32)offMove;
7519 }
7520 else
7521 {
7522 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7523 kwSandboxHandlePut(pHandle);
7524 SetLastError(ERROR_NEGATIVE_SEEK);
7525 return INVALID_SET_FILE_POINTER;
7526 }
7527 if (pcbMoveHi)
7528 *pcbMoveHi = (KU64)offMove >> 32;
7529 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7530 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7531 kwSandboxHandlePut(pHandle);
7532 SetLastError(NO_ERROR);
7533 return (KU32)offMove;
7534 }
7535
7536 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7537 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7538}
7539
7540
7541/** Kernel32 - SetFilePointerEx */
7542static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7543 DWORD dwMoveMethod)
7544{
7545 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7546 if (pHandle != NULL)
7547 {
7548 KI64 offMyMove = offMove.QuadPart;
7549 KU32 cbFile;
7550 switch (pHandle->enmType)
7551 {
7552 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7553 cbFile = pHandle->u.pCachedFile->cbCached;
7554 break;
7555#ifdef WITH_TEMP_MEMORY_FILES
7556 case KWHANDLETYPE_TEMP_FILE:
7557 cbFile = pHandle->u.pTempFile->cbFile;
7558 break;
7559 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7560#endif
7561 case KWHANDLETYPE_OUTPUT_BUF:
7562 default:
7563 kHlpAssertFailed();
7564 kwSandboxHandlePut(pHandle);
7565 SetLastError(ERROR_INVALID_FUNCTION);
7566 return FALSE;
7567 }
7568
7569 switch (dwMoveMethod)
7570 {
7571 case FILE_BEGIN:
7572 break;
7573 case FILE_CURRENT:
7574 offMyMove += pHandle->offFile;
7575 break;
7576 case FILE_END:
7577 offMyMove += cbFile;
7578 break;
7579 default:
7580 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7581 kwSandboxHandlePut(pHandle);
7582 SetLastError(ERROR_INVALID_PARAMETER);
7583 return FALSE;
7584 }
7585 if (offMyMove >= 0)
7586 {
7587 if (offMyMove >= (KSSIZE)cbFile)
7588 {
7589#ifdef WITH_TEMP_MEMORY_FILES
7590 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7591 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7592#endif
7593 offMyMove = (KSSIZE)cbFile;
7594#ifdef WITH_TEMP_MEMORY_FILES
7595 /* For writable files, seeking beyond the end is fine, but check that we've got
7596 the type range for the request. */
7597 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7598 {
7599 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7600 kwSandboxHandlePut(pHandle);
7601 SetLastError(ERROR_SEEK);
7602 return FALSE;
7603 }
7604#endif
7605 }
7606 pHandle->offFile = (KU32)offMyMove;
7607 }
7608 else
7609 {
7610 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7611 kwSandboxHandlePut(pHandle);
7612 SetLastError(ERROR_NEGATIVE_SEEK);
7613 return FALSE;
7614 }
7615 if (poffNew)
7616 poffNew->QuadPart = offMyMove;
7617 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7618 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7619 kwSandboxHandlePut(pHandle);
7620 return TRUE;
7621 }
7622 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7623 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7624}
7625
7626
7627/** Kernel32 - ReadFile */
7628static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7629 LPOVERLAPPED pOverlapped)
7630{
7631 BOOL fRet;
7632 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7633 g_cReadFileCalls++;
7634 if (pHandle != NULL)
7635 {
7636 switch (pHandle->enmType)
7637 {
7638 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7639 {
7640 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7641 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7642 if (cbActually > cbToRead)
7643 cbActually = cbToRead;
7644
7645#ifdef WITH_HASH_CACHE
7646 if (g_Sandbox.pHashHead)
7647 {
7648 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7649 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7650 g_Sandbox.LastHashRead.cbRead = cbActually;
7651 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7652 }
7653#endif
7654
7655 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7656 pHandle->offFile += cbActually;
7657
7658 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7659 *pcbActuallyRead = cbActually;
7660
7661 g_cbReadFileFromReadCached += cbActually;
7662 g_cbReadFileTotal += cbActually;
7663 g_cReadFileFromReadCached++;
7664
7665 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7666 kwSandboxHandlePut(pHandle);
7667 return TRUE;
7668 }
7669
7670#ifdef WITH_TEMP_MEMORY_FILES
7671 case KWHANDLETYPE_TEMP_FILE:
7672 {
7673 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7674 KU32 cbActually;
7675 if (pHandle->offFile < pTempFile->cbFile)
7676 {
7677 cbActually = pTempFile->cbFile - pHandle->offFile;
7678 if (cbActually > cbToRead)
7679 cbActually = cbToRead;
7680
7681 /* Copy the data. */
7682 if (cbActually > 0)
7683 {
7684 KU32 cbLeft;
7685 KU32 offSeg;
7686 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7687
7688 /* Locate the segment containing the byte at offFile. */
7689 KU32 iSeg = pTempFile->cSegs - 1;
7690 kHlpAssert(pTempFile->cSegs > 0);
7691 while (paSegs[iSeg].offData > pHandle->offFile)
7692 iSeg--;
7693
7694 /* Copy out the data. */
7695 cbLeft = cbActually;
7696 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7697 for (;;)
7698 {
7699 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7700 if (cbAvail >= cbLeft)
7701 {
7702 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7703 break;
7704 }
7705
7706 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7707 cbLeft -= cbAvail;
7708 offSeg = 0;
7709 iSeg++;
7710 kHlpAssert(iSeg < pTempFile->cSegs);
7711 }
7712
7713 /* Update the file offset. */
7714 pHandle->offFile += cbActually;
7715 }
7716 }
7717 /* Read does not commit file space, so return zero bytes. */
7718 else
7719 cbActually = 0;
7720
7721 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7722 *pcbActuallyRead = cbActually;
7723
7724 g_cbReadFileTotal += cbActually;
7725 g_cbReadFileFromInMemTemp += cbActually;
7726 g_cReadFileFromInMemTemp++;
7727
7728 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7729 kwSandboxHandlePut(pHandle);
7730 return TRUE;
7731 }
7732
7733 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7734#endif /* WITH_TEMP_MEMORY_FILES */
7735 case KWHANDLETYPE_OUTPUT_BUF:
7736 default:
7737 kHlpAssertFailed();
7738 kwSandboxHandlePut(pHandle);
7739 SetLastError(ERROR_INVALID_FUNCTION);
7740 *pcbActuallyRead = 0;
7741 return FALSE;
7742 }
7743 }
7744
7745 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7746 if (fRet && pcbActuallyRead)
7747 g_cbReadFileTotal += *pcbActuallyRead;
7748 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7749 return fRet;
7750}
7751
7752
7753/** Kernel32 - ReadFileEx */
7754static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7755 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7756{
7757 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7758
7759 KWFS_LOG(("ReadFile(%p)\n", hFile));
7760 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7761}
7762
7763#ifdef WITH_STD_OUT_ERR_BUFFERING
7764
7765/**
7766 * Write something to a handle, making sure everything is actually written.
7767 *
7768 * @param hHandle Where to write it to.
7769 * @param pchBuf What to write
7770 * @param cchToWrite How much to write.
7771 */
7772static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7773{
7774 if (cchToWrite > 0)
7775 {
7776 DWORD cchWritten = 0;
7777 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7778 {
7779 if (cchWritten == cchToWrite)
7780 { /* likely */ }
7781 else
7782 {
7783 do
7784 {
7785 pchBuf += cchWritten;
7786 cchToWrite -= cchWritten;
7787 cchWritten = 0;
7788 } while ( cchToWrite > 0
7789 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7790 }
7791 }
7792 else
7793 kHlpAssertFailed();
7794 }
7795}
7796
7797
7798/**
7799 * Worker for WriteFile when the output isn't going to the console.
7800 *
7801 * @param pSandbox The sandbox.
7802 * @param pOutBuf The output buffer.
7803 * @param pchBuffer What to write.
7804 * @param cchToWrite How much to write.
7805 */
7806static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7807{
7808 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7809 { /* likely */ }
7810 else
7811 {
7812 /* No realloc, max size is 64KB. */
7813 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7814 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7815 if (!pOutBuf->u.Fully.pchBuf)
7816 {
7817 while ( !pOutBuf->u.Fully.pchBuf
7818 && pOutBuf->u.Fully.cchBufAlloc > 64)
7819 {
7820 pOutBuf->u.Fully.cchBufAlloc /= 2;
7821 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7822 }
7823 if (!pOutBuf->u.Fully.pchBuf)
7824 {
7825 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7826 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7827 }
7828 }
7829 }
7830
7831 /*
7832 * Special case: Output ends with newline and fits in the buffer.
7833 */
7834 if ( cchToWrite > 1
7835 && pchBuffer[cchToWrite - 1] == '\n'
7836 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7837 {
7838 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7839 pOutBuf->u.Fully.cchBuf += cchToWrite;
7840 }
7841 else
7842 {
7843 /*
7844 * Work thru the text line by line, flushing the buffer when
7845 * appropriate. The buffer is not a line buffer here, it's a
7846 * full buffer.
7847 */
7848 while (cchToWrite > 0)
7849 {
7850 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7851 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7852 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7853 {
7854 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7855 pOutBuf->u.Fully.cchBuf += cchLine;
7856 }
7857 /*
7858 * Option one: Flush the buffer and the current line.
7859 *
7860 * We choose this one when the line won't ever fit, or when we have
7861 * an incomplete line in the buffer.
7862 */
7863 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7864 || pOutBuf->u.Fully.cchBuf == 0
7865 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7866 {
7867 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7868 if (pOutBuf->u.Fully.cchBuf > 0)
7869 {
7870 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7871 pOutBuf->u.Fully.cchBuf = 0;
7872 }
7873 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7874 }
7875 /*
7876 * Option two: Only flush the lines in the buffer.
7877 */
7878 else
7879 {
7880 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7881 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7882 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7883 pOutBuf->u.Fully.cchBuf = cchLine;
7884 }
7885
7886 /* advance */
7887 pchBuffer += cchLine;
7888 cchToWrite -= cchLine;
7889 }
7890 }
7891}
7892
7893#endif /* WITH_STD_OUT_ERR_BUFFERING */
7894
7895#ifdef WITH_TEMP_MEMORY_FILES
7896static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7897{
7898 KU32 cbMinFile = offFile + cbNeeded;
7899 if (cbMinFile >= offFile)
7900 {
7901 /* Calc how much space we've already allocated and */
7902 if (cbMinFile <= pTempFile->cbFileAllocated)
7903 return K_TRUE;
7904
7905 /* Grow the file. */
7906 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7907 {
7908 int rc;
7909 KU32 cSegs = pTempFile->cSegs;
7910 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7911 do
7912 {
7913 /* grow the segment array? */
7914 if ((cSegs % 16) == 0)
7915 {
7916 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7917 if (!pvNew)
7918 return K_FALSE;
7919 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7920 }
7921
7922 /* Use page alloc here to simplify mapping later. */
7923 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7924 if (rc == 0)
7925 { /* likely */ }
7926 else
7927 {
7928 cbNewSeg = 64*1024;
7929 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7930 if (rc != 0)
7931 {
7932 kHlpAssertFailed();
7933 return K_FALSE;
7934 }
7935 }
7936 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7937 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7938 pTempFile->cbFileAllocated += cbNewSeg;
7939 pTempFile->cSegs = ++cSegs;
7940
7941 } while (pTempFile->cbFileAllocated < cbMinFile);
7942
7943 return K_TRUE;
7944 }
7945 }
7946
7947 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7948 return K_FALSE;
7949}
7950#endif /* WITH_TEMP_MEMORY_FILES */
7951
7952
7953#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7954/** Kernel32 - WriteFile */
7955static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7956 LPOVERLAPPED pOverlapped)
7957{
7958 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7959 BOOL fRet;
7960 g_cWriteFileCalls++;
7961 if (pHandle != NULL)
7962 {
7963 switch (pHandle->enmType)
7964 {
7965# ifdef WITH_TEMP_MEMORY_FILES
7966 case KWHANDLETYPE_TEMP_FILE:
7967 {
7968 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7969
7970 kHlpAssert(!pOverlapped);
7971 kHlpAssert(pcbActuallyWritten);
7972
7973 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7974 {
7975 KU32 cbLeft;
7976 KU32 offSeg;
7977
7978 /* Locate the segment containing the byte at offFile. */
7979 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7980 KU32 iSeg = pTempFile->cSegs - 1;
7981 kHlpAssert(pTempFile->cSegs > 0);
7982 while (paSegs[iSeg].offData > pHandle->offFile)
7983 iSeg--;
7984
7985 /* Copy in the data. */
7986 cbLeft = cbToWrite;
7987 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7988 for (;;)
7989 {
7990 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7991 if (cbAvail >= cbLeft)
7992 {
7993 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7994 break;
7995 }
7996
7997 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7998 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7999 cbLeft -= cbAvail;
8000 offSeg = 0;
8001 iSeg++;
8002 kHlpAssert(iSeg < pTempFile->cSegs);
8003 }
8004
8005 /* Update the file offset. */
8006 pHandle->offFile += cbToWrite;
8007 if (pHandle->offFile > pTempFile->cbFile)
8008 pTempFile->cbFile = pHandle->offFile;
8009
8010 *pcbActuallyWritten = cbToWrite;
8011
8012 g_cbWriteFileTotal += cbToWrite;
8013 g_cbWriteFileToInMemTemp += cbToWrite;
8014 g_cWriteFileToInMemTemp++;
8015
8016 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
8017 kwSandboxHandlePut(pHandle);
8018 return TRUE;
8019 }
8020
8021 kHlpAssertFailed();
8022 kwSandboxHandlePut(pHandle);
8023 *pcbActuallyWritten = 0;
8024 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8025 return FALSE;
8026 }
8027# endif
8028
8029 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8030 kHlpAssertFailed();
8031 kwSandboxHandlePut(pHandle);
8032 SetLastError(ERROR_ACCESS_DENIED);
8033 *pcbActuallyWritten = 0;
8034 return FALSE;
8035
8036# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
8037 /*
8038 * Standard output & error.
8039 */
8040 case KWHANDLETYPE_OUTPUT_BUF:
8041 {
8042 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8043 if (pOutBuf->fIsConsole)
8044 {
8045 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
8046 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
8047 }
8048 else
8049 {
8050# ifdef WITH_STD_OUT_ERR_BUFFERING
8051 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
8052 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
8053 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
8054# else
8055 kHlpAssertFailed();
8056# endif
8057 }
8058 if (pcbActuallyWritten)
8059 *pcbActuallyWritten = cbToWrite;
8060 g_cbWriteFileTotal += cbToWrite;
8061 kwSandboxHandlePut(pHandle);
8062 return TRUE;
8063 }
8064# endif
8065
8066 default:
8067#ifdef WITH_TEMP_MEMORY_FILES
8068 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8069#endif
8070 kHlpAssertFailed();
8071 kwSandboxHandlePut(pHandle);
8072 SetLastError(ERROR_INVALID_FUNCTION);
8073 *pcbActuallyWritten = 0;
8074 return FALSE;
8075 }
8076 }
8077
8078 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
8079 if (fRet && pcbActuallyWritten)
8080 g_cbWriteFileTotal += *pcbActuallyWritten;
8081 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
8082 return fRet;
8083}
8084
8085
8086/** Kernel32 - WriteFileEx */
8087static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
8088 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
8089{
8090 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
8091
8092 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
8093 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
8094}
8095
8096#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
8097
8098#ifdef WITH_TEMP_MEMORY_FILES
8099
8100/** Kernel32 - SetEndOfFile; */
8101static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
8102{
8103 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8104 if (pHandle != NULL)
8105 {
8106 switch (pHandle->enmType)
8107 {
8108 case KWHANDLETYPE_TEMP_FILE:
8109 {
8110 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8111 if ( pHandle->offFile > pTempFile->cbFile
8112 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
8113 {
8114 kHlpAssertFailed();
8115 kwSandboxHandlePut(pHandle);
8116 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8117 return FALSE;
8118 }
8119
8120 pTempFile->cbFile = pHandle->offFile;
8121 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
8122 kwSandboxHandlePut(pHandle);
8123 return TRUE;
8124 }
8125
8126 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8127 kHlpAssertFailed();
8128 kwSandboxHandlePut(pHandle);
8129 SetLastError(ERROR_ACCESS_DENIED);
8130 return FALSE;
8131
8132# ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8133 case KWHANDLETYPE_OUTPUT_BUF:
8134 kHlpAssertFailed();
8135 kwSandboxHandlePut(pHandle);
8136 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
8137 return FALSE;
8138# endif
8139
8140 default:
8141 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8142 kHlpAssertFailed();
8143 kwSandboxHandlePut(pHandle);
8144 SetLastError(ERROR_INVALID_FUNCTION);
8145 return FALSE;
8146 }
8147 }
8148
8149 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
8150 return SetEndOfFile(hFile);
8151}
8152
8153
8154/** Kernel32 - GetFileType */
8155static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
8156{
8157 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8158 if (pHandle != NULL)
8159 {
8160 switch (pHandle->enmType)
8161 {
8162 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8163 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
8164 kwSandboxHandlePut(pHandle);
8165 return FILE_TYPE_DISK;
8166
8167 case KWHANDLETYPE_TEMP_FILE:
8168 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
8169 kwSandboxHandlePut(pHandle);
8170 return FILE_TYPE_DISK;
8171
8172#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8173 case KWHANDLETYPE_OUTPUT_BUF:
8174 {
8175 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8176 DWORD fRet;
8177 if (pOutBuf->fFileType != KU8_MAX)
8178 {
8179 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
8180 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
8181 }
8182 else
8183 {
8184 fRet = GetFileType(hFile);
8185 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
8186 }
8187 kwSandboxHandlePut(pHandle);
8188 return fRet;
8189 }
8190#endif
8191 }
8192 kwSandboxHandlePut(pHandle);
8193 }
8194
8195 KWFS_LOG(("GetFileType(%p)\n", hFile));
8196 return GetFileType(hFile);
8197}
8198
8199
8200/** Kernel32 - GetFileSize */
8201static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
8202{
8203 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8204 if (pHandle != NULL)
8205 {
8206 if (pcbHighDword)
8207 *pcbHighDword = 0;
8208 SetLastError(NO_ERROR);
8209 switch (pHandle->enmType)
8210 {
8211 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8212 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8213 kwSandboxHandlePut(pHandle);
8214 return pHandle->u.pCachedFile->cbCached;
8215
8216 case KWHANDLETYPE_TEMP_FILE:
8217 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8218 kwSandboxHandlePut(pHandle);
8219 return pHandle->u.pTempFile->cbFile;
8220
8221 case KWHANDLETYPE_OUTPUT_BUF:
8222 /* do default */
8223 break;
8224
8225 default:
8226 kHlpAssertFailed();
8227 kwSandboxHandlePut(pHandle);
8228 SetLastError(ERROR_INVALID_FUNCTION);
8229 return INVALID_FILE_SIZE;
8230 }
8231 kwSandboxHandlePut(pHandle);
8232 }
8233
8234 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
8235 return GetFileSize(hFile, pcbHighDword);
8236}
8237
8238
8239/** Kernel32 - GetFileSizeEx */
8240static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
8241{
8242 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8243 if (pHandle != NULL)
8244 {
8245 switch (pHandle->enmType)
8246 {
8247 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8248 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8249 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
8250 kwSandboxHandlePut(pHandle);
8251 return TRUE;
8252
8253 case KWHANDLETYPE_TEMP_FILE:
8254 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8255 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
8256 kwSandboxHandlePut(pHandle);
8257 return TRUE;
8258
8259 case KWHANDLETYPE_OUTPUT_BUF:
8260 /* do default */
8261 break;
8262
8263 default:
8264 kHlpAssertFailed();
8265 kwSandboxHandlePut(pHandle);
8266 SetLastError(ERROR_INVALID_FUNCTION);
8267 return INVALID_FILE_SIZE;
8268 }
8269 kwSandboxHandlePut(pHandle);
8270 }
8271
8272 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
8273 return GetFileSizeEx(hFile, pcbFile);
8274}
8275
8276
8277/** Kernel32 - CreateFileMappingW */
8278static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
8279 DWORD fProtect, DWORD dwMaximumSizeHigh,
8280 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
8281{
8282 HANDLE hMapping;
8283 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8284 if (pHandle != NULL)
8285 {
8286 switch (pHandle->enmType)
8287 {
8288 case KWHANDLETYPE_TEMP_FILE:
8289 {
8290 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8291 if ( ( fProtect == PAGE_READONLY
8292 || fProtect == PAGE_EXECUTE_READ)
8293 && dwMaximumSizeHigh == 0
8294 && ( dwMaximumSizeLow == 0
8295 || dwMaximumSizeLow == pTempFile->cbFile)
8296 && pwszName == NULL)
8297 {
8298 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
8299 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
8300 kwSandboxHandlePut(pHandle);
8301 return hMapping;
8302 }
8303 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
8304 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8305 kwSandboxHandlePut(pHandle);
8306 SetLastError(ERROR_ACCESS_DENIED);
8307 return INVALID_HANDLE_VALUE;
8308 }
8309
8310 /* moc.exe benefits from this. */
8311 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8312 {
8313 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8314 if ( ( fProtect == PAGE_READONLY
8315 || fProtect == PAGE_EXECUTE_READ)
8316 && dwMaximumSizeHigh == 0
8317 && ( dwMaximumSizeLow == 0
8318 || dwMaximumSizeLow == pCachedFile->cbCached)
8319 && pwszName == NULL)
8320 {
8321 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
8322 K_FALSE /*fIsFileHandle*/, &hMapping))
8323 { /* likely */ }
8324 else
8325 hMapping = NULL;
8326 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8327 kwSandboxHandlePut(pHandle);
8328 return hMapping;
8329 }
8330
8331 /* Do fallback (for .pch). */
8332 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8333 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8334 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8335
8336 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8337 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8338 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8339 kwSandboxHandlePut(pHandle);
8340 return hMapping;
8341 }
8342
8343 /** @todo read cached memory mapped files too for moc. */
8344 }
8345 kwSandboxHandlePut(pHandle);
8346 }
8347
8348 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8349 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8350 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8351 return hMapping;
8352}
8353
8354
8355/** Kernel32 - MapViewOfFile */
8356static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8357 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8358{
8359 PVOID pvRet;
8360 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8361 if (pHandle != NULL)
8362 {
8363 KU32 idxMapping;
8364
8365 /*
8366 * Ensure one free entry in the mapping tracking table first,
8367 * since this is common to both temporary and cached files.
8368 */
8369 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8370 { /* likely */ }
8371 else
8372 {
8373 void *pvNew;
8374 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8375 if (cNew)
8376 cNew *= 2;
8377 else
8378 cNew = 32;
8379 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8380 if (pvNew)
8381 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8382 else
8383 {
8384 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8385 kwSandboxHandlePut(pHandle);
8386 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8387 return NULL;
8388 }
8389 g_Sandbox.cMemMappingsAlloc = cNew;
8390 }
8391
8392 /*
8393 * Type specific work.
8394 */
8395 switch (pHandle->enmType)
8396 {
8397 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8398 case KWHANDLETYPE_TEMP_FILE:
8399 case KWHANDLETYPE_OUTPUT_BUF:
8400 default:
8401 kHlpAssertFailed();
8402 kwSandboxHandlePut(pHandle);
8403 SetLastError(ERROR_INVALID_OPERATION);
8404 return NULL;
8405
8406 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8407 {
8408 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8409 if ( dwDesiredAccess == FILE_MAP_READ
8410 && offFileHigh == 0
8411 && offFileLow == 0
8412 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8413 {
8414 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8415 if (pTempFile->cSegs != 1)
8416 {
8417 KU32 iSeg;
8418 KU32 cbLeft;
8419 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8420 KU8 *pbAll = NULL;
8421 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8422 if (rc != 0)
8423 {
8424 kHlpAssertFailed();
8425 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8426 return NULL;
8427 }
8428
8429 cbLeft = pTempFile->cbFile;
8430 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8431 {
8432 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8433 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8434 cbLeft -= cbToCopy;
8435 }
8436
8437 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8438 {
8439 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8440 pTempFile->paSegs[iSeg].pbData = NULL;
8441 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8442 }
8443
8444 pTempFile->cSegs = 1;
8445 pTempFile->cbFileAllocated = cbAll;
8446 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8447 pTempFile->paSegs[0].pbData = pbAll;
8448 pTempFile->paSegs[0].offData = 0;
8449 }
8450
8451 pTempFile->cMappings++;
8452 kHlpAssert(pTempFile->cMappings == 1);
8453
8454 pvRet = pTempFile->paSegs[0].pbData;
8455 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8456 break;
8457 }
8458
8459 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8460 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8461 kwSandboxHandlePut(pHandle);
8462 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8463 return NULL;
8464 }
8465
8466 /*
8467 * This is simple in comparison to the above temporary file code.
8468 */
8469 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8470 {
8471 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8472 if ( dwDesiredAccess == FILE_MAP_READ
8473 && offFileHigh == 0
8474 && offFileLow == 0
8475 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8476 {
8477 pvRet = pCachedFile->pbCached;
8478 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8479 break;
8480 }
8481 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8482 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8483 kwSandboxHandlePut(pHandle);
8484 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8485 return NULL;
8486 }
8487 }
8488
8489 /*
8490 * Insert into the mapping tracking table. This is common
8491 * and we should only get here with a non-NULL pvRet.
8492 *
8493 * Note! We could look for duplicates and do ref counting, but it's
8494 * easier to just append for now.
8495 */
8496 kHlpAssert(pvRet != NULL);
8497 idxMapping = g_Sandbox.cMemMappings;
8498 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8499
8500 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8501 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8502 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8503 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8504 g_Sandbox.cMemMappings++;
8505
8506 kwSandboxHandlePut(pHandle);
8507 return pvRet;
8508 }
8509
8510 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8511 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8512 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8513 return pvRet;
8514}
8515
8516
8517/** Kernel32 - MapViewOfFileEx */
8518static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8519 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8520{
8521 PVOID pvRet;
8522 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8523 if (pHandle != NULL)
8524 {
8525 switch (pHandle->enmType)
8526 {
8527 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8528 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8529 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8530 if (!pvMapAddr)
8531 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8532 else
8533 {
8534 kHlpAssertFailed();
8535 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8536 }
8537 kwSandboxHandlePut(pHandle);
8538 return NULL;
8539
8540 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8541 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8542 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8543 if (!pvMapAddr)
8544 {
8545 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8546 kwSandboxHandlePut(pHandle);
8547 return pvRet;
8548 }
8549 /* We can use fallback here as the handle is an actual section handle. */
8550 break;
8551
8552 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8553 case KWHANDLETYPE_TEMP_FILE:
8554 case KWHANDLETYPE_OUTPUT_BUF:
8555 default:
8556 kHlpAssertFailed();
8557 kwSandboxHandlePut(pHandle);
8558 SetLastError(ERROR_INVALID_OPERATION);
8559 return NULL;
8560 }
8561 kwSandboxHandlePut(pHandle);
8562 }
8563
8564 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8565 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8566 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8567 return pvRet;
8568
8569}
8570
8571/** Kernel32 - UnmapViewOfFile */
8572static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8573{
8574 /*
8575 * Consult the memory mapping tracker.
8576 */
8577 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8578 KU32 idxMapping = g_Sandbox.cMemMappings;
8579 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8580 while (idxMapping-- > 0)
8581 if (paMemMappings[idxMapping].pvMapping == pvBase)
8582 {
8583 /* Type specific stuff. */
8584 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8585 {
8586 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8587 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8588 }
8589 else
8590 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8591
8592 /* Deref and probably free it. */
8593 if (--paMemMappings[idxMapping].cRefs == 0)
8594 {
8595 g_Sandbox.cMemMappings--;
8596 if (idxMapping != g_Sandbox.cMemMappings)
8597 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8598 }
8599 return TRUE;
8600 }
8601
8602 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8603 return UnmapViewOfFile(pvBase);
8604}
8605
8606/** @todo UnmapViewOfFileEx */
8607
8608#endif /* WITH_TEMP_MEMORY_FILES */
8609
8610
8611/** Kernel32 - DuplicateHandle */
8612static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8613 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8614{
8615 BOOL fRet;
8616
8617 /*
8618 * We must catch our handles being duplicated.
8619 */
8620 if (hSrcProc == GetCurrentProcess())
8621 {
8622 PKWHANDLE pHandle = kwSandboxHandleGet(hSrc);
8623 if (pHandle)
8624 {
8625 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8626 if (fRet)
8627 {
8628 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8629 {
8630 pHandle->cRefs++;
8631 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8632 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8633 pHandle->enmType, pHandle->cRefs));
8634 }
8635 else
8636 {
8637 fRet = FALSE;
8638 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8639 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8640 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8641 }
8642 }
8643 else
8644 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8645 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8646 kwSandboxHandlePut(pHandle);
8647 return fRet;
8648 }
8649 }
8650
8651 /*
8652 * Not one of ours, just do what the caller asks and log it.
8653 */
8654 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8655 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8656 fInheritHandle, dwOptions, fRet, *phNew));
8657 return fRet;
8658}
8659
8660
8661/** Kernel32 - CloseHandle */
8662static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8663{
8664 BOOL fRet;
8665 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8666 PKWHANDLE pHandle = kwSandboxHandleGet(hObject);
8667 if (pHandle)
8668 {
8669 /* Prevent the closing of the standard output and error handles. */
8670 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8671 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) /* why this?!? */)
8672 {
8673 fRet = CloseHandle(hObject);
8674 if (fRet)
8675 {
8676 EnterCriticalSection(&g_Sandbox.HandlesLock);
8677 pHandle = g_Sandbox.papHandles[idxHandle];
8678 g_Sandbox.papHandles[idxHandle] = NULL;
8679 g_Sandbox.cActiveHandles--;
8680 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8681 if (--pHandle->cRefs == 0)
8682 {
8683#ifdef WITH_TEMP_MEMORY_FILES
8684 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8685 {
8686 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8687 pHandle->u.pTempFile->cActiveHandles--;
8688 }
8689#endif
8690 kHlpFree(pHandle);
8691 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8692 }
8693 else
8694 {
8695 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8696 kwSandboxHandlePut(pHandle);
8697 }
8698 LeaveCriticalSection(&g_Sandbox.HandlesLock);
8699 return fRet;
8700 }
8701 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8702 }
8703 else
8704 {
8705#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8706 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8707 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8708#else
8709 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of stdXXX!\n", hObject));
8710#endif
8711 fRet = TRUE;
8712 }
8713 kwSandboxHandlePut(pHandle);
8714 return fRet;
8715 }
8716
8717 fRet = CloseHandle(hObject);
8718 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8719 return fRet;
8720}
8721
8722
8723/** Kernel32 - GetFileAttributesA. */
8724static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8725{
8726 DWORD fRet;
8727 const char *pszExt = kHlpGetExt(pszFilename);
8728 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8729 {
8730 KFSLOOKUPERROR enmError;
8731 PKFSOBJ pFsObj;
8732 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8733
8734 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8735 if (pFsObj)
8736 {
8737 kHlpAssert(pFsObj->fHaveStats);
8738 fRet = pFsObj->Stats.st_attribs;
8739 kFsCacheObjRelease(g_pFsCache, pFsObj);
8740 }
8741 else
8742 {
8743 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8744 fRet = INVALID_FILE_ATTRIBUTES;
8745 }
8746
8747 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8748 return fRet;
8749 }
8750
8751 fRet = GetFileAttributesA(pszFilename);
8752 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8753 return fRet;
8754}
8755
8756
8757/** Kernel32 - GetFileAttributesW. */
8758static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8759{
8760 DWORD fRet;
8761 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8762 {
8763 KFSLOOKUPERROR enmError;
8764 PKFSOBJ pFsObj;
8765 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8766
8767 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8768 if (pFsObj)
8769 {
8770 kHlpAssert(pFsObj->fHaveStats);
8771 fRet = pFsObj->Stats.st_attribs;
8772 kFsCacheObjRelease(g_pFsCache, pFsObj);
8773 }
8774 else
8775 {
8776 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8777 fRet = INVALID_FILE_ATTRIBUTES;
8778 }
8779#ifndef NDEBUG
8780 {
8781 DWORD fCheck = GetFileAttributesW(pwszFilename);
8782 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8783 }
8784#endif
8785 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8786 return fRet;
8787 }
8788
8789 fRet = GetFileAttributesW(pwszFilename);
8790 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8791 return fRet;
8792}
8793
8794
8795/** Kernel32 - GetFileAttributesExA. */
8796static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8797 WIN32_FILE_ATTRIBUTE_DATA *pData)
8798{
8799 BOOL fRet;
8800 const char *pszExt = kHlpGetExt(pszFilename);
8801 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8802 {
8803 KFSLOOKUPERROR enmError;
8804 PKFSOBJ pFsObj;
8805 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8806
8807 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8808 if (pFsObj)
8809 {
8810 kHlpAssert(pFsObj->fHaveStats);
8811 if (enmLevel == GetFileExInfoStandard)
8812 {
8813 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8814 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8815 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8816 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8817 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8818 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8819 kFsCacheObjRelease(g_pFsCache, pFsObj);
8820 fRet = TRUE;
8821 }
8822 else
8823 {
8824 kFsCacheObjRelease(g_pFsCache, pFsObj);
8825 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8826 }
8827 }
8828 else
8829 {
8830 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8831 fRet = FALSE;
8832 }
8833
8834#ifdef K_STRICT
8835 {
8836 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8837 DWORD const dwErrSaved = GetLastError();
8838 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8839 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8840 if (fRetCheck && fRet)
8841 {
8842# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8843 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8844 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8845 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8846 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8847 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8848 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8849 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8850 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8851 }
8852 else
8853 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8854 SetLastError(dwErrSaved);
8855 }
8856#endif
8857 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8858 return fRet;
8859 }
8860
8861 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8862 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8863 return fRet;
8864}
8865
8866
8867/** Kernel32 - GetFileAttributesExW. */
8868static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8869 WIN32_FILE_ATTRIBUTE_DATA *pData)
8870{
8871 BOOL fRet;
8872 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8873 {
8874 KFSLOOKUPERROR enmError;
8875 PKFSOBJ pFsObj;
8876 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8877
8878 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8879 if (pFsObj)
8880 {
8881 kHlpAssert(pFsObj->fHaveStats);
8882 if (enmLevel == GetFileExInfoStandard)
8883 {
8884 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8885 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8886 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8887 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8888 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8889 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8890 kFsCacheObjRelease(g_pFsCache, pFsObj);
8891 fRet = TRUE;
8892 }
8893 else
8894 {
8895 kFsCacheObjRelease(g_pFsCache, pFsObj);
8896 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8897 }
8898 }
8899 else
8900 {
8901 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8902 fRet = FALSE;
8903 }
8904
8905#ifdef K_STRICT
8906 {
8907 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8908 DWORD const dwErrSaved = GetLastError();
8909 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8910 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8911 if (fRetCheck && fRet)
8912 {
8913# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8914 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8915 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8916 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8917 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8918 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8919 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8920 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8921 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8922 }
8923 else
8924 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8925 SetLastError(dwErrSaved);
8926 }
8927#endif
8928 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8929 return fRet;
8930 }
8931
8932 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8933 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8934 return fRet;
8935}
8936
8937
8938/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8939 * directory containing each include file. We cache the result to speed
8940 * things up a little. */
8941static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8942{
8943 DWORD cwcRet;
8944 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8945 {
8946 KFSLOOKUPERROR enmError;
8947 PKFSOBJ pObj;
8948 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8949
8950 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8951 if (pObj)
8952 {
8953 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8954 {
8955 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8956 {
8957 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8958
8959 /* Should preserve trailing slash on directory paths. */
8960 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8961 {
8962 if ( cwcRet + 1 < cwcShortPath
8963 && pwszShortPath[cwcRet - 1] != '\\')
8964 {
8965 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8966 if ( cwcIn > 0
8967 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8968 {
8969 pwszShortPath[cwcRet++] = '\\';
8970 pwszShortPath[cwcRet] = '\0';
8971 }
8972 }
8973 }
8974
8975 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8976 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8977 kFsCacheObjRelease(g_pFsCache, pObj);
8978 return cwcRet;
8979 }
8980
8981 /* fall back for complicated cases. */
8982 }
8983 kFsCacheObjRelease(g_pFsCache, pObj);
8984 }
8985 }
8986 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8987 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8988 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8989 return cwcRet;
8990}
8991
8992
8993#ifdef WITH_TEMP_MEMORY_FILES
8994/** Kernel32 - DeleteFileW
8995 * Skip deleting the in-memory files. */
8996static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8997{
8998 BOOL fRc;
8999 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9000 && kwFsIsClTempFileW(pwszFilename))
9001 {
9002 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9003 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
9004 fRc = TRUE;
9005 }
9006 else
9007 {
9008 fRc = DeleteFileW(pwszFilename);
9009 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
9010 }
9011 return fRc;
9012}
9013#endif /* WITH_TEMP_MEMORY_FILES */
9014
9015
9016
9017#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
9018
9019/*
9020 *
9021 * Console output buffering.
9022 * Console output buffering.
9023 * Console output buffering.
9024 *
9025 */
9026
9027
9028/**
9029 * Write a wide char string to the console.
9030 *
9031 * @param pSandbox The sandbox which output buffer to flush.
9032 */
9033static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
9034{
9035 if (cwcToWrite > 0)
9036 {
9037 DWORD cwcWritten = 0;
9038 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
9039 {
9040 if (cwcWritten == cwcToWrite)
9041 { /* likely */ }
9042 else
9043 {
9044 DWORD off = 0;
9045 do
9046 {
9047 off += cwcWritten;
9048 cwcWritten = 0;
9049 } while ( off < cwcToWrite
9050 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
9051 kHlpAssert(off == cwcWritten);
9052 }
9053 }
9054 else
9055 kHlpAssertFailed();
9056 pSandbox->Combined.cFlushes++;
9057 }
9058}
9059
9060
9061/**
9062 * Flushes the combined console output buffer.
9063 *
9064 * @param pSandbox The sandbox which output buffer to flush.
9065 */
9066static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
9067{
9068 if (pSandbox->Combined.cwcBuf > 0)
9069 {
9070 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
9071 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
9072 pSandbox->Combined.cwcBuf = 0;
9073 }
9074}
9075
9076
9077/**
9078 * For handling combined buffer overflow cases line by line.
9079 *
9080 * @param pSandbox The sandbox.
9081 * @param pwcBuf What to add to the combined buffer. Usually a
9082 * line, unless we're really low on buffer space.
9083 * @param cwcBuf The length of what to add.
9084 * @param fBrokenLine Whether this is a broken line.
9085 */
9086static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
9087{
9088 if (fBrokenLine)
9089 kwSandboxConsoleFlushCombined(pSandbox);
9090 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
9091 {
9092 kwSandboxConsoleFlushCombined(pSandbox);
9093 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
9094 }
9095 else
9096 {
9097 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
9098 pSandbox->Combined.cwcBuf += cwcBuf;
9099 }
9100}
9101
9102
9103/**
9104 * Called to final flush a line buffer via the combined buffer (if applicable).
9105 *
9106 * @param pSandbox The sandbox.
9107 * @param pLineBuf The line buffer.
9108 * @param pszName The line buffer name (for logging)
9109 */
9110static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
9111{
9112 if (pLineBuf->fIsConsole)
9113 {
9114 if (pLineBuf->u.Con.cwcBuf > 0)
9115 {
9116 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
9117
9118 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
9119 {
9120 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
9121 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
9122 }
9123 else
9124 {
9125 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
9126 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
9127 }
9128 pLineBuf->u.Con.cwcBuf = 0;
9129 }
9130 }
9131#ifdef WITH_STD_OUT_ERR_BUFFERING
9132 else if (pLineBuf->u.Fully.cchBuf > 0)
9133 {
9134 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
9135
9136 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
9137 pLineBuf->u.Fully.cchBuf = 0;
9138 }
9139#endif
9140}
9141
9142
9143/**
9144 * Called at the end of sandboxed execution to flush both stream buffers.
9145 *
9146 * @param pSandbox The sandbox.
9147 */
9148static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
9149{
9150 /*
9151 * First do the cl.exe source file supression trick, if applicable.
9152 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
9153 * handle.
9154 */
9155 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9156 && pSandbox->Combined.cFlushes == 0)
9157 {
9158 if ( pSandbox->StdOut.fIsConsole
9159 || pSandbox->StdErr.fIsConsole)
9160 {
9161 if ( pSandbox->Combined.cwcBuf >= 3
9162 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
9163 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
9164 {
9165 KI32 off = pSandbox->Combined.cwcBuf - 1;
9166 if (pSandbox->Combined.wszBuf[off] == '\n')
9167 {
9168 KBOOL fOk = K_TRUE;
9169 while (off-- > 0)
9170 {
9171 wchar_t const wc = pSandbox->Combined.wszBuf[off];
9172 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
9173 { /* likely */ }
9174 else
9175 {
9176 fOk = K_FALSE;
9177 break;
9178 }
9179 }
9180 if (fOk)
9181 {
9182 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
9183 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9184 pSandbox->Combined.cwcBuf = 0;
9185 return;
9186 }
9187 }
9188 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
9189 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9190 }
9191 }
9192#ifdef WITH_STD_OUT_ERR_BUFFERING
9193 /*
9194 * Otherwise, it goes to standard output (redirected).
9195 */
9196 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
9197 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
9198 {
9199 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
9200 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
9201 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
9202
9203 if (pchBuf[off] == '\n')
9204 {
9205 KBOOL fOk = K_TRUE;
9206 if (pchBuf[off - 1] == '\r')
9207 off--;
9208 while (off-- > 0)
9209 {
9210 char const ch = pchBuf[off];
9211 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
9212 { /* likely */ }
9213 else
9214 {
9215 fOk = K_FALSE;
9216 break;
9217 }
9218 }
9219 if (fOk)
9220 {
9221 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
9222 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9223 pSandbox->StdOut.u.Fully.cchBuf = 0;
9224 return;
9225 }
9226 }
9227 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
9228 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9229 }
9230#endif
9231 }
9232
9233 /*
9234 * Flush the two line buffer, then the combined buffer.
9235 */
9236 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
9237 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
9238 kwSandboxConsoleFlushCombined(pSandbox);
9239}
9240
9241
9242/**
9243 * Writes a string to the given output stream.
9244 *
9245 * @param pSandbox The sandbox.
9246 * @param pLineBuf The line buffer for the output stream.
9247 * @param pwcBuffer The buffer to write.
9248 * @param cwcToWrite The number of wchar_t's in the buffer.
9249 */
9250static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
9251{
9252 kHlpAssert(pLineBuf->fIsConsole);
9253 if (cwcToWrite > 0)
9254 {
9255 /*
9256 * First, find the start of the last incomplete line so we can figure
9257 * out how much line buffering we need to do.
9258 */
9259 KU32 cchLastIncompleteLine;
9260 KU32 offLastIncompleteLine = cwcToWrite;
9261 while ( offLastIncompleteLine > 0
9262 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
9263 offLastIncompleteLine--;
9264 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
9265
9266 /* Was there anything to line buffer? */
9267 if (offLastIncompleteLine < cwcToWrite)
9268 {
9269 /* Need to grow the line buffer? */
9270 KU32 cwcNeeded = offLastIncompleteLine == 0
9271 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
9272 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
9273 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
9274 {
9275 void *pvNew;
9276 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
9277 while (cwcNew < cwcNeeded)
9278 cwcNew *= 2;
9279 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
9280 if (pvNew)
9281 {
9282 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9283 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
9284 }
9285 else
9286 {
9287 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
9288 if (pvNew)
9289 {
9290 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9291 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
9292 }
9293 else
9294 {
9295 /* This isn't perfect, but it will have to do for now. */
9296 if (pLineBuf->u.Con.cwcBuf > 0)
9297 {
9298 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9299 K_TRUE /*fBrokenLine*/);
9300 pLineBuf->u.Con.cwcBuf = 0;
9301 }
9302 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
9303 return;
9304 }
9305 }
9306 }
9307
9308 /*
9309 * Handle the case where we only add to the line buffer.
9310 */
9311 if (offLastIncompleteLine == 0)
9312 {
9313 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
9314 pLineBuf->u.Con.cwcBuf += cwcToWrite;
9315 return;
9316 }
9317 }
9318
9319 /*
9320 * If there is sufficient combined buffer to handle this request, this is rather simple.
9321 */
9322 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
9323 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9324 {
9325 if (pLineBuf->u.Con.cwcBuf > 0)
9326 {
9327 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9328 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9329 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9330 pLineBuf->u.Con.cwcBuf = 0;
9331 }
9332
9333 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9334 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9335 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9336 }
9337 else
9338 {
9339 /*
9340 * Do line-by-line processing of the input, flusing the combined buffer
9341 * when it becomes necessary. We may have to write lines
9342 */
9343 KU32 off = 0;
9344 KU32 offNextLine = 0;
9345
9346 /* If there are buffered chars, we handle the first line outside the
9347 main loop. We must try our best outputting it as a complete line. */
9348 if (pLineBuf->u.Con.cwcBuf > 0)
9349 {
9350 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9351 offNextLine++;
9352 offNextLine++;
9353 kHlpAssert(offNextLine <= offLastIncompleteLine);
9354
9355 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9356 {
9357 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9358 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9359 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9360 pLineBuf->u.Con.cwcBuf = 0;
9361
9362 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9363 pSandbox->Combined.cwcBuf += offNextLine;
9364 }
9365 else
9366 {
9367 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9368 if (cwcLeft > 0)
9369 {
9370 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9371 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9372 pLineBuf->u.Con.cwcBuf += cwcCopy;
9373 off += cwcCopy;
9374 }
9375 if (pLineBuf->u.Con.cwcBuf > 0)
9376 {
9377 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9378 K_TRUE /*fBrokenLine*/);
9379 pLineBuf->u.Con.cwcBuf = 0;
9380 }
9381 if (off < offNextLine)
9382 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9383 }
9384 off = offNextLine;
9385 }
9386
9387 /* Deal with the remaining lines */
9388 while (off < offLastIncompleteLine)
9389 {
9390 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9391 offNextLine++;
9392 offNextLine++;
9393 kHlpAssert(offNextLine <= offLastIncompleteLine);
9394 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9395 off = offNextLine;
9396 }
9397 }
9398
9399 /*
9400 * Buffer any remaining incomplete line chars.
9401 */
9402 if (cchLastIncompleteLine)
9403 {
9404 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9405 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9406 }
9407 }
9408}
9409
9410
9411/**
9412 * Worker for WriteConsoleA and WriteFile.
9413 *
9414 * @param pSandbox The sandbox.
9415 * @param pLineBuf The line buffer.
9416 * @param pchBuffer What to write.
9417 * @param cchToWrite How much to write.
9418 */
9419static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9420{
9421 /*
9422 * Convert it to wide char and use the 'W' to do the work.
9423 */
9424 int cwcRet;
9425 KU32 cwcBuf = cchToWrite * 2 + 1;
9426 wchar_t *pwcBufFree = NULL;
9427 wchar_t *pwcBuf;
9428 kHlpAssert(pLineBuf->fIsConsole);
9429
9430 if (cwcBuf <= 4096)
9431 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9432 else
9433 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9434
9435 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9436 if (cwcRet > 0)
9437 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9438 else
9439 {
9440 DWORD cchWritten;
9441 kHlpAssertFailed();
9442
9443 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9444 if (pLineBuf->u.Con.cwcBuf > 0)
9445 {
9446 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9447 pLineBuf->u.Con.cwcBuf = 0;
9448 }
9449 kwSandboxConsoleFlushCombined(pSandbox);
9450
9451 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9452 {
9453 if (cchWritten >= cchToWrite)
9454 { /* likely */ }
9455 else
9456 {
9457 KU32 off = 0;
9458 do
9459 {
9460 off += cchWritten;
9461 cchWritten = 0;
9462 } while ( off < cchToWrite
9463 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9464 }
9465 }
9466 }
9467
9468 if (pwcBufFree)
9469 kHlpFree(pwcBufFree);
9470}
9471
9472
9473/** Kernel32 - WriteConsoleA */
9474BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9475 PVOID pvReserved)
9476{
9477 BOOL fRc;
9478 PKWOUTPUTSTREAMBUF pLineBuf;
9479 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9480
9481 if (hConOutput == g_Sandbox.StdErr.hOutput)
9482 pLineBuf = &g_Sandbox.StdErr;
9483 else
9484 pLineBuf = &g_Sandbox.StdOut;
9485 if (pLineBuf->fIsConsole)
9486 {
9487 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9488
9489 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9490 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9491 if (pcbWritten)
9492 *pcbWritten = cbToWrite;
9493 fRc = TRUE;
9494 }
9495 else
9496 {
9497 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9498 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9499 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9500 }
9501 return fRc;
9502}
9503
9504
9505/** Kernel32 - WriteConsoleW */
9506BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9507 PVOID pvReserved)
9508{
9509 BOOL fRc;
9510 PKWOUTPUTSTREAMBUF pLineBuf;
9511 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9512
9513 if (hConOutput == g_Sandbox.StdErr.hOutput)
9514 pLineBuf = &g_Sandbox.StdErr;
9515 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9516 pLineBuf = &g_Sandbox.StdOut;
9517 else
9518 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9519 if (pLineBuf->fIsConsole)
9520 {
9521 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9522
9523 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9524 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9525 if (pcwcWritten)
9526 *pcwcWritten = cwcToWrite;
9527 fRc = TRUE;
9528 }
9529 else
9530 {
9531 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9532 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9533 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9534 }
9535 return fRc;
9536}
9537
9538#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9539
9540
9541
9542/*
9543 *
9544 * Virtual memory leak prevension.
9545 * Virtual memory leak prevension.
9546 * Virtual memory leak prevension.
9547 *
9548 */
9549
9550#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9551
9552/** For debug logging. */
9553# ifndef NDEBUG
9554static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9555{
9556 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9557 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9558 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9559 if (cbMemInfo != 0)
9560 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9561 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9562 MemInfo.BaseAddress,
9563 MemInfo.AllocationBase,
9564 MemInfo.RegionSize,
9565 MemInfo.State,
9566 MemInfo.Protect,
9567 MemInfo.Type));
9568}
9569# else
9570# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9571# endif
9572
9573/**
9574 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9575 *
9576 * @param idxFixed The fixed allocation index to "free".
9577 */
9578static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9579{
9580 BOOL fRc;
9581 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9582 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9583 kHlpAssert(fRc); K_NOREF(fRc);
9584 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9585 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9586}
9587
9588#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9589
9590
9591/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9592 * location (~78MB in 32-bit 2010 compiler). */
9593static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9594{
9595 PVOID pvMem;
9596 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9597 {
9598 KU32 idxPreAllocated = KU32_MAX;
9599
9600#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9601 /*
9602 * Look for a pre-reserved CL.exe heap allocation.
9603 */
9604 pvMem = NULL;
9605 if ( pvAddr != 0
9606 && (fAllocType & MEM_RESERVE))
9607 {
9608 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9609 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9610 while (idxFixed-- > 0)
9611 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9612 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9613 {
9614 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9615 {
9616 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9617 {
9618 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9619 pvMem = pvAddr;
9620 idxPreAllocated = idxFixed;
9621 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9622 pvAddr, cb, fAllocType, fProt, pvMem));
9623 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9624 SetLastError(NO_ERROR);
9625 break;
9626 }
9627 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9628 }
9629 else
9630 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9631 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9632 }
9633 }
9634 if (!pvMem)
9635#endif
9636 {
9637 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9638 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9639 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9640 if ( pvAddr
9641 && pvAddr != pvMem
9642 && !( fAllocType == MEM_RESERVE /* After mapping the PCH, VS2019 ends up here (happens */
9643 && fProt == PAGE_READWRITE /* in real cl.exe runs too). Just shut it up to avoid confusion. */
9644#if K_ARCH_BITS >= 64
9645 && cb > 0x10000000 /* seen 67c00000, 33e00000, ++ */
9646#else
9647 && cb > 0x04000000 /* no idea */
9648#endif
9649 )
9650 )
9651 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9652 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9653 }
9654
9655 if (pvMem)
9656 {
9657 /*
9658 * Track it.
9659 */
9660 PKWVIRTALLOC pTracker;
9661
9662 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9663 pTracker = g_Sandbox.pVirtualAllocHead;
9664 while ( pTracker
9665 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9666 pTracker = pTracker->pNext;
9667 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9668 if (!pTracker)
9669 {
9670 DWORD dwErr = GetLastError();
9671 pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9672 if (pTracker)
9673 {
9674 pTracker->pvAlloc = pvMem;
9675 pTracker->cbAlloc = cb;
9676 pTracker->idxPreAllocated = idxPreAllocated;
9677 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9678 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9679 g_Sandbox.pVirtualAllocHead = pTracker;
9680 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9681 }
9682 SetLastError(dwErr);
9683 }
9684 }
9685 }
9686 else
9687 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9688 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9689 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9690 return pvMem;
9691}
9692
9693
9694/** Kernel32 - VirtualFree. */
9695static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9696{
9697 BOOL fRc;
9698 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9699 {
9700 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9701 if (dwFreeType & MEM_RELEASE)
9702 {
9703 PKWVIRTALLOC pTracker;
9704 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9705 pTracker = g_Sandbox.pVirtualAllocHead;
9706 if (pTracker)
9707 {
9708 if (pTracker->pvAlloc == pvAddr)
9709 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9710 else
9711 {
9712 PKWVIRTALLOC pPrev;
9713 do
9714 {
9715 pPrev = pTracker;
9716 pTracker = pTracker->pNext;
9717 } while (pTracker && pTracker->pvAlloc != pvAddr);
9718 if (pTracker)
9719 pPrev->pNext = pTracker->pNext;
9720 }
9721 if (pTracker)
9722 {
9723#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9724 if (pTracker->idxPreAllocated != KU32_MAX)
9725 {
9726 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9727 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9728 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9729 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9730 kHlpFree(pTracker);
9731 return TRUE;
9732 }
9733#endif
9734
9735 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9736 if (fRc)
9737 kHlpFree(pTracker);
9738 else
9739 {
9740 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9741 g_Sandbox.pVirtualAllocHead = pTracker;
9742 }
9743 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9744 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9745 return fRc;
9746 }
9747
9748 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9749 }
9750 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9751 }
9752 }
9753
9754#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9755 /*
9756 * Protect our fixed allocations (this isn't just paranoia, btw.).
9757 */
9758 if (dwFreeType & MEM_RELEASE)
9759 {
9760 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9761 while (idxFixed-- > 0)
9762 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9763 {
9764 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9765 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9766 return TRUE;
9767 }
9768 }
9769#endif
9770
9771 /*
9772 * Not tracker or not actually free the virtual range.
9773 */
9774 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9775 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9776 return fRc;
9777}
9778
9779
9780/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9781HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9782{
9783 HANDLE hHeap;
9784 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9785
9786 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9787 if (hHeap != NULL)
9788 {
9789 DWORD dwErr = GetLastError();
9790 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9791 if (pTracker)
9792 {
9793 pTracker->hHeap = hHeap;
9794 pTracker->pNext = g_Sandbox.pHeapHead;
9795 g_Sandbox.pHeapHead = pTracker;
9796 }
9797
9798 SetLastError(dwErr);
9799 }
9800 return hHeap;
9801
9802}
9803
9804
9805/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9806BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9807{
9808 BOOL fRc = HeapDestroy(hHeap);
9809 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9810 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9811 if (fRc)
9812 {
9813 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9814 if (pTracker)
9815 {
9816 if (pTracker->hHeap == hHeap)
9817 g_Sandbox.pHeapHead = pTracker->pNext;
9818 else
9819 {
9820 PKWHEAP pPrev;
9821 do
9822 {
9823 pPrev = pTracker;
9824 pTracker = pTracker->pNext;
9825 } while (pTracker && pTracker->hHeap == hHeap);
9826 if (pTracker)
9827 pPrev->pNext = pTracker->pNext;
9828 }
9829 if (pTracker)
9830 kHlpFree(pTracker);
9831 else
9832 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9833 }
9834 }
9835
9836 return fRc;
9837}
9838
9839
9840
9841/*
9842 *
9843 * Thread/Fiber local storage leak prevention.
9844 * Thread/Fiber local storage leak prevention.
9845 * Thread/Fiber local storage leak prevention.
9846 *
9847 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9848 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9849 * we're leaking these indexes, but more importantely we crash during
9850 * worker exit since the callback is triggered multiple times.
9851 */
9852
9853
9854/** Kernel32 - FlsAlloc */
9855DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9856{
9857 DWORD idxFls = FlsAlloc(pfnCallback);
9858 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9859 if (idxFls != FLS_OUT_OF_INDEXES)
9860 {
9861 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9862 if (pTracker)
9863 {
9864 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9865 pTracker->idx = idxFls;
9866 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9867 g_Sandbox.pFlsAllocHead = pTracker;
9868 }
9869 }
9870
9871 return idxFls;
9872}
9873
9874/** Kernel32 - FlsFree */
9875BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9876{
9877 BOOL fRc = FlsFree(idxFls);
9878 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9879 if (fRc)
9880 {
9881 PKWLOCALSTORAGE pTracker;
9882 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9883
9884 pTracker = g_Sandbox.pFlsAllocHead;
9885 if (pTracker)
9886 {
9887 if (pTracker->idx == idxFls)
9888 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9889 else
9890 {
9891 PKWLOCALSTORAGE pPrev;
9892 do
9893 {
9894 pPrev = pTracker;
9895 pTracker = pTracker->pNext;
9896 } while (pTracker && pTracker->idx != idxFls);
9897 if (pTracker)
9898 pPrev->pNext = pTracker->pNext;
9899 }
9900 if (pTracker)
9901 {
9902 pTracker->idx = FLS_OUT_OF_INDEXES;
9903 pTracker->pNext = NULL;
9904 kHlpFree(pTracker);
9905 }
9906 }
9907 }
9908 return fRc;
9909}
9910
9911
9912/** Kernel32 - TlsAlloc */
9913DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9914{
9915 DWORD idxTls = TlsAlloc();
9916 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9917 if (idxTls != TLS_OUT_OF_INDEXES)
9918 {
9919 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9920 if (pTracker)
9921 {
9922 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9923 pTracker->idx = idxTls;
9924 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9925 g_Sandbox.pTlsAllocHead = pTracker;
9926 }
9927 }
9928
9929 return idxTls;
9930}
9931
9932/** Kernel32 - TlsFree */
9933BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9934{
9935 BOOL fRc = TlsFree(idxTls);
9936 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9937 if (fRc)
9938 {
9939 PKWLOCALSTORAGE pTracker;
9940 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9941
9942 pTracker = g_Sandbox.pTlsAllocHead;
9943 if (pTracker)
9944 {
9945 if (pTracker->idx == idxTls)
9946 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9947 else
9948 {
9949 PKWLOCALSTORAGE pPrev;
9950 do
9951 {
9952 pPrev = pTracker;
9953 pTracker = pTracker->pNext;
9954 } while (pTracker && pTracker->idx != idxTls);
9955 if (pTracker)
9956 pPrev->pNext = pTracker->pNext;
9957 }
9958 if (pTracker)
9959 {
9960 pTracker->idx = TLS_OUT_OF_INDEXES;
9961 pTracker->pNext = NULL;
9962 kHlpFree(pTracker);
9963 }
9964 }
9965 }
9966 return fRc;
9967}
9968
9969
9970
9971/*
9972 *
9973 * Header file hashing.
9974 * Header file hashing.
9975 * Header file hashing.
9976 *
9977 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9978 * indicated that ~12% of the time was spent doing MD5 caluclation when
9979 * rebuiling openssl. The hashing it done right after reading the source
9980 * via ReadFile, same buffers and sizes.
9981 */
9982
9983#ifdef WITH_HASH_CACHE
9984
9985/**
9986 * Gets our crypto provider context/instance, creating it if needed.
9987 */
9988static HCRYPTPROV kwSandboxGetCryptoProvider(ALG_ID idAlg)
9989{
9990 DWORD dwProvider;
9991 HCRYPTPROV *phCryptProv;
9992 HCRYPTPROV hCryptProv;
9993 if ( idAlg == CALG_SHA_256
9994 || idAlg == CALG_SHA_512)
9995 {
9996 phCryptProv = &g_Sandbox.hCryptProvAes;
9997 dwProvider = PROV_RSA_AES;
9998 }
9999 else
10000 {
10001 phCryptProv = &g_Sandbox.hCryptProvRsa;
10002 dwProvider = PROV_RSA_FULL;
10003 }
10004 hCryptProv = *phCryptProv;
10005 if (hCryptProv)
10006 return hCryptProv;
10007
10008 /* Create it. */
10009 if (CryptAcquireContextW(&hCryptProv, NULL, NULL, dwProvider, CRYPT_VERIFYCONTEXT))
10010 {
10011 kHlpAssert(hCryptProv != 0);
10012 kHlpAssert(hCryptProv != KUPTR_MAX);
10013 *phCryptProv = hCryptProv;
10014 return hCryptProv;
10015 }
10016
10017 kwErrPrintf("kwSandboxGetCryptoProvider: CryptAcquireContext(,,,%#x, CRYPT_VERIFYCONTEXT) failed! %u\n",
10018 dwProvider, GetLastError());
10019 return (HCRYPTPROV)NULL;
10020}
10021
10022/** AdvApi32 - CryptCreateHash */
10023static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
10024 HCRYPTHASH *phHash)
10025{
10026 BOOL fRc;
10027
10028 /*
10029 * Only do this for cl.exe when it request normal MD5.
10030 */
10031 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
10032 {
10033 KU32 cbDigest;
10034 const char *pszName;
10035 switch (idAlg)
10036 {
10037 case CALG_MD5:
10038 cbDigest = 128/8;
10039 pszName = "MD5";
10040 g_cHashesMd5++;
10041 break;
10042 case CALG_SHA1:
10043 cbDigest = 160/8;
10044 pszName = "SHA1";
10045 g_cHashesSha1++;
10046 break;
10047 case CALG_SHA_256:
10048 cbDigest = 256/8;
10049 pszName = "SHA-256";
10050 g_cHashesSha256++;
10051 break;
10052 case CALG_SHA_512:
10053 cbDigest = 512/8;
10054 pszName = "SHA-512";
10055 g_cHashesSha512++;
10056 break;
10057 default:
10058 cbDigest = 0;
10059 pszName = NULL;
10060 break;
10061 }
10062
10063 if (cbDigest)
10064 {
10065 if (hKey == 0)
10066 {
10067 if (dwFlags == 0)
10068 {
10069 PKWCRYPTHASH pHash = (PKWCRYPTHASH)kHlpAllocZ(sizeof(*pHash));
10070 if (pHash)
10071 {
10072 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10073 pHash->uMagic = KWCRYPTHASH_MAGIC;
10074 pHash->cbHashed = 0;
10075 pHash->fGoneBad = K_FALSE;
10076 pHash->fFinal = K_FALSE;
10077 pHash->hFallback = KUPTR_MAX;
10078 pHash->idAlg = idAlg;
10079 pHash->pszAlgName = pszName;
10080 pHash->cbDigest = cbDigest;
10081
10082 /* link it. */
10083 pHash->pNext = g_Sandbox.pHashHead;
10084 g_Sandbox.pHashHead = pHash;
10085
10086 *phHash = (KUPTR)pHash;
10087 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%s, 0, 0, *phHash=%p) -> %d [cached]\n",
10088 hProv, pszName, *phHash, TRUE));
10089 return TRUE;
10090 }
10091
10092 kwErrPrintf("CryptCreateHash: out of memory!\n");
10093 }
10094 else
10095 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with %s\n", hKey, pszName);
10096 }
10097 else
10098 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with %s\n", hKey, pszName);
10099 }
10100 else
10101 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
10102 }
10103
10104 /*
10105 * Fallback.
10106 */
10107 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
10108 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
10109 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
10110 return fRc;
10111}
10112
10113
10114/** AdvApi32 - CryptHashData */
10115static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
10116{
10117 BOOL fRc;
10118 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10119 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10120 while (pHash && (KUPTR)pHash != hHash)
10121 pHash = pHash->pNext;
10122 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
10123 hHash, pHash, pbData, cbData, dwFlags));
10124 if (pHash)
10125 {
10126 /*
10127 * Validate the state.
10128 */
10129 if ( pHash->uMagic == KWCRYPTHASH_MAGIC
10130 && !pHash->fFinal
10131 && !pHash->fGoneBad)
10132 {
10133 if (pHash->hFallback == KUPTR_MAX)
10134 {
10135 /*
10136 * Does this match the previous ReadFile call to a cached file?
10137 * If it doesn't, try falling back.
10138 */
10139 if ( g_Sandbox.LastHashRead.cbRead == cbData
10140 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
10141 {
10142 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
10143 if ( pCachedFile
10144 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
10145 {
10146
10147 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
10148 {
10149 if ( pHash->pCachedFile == NULL
10150 && pHash->cbHashed == 0)
10151 pHash->pCachedFile = pCachedFile;
10152 if (pHash->pCachedFile == pCachedFile)
10153 {
10154 pHash->cbHashed += cbData;
10155 g_Sandbox.LastHashRead.pCachedFile = NULL;
10156 g_Sandbox.LastHashRead.pvRead = NULL;
10157 g_Sandbox.LastHashRead.cbRead = 0;
10158 g_Sandbox.LastHashRead.offRead = 0;
10159 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
10160 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
10161 return TRUE;
10162 }
10163
10164 /* Note! it's possible to fall back here too, if necessary. */
10165 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
10166 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
10167 }
10168 else
10169 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
10170 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
10171 }
10172 else if (!pCachedFile)
10173 KWCRYPT_LOG(("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n"));
10174 else
10175 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
10176 }
10177 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
10178 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
10179 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
10180 if (pHash->cbHashed == 0)
10181 {
10182 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
10183 HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(pHash->idAlg);
10184 if (hCryptProv)
10185 {
10186 HCRYPTHASH hCryptHash = KUPTR_MAX;
10187 if (CryptCreateHash(hCryptProv, pHash->idAlg, 0, 0, &hCryptHash))
10188 {
10189 kHlpAssert(hCryptHash != KUPTR_MAX);
10190 pHash->hFallback = hCryptHash;
10191 fRc = CryptHashData(hCryptHash, pbData, cbData, dwFlags);
10192 if (fRc)
10193 pHash->cbHashed = cbData;
10194 g_cHashesFallbacks++;
10195 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d (%u) [fallback!]\n",
10196 hHash, pbData, cbData, dwFlags, fRc, GetLastError()));
10197 }
10198 else
10199 {
10200 kwErrPrintf("kwSandbox_Advapi32_CryptHashData: Fallback CryptCreateHash(%u) failed: %u\n",
10201 pHash->idAlg, GetLastError());
10202 fRc = FALSE;
10203 }
10204 return fRc;
10205 }
10206 }
10207 pHash->fGoneBad = K_TRUE;
10208 SetLastError(ERROR_INVALID_PARAMETER);
10209 fRc = FALSE;
10210 }
10211 else
10212 {
10213 /* fallback. */
10214 fRc = CryptHashData(pHash->hFallback, pbData, cbData, dwFlags);
10215 if (fRc)
10216 pHash->cbHashed += cbData;
10217 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d [fallback]\n",
10218 hHash, pbData, cbData, dwFlags, fRc));
10219 }
10220 }
10221 /*
10222 * Bad handle state.
10223 */
10224 else
10225 {
10226 if (pHash->uMagic != KWCRYPTHASH_MAGIC)
10227 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
10228 else
10229 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
10230 SetLastError((DWORD)NTE_BAD_HASH);
10231 fRc = FALSE;
10232 }
10233 }
10234 else
10235 {
10236
10237 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
10238 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
10239 }
10240 return fRc;
10241}
10242
10243
10244/** Helper for simpe data hashing. */
10245static BOOL kwSandboxCalcHash(ALG_ID idAlg, void const *pvData, KSIZE cbData, KU8 *pbDigest, KSIZE cbDigest)
10246{
10247 BOOL fRet = FALSE;
10248 if (idAlg == CALG_MD5)
10249 {
10250 struct MD5Context Ctx;
10251 MD5Init(&Ctx);
10252 MD5Update(&Ctx, (unsigned char const *)pvData, (unsigned)cbData);
10253 MD5Final(pbDigest, &Ctx);
10254 fRet = TRUE;
10255 }
10256 else
10257 {
10258 HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(idAlg);
10259 if (hCryptProv)
10260 {
10261 HCRYPTHASH hCryptHash = KUPTR_MAX;
10262 if (CryptCreateHash(hCryptProv, idAlg, 0, 0, &hCryptHash))
10263 {
10264 if (CryptHashData(hCryptHash, (const BYTE *)pvData, (DWORD)cbData, 0))
10265 {
10266 DWORD cbActual = (DWORD)cbDigest;
10267 if (CryptGetHashParam(hCryptHash, HP_HASHVAL, pbDigest, &cbActual, 0))
10268 {
10269 fRet = TRUE;
10270 kHlpAssert(cbActual == cbDigest);
10271 }
10272 else
10273 kwErrPrintf("CryptGetHashParam([%#x],HP_HASHVAL,%p,%#x,0) failed: %u\n",
10274 idAlg, pbDigest, cbDigest, GetLastError());
10275 }
10276 else
10277 kwErrPrintf("CryptHashData([%#x],%p,%#x,0) failed: %u\n", idAlg, pvData, cbData, GetLastError());
10278 CryptDestroyHash(hCryptHash);
10279 }
10280 else
10281 kwErrPrintf("CryptCreateHash(%#x) failed: %u\n", idAlg, GetLastError());
10282 }
10283 }
10284 return fRet;
10285}
10286
10287
10288/** AdvApi32 - CryptGetHashParam */
10289static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
10290 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
10291{
10292 BOOL fRc;
10293 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10294 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10295 while (pHash && (KUPTR)pHash != hHash)
10296 pHash = pHash->pNext;
10297 if (pHash)
10298 {
10299 if (pHash->uMagic == KWCRYPTHASH_MAGIC)
10300 {
10301 if (dwFlags == 0)
10302 {
10303 DWORD cbRet;
10304 void *pvRet;
10305 union
10306 {
10307 DWORD dw;
10308 } uBuf;
10309
10310 switch (dwParam)
10311 {
10312 case HP_HASHVAL:
10313 {
10314 /* Check the hash progress. */
10315 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
10316 g_cHashes++;
10317 if (pCachedFile)
10318 {
10319 if ( pCachedFile->cbCached == pHash->cbHashed
10320 && !pHash->fGoneBad)
10321 {
10322 KBOOL *pfValid;
10323 switch (pHash->idAlg)
10324 {
10325 case CALG_MD5:
10326 pfValid = &pCachedFile->fValidMd5;
10327 pvRet = pCachedFile->abMd5Digest;
10328 break;
10329 case CALG_SHA1:
10330 pfValid = &pCachedFile->fValidSha1;
10331 pvRet = pCachedFile->abSha1Digest;
10332 break;
10333 case CALG_SHA_256:
10334 pfValid = &pCachedFile->fValidSha256;
10335 pvRet = pCachedFile->abSha256Digest;
10336 break;
10337 case CALG_SHA_512:
10338 pfValid = &pCachedFile->fValidSha512;
10339 pvRet = pCachedFile->abSha512Digest;
10340 break;
10341 default:
10342 kwErrPrintf("Invalid idAlg value: %#x\n", pHash->idAlg);
10343 SetLastError(ERROR_INVALID_SERVER_STATE);
10344 return FALSE;
10345 }
10346
10347 if (*pfValid)
10348 {
10349 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
10350 g_cHashesCached++;
10351 }
10352 else
10353 {
10354 fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pCachedFile->cbCached,
10355 pvRet, pHash->cbDigest);
10356 if (!fRc)
10357 return FALSE;
10358 *pfValid = K_TRUE;
10359 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
10360 }
10361 }
10362 else
10363 {
10364 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
10365 from what I can tell, so just deal with it. */
10366 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
10367 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
10368 pHash, pCachedFile, pCachedFile->szPath));
10369 g_cHashesPartial++;
10370 pHash->pCachedFile = NULL;
10371 pvRet = pHash->abDigest;
10372 fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pHash->cbHashed,
10373 pvRet, pHash->cbDigest);
10374 if (!fRc)
10375 {
10376 pHash->fGoneBad = K_TRUE;
10377 return FALSE;
10378 }
10379 }
10380 pHash->fFinal = K_TRUE;
10381 cbRet = pHash->cbDigest;
10382 break;
10383 }
10384
10385 pvRet = pHash->abDigest;
10386 cbRet = pHash->cbDigest;
10387 if (pHash->fFinal)
10388 break;
10389 if (pHash->hFallback != KUPTR_MAX)
10390 {
10391 DWORD cbActual = (DWORD)pHash->cbDigest;
10392 if (CryptGetHashParam(pHash->hFallback, HP_HASHVAL, pHash->abDigest, &cbActual, 0))
10393 {
10394 kHlpAssert(cbActual == pHash->cbDigest);
10395 pHash->fFinal = K_TRUE;
10396 break;
10397 }
10398 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: Fallback CryptGetHashParam failed: %u!!\n", GetLastError());
10399 }
10400 else
10401 {
10402 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
10403 SetLastError(ERROR_INVALID_SERVER_STATE);
10404 }
10405 return FALSE;
10406 }
10407
10408 case HP_HASHSIZE:
10409 uBuf.dw = pHash->cbDigest;
10410 pvRet = &uBuf;
10411 cbRet = sizeof(DWORD);
10412 break;
10413
10414 case HP_ALGID:
10415 uBuf.dw = pHash->idAlg;
10416 pvRet = &uBuf;
10417 cbRet = sizeof(DWORD);
10418 break;
10419
10420 default:
10421 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
10422 SetLastError((DWORD)NTE_BAD_TYPE);
10423 return FALSE;
10424 }
10425
10426 /*
10427 * Copy out cbRet from pvRet.
10428 */
10429 if (pbData)
10430 {
10431 if (*pcbData >= cbRet)
10432 {
10433 *pcbData = cbRet;
10434 kHlpMemCopy(pbData, pvRet, cbRet);
10435 if (cbRet == 4)
10436 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
10437 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
10438 else if (cbRet == 16)
10439 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",
10440 dwParam, pHash, pHash->pCachedFile, cbRet,
10441 pbData[0], pbData[1], pbData[2], pbData[3],
10442 pbData[4], pbData[5], pbData[6], pbData[7],
10443 pbData[8], pbData[9], pbData[10], pbData[11],
10444 pbData[12], pbData[13], pbData[14], pbData[15]));
10445 else if (cbRet == 20)
10446 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%02x%02x%02x%02x [cached]\n",
10447 dwParam, pHash, pHash->pCachedFile, cbRet,
10448 pbData[0], pbData[1], pbData[2], pbData[3],
10449 pbData[4], pbData[5], pbData[6], pbData[7],
10450 pbData[8], pbData[9], pbData[10], pbData[11],
10451 pbData[12], pbData[13], pbData[14], pbData[15],
10452 pbData[16], pbData[17], pbData[18], pbData[19] ));
10453 else if (cbRet >= 32)
10454 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%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%s [cached]\n",
10455 dwParam, pHash, pHash->pCachedFile, cbRet,
10456 pbData[0], pbData[1], pbData[2], pbData[3],
10457 pbData[4], pbData[5], pbData[6], pbData[7],
10458 pbData[8], pbData[9], pbData[10], pbData[11],
10459 pbData[12], pbData[13], pbData[14], pbData[15],
10460 pbData[16], pbData[17], pbData[18], pbData[19],
10461 pbData[20], pbData[21], pbData[22], pbData[23],
10462 pbData[24], pbData[25], pbData[26], pbData[27],
10463 pbData[28], pbData[29], pbData[30], pbData[31], cbRet > 32 ? "..." : ""));
10464 else
10465 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
10466 dwParam, pHash, pHash->pCachedFile, cbRet));
10467 return TRUE;
10468 }
10469
10470 kHlpMemCopy(pbData, pvRet, *pcbData);
10471 }
10472 SetLastError(ERROR_MORE_DATA);
10473 *pcbData = cbRet;
10474 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
10475 }
10476 else
10477 {
10478 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
10479 SetLastError((DWORD)NTE_BAD_FLAGS);
10480 }
10481 }
10482 else
10483 {
10484 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
10485 SetLastError((DWORD)NTE_BAD_HASH);
10486 }
10487 fRc = FALSE;
10488 }
10489 /*
10490 * Regular handle.
10491 */
10492 else
10493 {
10494 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
10495 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
10496 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
10497 }
10498
10499 return fRc;
10500}
10501
10502
10503/** AdvApi32 - CryptDestroyHash */
10504static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
10505{
10506 BOOL fRc;
10507 PKWCRYPTHASH pPrev = NULL;
10508 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10509 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10510 while (pHash && (KUPTR)pHash != hHash)
10511 {
10512 pPrev = pHash;
10513 pHash = pHash->pNext;
10514 }
10515 if (pHash)
10516 {
10517 if (pHash->uMagic == KWCRYPTHASH_MAGIC)
10518 {
10519 pHash->uMagic = 0;
10520 if (!pPrev)
10521 g_Sandbox.pHashHead = pHash->pNext;
10522 else
10523 pPrev->pNext = pHash->pNext;
10524 kHlpFree(pHash);
10525 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
10526 fRc = TRUE;
10527 }
10528 else
10529 {
10530 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10531 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10532 SetLastError(ERROR_INVALID_HANDLE);
10533 fRc = FALSE;
10534 }
10535 }
10536 /*
10537 * Regular handle.
10538 */
10539 else
10540 {
10541 fRc = CryptDestroyHash(hHash);
10542 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10543 }
10544 return fRc;
10545}
10546
10547#endif /* WITH_HASH_CACHE */
10548
10549
10550/*
10551 *
10552 * Reuse crypt context.
10553 * Reuse crypt context.
10554 * Reuse crypt context.
10555 *
10556 *
10557 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10558 *
10559 */
10560
10561#ifdef WITH_CRYPT_CTX_REUSE
10562
10563/** AdvApi32 - CryptAcquireContextW. */
10564static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10565 DWORD dwProvType, DWORD dwFlags)
10566{
10567 BOOL fRet;
10568
10569 /*
10570 * Lookup reusable context based on the input.
10571 */
10572 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10573 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10574 KU32 iCtx = g_Sandbox.cCryptCtxs;
10575 while (iCtx-- > 0)
10576 {
10577 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10578 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10579 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10580 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10581 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10582 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10583 {
10584 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10585 {
10586 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10587 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10588 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10589 return TRUE;
10590 }
10591 }
10592 }
10593
10594 /*
10595 * Create it and enter it into the reused array if possible.
10596 */
10597 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10598 if (fRet)
10599 {
10600 iCtx = g_Sandbox.cCryptCtxs;
10601 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10602 {
10603 /* Try duplicate the input strings. */
10604 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10605 (cwcContainer + 1) * sizeof(wchar_t));
10606 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10607 {
10608 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10609 (cwcProvider + 1) * sizeof(wchar_t));
10610 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10611 {
10612 /* Add a couple of references just to be on the safe side and all that. */
10613 HCRYPTPROV hProv = *phProv;
10614 if (CryptContextAddRef(hProv, NULL, 0))
10615 {
10616 if (CryptContextAddRef(hProv, NULL, 0))
10617 {
10618 /* Okay, finish the entry and return success */
10619 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10620 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10621 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10622 g_Sandbox.cCryptCtxs = iCtx + 1;
10623
10624 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10625 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10626 return TRUE;
10627 }
10628 CryptReleaseContext(hProv, 0);
10629 }
10630 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10631
10632 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10633 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10634 }
10635 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10636 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10637 }
10638 }
10639 else
10640 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10641 }
10642
10643 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10644 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10645 return fRet;
10646}
10647
10648
10649/** AdvApi32 - CryptReleaseContext */
10650static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10651{
10652 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10653 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10654 return fRet;
10655}
10656
10657
10658/** AdvApi32 - CryptContextAddRef */
10659static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10660{
10661 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10662 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10663 return fRet;
10664}
10665
10666#endif /* WITH_CRYPT_CTX_REUSE */
10667
10668/*
10669 *
10670 * Structured exception handling.
10671 * Structured exception handling.
10672 * Structured exception handling.
10673 *
10674 */
10675#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10676
10677# define EH_NONCONTINUABLE KU32_C(0x00000001)
10678# define EH_UNWINDING KU32_C(0x00000002)
10679# define EH_EXIT_UNWIND KU32_C(0x00000004)
10680# define EH_STACK_INVALID KU32_C(0x00000008)
10681# define EH_NESTED_CALL KU32_C(0x00000010)
10682
10683typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10684 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10685typedef struct _EXCEPTION_REGISTRATION_RECORD
10686{
10687 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10688 PFNXCPTHANDLER pfnXcptHandler;
10689};
10690
10691
10692/**
10693 * Calls @a pfnHandler.
10694 */
10695static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10696 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10697 PFNXCPTHANDLER pfnHandler)
10698{
10699# if 1
10700 /* This is a more robust version that isn't subject to calling
10701 convension cleanup disputes and such. */
10702 KU32 uSavedEdi;
10703 KU32 uSavedEsi;
10704 KU32 uSavedEbx;
10705 KU32 rcHandler;
10706
10707 __asm
10708 {
10709 mov [uSavedEdi], edi
10710 mov [uSavedEsi], esi
10711 mov [uSavedEbx], ebx
10712 mov esi, esp
10713 mov edi, esp
10714 mov edi, [pXcptRec]
10715 mov edx, [pRegRec]
10716 mov eax, [pXcptCtx]
10717 mov ebx, [ppRegRec]
10718 mov ecx, [pfnHandler]
10719 sub esp, 16
10720 and esp, 0fffffff0h
10721 mov [esp ], edi
10722 mov [esp + 4], edx
10723 mov [esp + 8], eax
10724 mov [esp + 12], ebx
10725 mov edi, esi
10726 call ecx
10727 mov esp, esi
10728 cmp esp, edi
10729 je stack_ok
10730 int 3
10731 stack_ok:
10732 mov edi, [uSavedEdi]
10733 mov esi, [uSavedEsi]
10734 mov ebx, [uSavedEbx]
10735 mov [rcHandler], eax
10736 }
10737 return rcHandler;
10738# else
10739 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10740# endif
10741}
10742
10743
10744/**
10745 * Vectored exception handler that emulates x86 chained exception handler.
10746 *
10747 * This is necessary because the RtlIsValidHandler check fails for self loaded
10748 * code and prevents cl.exe from working. (On AMD64 we can register function
10749 * tables, but on X86 cooking your own handling seems to be the only viabke
10750 * alternative.)
10751 *
10752 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10753 * @param pXcptPtrs The exception details.
10754 */
10755static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10756{
10757 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10758 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10759 if (g_Sandbox.fRunning)
10760 {
10761 HANDLE const hCurProc = GetCurrentProcess();
10762 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10763 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10764 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10765 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10766 {
10767 /* Read the exception record in a safe manner. */
10768 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10769 DWORD cbActuallyRead = 0;
10770 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10771 && cbActuallyRead == sizeof(RegRec))
10772 {
10773 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10774 KU32 rcHandler;
10775 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10776 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10777 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10778 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10779 if (rcHandler == ExceptionContinueExecution)
10780 {
10781 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10782 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10783 return EXCEPTION_CONTINUE_EXECUTION;
10784 }
10785
10786 if (rcHandler == ExceptionContinueSearch)
10787 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10788 else if (rcHandler == ExceptionNestedException)
10789 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10790 else
10791 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10792 }
10793 else
10794 {
10795 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10796 break;
10797 }
10798
10799 /*
10800 * Next.
10801 */
10802 pRegRec = RegRec.pPrevRegRec;
10803 }
10804 }
10805 return EXCEPTION_CONTINUE_SEARCH;
10806}
10807
10808
10809/** NtDll,Kernel32 - RtlUnwind */
10810static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10811 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10812{
10813 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10814 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10815 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10816 if (g_Sandbox.fRunning)
10817 {
10818 HANDLE const hCurProc = GetCurrentProcess();
10819 PCONTEXT pXcptCtx = NULL;
10820 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10821
10822 /*
10823 * Update / create an exception record.
10824 */
10825 if (pXcptRec)
10826 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10827 else
10828 {
10829 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10830 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10831 pXcptRec->ExceptionCode = (DWORD)STATUS_UNWIND;
10832 pXcptRec->ExceptionFlags = EH_UNWINDING;
10833 }
10834 if (!pStopXcptRec)
10835 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10836
10837 /*
10838 * Walk the chain till we find pStopXctpRec.
10839 */
10840 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10841 && pRegRec != NULL
10842 && pRegRec != pStopXcptRec)
10843 {
10844 /* Read the exception record in a safe manner. */
10845 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10846 DWORD cbActuallyRead = 0;
10847 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10848 && cbActuallyRead == sizeof(RegRec))
10849 {
10850 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10851 KU32 rcHandler;
10852 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10853 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10854 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10855 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10856
10857 if (rcHandler == ExceptionContinueSearch)
10858 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10859 else if (rcHandler == ExceptionCollidedUnwind)
10860 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10861 else
10862 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10863 }
10864 else
10865 {
10866 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10867 break;
10868 }
10869
10870 /*
10871 * Pop next.
10872 */
10873 pTib->ExceptionList = RegRec.pPrevRegRec;
10874 pRegRec = RegRec.pPrevRegRec;
10875 }
10876 return;
10877 }
10878
10879 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10880}
10881
10882#endif /* WINDOWS + X86 */
10883
10884
10885/*
10886 *
10887 * Misc function only intercepted while debugging.
10888 * Misc function only intercepted while debugging.
10889 * Misc function only intercepted while debugging.
10890 *
10891 */
10892
10893#ifndef NDEBUG
10894
10895/** CRT - memcpy */
10896static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10897{
10898 KU8 const *pbSrc = (KU8 const *)pvSrc;
10899 KU8 *pbDst = (KU8 *)pvDst;
10900 KSIZE cbLeft = cb;
10901 while (cbLeft-- > 0)
10902 *pbDst++ = *pbSrc++;
10903 return pvDst;
10904}
10905
10906
10907/** CRT - memset */
10908static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10909{
10910 KU8 *pbDst = (KU8 *)pvDst;
10911 KSIZE cbLeft = cb;
10912 while (cbLeft-- > 0)
10913 *pbDst++ = (KU8)bFiller;
10914 return pvDst;
10915}
10916
10917#endif /* NDEBUG */
10918
10919
10920/** @todo consider hooking NtQueryDirectoryFile as c1xx.dll/c1.dll in 2019
10921 * uses it directly to read the content of include directories, however
10922 * they do it one file at the time. We already have the info in the
10923 * cache (where we do bulk reads). There are a lot of calls for the
10924 * SDK include directories, as one can imagine. */
10925
10926/**
10927 * Functions that needs replacing for sandboxed execution.
10928 */
10929KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10930{
10931 /*
10932 * Kernel32.dll and friends.
10933 */
10934 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10935 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10936
10937 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10938 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10939 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10940 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10941 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10942 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10943 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10944 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10945 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10946 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10947 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10948
10949 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10950 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10951 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10952 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10953
10954 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10955
10956 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10957 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10958 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10959 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10960 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10961 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10962 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10963 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10964 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10965 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10966 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10967
10968 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10969 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10970 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10971 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10972#ifdef WITH_TEMP_MEMORY_FILES
10973 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10974 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10975 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10976 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10977 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10978 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10979 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10980 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10981 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10982 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10983#endif
10984 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10985 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10986 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10987 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10988 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10989 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10990 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10991 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10992 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10993#ifdef WITH_TEMP_MEMORY_FILES
10994 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10995#endif
10996
10997#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10998 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10999 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
11000#endif
11001
11002 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
11003 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
11004
11005 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
11006 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
11007
11008 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
11009 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
11010 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
11011 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
11012
11013 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
11014
11015#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
11016 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
11017#endif
11018
11019#ifdef WITH_HASH_CACHE
11020 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
11021 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
11022 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
11023 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
11024#endif
11025
11026#ifdef WITH_CRYPT_CTX_REUSE
11027 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
11028 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
11029 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
11030#endif
11031
11032 /*
11033 * MS Visual C++ CRTs.
11034 */
11035 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
11036 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
11037 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
11038 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
11039 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
11040 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
11041
11042 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
11043 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
11044 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
11045
11046 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
11047 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
11048 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
11049
11050 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
11051 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
11052 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
11053 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
11054 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
11055 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
11056 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
11057 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
11058 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
11059 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
11060 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
11061 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
11062 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
11063 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
11064 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
11065 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
11066 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
11067 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
11068 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
11069 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
11070
11071 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
11072 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
11073 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
11074 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
11075 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
11076 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
11077 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
11078 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
11079 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environC },
11080 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenvironC },
11081 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
11082 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
11083 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
11084 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
11085
11086#ifndef NDEBUG
11087 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
11088 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
11089#endif
11090};
11091/** Number of entries in g_aReplacements. */
11092KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
11093
11094
11095/**
11096 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
11097 * execution.
11098 */
11099KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
11100{
11101 /*
11102 * Kernel32.dll and friends.
11103 */
11104 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
11105 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
11106
11107 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
11108 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
11109
11110#if 0
11111 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
11112#endif
11113
11114 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
11115 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
11116 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
11117 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
11118#ifdef WITH_TEMP_MEMORY_FILES
11119 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
11120 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
11121 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
11122 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
11123 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
11124 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
11125 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
11126 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
11127 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
11128 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
11129#endif
11130 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
11131 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
11132 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
11133 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
11134 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
11135 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
11136 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
11137 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
11138 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
11139#ifdef WITH_TEMP_MEMORY_FILES
11140 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
11141#endif
11142 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
11143 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
11144 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExW },
11145#ifndef NDEBUG
11146 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_Native_GetProcAddress },
11147#endif
11148
11149#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11150 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
11151 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
11152#endif
11153
11154#ifdef WITH_HASH_CACHE
11155 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
11156 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
11157 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
11158 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
11159#endif
11160
11161 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
11162
11163#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
11164 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
11165#endif
11166
11167 /*
11168 * MS Visual C++ CRTs.
11169 */
11170 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
11171 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
11172 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
11173 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
11174 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
11175 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
11176 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
11177
11178#if 0 /* used by mspdbXXX.dll */
11179 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
11180 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
11181#endif
11182};
11183/** Number of entries in g_aSandboxNativeReplacements. */
11184KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
11185
11186
11187/**
11188 * Functions that needs replacing when queried by GetProcAddress.
11189 */
11190KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
11191{
11192 /*
11193 * Kernel32.dll and friends.
11194 */
11195 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
11196 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
11197 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
11198 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
11199};
11200/** Number of entries in g_aSandboxGetProcReplacements. */
11201KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
11202
11203
11204/**
11205 * Control handler.
11206 *
11207 * @returns TRUE if handled, FALSE if not.
11208 * @param dwCtrlType The signal.
11209 */
11210static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
11211{
11212 DWORD cbIgn;
11213 int volatile rc; /* volatile for debugging */
11214 int volatile rcPrev;
11215 const char *pszMsg;
11216 switch (dwCtrlType)
11217 {
11218 case CTRL_C_EVENT:
11219 rc = 9;
11220 pszMsg = "kWorker: Ctrl-C\r\n";
11221 break;
11222
11223 case CTRL_BREAK_EVENT:
11224 rc = 10;
11225 pszMsg = "kWorker: Ctrl-Break\r\n";
11226 break;
11227
11228 case CTRL_CLOSE_EVENT:
11229 rc = 11;
11230 pszMsg = "kWorker: console closed\r\n";
11231 break;
11232
11233 case CTRL_LOGOFF_EVENT:
11234 rc = 11;
11235 pszMsg = "kWorker: logoff event\r\n";
11236 break;
11237
11238 case CTRL_SHUTDOWN_EVENT:
11239 rc = 11;
11240 pszMsg = "kWorker: shutdown event\r\n";
11241 break;
11242
11243 default:
11244 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
11245 return TRUE;
11246 }
11247
11248 /*
11249 * Terminate the process after 5 seconds.
11250 * If we get here a second time we just terminate the process ourselves.
11251 *
11252 * Note! We do no try call exit() here as it turned out to deadlock a lot
11253 * flusing file descriptors (stderr back when we first wrote to it).
11254 */
11255 rcPrev = g_rcCtrlC;
11256 g_rcCtrlC = rc;
11257 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
11258 if (rcPrev == 0)
11259 {
11260 int i;
11261 for (i = 0; i < 10; i++)
11262 {
11263 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
11264 Sleep(500);
11265 }
11266 }
11267 TerminateProcess(GetCurrentProcess(), rc);
11268 return TRUE;
11269}
11270
11271
11272#if 0
11273/**
11274 * Resets the KWMODULE::fVisited flag for _all_ known modules.
11275 */
11276static void kwSandboxResetModuleVisited(void)
11277{
11278 PKWMODULE pMod = g_pModuleHead;
11279 while (pMod)
11280 {
11281 pMod->fVisited = K_FALSE;
11282 pMod = pMod->pNextList;
11283 }
11284}
11285
11286
11287/**
11288 * Used by kwSandboxExec to reset the state of the module tree.
11289 *
11290 * This is done recursively.
11291 *
11292 * @param pMod The root of the tree to consider.
11293 */
11294static void kwSandboxResetModuleState(PKWMODULE pMod)
11295{
11296 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
11297 if (!pMod->fNative)
11298 {
11299 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
11300 if (!pMod->fVisited) /* Avoid loops. */
11301 {
11302 KSIZE iImp;
11303 pMod->fVisited = K_TRUE;
11304 iImp = pMod->u.Manual.cImpMods;
11305 while (iImp-- > 0)
11306 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
11307 }
11308 }
11309 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11310 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
11311 {
11312 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11313 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11314 {
11315 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11316 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11317 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11318 pMod->pszPath, pszValue ? pszValue : "<null>"));
11319 }
11320 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11321 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11322 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11323 pMod->pszPath, pszValue ? pszValue : "<null>"));
11324 else
11325 {
11326 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11327 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11328 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11329 if (pszValue != NULL)
11330 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11331 else
11332 pMod->pszMsPdbSrvEndpoint = NULL;
11333 pMod->fNeedReInit = K_TRUE;
11334 }
11335 }
11336}
11337#else
11338/**
11339 * Used by kwSandboxExec to reset the state of the module tree.
11340 */
11341static void kwSandboxResetModuleState(void)
11342{
11343 PKWMODULE pMod = g_pModuleHead;
11344 while (pMod)
11345 {
11346 if (!pMod->fNative)
11347 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
11348 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11349 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
11350 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
11351 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
11352 {
11353 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11354 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11355 {
11356 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11357 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11358 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11359 pMod->pszPath, pszValue ? pszValue : "<null>"));
11360 }
11361 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11362 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11363 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11364 pMod->pszPath, pszValue ? pszValue : "<null>"));
11365 else
11366 {
11367 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11368 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11369 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11370 if (pszValue != NULL)
11371 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11372 else
11373 pMod->pszMsPdbSrvEndpoint = NULL;
11374 pMod->fNeedReInit = K_TRUE;
11375 }
11376 }
11377
11378 pMod = pMod->pNextList;
11379 }
11380}
11381#endif
11382
11383static PPEB kwSandboxGetProcessEnvironmentBlock(void)
11384{
11385#if K_ARCH == K_ARCH_X86_32
11386 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
11387#elif K_ARCH == K_ARCH_AMD64
11388 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
11389#else
11390# error "Port me!"
11391#endif
11392}
11393
11394
11395/**
11396 * Enters the given handle into the handle table.
11397 *
11398 * @returns K_TRUE on success, K_FALSE on failure.
11399 * @param pSandbox The sandbox.
11400 * @param pHandle The handle.
11401 * @param hHandle The handle value to enter it under (for the
11402 * duplicate handle API).
11403 */
11404static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
11405{
11406 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
11407 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
11408
11409 EnterCriticalSection(&g_Sandbox.HandlesLock);
11410
11411 /*
11412 * Grow handle table.
11413 */
11414 if (idxHandle >= pSandbox->cHandles)
11415 {
11416 void *pvNew;
11417 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
11418 while (cHandles <= idxHandle)
11419 cHandles *= 2;
11420 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
11421 if (!pvNew)
11422 {
11423 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11424 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
11425 return K_FALSE;
11426 }
11427 pSandbox->papHandles = (PKWHANDLE *)pvNew;
11428 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
11429 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
11430 pSandbox->cHandles = cHandles;
11431 }
11432
11433 /*
11434 * Check that the entry is unused then insert it.
11435 */
11436 kHlpAssertStmtReturn(pSandbox->papHandles[idxHandle] == NULL, LeaveCriticalSection(&g_Sandbox.HandlesLock), K_FALSE);
11437 pSandbox->papHandles[idxHandle] = pHandle;
11438 pSandbox->cActiveHandles++;
11439 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11440 return K_TRUE;
11441}
11442
11443
11444/**
11445 * Safely looks up a handle, does not get it and it must not be 'put'.
11446 *
11447 * @returns Pointer to the handle structure if found, otherwise NULL.
11448 * @param hFile The handle to resolve.
11449 */
11450static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile)
11451{
11452 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11453 EnterCriticalSection(&g_Sandbox.HandlesLock);
11454 if (idxHandle < g_Sandbox.cHandles)
11455 {
11456 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11457 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11458 return pHandle;
11459 }
11460 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11461 return NULL;
11462}
11463
11464
11465/**
11466 * Safely gets a handle, must be "put" when done with it.
11467 *
11468 * @returns Pointer to the handle structure if found, otherwise NULL.
11469 * @param hFile The handle to resolve.
11470 */
11471static PKWHANDLE kwSandboxHandleGet(HANDLE hFile)
11472{
11473 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11474 EnterCriticalSection(&g_Sandbox.HandlesLock);
11475 if (idxHandle < g_Sandbox.cHandles)
11476 {
11477 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11478 if (pHandle)
11479 {
11480 kHlpAssertMsg(pHandle->tidOwner == KU32_MAX, ("hFile=%p tidOwner=%#x\n", hFile, pHandle->tidOwner));
11481 pHandle->tidOwner = GetCurrentThreadId();
11482 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11483 return pHandle;
11484 }
11485 }
11486 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11487 return NULL;
11488}
11489
11490
11491/**
11492 * Puts a handle returned by kwSandboxHandleGet.
11493 *
11494 * @param pHandle The handle to "put".
11495 */
11496K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle)
11497{
11498 kHlpAssertMsg(pHandle->tidOwner == GetCurrentThreadId(),
11499 ("hFile tidOwner=%#x tidMe=%#x\n", pHandle->hHandle, pHandle->tidOwner, GetCurrentThreadId()));
11500 pHandle->tidOwner = KU32_MAX;
11501}
11502
11503
11504/**
11505 * Creates a correctly quoted ANSI command line string from the given argv.
11506 *
11507 * @returns Pointer to the command line.
11508 * @param cArgs Number of arguments.
11509 * @param papszArgs The argument vector.
11510 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11511 * @param pcbCmdLine Where to return the command line length,
11512 * including one terminator.
11513 */
11514static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
11515{
11516 KU32 i;
11517 KSIZE cbCmdLine;
11518 char *pszCmdLine;
11519
11520 /* Make a copy of the argument vector that we'll be quoting. */
11521 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
11522 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
11523
11524 /* Quote the arguments that need it. */
11525 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
11526
11527 /* figure out cmd line length. */
11528 cbCmdLine = 0;
11529 for (i = 0; i < cArgs; i++)
11530 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
11531 *pcbCmdLine = cbCmdLine;
11532
11533 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
11534 if (pszCmdLine)
11535 {
11536 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
11537 if (papszQuotedArgs[0] != papszArgs[0])
11538 free(papszQuotedArgs[0]);
11539
11540 for (i = 1; i < cArgs; i++)
11541 {
11542 *psz++ = ' ';
11543 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
11544 if (papszQuotedArgs[i] != papszArgs[i])
11545 free(papszQuotedArgs[i]);
11546 }
11547 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
11548
11549 *psz++ = '\0';
11550 *psz++ = '\0';
11551 }
11552
11553 return pszCmdLine;
11554}
11555
11556
11557
11558static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
11559 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11560 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11561{
11562 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11563 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11564 wchar_t *pwcPool;
11565 KSIZE cbStrings;
11566 KSIZE cwc;
11567 KSIZE cbCmdLine;
11568 KU32 i;
11569
11570 /* Simple stuff. */
11571 pSandbox->rcExitCode = 256;
11572 pSandbox->pTool = pTool;
11573 pSandbox->idMainThread = GetCurrentThreadId();
11574 pSandbox->pgmptr = (char *)pTool->pszPath;
11575 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
11576#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11577 if (pSandbox->StdOut.fIsConsole)
11578 pSandbox->StdOut.u.Con.cwcBuf = 0;
11579 else
11580 pSandbox->StdOut.u.Fully.cchBuf = 0;
11581 if (pSandbox->StdErr.fIsConsole)
11582 pSandbox->StdErr.u.Con.cwcBuf = 0;
11583 else
11584 pSandbox->StdErr.u.Fully.cchBuf = 0;
11585 pSandbox->Combined.cwcBuf = 0;
11586 pSandbox->Combined.cFlushes = 0;
11587#endif
11588 pSandbox->fNoPchCaching = fNoPchCaching;
11589 pSandbox->cArgs = cArgs;
11590 pSandbox->papszArgs = (char **)papszArgs;
11591 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
11592 if (!pSandbox->pszCmdLine)
11593 return KERR_NO_MEMORY;
11594
11595 /*
11596 * Convert command line and argv to UTF-16.
11597 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
11598 */
11599 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
11600 if (!pSandbox->papwszArgs)
11601 return KERR_NO_MEMORY;
11602 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
11603 for (i = 0; i < cArgs; i++)
11604 {
11605 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
11606 pSandbox->papwszArgs[i] = pwcPool;
11607 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11608 pwcPool++;
11609 }
11610 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11611 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11612
11613 /*
11614 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11615 */
11616 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11617 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11618 if (!pSandbox->pwszCmdLine)
11619 return KERR_NO_MEMORY;
11620 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11621
11622 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11623 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11624 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11625
11626 /*
11627 * Setup the environment.
11628 */
11629 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11630 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11631 {
11632 KU32 iDst = 0;
11633 for (i = 0; i < cEnvVars; i++)
11634 {
11635 const char *pszVar = papszEnvVars[i];
11636 KSIZE cchVar = kHlpStrLen(pszVar);
11637 const char *pszEqual;
11638 if ( cchVar > 0
11639 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11640 {
11641 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11642 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11643 if (pszCopy && pwszCopy)
11644 {
11645 pSandbox->papszEnvVars[iDst] = pszCopy;
11646 pSandbox->environC[iDst] = pszCopy;
11647 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11648 pSandbox->wenvironC[iDst] = pwszCopy;
11649
11650 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11651 if ( (pszEqual - pszVar) == 4
11652 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11653 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11654 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11655 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11656 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11657 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11658
11659 iDst++;
11660 }
11661 else
11662 {
11663 kHlpFree(pszCopy);
11664 kHlpFree(pwszCopy);
11665 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11666 }
11667 }
11668 else
11669 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11670 }
11671 pSandbox->papszEnvVars[iDst] = NULL;
11672 pSandbox->environC[iDst] = NULL;
11673 pSandbox->papwszEnvVars[iDst] = NULL;
11674 pSandbox->wenvironC[iDst] = NULL;
11675 }
11676 else
11677 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11678
11679 /*
11680 * Invalidate the volatile parts of cache (kBuild output directory,
11681 * temporary directory, whatever).
11682 */
11683 kFsCacheInvalidateCustomBoth(g_pFsCache);
11684
11685#ifdef WITH_HISTORY
11686 /*
11687 * Record command line in debug history.
11688 */
11689 kHlpFree(g_apszHistory[g_iHistoryNext]);
11690 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11691 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11692#endif
11693
11694 return 0;
11695}
11696
11697
11698/**
11699 * Does sandbox cleanup between jobs.
11700 *
11701 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11702 * influence the result, so that kmk can get on with things ASAP.
11703 *
11704 * @param pSandbox The sandbox.
11705 */
11706static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11707{
11708 PROCESS_MEMORY_COUNTERS MemInfo;
11709 PKWVIRTALLOC pTracker;
11710 PKWHEAP pHeap;
11711 PKWLOCALSTORAGE pLocalStorage;
11712#ifdef WITH_HASH_CACHE
11713 PKWCRYPTHASH pHash;
11714#endif
11715#ifdef WITH_TEMP_MEMORY_FILES
11716 PKWFSTEMPFILE pTempFile;
11717#endif
11718 PKWEXITCALLACK pExitCallback;
11719
11720 /*
11721 * First stuff that may cause code to run.
11722 */
11723
11724 /* Do exit callback first. */
11725 pExitCallback = g_Sandbox.pExitCallbackHead;
11726 g_Sandbox.pExitCallbackHead = NULL;
11727 while (pExitCallback)
11728 {
11729 PKWEXITCALLACK pNext = pExitCallback->pNext;
11730 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11731 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11732 __try
11733 {
11734 pExitCallback->pfnCallback();
11735 }
11736 __except (EXCEPTION_EXECUTE_HANDLER)
11737 {
11738 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11739 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11740 kHlpAssertFailed();
11741 }
11742 kHlpFree(pExitCallback);
11743 pExitCallback = pNext;
11744 }
11745
11746 /* Free left behind FlsAlloc leaks. */
11747 pLocalStorage = g_Sandbox.pFlsAllocHead;
11748 g_Sandbox.pFlsAllocHead = NULL;
11749 while (pLocalStorage)
11750 {
11751 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11752 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11753 FlsFree(pLocalStorage->idx);
11754 kHlpFree(pLocalStorage);
11755 pLocalStorage = pNext;
11756 }
11757
11758 /* Free left behind TlsAlloc leaks. */
11759 pLocalStorage = g_Sandbox.pTlsAllocHead;
11760 g_Sandbox.pTlsAllocHead = NULL;
11761 while (pLocalStorage)
11762 {
11763 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11764 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11765 TlsFree(pLocalStorage->idx);
11766 kHlpFree(pLocalStorage);
11767 pLocalStorage = pNext;
11768 }
11769
11770
11771 /*
11772 * Then free resources associated with the sandbox run.
11773 */
11774
11775 /* Open handles, except fixed handles (stdout and stderr). */
11776 EnterCriticalSection(&pSandbox->HandlesLock);
11777 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11778 {
11779 KU32 idxHandle = pSandbox->cHandles;
11780 while (idxHandle-- > 0)
11781 if (pSandbox->papHandles[idxHandle] == NULL)
11782 { /* likely */ }
11783 else
11784 {
11785 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11786 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11787 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11788 {
11789 pSandbox->papHandles[idxHandle] = NULL;
11790 pSandbox->cLeakedHandles++;
11791
11792 switch (pHandle->enmType)
11793 {
11794 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11795 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11796 idxHandle, pHandle->hHandle, pHandle->cRefs));
11797 break;
11798 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11799 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11800 idxHandle, pHandle->hHandle, pHandle->cRefs));
11801 break;
11802 case KWHANDLETYPE_OUTPUT_BUF:
11803 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11804 idxHandle, pHandle->hHandle, pHandle->cRefs));
11805 break;
11806#ifdef WITH_TEMP_MEMORY_FILES
11807 case KWHANDLETYPE_TEMP_FILE:
11808 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11809 idxHandle, pHandle->hHandle, pHandle->cRefs));
11810 pHandle->u.pTempFile->cActiveHandles--;
11811 break;
11812 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11813 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11814 idxHandle, pHandle->hHandle, pHandle->cRefs));
11815 pHandle->u.pTempFile->cActiveHandles--;
11816 break;
11817#endif
11818 default:
11819 kHlpAssertFailed();
11820 }
11821 if (--pHandle->cRefs == 0)
11822 kHlpFree(pHandle);
11823 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11824 break;
11825 }
11826 }
11827 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11828 }
11829 LeaveCriticalSection(&pSandbox->HandlesLock);
11830
11831 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11832 g_Sandbox.cMemMappings = 0;
11833
11834#ifdef WITH_TEMP_MEMORY_FILES
11835 /* The temporary files aren't externally visible, they're all in memory. */
11836 pTempFile = pSandbox->pTempFileHead;
11837 pSandbox->pTempFileHead = NULL;
11838 while (pTempFile)
11839 {
11840 PKWFSTEMPFILE pNext = pTempFile->pNext;
11841 KU32 iSeg = pTempFile->cSegs;
11842 while (iSeg-- > 0)
11843 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11844 kHlpFree(pTempFile->paSegs);
11845 pTempFile->pNext = NULL;
11846 kHlpFree(pTempFile);
11847
11848 pTempFile = pNext;
11849 }
11850#endif
11851
11852 /* Free left behind HeapCreate leaks. */
11853 pHeap = g_Sandbox.pHeapHead;
11854 g_Sandbox.pHeapHead = NULL;
11855 while (pHeap != NULL)
11856 {
11857 PKWHEAP pNext = pHeap->pNext;
11858 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11859 HeapDestroy(pHeap->hHeap);
11860 pHeap = pNext;
11861 }
11862
11863 /* Free left behind VirtualAlloc leaks. */
11864 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11865 pTracker = g_Sandbox.pVirtualAllocHead;
11866 g_Sandbox.pVirtualAllocHead = NULL;
11867 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11868 while (pTracker)
11869 {
11870 PKWVIRTALLOC pNext = pTracker->pNext;
11871 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11872
11873#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11874 if (pTracker->idxPreAllocated != KU32_MAX)
11875 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11876 else
11877#endif
11878 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11879 kHlpFree(pTracker);
11880 pTracker = pNext;
11881 }
11882
11883 /* Free the environment. */
11884 if (pSandbox->papszEnvVars)
11885 {
11886 KU32 i;
11887 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11888 kHlpFree(pSandbox->papszEnvVars[i]);
11889 pSandbox->environC[0] = NULL;
11890 pSandbox->papszEnvVars[0] = NULL;
11891
11892 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11893 kHlpFree(pSandbox->papwszEnvVars[i]);
11894 pSandbox->wenvironC[0] = NULL;
11895 pSandbox->papwszEnvVars[0] = NULL;
11896 }
11897
11898#ifdef WITH_HASH_CACHE
11899 /*
11900 * Hash handles.
11901 */
11902 pHash = pSandbox->pHashHead;
11903 pSandbox->pHashHead = NULL;
11904 while (pHash)
11905 {
11906 PKWCRYPTHASH pNext = pHash->pNext;
11907 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11908 if (pHash->hFallback != KUPTR_MAX)
11909 CryptDestroyHash(pHash->hFallback);
11910 kHlpFree(pHash);
11911 pHash = pNext;
11912 }
11913#endif
11914
11915 /*
11916 * Check the memory usage. If it's getting high, trigger a respawn
11917 * after the next job.
11918 */
11919 MemInfo.WorkingSetSize = 0;
11920 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11921 {
11922 /* The first time thru, we figure out approximately when to restart
11923 based on installed RAM and CPU threads. */
11924 static KU64 s_cbMaxWorkingSet = 0;
11925 if (s_cbMaxWorkingSet != 0)
11926 { /* likely */ }
11927 else
11928 {
11929 SYSTEM_INFO SysInfo;
11930 MEMORYSTATUSEX GlobalMemInfo;
11931 const char *pszValue;
11932
11933 /* Calculate a reasonable estimate. */
11934 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11935 GetNativeSystemInfo(&SysInfo);
11936
11937 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11938 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11939 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11940#if K_ARCH_BITS >= 64
11941 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11942#else
11943 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11944#endif
11945 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11946 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11947
11948 /* User limit. */
11949 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11950 if (pszValue != NULL)
11951 {
11952 char *pszNext;
11953 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11954 if (*pszNext == '\0' || *pszNext == 'M')
11955 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11956 else if (*pszNext == 'K')
11957 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11958 else if (*pszNext == 'G')
11959 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11960 else
11961 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11962 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11963 }
11964
11965 /* Clamp it a little. */
11966 if (s_cbMaxWorkingSet < 168*1024*1024)
11967 s_cbMaxWorkingSet = 168*1024*1024;
11968#if K_ARCH_BITS < 64
11969 else
11970 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11971 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11972 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11973 : 1536*1024*1024 /* got 4GB VA */);
11974#endif
11975 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11976 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11977 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11978 }
11979
11980 /* Finally the check. */
11981 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11982 {
11983 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11984 g_fRestart = K_TRUE;
11985 }
11986 }
11987
11988 /*
11989 * The CRT has a max of 8192 handles, so we better restart after a while if
11990 * someone is leaking handles or we risk running out of descriptors.
11991 *
11992 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11993 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11994 */
11995 if (pSandbox->cLeakedHandles > 6000)
11996 {
11997 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11998 g_fRestart = K_TRUE;
11999 }
12000}
12001
12002
12003/**
12004 * Does essential cleanups and restoring, anything externally visible.
12005 *
12006 * All cleanups that aren't externally visible are postponed till after we've
12007 * informed kmk of the result, so it can be done in the dead time between jobs.
12008 *
12009 * @param pSandbox The sandbox.
12010 */
12011static void kwSandboxCleanup(PKWSANDBOX pSandbox)
12012{
12013 /*
12014 * Restore the parent command line string.
12015 */
12016 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12017 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12018 pProcParams->CommandLine = pSandbox->SavedCommandLine;
12019#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12020 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
12021 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
12022#endif
12023}
12024
12025
12026static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12027 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
12028{
12029 int rcExit = 42;
12030 int rc;
12031
12032 /*
12033 * Initialize the sandbox environment.
12034 */
12035 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
12036 if (rc == 0)
12037 {
12038 if (g_cVerbose > 2)
12039 fprintf(stderr, "kWorker: info: Executing (sandboxed): %s\n", g_Sandbox.pszCmdLine);
12040
12041 /*
12042 * Do module initialization.
12043 */
12044#if 0
12045 //kwSandboxResetModuleVisited();
12046 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
12047#else
12048 kwSandboxResetModuleState();
12049#endif
12050 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
12051 if (rc == 0)
12052 {
12053 /*
12054 * Call the main function.
12055 */
12056#if K_ARCH == K_ARCH_AMD64
12057 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
12058#elif K_ARCH == K_ARCH_X86_32
12059 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
12060#else
12061# error "Port me!"
12062#endif
12063
12064 /* Save the NT TIB first (should do that here, not in some other function). */
12065 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
12066 pSandbox->TibMainThread = *pTib;
12067
12068 /* Make the call in a guarded fashion. */
12069#if K_ARCH == K_ARCH_AMD64
12070 /* AMD64 */
12071 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
12072 __try
12073 {
12074 pSandbox->pOutXcptListHead = pTib->ExceptionList;
12075 if (setjmp(pSandbox->JmpBuf) == 0)
12076 {
12077 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
12078 pSandbox->fRunning = K_TRUE;
12079 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
12080 pSandbox->fRunning = K_FALSE;
12081 }
12082 else
12083 rcExit = pSandbox->rcExitCode;
12084 }
12085#elif K_ARCH == K_ARCH_X86_32
12086 /* x86 (see _tmainCRTStartup) */
12087 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
12088 __try
12089 {
12090 pSandbox->pOutXcptListHead = pTib->ExceptionList;
12091 if (setjmp(pSandbox->JmpBuf) == 0)
12092 {
12093 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
12094 pSandbox->fRunning = K_TRUE;
12095 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
12096 pSandbox->fRunning = K_FALSE;
12097 }
12098 else
12099 rcExit = pSandbox->rcExitCode;
12100 }
12101#endif
12102 __except (EXCEPTION_EXECUTE_HANDLER)
12103 {
12104 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
12105#ifdef WITH_HISTORY
12106 {
12107 KU32 cPrinted = 0;
12108 while (cPrinted++ < 5)
12109 {
12110 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
12111 if (g_apszHistory[idx])
12112 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
12113 }
12114 }
12115#endif
12116 rcExit = 512;
12117 }
12118 pSandbox->fRunning = K_FALSE;
12119
12120 /* Now, restore the NT TIB. */
12121 *pTib = pSandbox->TibMainThread;
12122 }
12123 else
12124 rcExit = 42 + 4;
12125
12126 /*
12127 * Flush and clean up the essential bits only, postpone whatever we
12128 * can till after we've replied to kmk.
12129 */
12130#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12131 kwSandboxConsoleFlushAll(&g_Sandbox);
12132#endif
12133 kwSandboxCleanup(&g_Sandbox);
12134 /** @todo Flush sandboxed native CRTs too. */
12135 }
12136 else
12137 rcExit = 42 + 3;
12138
12139 return rcExit;
12140}
12141
12142
12143/**
12144 * Does the post command part of a job (optional).
12145 *
12146 * @returns The exit code of the job.
12147 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12148 * @param papszPostCmdArgs The post command and its argument.
12149 */
12150static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12151{
12152 const char *pszCmd = papszPostCmdArgs[0];
12153
12154 /* Allow the kmk builtin prefix. */
12155 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
12156 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
12157 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
12158
12159 /* Command switch. */
12160 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
12161 {
12162 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
12163 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
12164 }
12165
12166 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
12167}
12168
12169
12170/**
12171 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
12172 */
12173static unsigned kwGetCurrentProcessorGroup(void)
12174{
12175 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
12176 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
12177 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
12178 if (pfnGetter)
12179 {
12180 GROUP_AFFINITY GroupAffinity;
12181 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
12182 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
12183 return GroupAffinity.Group;
12184 }
12185 return 0;
12186}
12187
12188
12189/**
12190 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
12191 */
12192static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
12193{
12194 KSIZE cchRet = 0;
12195 HANDLE hToken;
12196 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
12197 {
12198 DWORD cbRet;
12199 TOKEN_STATISTICS TokenStats;
12200 memset(&TokenStats, 0, sizeof(TokenStats));
12201 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
12202 cchRet = sprintf(pszValue, "%" KX64_PRI,
12203 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
12204 else
12205 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
12206 CloseHandle(hToken);
12207 }
12208 else
12209 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
12210 return cchRet;
12211}
12212
12213
12214/**
12215 * Look for and expand the special environment variable.
12216 *
12217 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
12218 * couldn't accuratly determine. Currently the following variables are
12219 * implemented:
12220 * - "@@PROCESSOR_GROUP@@" - The processor group number.
12221 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
12222 * - "@@PID@@" - The kWorker process ID.
12223 * - "@@@@" - Escaped "@@".
12224 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
12225 */
12226static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
12227{
12228 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
12229 KU32 i = cEnvVars;
12230 while (i-- > 0)
12231 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
12232 && papszEnvVars[i][cchSpecialEnv] == '=')
12233 {
12234 /* We will expand stuff like @@NAME@@ */
12235 const char *pszValue = papszEnvVars[i];
12236 KSIZE offDst = 0;
12237 char szTmp[1024];
12238 for (;;)
12239 {
12240 const char *pszAt = kHlpStrChr(pszValue, '@');
12241 while (pszAt && pszAt[1] != '@')
12242 pszAt = kHlpStrChr(pszAt + 1, '@');
12243 if (pszAt)
12244 {
12245 KSIZE cchSrc = pszAt - pszValue;
12246 if (offDst + cchSrc < sizeof(szTmp))
12247 {
12248 char szSrc[64];
12249
12250 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12251 offDst += cchSrc;
12252 pszValue = pszAt + 2;
12253
12254 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
12255 {
12256 pszValue += 15;
12257 if (g_iProcessGroup == -1)
12258 g_iProcessGroup = kwGetCurrentProcessorGroup();
12259 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
12260 }
12261 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
12262 {
12263 pszValue += 19;
12264 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
12265 }
12266 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
12267 {
12268 pszValue += 5;
12269 cchSrc = sprintf(szSrc, "%d", getpid());
12270 }
12271 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
12272 {
12273 pszValue += 2;
12274 szSrc[0] = '@';
12275 szSrc[1] = '@';
12276 szSrc[2] = '\0';
12277 cchSrc = 2;
12278 }
12279 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
12280 {
12281 static unsigned int s_iCounter = 0;
12282 pszValue += 15;
12283 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
12284 }
12285 else
12286 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
12287 pszValue - 2);
12288 if (offDst + cchSrc < sizeof(szTmp))
12289 {
12290 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
12291 offDst += cchSrc;
12292 continue;
12293 }
12294 }
12295 }
12296 else
12297 {
12298 KSIZE cchSrc = kHlpStrLen(pszValue);
12299 if (offDst + cchSrc < sizeof(szTmp))
12300 {
12301 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12302 offDst += cchSrc;
12303 break;
12304 }
12305 }
12306 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
12307 }
12308 szTmp[offDst] = '\0';
12309
12310 /* Return a copy of it: */
12311 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
12312 if (papszEnvVars[i])
12313 {
12314 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
12315 return 0;
12316 }
12317 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
12318 }
12319
12320 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
12321}
12322
12323
12324/**
12325 * Part 2 of the "JOB" command handler.
12326 *
12327 * @returns The exit code of the job.
12328 * @param pszExecutable The executable to execute.
12329 * @param pszCwd The current working directory of the job.
12330 * @param cArgs The number of arguments.
12331 * @param papszArgs The argument vector.
12332 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
12333 * @param cEnvVars The number of environment variables.
12334 * @param papszEnvVars The environment vector.
12335 * @param pszSpecialEnv Name of special environment variable that
12336 * requires selective expansion here.
12337 * @param fNoPchCaching Whether to disable precompiled header file
12338 * caching. Avoid trouble when creating them.
12339 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12340 * @param papszPostCmdArgs The post command and its argument.
12341 */
12342static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
12343 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12344 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
12345 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12346{
12347 int rcExit;
12348 PKWTOOL pTool;
12349 char *pszSpecialEnvFree = NULL;
12350
12351 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
12352 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
12353#ifdef KW_LOG_ENABLED
12354 {
12355 KU32 i;
12356 for (i = 0; i < cArgs; i++)
12357 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
12358 for (i = 0; i < cPostCmdArgs; i++)
12359 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
12360 }
12361#endif
12362 g_cJobs++;
12363
12364 /*
12365 * Expand pszSpecialEnv if present.
12366 */
12367 if (pszSpecialEnv && *pszSpecialEnv)
12368 {
12369 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
12370 if (!rcExit)
12371 { /* likely */ }
12372 else
12373 return rcExit;
12374 }
12375
12376 /*
12377 * Lookup the tool.
12378 */
12379 g_Sandbox.pTool = NULL; /* Avoid confusion between the SetDllDirectoryW hacks. */
12380 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
12381 if (pTool)
12382 {
12383 /*
12384 * Change the directory if we're going to execute the job inside
12385 * this process. Then invoke the tool type specific handler.
12386 */
12387 switch (pTool->enmType)
12388 {
12389 case KWTOOLTYPE_SANDBOXED:
12390 case KWTOOLTYPE_WATCOM:
12391 {
12392 /* Change dir. */
12393 KFSLOOKUPERROR enmError;
12394 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
12395 if ( pNewCurDir == g_pCurDirObj
12396 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
12397 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12398 else if (SetCurrentDirectoryA(pszCwd))
12399 {
12400 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
12401 g_pCurDirObj = pNewCurDir;
12402 }
12403 else
12404 {
12405 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
12406 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12407 rcExit = 42 + 1;
12408 break;
12409 }
12410
12411 /* Call specific handler. */
12412 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
12413 {
12414 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
12415 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
12416 cEnvVars, papszEnvVars, fNoPchCaching);
12417 }
12418 else
12419 {
12420 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
12421 rcExit = 42 + 2;
12422 }
12423 break;
12424 }
12425
12426 case KWTOOLTYPE_EXEC:
12427 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
12428 rcExit = 42 + 2;
12429 break;
12430
12431 default:
12432 kHlpAssertFailed();
12433 kwErrPrintf("Internal tool type corruption!!\n");
12434 rcExit = 42 + 2;
12435 g_fRestart = K_TRUE;
12436 break;
12437 }
12438
12439 /*
12440 * Do the post command, if present.
12441 */
12442 if (cPostCmdArgs && rcExit == 0)
12443 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
12444 }
12445 else
12446 rcExit = 42 + 1;
12447 if (pszSpecialEnvFree)
12448 {
12449 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
12450 kHlpFree(pszSpecialEnvFree);
12451 }
12452 return rcExit;
12453}
12454
12455
12456/**
12457 * Handles a "JOB" command.
12458 *
12459 * @returns The exit code of the job.
12460 * @param pszMsg Points to the "JOB" command part of the message.
12461 * @param cbMsg Number of message bytes at @a pszMsg. There are
12462 * 4 more zero bytes after the message body to
12463 * simplify parsing.
12464 */
12465static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
12466{
12467 int rcExit = 42;
12468
12469 /*
12470 * Unpack the message.
12471 */
12472 const char *pszExecutable;
12473 KSIZE cbTmp;
12474
12475 pszMsg += sizeof("JOB");
12476 cbMsg -= sizeof("JOB");
12477
12478 /* Executable name. */
12479 pszExecutable = pszMsg;
12480 cbTmp = kHlpStrLen(pszMsg) + 1;
12481 pszMsg += cbTmp;
12482 if ( cbTmp < cbMsg
12483 && cbTmp > 2)
12484 {
12485 const char *pszCwd;
12486 cbMsg -= cbTmp;
12487
12488 /* Current working directory. */
12489 pszCwd = pszMsg;
12490 cbTmp = kHlpStrLen(pszMsg) + 1;
12491 pszMsg += cbTmp;
12492 if ( cbTmp + sizeof(KU32) < cbMsg
12493 && cbTmp >= 2)
12494 {
12495 KU32 cArgs;
12496 cbMsg -= cbTmp;
12497
12498 /* Argument count. */
12499 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
12500 pszMsg += sizeof(cArgs);
12501 cbMsg -= sizeof(cArgs);
12502
12503 if (cArgs > 0 && cArgs < 4096)
12504 {
12505 /* The argument vector. */
12506 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
12507 if (papszArgs)
12508 {
12509 KU32 i;
12510 for (i = 0; i < cArgs; i++)
12511 {
12512 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
12513 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
12514 pszMsg += cbTmp;
12515 if (cbTmp < cbMsg)
12516 cbMsg -= cbTmp;
12517 else
12518 {
12519 cbMsg = 0;
12520 break;
12521 }
12522
12523 }
12524 papszArgs[cArgs] = 0;
12525
12526 /* Environment variable count. */
12527 if (cbMsg > sizeof(KU32))
12528 {
12529 KU32 cEnvVars;
12530 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
12531 pszMsg += sizeof(cEnvVars);
12532 cbMsg -= sizeof(cEnvVars);
12533
12534 if (cEnvVars >= 0 && cEnvVars < 4096)
12535 {
12536 /* The argument vector. */
12537 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
12538 if (papszEnvVars)
12539 {
12540 for (i = 0; i < cEnvVars; i++)
12541 {
12542 papszEnvVars[i] = pszMsg;
12543 cbTmp = kHlpStrLen(pszMsg) + 1;
12544 pszMsg += cbTmp;
12545 if (cbTmp < cbMsg)
12546 cbMsg -= cbTmp;
12547 else
12548 {
12549 cbMsg = 0;
12550 break;
12551 }
12552 }
12553 papszEnvVars[cEnvVars] = 0;
12554
12555 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
12556 if (cbMsg >= sizeof(KU8) * 2)
12557 {
12558 KBOOL fWatcomBrainDamange = *pszMsg++;
12559 KBOOL fNoPchCaching = *pszMsg++;
12560 cbMsg -= 2;
12561
12562 /* Name of special enviornment variable requiring selective expansion. */
12563 if (cbMsg >= 1)
12564 {
12565 const char *pszSpecialEnv = pszMsg;
12566 cbTmp = kHlpStrLen(pszMsg);
12567 pszMsg += cbTmp + 1;
12568 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
12569
12570 /* Post command argument count (can be zero). */
12571 if (cbMsg >= sizeof(KU32))
12572 {
12573 KU32 cPostCmdArgs;
12574 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
12575 pszMsg += sizeof(cPostCmdArgs);
12576 cbMsg -= sizeof(cPostCmdArgs);
12577
12578 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
12579 {
12580 char const *apszPostCmdArgs[32+1];
12581 for (i = 0; i < cPostCmdArgs; i++)
12582 {
12583 apszPostCmdArgs[i] = pszMsg;
12584 cbTmp = kHlpStrLen(pszMsg) + 1;
12585 pszMsg += cbTmp;
12586 if ( cbTmp < cbMsg
12587 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
12588 cbMsg -= cbTmp;
12589 else
12590 {
12591 cbMsg = KSIZE_MAX;
12592 break;
12593 }
12594 }
12595 if (cbMsg == 0)
12596 {
12597 apszPostCmdArgs[cPostCmdArgs] = NULL;
12598
12599 /*
12600 * The next step.
12601 */
12602 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
12603 cArgs, papszArgs, fWatcomBrainDamange,
12604 cEnvVars, papszEnvVars, pszSpecialEnv,
12605 fNoPchCaching,
12606 cPostCmdArgs, apszPostCmdArgs);
12607 }
12608 else if (cbMsg == KSIZE_MAX)
12609 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
12610 else
12611 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
12612 }
12613 else
12614 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
12615 }
12616 else
12617 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
12618 }
12619 else
12620 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12621 }
12622 else
12623 kwErrPrintf("Detected bogus message unpacking flags!\n");
12624 kHlpFree((void *)papszEnvVars);
12625 }
12626 else
12627 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12628 }
12629 else
12630 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12631 }
12632 else
12633 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12634 kHlpFree((void *)papszArgs);
12635 }
12636 else
12637 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12638 }
12639 else
12640 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12641 }
12642 else
12643 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12644 }
12645 else
12646 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12647 return rcExit;
12648}
12649
12650
12651/**
12652 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12653 *
12654 * @retval 0 on success.
12655 * @retval -1 on error (fully bitched).
12656 *
12657 * @param hPipe The pipe handle.
12658 * @param pvBuf The buffer to write out out.
12659 * @param cbToWrite The number of bytes to write.
12660 */
12661static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12662{
12663 KU8 const *pbBuf = (KU8 const *)pvBuf;
12664 KU32 cbLeft = cbToWrite;
12665 while (g_rcCtrlC == 0)
12666 {
12667 DWORD cbActuallyWritten = 0;
12668 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12669 {
12670 cbLeft -= cbActuallyWritten;
12671 if (!cbLeft)
12672 return 0;
12673 pbBuf += cbActuallyWritten;
12674 }
12675 else
12676 {
12677 DWORD dwErr = GetLastError();
12678 if (cbLeft == cbToWrite)
12679 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12680 else
12681 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12682 return -1;
12683 }
12684 }
12685 return -1;
12686}
12687
12688
12689/**
12690 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12691 *
12692 * @retval 0 on success.
12693 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12694 * @retval -1 on error (fully bitched).
12695 * @param hPipe The pipe handle.
12696 * @param pvBuf The buffer to read into.
12697 * @param cbToRead The number of bytes to read.
12698 * @param fShutdownOkay Whether connection shutdown while reading the
12699 * first byte is okay or not.
12700 */
12701static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12702{
12703 KU8 *pbBuf = (KU8 *)pvBuf;
12704 KU32 cbLeft = cbToRead;
12705 while (g_rcCtrlC == 0)
12706 {
12707 DWORD cbActuallyRead = 0;
12708 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12709 {
12710 cbLeft -= cbActuallyRead;
12711 if (!cbLeft)
12712 return 0;
12713 pbBuf += cbActuallyRead;
12714 }
12715 else
12716 {
12717 DWORD dwErr = GetLastError();
12718 if (cbLeft == cbToRead)
12719 {
12720 if ( fMayShutdown
12721 && dwErr == ERROR_BROKEN_PIPE)
12722 return 1;
12723 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12724 }
12725 else
12726 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12727 return -1;
12728 }
12729 }
12730 return -1;
12731}
12732
12733
12734/**
12735 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12736 *
12737 * @returns pszBuf
12738 * @param pszBuf The buffer (sufficiently large).
12739 * @param uValue The value.
12740 */
12741static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12742{
12743 char szTmp[64];
12744 char *psz = &szTmp[63];
12745 int cch = 4;
12746
12747 *psz-- = '\0';
12748 do
12749 {
12750 if (--cch == 0)
12751 {
12752 *psz-- = ' ';
12753 cch = 3;
12754 }
12755 *psz-- = (uValue % 10) + '0';
12756 uValue /= 10;
12757 } while (uValue != 0);
12758
12759 return strcpy(pszBuf, psz + 1);
12760}
12761
12762
12763/**
12764 * Prints statistics.
12765 */
12766static void kwPrintStats(void)
12767{
12768 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12769 MEMORYSTATUSEX MemStatus;
12770 IO_COUNTERS IoCounters;
12771 DWORD cHandles;
12772 KSIZE cMisses;
12773 char szBuf[16*1024];
12774 int off = 0;
12775 char szPrf[24];
12776 char sz1[64];
12777 char sz2[64];
12778 char sz3[64];
12779 char sz4[64];
12780
12781 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12782
12783 szBuf[off++] = '\n';
12784
12785 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12786 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12787 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12788 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12789 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12790
12791 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12792 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12793
12794 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12795 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12796 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12797
12798 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12799 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12800 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12801
12802 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12803 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12804 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12805 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12806 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12807 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12808 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12809
12810 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12811 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12812 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12813 kwFmtU64(sz1, g_pFsCache->cObjects),
12814 kwFmtU64(sz2, g_pFsCache->cbObjects),
12815 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12816 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12817 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12818 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12819 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12820 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12821#ifdef KFSCACHE_CFG_UTF16
12822 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12823 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12824 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12825 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12826 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12827#endif
12828 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12829 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12830 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12831 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12832 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12833
12834 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12835 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12836 kwFmtU64(sz1, g_pFsCache->cLookups),
12837 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12838 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12839 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12840 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12841 kwFmtU64(sz4, cMisses),
12842 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12843
12844 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12845 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12846 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12847 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12848 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12849 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12850 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12851 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12852
12853#ifdef WITH_HASH_CACHE
12854 off += sprintf(&szBuf[off], "%s %14s hashes calculated, %s cache hits (%u%%), %s fallbacks, %s partial\n", szPrf,
12855 kwFmtU64(sz1, g_cHashes),
12856 kwFmtU64(sz2, g_cHashesCached),
12857 (unsigned)(g_cHashesCached * 100 / K_MAX(g_cHashes, 1)),
12858 kwFmtU64(sz3, g_cHashesFallbacks),
12859 kwFmtU64(sz4, g_cHashesPartial));
12860 off += sprintf(&szBuf[off], "%s %14s MD5: %s SHA-1: %s SHA-256: %s SHA-512: %s\n", szPrf, "", kwFmtU64(sz1, g_cHashesMd5),
12861 kwFmtU64(sz2, g_cHashesSha1), kwFmtU64(sz3, g_cHashesSha256), kwFmtU64(sz4, g_cHashesSha512));
12862#endif
12863
12864 /*
12865 * Process & Memory details.
12866 */
12867 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12868 cHandles = 0;
12869 MemInfo.cb = sizeof(MemInfo);
12870 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12871 memset(&MemInfo, 0, sizeof(MemInfo));
12872 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12873 kwFmtU64(sz1, cHandles),
12874 kwFmtU64(sz2, MemInfo.PageFaultCount),
12875 kwFmtU64(sz3, MemInfo.PagefileUsage),
12876 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12877 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12878 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12879 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12880 kwFmtU64(sz3, MemInfo.PrivateUsage));
12881 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12882 szPrf,
12883 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12884 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12885 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12886 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12887
12888 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12889 memset(&IoCounters, 0, sizeof(IoCounters));
12890 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12891 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12892 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12893 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12894 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12895 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12896 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12897 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12898 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12899
12900 MemStatus.dwLength = sizeof(MemStatus);
12901 if (!GlobalMemoryStatusEx(&MemStatus))
12902 memset(&MemStatus, 0, sizeof(MemStatus));
12903 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12904 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12905 MemStatus.ullAvailVirtual);
12906 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12907
12908 maybe_con_fwrite(szBuf, off, 1, stdout);
12909 fflush(stdout);
12910}
12911
12912
12913/**
12914 * Handles what comes after --test.
12915 *
12916 * @returns Exit code.
12917 * @param argc Number of arguments after --test.
12918 * @param argv Arguments after --test.
12919 */
12920static int kwTestRun(int argc, char **argv)
12921{
12922 int i;
12923 int j;
12924 int rcExit;
12925 int cRepeats;
12926 char szCwd[MAX_PATH];
12927 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12928 KU32 cEnvVars;
12929 char **papszEnvVars;
12930 const char *pszSpecialEnv = "";
12931 const char *pszSpecialEnvFull = NULL;
12932 KBOOL fWatcomBrainDamange = K_FALSE;
12933 KBOOL fNoPchCaching = K_FALSE;
12934
12935 /*
12936 * Parse arguments.
12937 */
12938 /* Repeat count. */
12939 i = 0;
12940 if (i >= argc)
12941 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12942 if (strcmp(argv[i], "--") != 0)
12943 {
12944 cRepeats = atoi(argv[i]);
12945 if (cRepeats <= 0)
12946 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12947 i++;
12948
12949 /* Optional directory change. */
12950 if ( i < argc
12951 && ( strcmp(argv[i], "--chdir") == 0
12952 || strcmp(argv[i], "-C") == 0 ) )
12953 {
12954 i++;
12955 if (i >= argc)
12956 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12957 pszCwd = argv[i++];
12958 }
12959
12960 /* Optional watcom flag directory change. */
12961 if ( i < argc
12962 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12963 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12964 {
12965 fWatcomBrainDamange = K_TRUE;
12966 i++;
12967 }
12968
12969 /* Optional watcom flag directory change. */
12970 if ( i < argc
12971 && strcmp(argv[i], "--no-pch-caching") == 0)
12972 {
12973 fNoPchCaching = K_TRUE;
12974 i++;
12975 }
12976
12977 /* Optional directory change. */
12978 if ( i < argc
12979 && ( strcmp(argv[i], "--set-special") == 0
12980 || strcmp(argv[i], "-s") == 0 ) )
12981 {
12982 i++;
12983 if (i >= argc)
12984 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12985 pszSpecialEnvFull = argv[i++];
12986 putenv(pszSpecialEnvFull);
12987 pszSpecialEnv = strdup(pszSpecialEnvFull);
12988 *strchr(pszSpecialEnv, '=') = '\0';
12989 }
12990
12991 /* Trigger breakpoint */
12992 if ( i < argc
12993 && strcmp(argv[i], "--breakpoint") == 0)
12994 {
12995 __debugbreak();
12996 i++;
12997 }
12998
12999 /* Check for '--'. */
13000 if (i >= argc)
13001 return kwErrPrintfRc(2, "Missing '--'\n");
13002 if (strcmp(argv[i], "--") != 0)
13003 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
13004 i++;
13005 }
13006 else
13007 {
13008 cRepeats = 1;
13009 i++;
13010 }
13011 if (i >= argc)
13012 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
13013
13014 /*
13015 * Duplicate the environment.
13016 */
13017 cEnvVars = 0;
13018 while (environ[cEnvVars] != NULL)
13019 cEnvVars++;
13020 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
13021
13022 /*
13023 * Do the job.
13024 */
13025 rcExit = 0;
13026 for (j = 0; j < cRepeats; j++)
13027 {
13028 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
13029 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
13030 argc - i, &argv[i], fWatcomBrainDamange,
13031 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
13032 0, NULL);
13033 KW_LOG(("rcExit=%d\n", rcExit));
13034 kwSandboxCleanupLate(&g_Sandbox);
13035 }
13036
13037 if (getenv("KWORKER_STATS") != NULL)
13038 kwPrintStats();
13039
13040# ifdef WITH_LOG_FILE
13041 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13042 CloseHandle(g_hLogFile);
13043# endif
13044 return rcExit;
13045}
13046
13047
13048/**
13049 * Reads @a pszFile into memory and chops it up into an argument vector.
13050 *
13051 * @returns Pointer to the argument vector on success, NULL on failure.
13052 * @param pszFile The file to load.
13053 * @param pcArgs Where to return the number of arguments.
13054 * @param ppszFileContent Where to return the allocation.
13055 */
13056static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
13057{
13058 char **papszArgs = NULL;
13059 FILE *pFile = fopen(pszFile, "r");
13060 if (pFile)
13061 {
13062 long cbFile;
13063 if ( fseek(pFile, 0, SEEK_END) == 0
13064 && (cbFile = ftell(pFile)) >= 0
13065 && fseek(pFile, 0, SEEK_SET) == 0)
13066 {
13067 char *pszContent = kHlpAllocZ(cbFile + 3);
13068 *ppszFileContent = pszContent;
13069 if (pszContent)
13070 {
13071 size_t cbRead = fread(pszContent, 1, cbFile + 1, pFile);
13072 if ( feof(pFile)
13073 && !ferror(pFile))
13074 {
13075 size_t off = 0;
13076 int cArgs = 0;
13077 int cAllocated = 0;
13078 char ch;
13079
13080 pszContent[cbRead] = '\0';
13081 pszContent[cbRead + 1] = '\0';
13082 pszContent[cbRead + 2] = '\0';
13083
13084 while ((ch = pszContent[off]) != '\0')
13085 {
13086 char *pszArg;
13087 switch (ch)
13088 {
13089 case ' ':
13090 case '\t':
13091 case '\n':
13092 case '\r':
13093 off++;
13094 continue;
13095
13096 case '\\':
13097 if (pszContent[off + 1] == '\n' || pszContent[off + 1] == '\r')
13098 {
13099 off += 2;
13100 continue;
13101 }
13102 /* fall thru */
13103 default:
13104 pszArg = &pszContent[off];
13105 do
13106 ch = pszContent[++off];
13107 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
13108 pszContent[off++] = '\0';
13109 break;
13110
13111 case '\'':
13112 pszArg = &pszContent[++off];
13113 while ((ch = pszContent[off]) != '\0' && ch != '\'')
13114 off++;
13115 pszContent[off++] = '\0';
13116 break;
13117
13118 case '\"': /** @todo escape sequences */
13119 pszArg = &pszContent[++off];
13120 while ((ch = pszContent[off]) != '\0' && ch != '"')
13121 off++;
13122 pszContent[off++] = '\0';
13123 break;
13124 }
13125 if (cArgs + 1 >= cAllocated)
13126 {
13127 void *pvNew;
13128 cAllocated = cAllocated ? cAllocated * 2 : 16;
13129 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
13130 if (pvNew)
13131 papszArgs = (char **)pvNew;
13132 else
13133 {
13134 kHlpFree(papszArgs);
13135 papszArgs = NULL;
13136 break;
13137 }
13138 }
13139 papszArgs[cArgs] = pszArg;
13140 papszArgs[++cArgs] = NULL;
13141 }
13142 *pcArgs = cArgs;
13143 }
13144 else
13145 kwErrPrintf("Error reading '%s'!\n", pszFile);
13146 }
13147 else
13148 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
13149 }
13150 else
13151 kwErrPrintf("Error seeking '%s'!\n", pszFile);
13152 fclose(pFile);
13153 }
13154 else
13155 kwErrPrintf("Error opening '%s'!\n", pszFile);
13156 return papszArgs;
13157}
13158
13159/**
13160 * Appends a string to an string vector (arguments or enviornment).
13161 *
13162 * @returns 0 on success, non-zero on failure (exit code).
13163 * @param ppapszVector Pointer to the string pointer array.
13164 * @param pcEntries Pointer to the array size.
13165 * @param pszAppend The string to append.
13166 */
13167static int kwFullTestVectorAppend(const char ***ppapszVector, KU32 *pcEntries, char const *pszAppend)
13168{
13169 KU32 cEntries = *pcEntries;
13170 if (!(cEntries & 15))
13171 {
13172 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
13173 if (pvNew)
13174 *ppapszVector = (const char **)pvNew;
13175 else
13176 return kwErrPrintfRc(2, "Out of memory!\n");
13177 }
13178 (*ppapszVector)[cEntries] = pszAppend;
13179 (*ppapszVector)[++cEntries] = NULL;
13180 *pcEntries = cEntries;
13181 return 0;
13182}
13183
13184
13185/**
13186 * Parses arguments for --full-test.
13187 *
13188 * @returns 0 on success, non-zero on failure (exit code).
13189 */
13190static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
13191 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
13192{
13193 PKWONETEST pCur = *ppHead;
13194 int i;
13195 for (i = 0; i < argc; i++)
13196 {
13197 int rc = 0;
13198 const char *pszArg = argv[i];
13199 if (*pszArg == 'k')
13200 {
13201 if (kHlpStrComp(pszArg, "kSubmit") == 0)
13202 {
13203 if (*piState != 0)
13204 {
13205 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
13206 if (!pCur)
13207 return kwErrPrintfRc(2, "Out of memory!\n");
13208 pCur->fVirgin = K_TRUE;
13209 pCur->pszCwd = pszDefaultCwd;
13210 pCur->cRuns = 1;
13211 pCur->pNext = *ppHead;
13212 *ppHead = pCur;
13213 *piState = 0;
13214 }
13215 else if (!pCur->fVirgin)
13216 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
13217 pCur->pszJobSrc = pszJobSrc;
13218 pCur->iJobSrc = i;
13219 continue; /* (to stay virgin) */
13220 }
13221
13222 /* Ignore "kWorker 378172/62:" sequences that kmk/kSubmit spews out on failure. */
13223 if ( kHlpStrComp(pszArg, "kWorker") == 0
13224 && i + 1 < argc
13225 && (unsigned)(argv[i + 1][0] - '0') <= 9)
13226 {
13227 i++;
13228 continue;
13229 }
13230 }
13231
13232 if ( *pszArg == '-'
13233 && ( *piState == 0
13234 || pszArg[1] == '@'))
13235 {
13236 const char *pszValue = NULL;
13237 char ch = *++pszArg;
13238 pszArg++;
13239 if (ch == '-')
13240 {
13241 ch = '\0';
13242 if (*pszArg == '\0') /* -- */
13243 *piState = 2;
13244 /* Translate or handle long options: */
13245 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
13246 ch = 'E';
13247 else if (kHlpStrComp(pszArg, "special-env") == 0)
13248 ch = 's';
13249 else if (kHlpStrComp(pszArg, "default-env") == 0)
13250 {
13251 unsigned j;
13252 pCur->cEnvVars = 0;
13253 for (j = 0; environ[j] && rc == 0; j++)
13254 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[j])); /* leaks; unchecked */
13255 }
13256 else if (kHlpStrComp(pszArg, "chdir") == 0)
13257 ch = 'C';
13258 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
13259 ch = 'P';
13260 else if (kHlpStrComp(pszArg, "response-file") == 0)
13261 ch = '@';
13262 else if (kHlpStrComp(pszArg, "runs") == 0)
13263 ch = 'R';
13264 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
13265 pCur->fWatcomBrainDamange = K_TRUE;
13266 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
13267 pCur->fNoPchCaching = K_TRUE;
13268 else if (kHlpStrComp(pszArg, "executable") == 0)
13269 ch = 'e';
13270 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
13271 {
13272 __debugbreak();
13273 continue; /* (to stay virgin) */
13274 }
13275 else
13276 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
13277 pszArg = "";
13278 }
13279
13280 while (ch != '\0' && rc == 0)
13281 {
13282 /* Fetch value if needed: */
13283 switch (ch)
13284 {
13285 case '@':
13286 case 'e':
13287 case 'E':
13288 case 's':
13289 case 'C':
13290 case 'R':
13291 if (*pszArg == ':' || *pszArg == '=')
13292 pszValue = &pszArg[1];
13293 else if (*pszArg)
13294 pszValue = pszArg;
13295 else if (i + 1 < argc)
13296 pszValue = argv[++i];
13297 else
13298 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
13299 pszArg = "";
13300 break;
13301 }
13302
13303 /* Handle the option: */
13304 switch (ch)
13305 {
13306 case 'E':
13307 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
13308 break;
13309 case 'C':
13310 pCur->pszCwd = pszValue;
13311 break;
13312 case 's':
13313 pCur->pszSpecialEnv = pszValue;
13314 break;
13315 case 'e':
13316 pCur->pszExecutable = pszValue;
13317 break;
13318 case 'P':
13319 *piState = 1;
13320 if (*pszArg)
13321 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
13322 break;
13323 case 'R':
13324 pCur->cRuns = atoi(pszValue);
13325 if ((int)pCur->cRuns < 0)
13326 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
13327 break;
13328 case '@':
13329 if (cRecursions < 5)
13330 {
13331 char *pszLeaked = NULL;
13332 int cArgs = 0;
13333 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
13334 if (papszArgsLeaked)
13335 {
13336 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
13337 cRecursions + 1, pszValue);
13338 pCur = *ppHead;
13339 }
13340 else
13341 return 2;
13342 }
13343 else
13344 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
13345 break;
13346 }
13347
13348 /* next */
13349 ch = *pszArg++;
13350 }
13351 }
13352 else if (*piState == 2)
13353 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
13354 else if (*piState == 1)
13355 {
13356 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
13357 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
13358 else
13359 *piState = 2;
13360 }
13361 else
13362 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
13363 if (rc)
13364 return rc;
13365 pCur->fVirgin = K_FALSE;
13366 }
13367 return 0;
13368}
13369
13370
13371/**
13372 * Handles what comes after --full-test.
13373 *
13374 * @returns Exit code.
13375 * @param argc Number of arguments after --full-test.
13376 * @param argv Arguments after --full-test.
13377 */
13378static int kwFullTestRun(int argc, char **argv)
13379{
13380 char szDefaultCwd[MAX_PATH];
13381 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
13382 KWONETEST FirstTest;
13383 PKWONETEST pHead = &FirstTest;
13384 PKWONETEST pCur;
13385 int iState = 0;
13386 int rcExit;
13387
13388 /*
13389 * Parse arguments.
13390 */
13391 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
13392 FirstTest.pszJobSrc = "command-line";
13393 FirstTest.iJobSrc = 1;
13394 FirstTest.fVirgin = K_TRUE;
13395 FirstTest.pszCwd = pszDefaultCwd;
13396 FirstTest.cRuns = 1;
13397
13398 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
13399 if (rcExit)
13400 return rcExit;
13401
13402 /*
13403 * Do the job. LIFO ordering (see kSubmit).
13404 */
13405 for (pCur = pHead; pCur; pCur = pCur->pNext)
13406 {
13407 if (!pCur->pszExecutable && pCur->papszArgs)
13408 pCur->pszExecutable = pCur->papszArgs[0];
13409 if ( pCur->pszExecutable
13410 && pCur->cArgs > 0
13411 && pCur->cEnvVars > 0)
13412 {
13413 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
13414 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
13415 unsigned iRun;
13416
13417 for (iRun = 0; iRun < pCur->cRuns; iRun++)
13418 {
13419 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
13420 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
13421 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
13422 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
13423
13424 KW_LOG(("rcExit=%d\n", rcExit));
13425 kwSandboxCleanupLate(&g_Sandbox);
13426
13427 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
13428 }
13429 kHlpFree(papszEnvVarsCopy);
13430 }
13431 else
13432 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
13433 pCur->pszExecutable ? "" : " No executable!",
13434 pCur->cArgs < 1 ? " No arguments!" : "",
13435 pCur->cEnvVars < 1 ? " No environment!" : "",
13436 pCur->iJobSrc, pCur->pszJobSrc);
13437 }
13438
13439 if (getenv("KWORKER_STATS") != NULL)
13440 kwPrintStats();
13441
13442# ifdef WITH_LOG_FILE
13443 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13444 CloseHandle(g_hLogFile);
13445# endif
13446 return rcExit;
13447}
13448
13449
13450/**
13451 * Helper for main() argument handling that sets the processor group if
13452 * possible.
13453 */
13454static void kwSetProcessorGroup(unsigned long uGroup)
13455{
13456 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
13457 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
13458 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
13459
13460 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
13461 if (pfnSetThreadGroupAffinity)
13462 {
13463 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
13464 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
13465 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
13466 if (NewAff.Mask && (WORD)uGroup == uGroup)
13467 {
13468 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
13469 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
13470 }
13471 else if (GetLastError() == NO_ERROR)
13472 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
13473 else
13474 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
13475 uGroup, GetLastError());
13476 }
13477 else
13478 {
13479 OSVERSIONINFOA VerInfo = {0};
13480 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
13481 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
13482 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
13483 }
13484}
13485
13486
13487int main(int argc, char **argv)
13488{
13489#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
13490 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
13491 kwSandboxVecXcptEmulateChained);
13492#endif
13493 KSIZE cbMsgBuf = 0;
13494 KU8 *pbMsgBuf = NULL;
13495 int i;
13496 HANDLE hPipe = INVALID_HANDLE_VALUE;
13497 const char *pszTmp;
13498 KFSLOOKUPERROR enmIgnored;
13499 DWORD dwType;
13500#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13501 HANDLE hCurProc = GetCurrentProcess();
13502 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
13503 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
13504#endif
13505#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
13506 K_NOREF(pvVecXcptHandler);
13507#endif
13508
13509#ifdef WITH_FIXED_VIRTUAL_ALLOCS
13510 /*
13511 * Reserve memory for cl.exe
13512 */
13513 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
13514 {
13515 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
13516 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
13517 g_aFixedVirtualAllocs[i].cbFixed,
13518 MEM_RESERVE, PAGE_READWRITE);
13519 if ( !g_aFixedVirtualAllocs[i].pvReserved
13520 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
13521 {
13522 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
13523 GetLastError());
13524 if (g_aFixedVirtualAllocs[i].pvReserved)
13525 {
13526 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
13527 g_aFixedVirtualAllocs[i].pvReserved = NULL;
13528 }
13529 }
13530 }
13531#endif
13532
13533 /*
13534 * Register our Control-C and Control-Break handlers.
13535 */
13536 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
13537 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
13538
13539 /*
13540 * Create the cache and mark the temporary directory as using the custom revision.
13541 */
13542 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
13543 if (!g_pFsCache)
13544 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
13545
13546 pszTmp = getenv("TEMP");
13547 if (pszTmp && *pszTmp != '\0')
13548 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13549 pszTmp = getenv("TMP");
13550 if (pszTmp && *pszTmp != '\0')
13551 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13552 pszTmp = getenv("TMPDIR");
13553 if (pszTmp && *pszTmp != '\0')
13554 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13555
13556 /*
13557 * Make g_abDefLdBuf executable.
13558 */
13559 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
13560 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
13561 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
13562 InitializeCriticalSection(&g_Sandbox.HandlesLock);
13563 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
13564
13565#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13566 /*
13567 * Get and duplicate the console handles.
13568 */
13569 /* Standard output. */
13570 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
13571 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
13572 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13573 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
13574 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
13575 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
13576 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13577 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13578 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
13579 g_Sandbox.HandleStdOut.cRefs = 0x10001;
13580 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
13581 g_Sandbox.HandleStdOut.tidOwner = KU32_MAX;
13582 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
13583 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
13584 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
13585 {
13586 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
13587 g_Sandbox.cFixedHandles++;
13588 else
13589 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
13590 }
13591 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13592 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
13593
13594 /* Standard error. */
13595 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
13596 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
13597 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13598 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
13599 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
13600 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
13601 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13602 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13603 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
13604 g_Sandbox.HandleStdErr.cRefs = 0x10001;
13605 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
13606 g_Sandbox.HandleStdErr.tidOwner = KU32_MAX;
13607 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
13608 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
13609 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
13610 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
13611 {
13612 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
13613 g_Sandbox.cFixedHandles++;
13614 else
13615 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
13616 }
13617 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13618 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
13619
13620 /* Combined console buffer. */
13621 if (g_Sandbox.StdErr.fIsConsole)
13622 {
13623 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
13624 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13625 }
13626 else if (g_Sandbox.StdOut.fIsConsole)
13627 {
13628 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
13629 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13630 }
13631 else
13632 {
13633 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
13634 g_Sandbox.Combined.uCodepage = CP_ACP;
13635 }
13636 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
13637#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
13638
13639
13640 /*
13641 * Parse arguments.
13642 */
13643 for (i = 1; i < argc; i++)
13644 {
13645 if (strcmp(argv[i], "--pipe") == 0)
13646 {
13647 i++;
13648 if (i < argc)
13649 {
13650 char *pszEnd = NULL;
13651 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13652 if ( *argv[i]
13653 && pszEnd != NULL
13654 && *pszEnd == '\0'
13655 && u64Value != 0
13656 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13657 && (uintptr_t)u64Value == u64Value)
13658 hPipe = (HANDLE)(uintptr_t)u64Value;
13659 else
13660 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13661 }
13662 else
13663 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13664 }
13665 else if (strcmp(argv[i], "--volatile") == 0)
13666 {
13667 i++;
13668 if (i < argc)
13669 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13670 else
13671 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13672 }
13673 else if (strcmp(argv[i], "--test") == 0)
13674 return kwTestRun(argc - i - 1, &argv[i + 1]);
13675 else if (strcmp(argv[i], "--full-test") == 0)
13676 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13677 else if (strcmp(argv[i], "--priority") == 0)
13678 {
13679 i++;
13680 if (i < argc)
13681 {
13682 char *pszEnd = NULL;
13683 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13684 if ( *argv[i]
13685 && pszEnd != NULL
13686 && *pszEnd == '\0'
13687 && uValue >= 1
13688 && uValue <= 5)
13689 {
13690 DWORD dwClass;
13691 int dwPriority;
13692 switch (uValue)
13693 {
13694 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13695 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13696 default:
13697 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13698 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13699 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13700 }
13701 SetPriorityClass(GetCurrentProcess(), dwClass);
13702 if (dwPriority != INT_MAX)
13703 SetThreadPriority(GetCurrentThread(), dwPriority);
13704 }
13705 else
13706 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13707 }
13708 else
13709 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13710 }
13711 else if (strcmp(argv[i], "--group") == 0)
13712 {
13713 i++;
13714 if (i < argc)
13715 {
13716 char *pszEnd = NULL;
13717 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13718 if ( *argv[i]
13719 && pszEnd != NULL
13720 && *pszEnd == '\0'
13721 && uValue == (WORD)uValue)
13722 kwSetProcessorGroup(uValue);
13723 else
13724 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13725 }
13726 else
13727 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13728 }
13729 else if ( strcmp(argv[i], "--verbose") == 0
13730 || strcmp(argv[i], "-v") == 0)
13731 g_cVerbose++;
13732 else if ( strcmp(argv[i], "--help") == 0
13733 || strcmp(argv[i], "-h") == 0
13734 || strcmp(argv[i], "-?") == 0)
13735 {
13736 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13737 "usage: kWorker <--help|-h>\n"
13738 "usage: kWorker <--version|-V>\n"
13739 "usage: kWorker [--volatile dir] --full-test kSubmit ...\n"
13740 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13741 "\n"
13742 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13743 return 0;
13744 }
13745 else if ( strcmp(argv[i], "--version") == 0
13746 || strcmp(argv[i], "-V") == 0)
13747 return kbuild_version(argv[0]);
13748 else
13749 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13750 }
13751
13752 /*
13753 * If no --pipe argument, then assume its standard input.
13754 * We need to carefully replace the CRT stdin with a handle to "nul".
13755 */
13756 if (hPipe == INVALID_HANDLE_VALUE)
13757 {
13758 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13759 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13760 {
13761 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13762 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13763 {
13764 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13765 if (fdNul >= 0)
13766 {
13767 if (_dup2(fdNul, 0) >= 0)
13768 {
13769 close(fdNul);
13770 hPipe = hDuplicate;
13771 }
13772 else
13773 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13774 }
13775 else
13776 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13777 }
13778 else
13779 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13780 }
13781 else
13782 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13783 GetFileType(hPipe), GetLastError());
13784 }
13785 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13786 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13787 GetFileType(hPipe), GetLastError());
13788 g_hPipe = hPipe;
13789
13790 /*
13791 * Serve the pipe.
13792 */
13793 for (;;)
13794 {
13795 KU32 cbMsg = 0;
13796 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13797 if (rc == 0)
13798 {
13799 /* Make sure the message length is within sane bounds. */
13800 if ( cbMsg > 4
13801 && cbMsg <= 256*1024*1024)
13802 {
13803 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13804 if (cbMsg + 4 <= cbMsgBuf)
13805 { /* likely */ }
13806 else
13807 {
13808 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13809 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13810 if (!pbMsgBuf)
13811 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13812 }
13813
13814 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13815 *(KU32 *)pbMsgBuf = cbMsg;
13816 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13817 if (rc == 0)
13818 {
13819 const char *psz;
13820
13821 pbMsgBuf[cbMsg] = '\0';
13822 pbMsgBuf[cbMsg + 1] = '\0';
13823 pbMsgBuf[cbMsg + 2] = '\0';
13824 pbMsgBuf[cbMsg + 3] = '\0';
13825
13826 /* The first string after the header is the command. */
13827 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13828 if ( strcmp(psz, "JOB") == 0
13829 && g_rcCtrlC == 0)
13830 {
13831 struct
13832 {
13833 KI32 rcExitCode;
13834 KU8 bExiting;
13835 KU8 abZero[3];
13836 } Reply;
13837 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13838 Reply.bExiting = g_fRestart;
13839 Reply.abZero[0] = 0;
13840 Reply.abZero[1] = 0;
13841 Reply.abZero[2] = 0;
13842 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13843 if ( rc == 0
13844 && !g_fRestart)
13845 {
13846 kwSandboxCleanupLate(&g_Sandbox);
13847 if (g_rcCtrlC == 0)
13848 continue;
13849 }
13850 }
13851 else
13852 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13853 }
13854 }
13855 else
13856 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13857 }
13858
13859 /*
13860 * If we're exitting because we're restarting, we need to delay till
13861 * kmk/kSubmit has read the result. Windows documentation says it
13862 * immediately discards pipe buffers once the pipe is broken by the
13863 * server (us). So, We flush the buffer and queues a 1 byte read
13864 * waiting for kSubmit to close the pipe when it receives the
13865 * bExiting = K_TRUE result.
13866 */
13867 if (g_fRestart)
13868 {
13869 DWORD cbIgnored = 1;
13870 KU8 b;
13871 FlushFileBuffers(hPipe);
13872 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13873 }
13874
13875 CloseHandle(hPipe);
13876#ifdef WITH_LOG_FILE
13877 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13878 CloseHandle(g_hLogFile);
13879#endif
13880 if (getenv("KWORKER_STATS") != NULL)
13881 kwPrintStats();
13882 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13883 }
13884}
13885
13886
13887/** @page pg_kWorker kSubmit / kWorker
13888 *
13889 * @section sec_kWorker_Motivation Motivation / Inspiration
13890 *
13891 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13892 * builds on machines "infested" by Anti Virus protection and disk encryption
13893 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13894 * got "infected".
13895 *
13896 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13897 * mainly a bunch of tiny assembly and C files being compiler a million times.
13898 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13899 * own toolchain from within the same process, saving a lot of process creation
13900 * and teardown overhead.
13901 *
13902 *
13903 * @section sec_kWorker_kSubmit About kSubmit
13904 *
13905 * When wanting to execute a job in a kWorker instance, it must be submitted
13906 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13907 * built into kmk and does not exist as an external program. The reason for
13908 * this is that it keep track of the kWorker instances.
13909 *
13910 * The kSubmit command has the --32-bit and --64-bit options for selecting
13911 * between 32-bit and 64-bit worker instance. We generally assume the user of
13912 * the command knows which bit count the executable has, so kSubmit is spared
13913 * the extra work of finding out.
13914 *
13915 * The kSubmit command shares a environment and current directory manipulation
13916 * with the kRedirect command, but not the file redirection. So long no file
13917 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13918 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13919 * and/or current directory changes to work.
13920 *
13921 * Unlike the kRedirect command, the kSubmit command can also specify an
13922 * internall post command to be executed after the main command succeeds.
13923 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13924 * information from Microsoft COFF object files and Watcom OMF object files and
13925 * is scheduled to replace kDepIDB.
13926 *
13927 *
13928 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13929 *
13930 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13931 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13932 * it. When the job is completed, kWorker writes a short reply with the exit
13933 * code and an internal status indicating whether it is going to restart.
13934 *
13935 * The kWorker intance will reply to kSubmit before completing all the internal
13936 * cleanup work, so as not to delay the next job execution unnecessarily. This
13937 * includes checking its own memory consumption and checking whether it needs
13938 * restarting. So, a decision to restart unfortunately have to wait till after
13939 * the next job has completed. This is a little bit unfortunate if the next job
13940 * requires a lot of memory and kWorker has already leaked/used a lot.
13941 *
13942 *
13943 * @section sec_kWorker_How_Works How kWorker Works
13944 *
13945 * kWorker will load the executable specified by kSubmit into memory and call
13946 * it's entrypoint in a lightly sandbox'ed environment.
13947 *
13948 *
13949 * @subsection ssec_kWorker_Loaing Image loading
13950 *
13951 * kWorker will manually load all the executable images into memory, fix them
13952 * up, and make a copy of the virgin image so it can be restored using memcpy
13953 * the next time it is used.
13954 *
13955 * Imported functions are monitored and replacements used for a few of them.
13956 * These replacements are serve the following purposes:
13957 * - Provide a different command line.
13958 * - Provide a different environment.
13959 * - Intercept process termination.
13960 * - Intercept thread creation (only linker is allowed to create threads).
13961 * - Intercept file reading for caching (header files, ++) as file system
13962 * access is made even slower by anti-virus software.
13963 * - Intercept crypto hash APIs to cache MD5 digests of header files
13964 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13965 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13966 * in memory as writing files grows expensive with encryption and
13967 * anti-virus software active.
13968 * - Intercept some file system queries to use the kFsCache instead of
13969 * going to the kernel and slowly worm thru the AV filter driver.
13970 * - Intercept standard output/error and console writes to aggressivly
13971 * buffer the output. The MS CRT does not buffer either when it goes to
13972 * the console, resulting in terrible performance and mixing up output
13973 * with other compile jobs.
13974 * This also allows us to filter out the annoying source file announcements
13975 * by cl.exe.
13976 * - Intercept VirtualAlloc and VirtualFree to prevent
13977 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13978 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13979 * the callbacks run after each job.
13980 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13981 * executables and tools using custom heaps (like the microsoft linker).
13982 * [exectuable images only]
13983 * - Intercept atexit and _onexit registration to be able run them after
13984 * each job instead of crashing as kWorker exits. This also helps avoid
13985 * some leaks. [executable image only]
13986 *
13987 * DLLs falls into two categories, system DLLs which we always load using the
13988 * native loader, and tool DLLs which can be handled like the executable or
13989 * optionally using the native loader. We maintain a hardcoded white listing of
13990 * tool DLLs we trust to load using the native loader.
13991 *
13992 * Imports of natively loaded DLLs are processed too, but we only replace a
13993 * subset of the functions compared to natively loaded excutable and DLL images.
13994 *
13995 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13996 * This is to speed up job execution.
13997 *
13998 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13999 * for each job run, but so far this hasn't been necessary.
14000 *
14001 *
14002 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
14003 *
14004 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
14005 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
14006 * intermediate representation between the first (c1/c1xx.dll) and second pass
14007 * (c2.dll).
14008 *
14009 * kWorker helps the compiler as best as it can. Given a little knowledge about
14010 * stable and volatile file system areas, it can do a lot of caching that a
14011 * normal compiler driver cannot easily do when given a single file.
14012 *
14013 *
14014 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
14015 *
14016 * The preprocessor part will open and process header files exactly as they are
14017 * encountered in the source files. If string.h is included by the main source
14018 * and five other header files, it will be searched for (include path), opened,
14019 * read, MD5-summed, and pre-processed six times. The last five times is just a
14020 * waste of time because of the guards or \#pragma once. A smart compiler would
14021 * make a little extra effort and realize this.
14022 *
14023 * kWorker will cache help the preprocessor by remembering places where the
14024 * header was not found with help of kFsCache, and cache the file in memory when
14025 * found. The first part is taken care of by intercepting GetFileAttributesW,
14026 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
14027 * cached, the file is kept open and the CreateFileW call returns a duplicate of
14028 * that handle. An internal handle table is used by ReadFile and CloseFile to
14029 * keep track of intercepted handles (also used for temporary file, temporary
14030 * file mappings, console buffering, and standard out/err buffering).
14031 *
14032 * PS. The header search optimization also comes in handy when cl.exe goes on
14033 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
14034 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
14035 * optionally compile the three pass DLLs as executables during development
14036 * and problem analysis.
14037 *
14038 *
14039 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
14040 *
14041 * The issues of the temporary files is pretty severe on the Dell machine used
14042 * for benchmarking with full AV and encryption. The synthetic benchmark
14043 * improved by 30% when kWorker implemented measures to keep them entirely in
14044 * memory.
14045 *
14046 * kWorker implement these by recognizing the filename pattern in CreateFileW
14047 * and creating/opening the given file as needed. The handle returned is a
14048 * duplicate of the current process, thus giving us a good chance of catching
14049 * API calls we're not intercepting.
14050 *
14051 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
14052 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
14053 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
14054 * UnmapViewOfFile.
14055 *
14056 *
14057 * @section sec_kWorker_Numbers Some measurements.
14058 *
14059 * - r2881 building src/VBox/Runtime:
14060 * - without: 2m01.016388s = 120.016388 s
14061 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
14062 * - r2884 building vbox/debug (r110512):
14063 * - without: 11m14.446609s = 674.446609 s
14064 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
14065 * - r2896 building vbox/debug (r110577):
14066 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
14067 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
14068 * MS Defender as AV):
14069 * - without: 10m24.990389s = 624.990389s
14070 * - with: 08m04.738184s = 484.738184s
14071 * - delta: 624.99s - 484.74s = 140.25s
14072 * - saved: 140.25/624.99 = 22% faster
14073 *
14074 *
14075 * @subsection subsec_kWorker_Early_Numbers Early Experiments
14076 *
14077 * These are some early experiments doing 1024 compilations of
14078 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
14079 * main function:
14080 *
14081 * Skylake (W10/amd64, only stdandard MS defender):
14082 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
14083 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
14084 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
14085 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
14086 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
14087 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
14088 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
14089 *
14090 * Dell (W7/amd64, infected by mcafee):
14091 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
14092 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
14093 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
14094 *
14095 * The command line:
14096 * @code{.cpp}
14097 "\"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"
14098 * @endcode
14099 */
14100
Note: See TracBrowser for help on using the repository browser.

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