VirtualBox

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

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

kWorker: Hack for older windows versions where api-ms-win-crt-*.dll resolve to actual forwarder DLLs. We need to link them to ucrtbase.dll so we can patch it. Forwarder DLLs cannot be patched.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 534.2 KB
Line 
1/* $Id: kWorker.c 3384 2020-06-17 20:22:04Z 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 **environ;
895 /** The _wenviron msvcrt variable. */
896 wchar_t **wenviron;
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 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2767 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2768 KUPTR const *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.environ;
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.wenviron;
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::environ, KWSANDBOX::papszEnvVars,
4993 * KWSANDBOX::wenviron, 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->environ, cNew * sizeof(pSandbox->environ[0]));
5008 if (pvNew)
5009 {
5010 pSandbox->environ = (char **)pvNew;
5011 pSandbox->environ[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->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
5020 if (pvNew)
5021 {
5022 pSandbox->wenviron = (wchar_t **)pvNew;
5023 pSandbox->wenviron[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->environ, pSandbox->wenviron, 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->environ[iVar] = pszNew;
5087
5088 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5089 pSandbox->papwszEnvVars[iVar] = pwszNew;
5090 pSandbox->wenviron[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->environ[iVar + 1] = NULL;
5106 pSandbox->environ[iVar] = pszNew;
5107
5108 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5109 pSandbox->papwszEnvVars[iVar] = pwszNew;
5110 pSandbox->wenviron[iVar + 1] = NULL;
5111 pSandbox->wenviron[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->environ[iVar] = pszNew;
5167
5168 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5169 pSandbox->papwszEnvVars[iVar] = pwszNew;
5170 pSandbox->wenviron[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->environ[iVar + 1] = NULL;
5186 pSandbox->environ[iVar] = pszNew;
5187
5188 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5189 pSandbox->papwszEnvVars[iVar] = pwszNew;
5190 pSandbox->wenviron[iVar + 1] = NULL;
5191 pSandbox->wenviron[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->environ[iVar] = pSandbox->papszEnvVars[cVars];
5227 pSandbox->papszEnvVars[cVars] = NULL;
5228 pSandbox->environ[cVars] = NULL;
5229
5230 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5231 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5232 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5233 pSandbox->papwszEnvVars[cVars] = NULL;
5234 pSandbox->wenviron[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->environ[iVar] = pSandbox->papszEnvVars[cVars];
5266 pSandbox->papszEnvVars[cVars] = NULL;
5267 pSandbox->environ[cVars] = NULL;
5268
5269 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5270 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5271 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5272 pSandbox->papwszEnvVars[cVars] = NULL;
5273 pSandbox->wenviron[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.environ;
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.wenviron;
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.environ;
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.wenviron;
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 KU32 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 int 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 KU32 i;
6375 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6376 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6377 {
6378 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6379 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6380 break;
6381 }
6382 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6383 while (i-- > 0)
6384 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6385 {
6386 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6387 g_aCxInterceptorEntries[i].pModule = pMod;
6388 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6389 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6390 break;
6391 }
6392 }
6393#endif
6394 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6395 kwLdrModuleRelease(pMod);
6396 //s_cDbgGets++;
6397 //if (s_cGets >= 3)
6398 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6399 return (FARPROC)(KUPTR)uValue;
6400 }
6401
6402 KWFS_TODO();
6403 SetLastError(ERROR_PROC_NOT_FOUND);
6404 kwLdrModuleRelease(pMod);
6405 return NULL;
6406 }
6407
6408 /*
6409 * Hmm... could be a cached module-by-name.
6410 */
6411 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6412 if (g_aGetModuleHandleCache[i].hmod == hmod)
6413 return GetProcAddress(hmod, pszProc);
6414
6415 KWFS_TODO();
6416 return GetProcAddress(hmod, pszProc);
6417}
6418
6419
6420#ifndef NDEBUG
6421/** Kernel32 - GetProcAddress() - native replacement for debugging only. */
6422static FARPROC WINAPI kwSandbox_Kernel32_Native_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6423{
6424 FARPROC pfnRet = GetProcAddress(hmod, pszProc);
6425 KWLDR_LOG(("kwSandbox_Kernel32_Native_GetProcAddress(%p, %s) -> %p\n", hmod, pszProc, pfnRet));
6426 return pfnRet;
6427}
6428#endif
6429
6430
6431/** Kernel32 - GetModuleFileNameA() */
6432static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6433{
6434 PKWMODULE pMod;
6435 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6436
6437 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6438 if (pMod != NULL)
6439 {
6440 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6441 kwLdrModuleRelease(pMod);
6442 return cbRet;
6443 }
6444 KWFS_TODO();
6445 return 0;
6446}
6447
6448
6449/** Kernel32 - GetModuleFileNameW() */
6450static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6451{
6452 PKWMODULE pMod;
6453 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6454
6455 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6456 if (pMod)
6457 {
6458 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6459 kwLdrModuleRelease(pMod);
6460 return cwcRet;
6461 }
6462
6463 KWFS_TODO();
6464 return 0;
6465}
6466
6467
6468/** NtDll - RtlPcToFileHeader
6469 * This is necessary for msvcr100.dll!CxxThrowException. */
6470static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6471{
6472 PVOID pvRet;
6473
6474 /*
6475 * Do a binary lookup of the module table for the current tool.
6476 * This will give us a
6477 */
6478 if (g_Sandbox.fRunning)
6479 {
6480 KUPTR const uPC = (KUPTR)pvPC;
6481 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6482 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6483 KU32 i;
6484 if (iEnd)
6485 {
6486 KU32 iStart = 0;
6487 i = iEnd / 2;
6488 for (;;)
6489 {
6490 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6491 if (uPC < uHModCur)
6492 {
6493 iEnd = i;
6494 if (iStart < i)
6495 { }
6496 else
6497 break;
6498 }
6499 else if (uPC != uHModCur)
6500 {
6501 iStart = ++i;
6502 if (i < iEnd)
6503 { }
6504 else
6505 break;
6506 }
6507 else
6508 {
6509 /* This isn't supposed to happen. */
6510 break;
6511 }
6512
6513 i = iStart + (iEnd - iStart) / 2;
6514 }
6515
6516 /* For reasons of simplicity (= copy & paste), we end up with the
6517 module after the one we're interested in here. */
6518 i--;
6519 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6520 && papMods[i]->pLdrMod)
6521 {
6522 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6523 if (uRvaPC < papMods[i]->cbImage)
6524 {
6525 *ppvImageBase = papMods[i]->hOurMod;
6526 pvRet = papMods[i]->hOurMod;
6527 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6528 return pvRet;
6529 }
6530 }
6531 }
6532 else
6533 i = 0;
6534 }
6535
6536 /*
6537 * Call the regular API.
6538 */
6539 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6540 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6541 return pvRet;
6542}
6543
6544
6545/*
6546 *
6547 * File access APIs (for speeding them up).
6548 * File access APIs (for speeding them up).
6549 * File access APIs (for speeding them up).
6550 *
6551 */
6552
6553
6554/**
6555 * Converts a lookup error to a windows error code.
6556 *
6557 * @returns The windows error code.
6558 * @param enmError The lookup error.
6559 */
6560static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6561{
6562 switch (enmError)
6563 {
6564 case KFSLOOKUPERROR_NOT_FOUND:
6565 case KFSLOOKUPERROR_NOT_DIR:
6566 return ERROR_FILE_NOT_FOUND;
6567
6568 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6569 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6570 case KFSLOOKUPERROR_PATH_TOO_SHORT:
6571 return ERROR_PATH_NOT_FOUND;
6572
6573 case KFSLOOKUPERROR_PATH_TOO_LONG:
6574 return ERROR_FILENAME_EXCED_RANGE;
6575
6576 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6577 return ERROR_NOT_ENOUGH_MEMORY;
6578
6579 default:
6580 return ERROR_PATH_NOT_FOUND;
6581 }
6582}
6583
6584#ifdef WITH_TEMP_MEMORY_FILES
6585
6586/**
6587 * Checks for a cl.exe temporary file.
6588 *
6589 * There are quite a bunch of these. They seems to be passing data between the
6590 * first and second compiler pass. Since they're on disk, they get subjected to
6591 * AV software screening and normal file consistency rules. So, not necessarily
6592 * a very efficient way of handling reasonably small amounts of data.
6593 *
6594 * We make the files live in virtual memory by intercepting their opening,
6595 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6596 *
6597 * @returns K_TRUE / K_FALSE
6598 * @param pwszFilename The file name being accessed.
6599 */
6600static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6601{
6602 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6603 if (pwszName)
6604 {
6605 /* The name starts with _CL_... */
6606 if ( pwszName[0] == '_'
6607 && pwszName[1] == 'C'
6608 && pwszName[2] == 'L'
6609 && pwszName[3] == '_' )
6610 {
6611 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6612 this check by just checking that it's alpha numerical ascii from here on. */
6613 wchar_t wc;
6614 pwszName += 4;
6615 while ((wc = *pwszName++) != '\0')
6616 {
6617 if (wc < 127 && iswalnum(wc))
6618 { /* likely */ }
6619 else
6620 return K_FALSE;
6621 }
6622 return K_TRUE;
6623 }
6624
6625 /* In VC2019 there is also one {UUID} file in temp: */
6626 if (pwszName[0] == '{')
6627 {
6628 KSIZE cwcName = kwUtf16Len(pwszName);
6629 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6630 && pwszName[37] == '}'
6631 && iswalnum(pwszName[1]) // 4
6632 && iswalnum(pwszName[2]) // 4
6633 && iswalnum(pwszName[3]) // 6
6634 && iswalnum(pwszName[4]) // 5
6635 && iswalnum(pwszName[5]) // d
6636 && iswalnum(pwszName[6]) // d
6637 && iswalnum(pwszName[7]) // d
6638 && iswalnum(pwszName[8]) // 9
6639 && pwszName[9] == '-' // -
6640 && iswalnum(pwszName[10]) // e
6641 && iswalnum(pwszName[11]) // 4
6642 && iswalnum(pwszName[12]) // 9
6643 && iswalnum(pwszName[13]) // 4
6644 && pwszName[14] == '-' // -
6645 && iswalnum(pwszName[15]) // 4
6646 && iswalnum(pwszName[16]) // 7
6647 && iswalnum(pwszName[17]) // 1
6648 && iswalnum(pwszName[18]) // b
6649 && pwszName[19] == '-' // -
6650 && iswalnum(pwszName[20]) // 9
6651 && iswalnum(pwszName[21]) // 9
6652 && iswalnum(pwszName[22]) // 6
6653 && iswalnum(pwszName[23]) // b
6654 && pwszName[24] == '-' // -
6655 && iswalnum(pwszName[25]) // 9
6656 && iswalnum(pwszName[26]) // b
6657 && iswalnum(pwszName[27]) // 5
6658 && iswalnum(pwszName[28]) // 5
6659 && iswalnum(pwszName[29]) // 6
6660 && iswalnum(pwszName[30]) // e
6661 && iswalnum(pwszName[31]) // 2
6662 && iswalnum(pwszName[32]) // 5
6663 && iswalnum(pwszName[33]) // a
6664 && iswalnum(pwszName[34]) // 3
6665 && iswalnum(pwszName[35]) // f
6666 && iswalnum(pwszName[36])) // 8
6667 return K_TRUE;
6668 }
6669 }
6670 return K_FALSE;
6671}
6672
6673
6674/**
6675 * Creates a handle to a temporary file.
6676 *
6677 * @returns The handle on success.
6678 * INVALID_HANDLE_VALUE and SetLastError on failure.
6679 * @param pTempFile The temporary file.
6680 * @param dwDesiredAccess The desired access to the handle.
6681 * @param fMapping Whether this is a mapping (K_TRUE) or file
6682 * (K_FALSE) handle type.
6683 */
6684static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6685{
6686 /*
6687 * Create a handle to the temporary file.
6688 */
6689 HANDLE hFile = INVALID_HANDLE_VALUE;
6690 HANDLE hProcSelf = GetCurrentProcess();
6691 if (DuplicateHandle(hProcSelf, hProcSelf,
6692 hProcSelf, &hFile,
6693 SYNCHRONIZE, FALSE,
6694 0 /*dwOptions*/))
6695 {
6696 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6697 if (pHandle)
6698 {
6699 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6700 pHandle->cRefs = 1;
6701 pHandle->offFile = 0;
6702 pHandle->hHandle = hFile;
6703 pHandle->dwDesiredAccess = dwDesiredAccess;
6704 pHandle->tidOwner = KU32_MAX;
6705 pHandle->u.pTempFile = pTempFile;
6706 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6707 {
6708 pTempFile->cActiveHandles++;
6709 kHlpAssert(pTempFile->cActiveHandles >= 1);
6710 kHlpAssert(pTempFile->cActiveHandles <= 2);
6711 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6712 return hFile;
6713 }
6714
6715 kHlpFree(pHandle);
6716 }
6717 else
6718 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6719 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6720 }
6721 else
6722 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6723 return INVALID_HANDLE_VALUE;
6724}
6725
6726
6727static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6728 KBOOL *pfFallback)
6729{
6730 HANDLE hFile;
6731 DWORD dwErr;
6732
6733 /*
6734 * Check if we've got an existing temp file.
6735 * ASSUME exact same path for now.
6736 */
6737 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6738 PKWFSTEMPFILE pTempFile;
6739 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6740 {
6741 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6742 if ( pTempFile->cwcPath == cwcFilename
6743 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6744 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6745 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6746 break;
6747 }
6748
6749 /*
6750 * Create a new temporary file instance if not found.
6751 */
6752 *pfFallback = K_FALSE;
6753 if (pTempFile == NULL)
6754 {
6755 KSIZE cbFilename;
6756
6757 switch (dwCreationDisposition)
6758 {
6759 case CREATE_ALWAYS:
6760 case OPEN_ALWAYS:
6761 case CREATE_NEW:
6762 dwErr = NO_ERROR;
6763 break;
6764
6765 case OPEN_EXISTING:
6766 case TRUNCATE_EXISTING:
6767 *pfFallback = K_TRUE;
6768 kHlpAssertFailed();
6769 SetLastError(ERROR_FILE_NOT_FOUND);
6770 return INVALID_HANDLE_VALUE;
6771
6772 default:
6773 kHlpAssertFailed();
6774 SetLastError(ERROR_INVALID_PARAMETER);
6775 return INVALID_HANDLE_VALUE;
6776 }
6777
6778 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6779 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6780 if (pTempFile)
6781 {
6782 pTempFile->cwcPath = (KU16)cwcFilename;
6783 pTempFile->cbFile = 0;
6784 pTempFile->cbFileAllocated = 0;
6785 pTempFile->cActiveHandles = 0;
6786 pTempFile->cMappings = 0;
6787 pTempFile->cSegs = 0;
6788 pTempFile->paSegs = NULL;
6789 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6790
6791 pTempFile->pNext = g_Sandbox.pTempFileHead;
6792 g_Sandbox.pTempFileHead = pTempFile;
6793 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6794 }
6795 else
6796 {
6797 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6798 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6799 return INVALID_HANDLE_VALUE;
6800 }
6801 }
6802 else
6803 {
6804 switch (dwCreationDisposition)
6805 {
6806 case OPEN_EXISTING:
6807 dwErr = NO_ERROR;
6808 break;
6809 case OPEN_ALWAYS:
6810 dwErr = ERROR_ALREADY_EXISTS;
6811 break;
6812
6813 case TRUNCATE_EXISTING:
6814 case CREATE_ALWAYS:
6815 kHlpAssertFailed();
6816 pTempFile->cbFile = 0;
6817 dwErr = ERROR_ALREADY_EXISTS;
6818 break;
6819
6820 case CREATE_NEW:
6821 kHlpAssertFailed();
6822 SetLastError(ERROR_FILE_EXISTS);
6823 return INVALID_HANDLE_VALUE;
6824
6825 default:
6826 kHlpAssertFailed();
6827 SetLastError(ERROR_INVALID_PARAMETER);
6828 return INVALID_HANDLE_VALUE;
6829 }
6830 }
6831
6832 /*
6833 * Create a handle to the temporary file.
6834 */
6835 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6836 if (hFile != INVALID_HANDLE_VALUE)
6837 SetLastError(dwErr);
6838 return hFile;
6839}
6840
6841#endif /* WITH_TEMP_MEMORY_FILES */
6842
6843/**
6844 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6845 *
6846 * @returns K_TRUE if cacheable, K_FALSE if not.
6847 * @param wcFirst The first extension character.
6848 * @param wcSecond The second extension character.
6849 * @param wcThird The third extension character.
6850 * @param fAttrQuery Set if it's for an attribute query, clear if for
6851 * file creation.
6852 */
6853static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6854{
6855 /* C++ header without an extension or a directory. */
6856 if (wcFirst == '\0')
6857 {
6858 /** @todo exclude temporary files... */
6859 return K_TRUE;
6860 }
6861
6862 /* C Header: .h */
6863 if (wcFirst == 'h' || wcFirst == 'H')
6864 {
6865 if (wcSecond == '\0')
6866 return K_TRUE;
6867
6868 /* C++ Header: .hpp, .hxx */
6869 if ( (wcSecond == 'p' || wcSecond == 'P')
6870 && (wcThird == 'p' || wcThird == 'P'))
6871 return K_TRUE;
6872 if ( (wcSecond == 'x' || wcSecond == 'X')
6873 && (wcThird == 'x' || wcThird == 'X'))
6874 return K_TRUE;
6875 }
6876 /* Misc starting with i. */
6877 else if (wcFirst == 'i' || wcFirst == 'I')
6878 {
6879 if (wcSecond != '\0')
6880 {
6881 if (wcSecond == 'n' || wcSecond == 'N')
6882 {
6883 /* C++ inline header: .inl */
6884 if (wcThird == 'l' || wcThird == 'L')
6885 return K_TRUE;
6886
6887 /* Assembly include file: .inc */
6888 if (wcThird == 'c' || wcThird == 'C')
6889 return K_TRUE;
6890 }
6891 }
6892 }
6893 /* Assembly header: .mac */
6894 else if (wcFirst == 'm' || wcFirst == 'M')
6895 {
6896 if (wcSecond == 'a' || wcSecond == 'A')
6897 {
6898 if (wcThird == 'c' || wcThird == 'C')
6899 return K_TRUE;
6900 }
6901 }
6902#ifdef WITH_PCH_CACHING
6903 /* Precompiled header: .pch */
6904 else if (wcFirst == 'p' || wcFirst == 'P')
6905 {
6906 if (wcSecond == 'c' || wcSecond == 'C')
6907 {
6908 if (wcThird == 'h' || wcThird == 'H')
6909 return !g_Sandbox.fNoPchCaching;
6910 }
6911 }
6912#endif
6913#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6914 /* Linker - Object file: .obj */
6915 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6916 {
6917 if (wcSecond == 'b' || wcSecond == 'B')
6918 {
6919 if (wcThird == 'j' || wcThird == 'J')
6920 return K_TRUE;
6921 }
6922 }
6923#endif
6924 else if (fAttrQuery)
6925 {
6926 /* Dynamic link library: .dll */
6927 if (wcFirst == 'd' || wcFirst == 'D')
6928 {
6929 if (wcSecond == 'l' || wcSecond == 'L')
6930 {
6931 if (wcThird == 'l' || wcThird == 'L')
6932 return K_TRUE;
6933 }
6934 }
6935 /* Executable file: .exe */
6936 else if (wcFirst == 'e' || wcFirst == 'E')
6937 {
6938 if (wcSecond == 'x' || wcSecond == 'X')
6939 {
6940 if (wcThird == 'e' || wcThird == 'E')
6941 return K_TRUE;
6942 }
6943 }
6944 /* Response file: .rsp */
6945 else if (wcFirst == 'r' || wcFirst == 'R')
6946 {
6947 if (wcSecond == 's' || wcSecond == 'S')
6948 {
6949 if (wcThird == 'p' || wcThird == 'P')
6950 return !g_Sandbox.fNoPchCaching;
6951 }
6952 }
6953 /* Linker: */
6954 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6955 {
6956 /* Object file: .obj */
6957 if (wcFirst == 'o' || wcFirst == 'O')
6958 {
6959 if (wcSecond == 'b' || wcSecond == 'B')
6960 {
6961 if (wcThird == 'j' || wcThird == 'J')
6962 return K_TRUE;
6963 }
6964 }
6965 /* Library file: .lib */
6966 else if (wcFirst == 'l' || wcFirst == 'L')
6967 {
6968 if (wcSecond == 'i' || wcSecond == 'I')
6969 {
6970 if (wcThird == 'b' || wcThird == 'B')
6971 return K_TRUE;
6972 }
6973 }
6974 /* Linker definition file: .def */
6975 else if (wcFirst == 'd' || wcFirst == 'D')
6976 {
6977 if (wcSecond == 'e' || wcSecond == 'E')
6978 {
6979 if (wcThird == 'f' || wcThird == 'F')
6980 return K_TRUE;
6981 }
6982 }
6983 }
6984 }
6985
6986 return K_FALSE;
6987}
6988
6989
6990/**
6991 * Checks if the file extension indicates that the file/dir is something we
6992 * ought to cache.
6993 *
6994 * @returns K_TRUE if cachable, K_FALSE if not.
6995 * @param pszExt The kHlpGetExt result.
6996 * @param fAttrQuery Set if it's for an attribute query, clear if for
6997 * file creation.
6998 */
6999static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
7000{
7001 wchar_t const wcFirst = *pszExt;
7002 if (wcFirst)
7003 {
7004 wchar_t const wcSecond = pszExt[1];
7005 if (wcSecond)
7006 {
7007 wchar_t const wcThird = pszExt[2];
7008 if (pszExt[3] == '\0')
7009 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
7010 return K_FALSE;
7011 }
7012 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
7013 }
7014 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
7015}
7016
7017
7018/**
7019 * Checks if the extension of the given UTF-16 path indicates that the file/dir
7020 * should be cached.
7021 *
7022 * @returns K_TRUE if cachable, K_FALSE if not.
7023 * @param pwszPath The UTF-16 path to examine.
7024 * @param fAttrQuery Set if it's for an attribute query, clear if for
7025 * file creation.
7026 */
7027static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
7028{
7029 KSIZE cwcExt;
7030 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
7031 switch (cwcExt)
7032 {
7033 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
7034 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
7035 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
7036 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
7037 }
7038 return K_FALSE;
7039}
7040
7041
7042
7043/**
7044 * Creates a new
7045 *
7046 * @returns
7047 * @param pFsObj .
7048 * @param pwszFilename .
7049 */
7050static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
7051{
7052 HANDLE hFile;
7053 MY_IO_STATUS_BLOCK Ios;
7054 MY_OBJECT_ATTRIBUTES ObjAttr;
7055 MY_UNICODE_STRING UniStr;
7056 MY_NTSTATUS rcNt;
7057 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7058
7059 /*
7060 * Open the file relative to the parent directory.
7061 */
7062 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
7063 kHlpAssert(pFsObj->pParent);
7064 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
7065
7066 Ios.Information = ~(ULONG_PTR)0;
7067 Ios.u.Status = -1;
7068
7069 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
7070 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
7071 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
7072
7073 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
7074
7075 rcNt = g_pfnNtCreateFile(&hFile,
7076 GENERIC_READ | SYNCHRONIZE,
7077 &ObjAttr,
7078 &Ios,
7079 NULL, /*cbFileInitialAlloc */
7080 FILE_ATTRIBUTE_NORMAL,
7081 FILE_SHARE_READ,
7082 FILE_OPEN,
7083 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
7084 NULL, /*pEaBuffer*/
7085 0); /*cbEaBuffer*/
7086 if (MY_NT_SUCCESS(rcNt))
7087 {
7088 /*
7089 * Read the whole file into memory.
7090 */
7091 LARGE_INTEGER cbFile;
7092 if (GetFileSizeEx(hFile, &cbFile))
7093 {
7094 if ( cbFile.QuadPart >= 0
7095#ifdef WITH_PCH_CACHING
7096 && ( cbFile.QuadPart < 16*1024*1024
7097 || ( cbFile.QuadPart < 96*1024*1024
7098 && pFsObj->cchName > 4
7099 && !g_Sandbox.fNoPchCaching
7100 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
7101#endif
7102 )
7103 {
7104 KU32 cbCache = (KU32)cbFile.QuadPart;
7105 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
7106 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
7107 if (hMapping != NULL)
7108 {
7109 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
7110 if (pbCache)
7111 {
7112 /*
7113 * Create the cached file object.
7114 */
7115 PKFSWCACHEDFILE pCachedFile;
7116 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
7117 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
7118 sizeof(*pCachedFile) + cbPath);
7119 if (pCachedFile)
7120 {
7121 pCachedFile->hCached = hFile;
7122 pCachedFile->hSection = hMapping;
7123 pCachedFile->cbCached = cbCache;
7124 pCachedFile->pbCached = pbCache;
7125 pCachedFile->pFsObj = pFsObj;
7126 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
7127 kFsCacheObjRetain(pFsObj);
7128
7129 g_cReadCachedFiles++;
7130 g_cbReadCachedFiles += cbCache;
7131
7132 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
7133 return pCachedFile;
7134 }
7135
7136 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
7137 }
7138 else
7139 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
7140 CloseHandle(hMapping);
7141 }
7142 else
7143 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
7144 }
7145 else
7146 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
7147 }
7148 else
7149 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
7150 g_pfnNtClose(hFile);
7151 }
7152 else
7153 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
7154 return NULL;
7155}
7156
7157
7158/**
7159 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
7160 */
7161static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
7162 KBOOL fIsFileHandle, HANDLE *phFile)
7163{
7164 HANDLE hProcSelf = GetCurrentProcess();
7165 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
7166 hProcSelf, phFile,
7167 dwDesiredAccess, fInheritHandle,
7168 0 /*dwOptions*/))
7169 {
7170 /*
7171 * Create handle table entry for the duplicate handle.
7172 */
7173 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
7174 if (pHandle)
7175 {
7176 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
7177 pHandle->cRefs = 1;
7178 pHandle->offFile = 0;
7179 pHandle->hHandle = *phFile;
7180 pHandle->dwDesiredAccess = dwDesiredAccess;
7181 pHandle->tidOwner = KU32_MAX;
7182 pHandle->u.pCachedFile = pCachedFile;
7183 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
7184 return K_TRUE;
7185
7186 kHlpFree(pHandle);
7187 }
7188 else
7189 KWFS_LOG(("Out of memory for handle!\n"));
7190
7191 CloseHandle(*phFile);
7192 *phFile = INVALID_HANDLE_VALUE;
7193 }
7194 else
7195 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
7196 return K_FALSE;
7197}
7198
7199
7200/**
7201 * Kernel32 - Common code for CreateFileW and CreateFileA.
7202 */
7203static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
7204{
7205 *phFile = INVALID_HANDLE_VALUE;
7206
7207 /*
7208 * At the moment we only handle existing files.
7209 */
7210 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
7211 {
7212 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
7213 kHlpAssert(pFsObj->fHaveStats);
7214 if ( pCachedFile != NULL
7215 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
7216 {
7217 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
7218 return K_TRUE;
7219 }
7220 }
7221 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
7222
7223 /* Do fallback, please. */
7224 return K_FALSE;
7225}
7226
7227
7228/** Kernel32 - CreateFileA */
7229static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7230 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7231 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7232{
7233 HANDLE hFile;
7234
7235 /*
7236 * Check for include files and similar that we do read-only caching of.
7237 */
7238 if (dwCreationDisposition == OPEN_EXISTING)
7239 {
7240 if ( dwDesiredAccess == GENERIC_READ
7241 || dwDesiredAccess == FILE_GENERIC_READ)
7242 {
7243 if (dwShareMode & FILE_SHARE_READ)
7244 {
7245 if ( !pSecAttrs
7246 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7247 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7248 {
7249 const char *pszExt = kHlpGetExt(pszFilename);
7250 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
7251 {
7252 KFSLOOKUPERROR enmError;
7253 PKFSOBJ pFsObj;
7254 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7255
7256 pFsObj = kFsCacheLookupA(g_pFsCache, pszFilename, &enmError);
7257 if (pFsObj)
7258 {
7259 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7260 {
7261 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7262 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7263 kFsCacheObjRelease(g_pFsCache, pFsObj);
7264 if (fRc)
7265 {
7266 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
7267 return hFile;
7268 }
7269 }
7270 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7271 {
7272 KWFS_LOG(("CreateFileA(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7273 SetLastError(ERROR_FILE_NOT_FOUND);
7274 return INVALID_HANDLE_VALUE;
7275 }
7276 /* Always fall back on missing files in volatile areas. */
7277 }
7278 /* These are for nasm and yasm header searching. Cache will already
7279 have checked the directories for the file, no need to call
7280 CreateFile to do it again. */
7281 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7282 {
7283 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7284 SetLastError(ERROR_FILE_NOT_FOUND);
7285 return INVALID_HANDLE_VALUE;
7286 }
7287 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7288 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7289 {
7290 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
7291 SetLastError(ERROR_PATH_NOT_FOUND);
7292 return INVALID_HANDLE_VALUE;
7293 }
7294
7295 /* fallback */
7296 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7297 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7298 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
7299 return hFile;
7300 }
7301 }
7302 }
7303 }
7304 }
7305
7306 /*
7307 * Okay, normal.
7308 */
7309 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7310 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7311 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7312
7313 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7314 return hFile;
7315}
7316
7317
7318/** Kernel32 - CreateFileW */
7319static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7320 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7321 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7322{
7323 HANDLE hFile;
7324
7325#ifdef WITH_TEMP_MEMORY_FILES
7326 /*
7327 * Check for temporary files (cl.exe only).
7328 */
7329 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7330 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7331 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7332 && kwFsIsClTempFileW(pwszFilename))
7333 {
7334 KBOOL fFallback = K_FALSE;
7335 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7336 if (!fFallback)
7337 {
7338 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7339 return hFile;
7340 }
7341 }
7342#endif
7343
7344 /*
7345 * Check for include files and similar that we do read-only caching of.
7346 */
7347 if (dwCreationDisposition == OPEN_EXISTING)
7348 {
7349 if ( dwDesiredAccess == GENERIC_READ
7350 || dwDesiredAccess == FILE_GENERIC_READ)
7351 {
7352 if (dwShareMode & FILE_SHARE_READ)
7353 {
7354 if ( !pSecAttrs
7355 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7356 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7357 {
7358 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7359 {
7360 KFSLOOKUPERROR enmError;
7361 PKFSOBJ pFsObj;
7362 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7363
7364 pFsObj = kFsCacheLookupW(g_pFsCache, pwszFilename, &enmError);
7365 if (pFsObj)
7366 {
7367 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7368 {
7369 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7370 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7371 kFsCacheObjRelease(g_pFsCache, pFsObj);
7372 if (fRc)
7373 {
7374 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7375 return hFile;
7376 }
7377 }
7378 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7379 {
7380 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7381 SetLastError(ERROR_FILE_NOT_FOUND);
7382#if 0
7383 if ( pFsObj->cchName > sizeof("generated.h")
7384 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - sizeof("generated.h") + 1], "generated.h") == 0)
7385 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; pFsObj->fFlags=%#x\n", pwszFilename, pFsObj->fFlags);
7386#endif
7387 return INVALID_HANDLE_VALUE;
7388 }
7389 /* Always fall back on missing files in volatile areas. */
7390 }
7391 /* These are for nasm and yasm style header searching. Cache will
7392 already have checked the directories for the file, no need to call
7393 CreateFile to do it again. */
7394 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7395 {
7396#if 0
7397 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7398 if ( cwcFilename > sizeof("generated.h")
7399 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7400 L"generated.h", sizeof(L"generated.h")) == 0)
7401 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; (KFSLOOKUPERROR_NOT_FOUND)\n", pwszFilename, pFsObj->fFlags);
7402#endif
7403 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7404 SetLastError(ERROR_FILE_NOT_FOUND);
7405 return INVALID_HANDLE_VALUE;
7406 }
7407 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7408 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7409 {
7410#if 0
7411 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7412 if ( cwcFilename > sizeof("generated.h")
7413 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7414 L"generated.h", sizeof(L"generated.h")) == 0)
7415 kwErrPrintf("CreateFileW(%ls) -> ERROR_PATH_NOT_FOUND; (%d)\n", pwszFilename, enmError);
7416#endif
7417 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7418 SetLastError(ERROR_PATH_NOT_FOUND);
7419 return INVALID_HANDLE_VALUE;
7420 }
7421
7422 /* fallback */
7423 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7424 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7425 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7426 return hFile;
7427 }
7428 }
7429 else
7430 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7431 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7432 }
7433 else
7434 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7435 }
7436 else
7437 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7438 }
7439 else
7440 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7441
7442 /*
7443 * Okay, normal.
7444 */
7445 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7446 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7447 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7448
7449 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7450 return hFile;
7451}
7452
7453
7454
7455/** Kernel32 - SetFilePointer */
7456static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7457{
7458 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7459 if (pHandle != NULL)
7460 {
7461 KU32 cbFile;
7462 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7463 switch (pHandle->enmType)
7464 {
7465 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7466 cbFile = pHandle->u.pCachedFile->cbCached;
7467 break;
7468#ifdef WITH_TEMP_MEMORY_FILES
7469 case KWHANDLETYPE_TEMP_FILE:
7470 cbFile = pHandle->u.pTempFile->cbFile;
7471 break;
7472 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7473#endif
7474 case KWHANDLETYPE_OUTPUT_BUF:
7475 default:
7476 kHlpAssertFailed();
7477 kwSandboxHandlePut(pHandle);
7478 SetLastError(ERROR_INVALID_FUNCTION);
7479 return INVALID_SET_FILE_POINTER;
7480 }
7481
7482 switch (dwMoveMethod)
7483 {
7484 case FILE_BEGIN:
7485 break;
7486 case FILE_CURRENT:
7487 offMove += pHandle->offFile;
7488 break;
7489 case FILE_END:
7490 offMove += cbFile;
7491 break;
7492 default:
7493 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7494 kwSandboxHandlePut(pHandle);
7495 SetLastError(ERROR_INVALID_PARAMETER);
7496 return INVALID_SET_FILE_POINTER;
7497 }
7498 if (offMove >= 0)
7499 {
7500 if (offMove >= (KSSIZE)cbFile)
7501 {
7502#ifdef WITH_TEMP_MEMORY_FILES
7503 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7504 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7505#endif
7506 offMove = (KSSIZE)cbFile;
7507#ifdef WITH_TEMP_MEMORY_FILES
7508 /* For writable files, seeking beyond the end is fine, but check that we've got
7509 the type range for the request. */
7510 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7511 {
7512 kHlpAssertMsgFailed(("%#llx\n", offMove));
7513 kwSandboxHandlePut(pHandle);
7514 SetLastError(ERROR_SEEK);
7515 return INVALID_SET_FILE_POINTER;
7516 }
7517#endif
7518 }
7519 pHandle->offFile = (KU32)offMove;
7520 }
7521 else
7522 {
7523 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7524 kwSandboxHandlePut(pHandle);
7525 SetLastError(ERROR_NEGATIVE_SEEK);
7526 return INVALID_SET_FILE_POINTER;
7527 }
7528 if (pcbMoveHi)
7529 *pcbMoveHi = (KU64)offMove >> 32;
7530 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7531 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7532 kwSandboxHandlePut(pHandle);
7533 SetLastError(NO_ERROR);
7534 return (KU32)offMove;
7535 }
7536
7537 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7538 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7539}
7540
7541
7542/** Kernel32 - SetFilePointerEx */
7543static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7544 DWORD dwMoveMethod)
7545{
7546 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7547 if (pHandle != NULL)
7548 {
7549 KI64 offMyMove = offMove.QuadPart;
7550 KU32 cbFile;
7551 switch (pHandle->enmType)
7552 {
7553 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7554 cbFile = pHandle->u.pCachedFile->cbCached;
7555 break;
7556#ifdef WITH_TEMP_MEMORY_FILES
7557 case KWHANDLETYPE_TEMP_FILE:
7558 cbFile = pHandle->u.pTempFile->cbFile;
7559 break;
7560 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7561#endif
7562 case KWHANDLETYPE_OUTPUT_BUF:
7563 default:
7564 kHlpAssertFailed();
7565 kwSandboxHandlePut(pHandle);
7566 SetLastError(ERROR_INVALID_FUNCTION);
7567 return FALSE;
7568 }
7569
7570 switch (dwMoveMethod)
7571 {
7572 case FILE_BEGIN:
7573 break;
7574 case FILE_CURRENT:
7575 offMyMove += pHandle->offFile;
7576 break;
7577 case FILE_END:
7578 offMyMove += cbFile;
7579 break;
7580 default:
7581 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7582 kwSandboxHandlePut(pHandle);
7583 SetLastError(ERROR_INVALID_PARAMETER);
7584 return FALSE;
7585 }
7586 if (offMyMove >= 0)
7587 {
7588 if (offMyMove >= (KSSIZE)cbFile)
7589 {
7590#ifdef WITH_TEMP_MEMORY_FILES
7591 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7592 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7593#endif
7594 offMyMove = (KSSIZE)cbFile;
7595#ifdef WITH_TEMP_MEMORY_FILES
7596 /* For writable files, seeking beyond the end is fine, but check that we've got
7597 the type range for the request. */
7598 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7599 {
7600 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7601 kwSandboxHandlePut(pHandle);
7602 SetLastError(ERROR_SEEK);
7603 return FALSE;
7604 }
7605#endif
7606 }
7607 pHandle->offFile = (KU32)offMyMove;
7608 }
7609 else
7610 {
7611 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7612 kwSandboxHandlePut(pHandle);
7613 SetLastError(ERROR_NEGATIVE_SEEK);
7614 return FALSE;
7615 }
7616 if (poffNew)
7617 poffNew->QuadPart = offMyMove;
7618 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7619 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7620 kwSandboxHandlePut(pHandle);
7621 return TRUE;
7622 }
7623 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7624 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7625}
7626
7627
7628/** Kernel32 - ReadFile */
7629static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7630 LPOVERLAPPED pOverlapped)
7631{
7632 BOOL fRet;
7633 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7634 g_cReadFileCalls++;
7635 if (pHandle != NULL)
7636 {
7637 switch (pHandle->enmType)
7638 {
7639 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7640 {
7641 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7642 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7643 if (cbActually > cbToRead)
7644 cbActually = cbToRead;
7645
7646#ifdef WITH_HASH_CACHE
7647 if (g_Sandbox.pHashHead)
7648 {
7649 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7650 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7651 g_Sandbox.LastHashRead.cbRead = cbActually;
7652 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7653 }
7654#endif
7655
7656 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7657 pHandle->offFile += cbActually;
7658
7659 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7660 *pcbActuallyRead = cbActually;
7661
7662 g_cbReadFileFromReadCached += cbActually;
7663 g_cbReadFileTotal += cbActually;
7664 g_cReadFileFromReadCached++;
7665
7666 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7667 kwSandboxHandlePut(pHandle);
7668 return TRUE;
7669 }
7670
7671#ifdef WITH_TEMP_MEMORY_FILES
7672 case KWHANDLETYPE_TEMP_FILE:
7673 {
7674 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7675 KU32 cbActually;
7676 if (pHandle->offFile < pTempFile->cbFile)
7677 {
7678 cbActually = pTempFile->cbFile - pHandle->offFile;
7679 if (cbActually > cbToRead)
7680 cbActually = cbToRead;
7681
7682 /* Copy the data. */
7683 if (cbActually > 0)
7684 {
7685 KU32 cbLeft;
7686 KU32 offSeg;
7687 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7688
7689 /* Locate the segment containing the byte at offFile. */
7690 KU32 iSeg = pTempFile->cSegs - 1;
7691 kHlpAssert(pTempFile->cSegs > 0);
7692 while (paSegs[iSeg].offData > pHandle->offFile)
7693 iSeg--;
7694
7695 /* Copy out the data. */
7696 cbLeft = cbActually;
7697 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7698 for (;;)
7699 {
7700 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7701 if (cbAvail >= cbLeft)
7702 {
7703 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7704 break;
7705 }
7706
7707 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7708 cbLeft -= cbAvail;
7709 offSeg = 0;
7710 iSeg++;
7711 kHlpAssert(iSeg < pTempFile->cSegs);
7712 }
7713
7714 /* Update the file offset. */
7715 pHandle->offFile += cbActually;
7716 }
7717 }
7718 /* Read does not commit file space, so return zero bytes. */
7719 else
7720 cbActually = 0;
7721
7722 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7723 *pcbActuallyRead = cbActually;
7724
7725 g_cbReadFileTotal += cbActually;
7726 g_cbReadFileFromInMemTemp += cbActually;
7727 g_cReadFileFromInMemTemp++;
7728
7729 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7730 kwSandboxHandlePut(pHandle);
7731 return TRUE;
7732 }
7733
7734 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7735#endif /* WITH_TEMP_MEMORY_FILES */
7736 case KWHANDLETYPE_OUTPUT_BUF:
7737 default:
7738 kHlpAssertFailed();
7739 kwSandboxHandlePut(pHandle);
7740 SetLastError(ERROR_INVALID_FUNCTION);
7741 *pcbActuallyRead = 0;
7742 return FALSE;
7743 }
7744 }
7745
7746 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7747 if (fRet && pcbActuallyRead)
7748 g_cbReadFileTotal += *pcbActuallyRead;
7749 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7750 return fRet;
7751}
7752
7753
7754/** Kernel32 - ReadFileEx */
7755static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7756 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7757{
7758 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7759
7760 KWFS_LOG(("ReadFile(%p)\n", hFile));
7761 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7762}
7763
7764#ifdef WITH_STD_OUT_ERR_BUFFERING
7765
7766/**
7767 * Write something to a handle, making sure everything is actually written.
7768 *
7769 * @param hHandle Where to write it to.
7770 * @param pchBuf What to write
7771 * @param cchToWrite How much to write.
7772 */
7773static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7774{
7775 if (cchToWrite > 0)
7776 {
7777 DWORD cchWritten = 0;
7778 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7779 {
7780 if (cchWritten == cchToWrite)
7781 { /* likely */ }
7782 else
7783 {
7784 do
7785 {
7786 pchBuf += cchWritten;
7787 cchToWrite -= cchWritten;
7788 cchWritten = 0;
7789 } while ( cchToWrite > 0
7790 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7791 }
7792 }
7793 else
7794 kHlpAssertFailed();
7795 }
7796}
7797
7798
7799/**
7800 * Worker for WriteFile when the output isn't going to the console.
7801 *
7802 * @param pSandbox The sandbox.
7803 * @param pOutBuf The output buffer.
7804 * @param pchBuffer What to write.
7805 * @param cchToWrite How much to write.
7806 */
7807static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7808{
7809 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7810 { /* likely */ }
7811 else
7812 {
7813 /* No realloc, max size is 64KB. */
7814 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7815 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7816 if (!pOutBuf->u.Fully.pchBuf)
7817 {
7818 while ( !pOutBuf->u.Fully.pchBuf
7819 && pOutBuf->u.Fully.cchBufAlloc > 64)
7820 {
7821 pOutBuf->u.Fully.cchBufAlloc /= 2;
7822 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7823 }
7824 if (!pOutBuf->u.Fully.pchBuf)
7825 {
7826 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7827 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7828 }
7829 }
7830 }
7831
7832 /*
7833 * Special case: Output ends with newline and fits in the buffer.
7834 */
7835 if ( cchToWrite > 1
7836 && pchBuffer[cchToWrite - 1] == '\n'
7837 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7838 {
7839 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7840 pOutBuf->u.Fully.cchBuf += cchToWrite;
7841 }
7842 else
7843 {
7844 /*
7845 * Work thru the text line by line, flushing the buffer when
7846 * appropriate. The buffer is not a line buffer here, it's a
7847 * full buffer.
7848 */
7849 while (cchToWrite > 0)
7850 {
7851 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7852 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7853 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7854 {
7855 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7856 pOutBuf->u.Fully.cchBuf += cchLine;
7857 }
7858 /*
7859 * Option one: Flush the buffer and the current line.
7860 *
7861 * We choose this one when the line won't ever fit, or when we have
7862 * an incomplete line in the buffer.
7863 */
7864 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7865 || pOutBuf->u.Fully.cchBuf == 0
7866 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7867 {
7868 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7869 if (pOutBuf->u.Fully.cchBuf > 0)
7870 {
7871 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7872 pOutBuf->u.Fully.cchBuf = 0;
7873 }
7874 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7875 }
7876 /*
7877 * Option two: Only flush the lines in the buffer.
7878 */
7879 else
7880 {
7881 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7882 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7883 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7884 pOutBuf->u.Fully.cchBuf = cchLine;
7885 }
7886
7887 /* advance */
7888 pchBuffer += cchLine;
7889 cchToWrite -= cchLine;
7890 }
7891 }
7892}
7893
7894#endif /* WITH_STD_OUT_ERR_BUFFERING */
7895
7896#ifdef WITH_TEMP_MEMORY_FILES
7897static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7898{
7899 KU32 cbMinFile = offFile + cbNeeded;
7900 if (cbMinFile >= offFile)
7901 {
7902 /* Calc how much space we've already allocated and */
7903 if (cbMinFile <= pTempFile->cbFileAllocated)
7904 return K_TRUE;
7905
7906 /* Grow the file. */
7907 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7908 {
7909 int rc;
7910 KU32 cSegs = pTempFile->cSegs;
7911 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7912 do
7913 {
7914 /* grow the segment array? */
7915 if ((cSegs % 16) == 0)
7916 {
7917 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7918 if (!pvNew)
7919 return K_FALSE;
7920 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7921 }
7922
7923 /* Use page alloc here to simplify mapping later. */
7924 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7925 if (rc == 0)
7926 { /* likely */ }
7927 else
7928 {
7929 cbNewSeg = 64*1024;
7930 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7931 if (rc != 0)
7932 {
7933 kHlpAssertFailed();
7934 return K_FALSE;
7935 }
7936 }
7937 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7938 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7939 pTempFile->cbFileAllocated += cbNewSeg;
7940 pTempFile->cSegs = ++cSegs;
7941
7942 } while (pTempFile->cbFileAllocated < cbMinFile);
7943
7944 return K_TRUE;
7945 }
7946 }
7947
7948 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7949 return K_FALSE;
7950}
7951#endif /* WITH_TEMP_MEMORY_FILES */
7952
7953
7954#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7955/** Kernel32 - WriteFile */
7956static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7957 LPOVERLAPPED pOverlapped)
7958{
7959 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7960 BOOL fRet;
7961 g_cWriteFileCalls++;
7962 if (pHandle != NULL)
7963 {
7964 switch (pHandle->enmType)
7965 {
7966# ifdef WITH_TEMP_MEMORY_FILES
7967 case KWHANDLETYPE_TEMP_FILE:
7968 {
7969 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7970
7971 kHlpAssert(!pOverlapped);
7972 kHlpAssert(pcbActuallyWritten);
7973
7974 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7975 {
7976 KU32 cbLeft;
7977 KU32 offSeg;
7978
7979 /* Locate the segment containing the byte at offFile. */
7980 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7981 KU32 iSeg = pTempFile->cSegs - 1;
7982 kHlpAssert(pTempFile->cSegs > 0);
7983 while (paSegs[iSeg].offData > pHandle->offFile)
7984 iSeg--;
7985
7986 /* Copy in the data. */
7987 cbLeft = cbToWrite;
7988 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7989 for (;;)
7990 {
7991 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7992 if (cbAvail >= cbLeft)
7993 {
7994 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7995 break;
7996 }
7997
7998 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7999 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
8000 cbLeft -= cbAvail;
8001 offSeg = 0;
8002 iSeg++;
8003 kHlpAssert(iSeg < pTempFile->cSegs);
8004 }
8005
8006 /* Update the file offset. */
8007 pHandle->offFile += cbToWrite;
8008 if (pHandle->offFile > pTempFile->cbFile)
8009 pTempFile->cbFile = pHandle->offFile;
8010
8011 *pcbActuallyWritten = cbToWrite;
8012
8013 g_cbWriteFileTotal += cbToWrite;
8014 g_cbWriteFileToInMemTemp += cbToWrite;
8015 g_cWriteFileToInMemTemp++;
8016
8017 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
8018 kwSandboxHandlePut(pHandle);
8019 return TRUE;
8020 }
8021
8022 kHlpAssertFailed();
8023 kwSandboxHandlePut(pHandle);
8024 *pcbActuallyWritten = 0;
8025 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8026 return FALSE;
8027 }
8028# endif
8029
8030 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8031 kHlpAssertFailed();
8032 kwSandboxHandlePut(pHandle);
8033 SetLastError(ERROR_ACCESS_DENIED);
8034 *pcbActuallyWritten = 0;
8035 return FALSE;
8036
8037# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
8038 /*
8039 * Standard output & error.
8040 */
8041 case KWHANDLETYPE_OUTPUT_BUF:
8042 {
8043 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8044 if (pOutBuf->fIsConsole)
8045 {
8046 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
8047 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
8048 }
8049 else
8050 {
8051# ifdef WITH_STD_OUT_ERR_BUFFERING
8052 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
8053 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
8054 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
8055# else
8056 kHlpAssertFailed();
8057# endif
8058 }
8059 if (pcbActuallyWritten)
8060 *pcbActuallyWritten = cbToWrite;
8061 g_cbWriteFileTotal += cbToWrite;
8062 kwSandboxHandlePut(pHandle);
8063 return TRUE;
8064 }
8065# endif
8066
8067 default:
8068#ifdef WITH_TEMP_MEMORY_FILES
8069 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8070#endif
8071 kHlpAssertFailed();
8072 kwSandboxHandlePut(pHandle);
8073 SetLastError(ERROR_INVALID_FUNCTION);
8074 *pcbActuallyWritten = 0;
8075 return FALSE;
8076 }
8077 }
8078
8079 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
8080 if (fRet && pcbActuallyWritten)
8081 g_cbWriteFileTotal += *pcbActuallyWritten;
8082 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
8083 return fRet;
8084}
8085
8086
8087/** Kernel32 - WriteFileEx */
8088static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
8089 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
8090{
8091 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
8092
8093 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
8094 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
8095}
8096
8097#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
8098
8099#ifdef WITH_TEMP_MEMORY_FILES
8100
8101/** Kernel32 - SetEndOfFile; */
8102static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
8103{
8104 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8105 if (pHandle != NULL)
8106 {
8107 switch (pHandle->enmType)
8108 {
8109 case KWHANDLETYPE_TEMP_FILE:
8110 {
8111 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8112 if ( pHandle->offFile > pTempFile->cbFile
8113 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
8114 {
8115 kHlpAssertFailed();
8116 kwSandboxHandlePut(pHandle);
8117 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8118 return FALSE;
8119 }
8120
8121 pTempFile->cbFile = pHandle->offFile;
8122 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
8123 kwSandboxHandlePut(pHandle);
8124 return TRUE;
8125 }
8126
8127 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8128 kHlpAssertFailed();
8129 kwSandboxHandlePut(pHandle);
8130 SetLastError(ERROR_ACCESS_DENIED);
8131 return FALSE;
8132
8133# ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8134 case KWHANDLETYPE_OUTPUT_BUF:
8135 kHlpAssertFailed();
8136 kwSandboxHandlePut(pHandle);
8137 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
8138 return FALSE;
8139# endif
8140
8141 default:
8142 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8143 kHlpAssertFailed();
8144 kwSandboxHandlePut(pHandle);
8145 SetLastError(ERROR_INVALID_FUNCTION);
8146 return FALSE;
8147 }
8148 }
8149
8150 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
8151 return SetEndOfFile(hFile);
8152}
8153
8154
8155/** Kernel32 - GetFileType */
8156static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
8157{
8158 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8159 if (pHandle != NULL)
8160 {
8161 switch (pHandle->enmType)
8162 {
8163 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8164 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
8165 kwSandboxHandlePut(pHandle);
8166 return FILE_TYPE_DISK;
8167
8168 case KWHANDLETYPE_TEMP_FILE:
8169 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
8170 kwSandboxHandlePut(pHandle);
8171 return FILE_TYPE_DISK;
8172
8173#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8174 case KWHANDLETYPE_OUTPUT_BUF:
8175 {
8176 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8177 DWORD fRet;
8178 if (pOutBuf->fFileType != KU8_MAX)
8179 {
8180 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
8181 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
8182 }
8183 else
8184 {
8185 fRet = GetFileType(hFile);
8186 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
8187 }
8188 kwSandboxHandlePut(pHandle);
8189 return fRet;
8190 }
8191#endif
8192 }
8193 kwSandboxHandlePut(pHandle);
8194 }
8195
8196 KWFS_LOG(("GetFileType(%p)\n", hFile));
8197 return GetFileType(hFile);
8198}
8199
8200
8201/** Kernel32 - GetFileSize */
8202static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
8203{
8204 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8205 if (pHandle != NULL)
8206 {
8207 if (pcbHighDword)
8208 *pcbHighDword = 0;
8209 SetLastError(NO_ERROR);
8210 switch (pHandle->enmType)
8211 {
8212 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8213 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8214 kwSandboxHandlePut(pHandle);
8215 return pHandle->u.pCachedFile->cbCached;
8216
8217 case KWHANDLETYPE_TEMP_FILE:
8218 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8219 kwSandboxHandlePut(pHandle);
8220 return pHandle->u.pTempFile->cbFile;
8221
8222 case KWHANDLETYPE_OUTPUT_BUF:
8223 /* do default */
8224 break;
8225
8226 default:
8227 kHlpAssertFailed();
8228 kwSandboxHandlePut(pHandle);
8229 SetLastError(ERROR_INVALID_FUNCTION);
8230 return INVALID_FILE_SIZE;
8231 }
8232 kwSandboxHandlePut(pHandle);
8233 }
8234
8235 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
8236 return GetFileSize(hFile, pcbHighDword);
8237}
8238
8239
8240/** Kernel32 - GetFileSizeEx */
8241static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
8242{
8243 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8244 if (pHandle != NULL)
8245 {
8246 switch (pHandle->enmType)
8247 {
8248 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8249 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8250 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
8251 kwSandboxHandlePut(pHandle);
8252 return TRUE;
8253
8254 case KWHANDLETYPE_TEMP_FILE:
8255 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8256 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
8257 kwSandboxHandlePut(pHandle);
8258 return TRUE;
8259
8260 case KWHANDLETYPE_OUTPUT_BUF:
8261 /* do default */
8262 break;
8263
8264 default:
8265 kHlpAssertFailed();
8266 kwSandboxHandlePut(pHandle);
8267 SetLastError(ERROR_INVALID_FUNCTION);
8268 return INVALID_FILE_SIZE;
8269 }
8270 kwSandboxHandlePut(pHandle);
8271 }
8272
8273 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
8274 return GetFileSizeEx(hFile, pcbFile);
8275}
8276
8277
8278/** Kernel32 - CreateFileMappingW */
8279static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
8280 DWORD fProtect, DWORD dwMaximumSizeHigh,
8281 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
8282{
8283 HANDLE hMapping;
8284 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8285 if (pHandle != NULL)
8286 {
8287 switch (pHandle->enmType)
8288 {
8289 case KWHANDLETYPE_TEMP_FILE:
8290 {
8291 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8292 if ( ( fProtect == PAGE_READONLY
8293 || fProtect == PAGE_EXECUTE_READ)
8294 && dwMaximumSizeHigh == 0
8295 && ( dwMaximumSizeLow == 0
8296 || dwMaximumSizeLow == pTempFile->cbFile)
8297 && pwszName == NULL)
8298 {
8299 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
8300 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
8301 kwSandboxHandlePut(pHandle);
8302 return hMapping;
8303 }
8304 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
8305 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8306 kwSandboxHandlePut(pHandle);
8307 SetLastError(ERROR_ACCESS_DENIED);
8308 return INVALID_HANDLE_VALUE;
8309 }
8310
8311 /* moc.exe benefits from this. */
8312 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8313 {
8314 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8315 if ( ( fProtect == PAGE_READONLY
8316 || fProtect == PAGE_EXECUTE_READ)
8317 && dwMaximumSizeHigh == 0
8318 && ( dwMaximumSizeLow == 0
8319 || dwMaximumSizeLow == pCachedFile->cbCached)
8320 && pwszName == NULL)
8321 {
8322 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
8323 K_FALSE /*fIsFileHandle*/, &hMapping))
8324 { /* likely */ }
8325 else
8326 hMapping = NULL;
8327 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8328 kwSandboxHandlePut(pHandle);
8329 return hMapping;
8330 }
8331
8332 /* Do fallback (for .pch). */
8333 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8334 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8335 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8336
8337 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8338 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8339 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8340 kwSandboxHandlePut(pHandle);
8341 return hMapping;
8342 }
8343
8344 /** @todo read cached memory mapped files too for moc. */
8345 }
8346 kwSandboxHandlePut(pHandle);
8347 }
8348
8349 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8350 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8351 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8352 return hMapping;
8353}
8354
8355
8356/** Kernel32 - MapViewOfFile */
8357static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8358 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8359{
8360 PVOID pvRet;
8361 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8362 if (pHandle != NULL)
8363 {
8364 KU32 idxMapping;
8365
8366 /*
8367 * Ensure one free entry in the mapping tracking table first,
8368 * since this is common to both temporary and cached files.
8369 */
8370 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8371 { /* likely */ }
8372 else
8373 {
8374 void *pvNew;
8375 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8376 if (cNew)
8377 cNew *= 2;
8378 else
8379 cNew = 32;
8380 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8381 if (pvNew)
8382 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8383 else
8384 {
8385 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8386 kwSandboxHandlePut(pHandle);
8387 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8388 return NULL;
8389 }
8390 g_Sandbox.cMemMappingsAlloc = cNew;
8391 }
8392
8393 /*
8394 * Type specific work.
8395 */
8396 switch (pHandle->enmType)
8397 {
8398 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8399 case KWHANDLETYPE_TEMP_FILE:
8400 case KWHANDLETYPE_OUTPUT_BUF:
8401 default:
8402 kHlpAssertFailed();
8403 kwSandboxHandlePut(pHandle);
8404 SetLastError(ERROR_INVALID_OPERATION);
8405 return NULL;
8406
8407 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8408 {
8409 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8410 if ( dwDesiredAccess == FILE_MAP_READ
8411 && offFileHigh == 0
8412 && offFileLow == 0
8413 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8414 {
8415 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8416 if (pTempFile->cSegs != 1)
8417 {
8418 KU32 iSeg;
8419 KU32 cbLeft;
8420 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8421 KU8 *pbAll = NULL;
8422 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8423 if (rc != 0)
8424 {
8425 kHlpAssertFailed();
8426 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8427 return NULL;
8428 }
8429
8430 cbLeft = pTempFile->cbFile;
8431 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8432 {
8433 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8434 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8435 cbLeft -= cbToCopy;
8436 }
8437
8438 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8439 {
8440 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8441 pTempFile->paSegs[iSeg].pbData = NULL;
8442 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8443 }
8444
8445 pTempFile->cSegs = 1;
8446 pTempFile->cbFileAllocated = cbAll;
8447 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8448 pTempFile->paSegs[0].pbData = pbAll;
8449 pTempFile->paSegs[0].offData = 0;
8450 }
8451
8452 pTempFile->cMappings++;
8453 kHlpAssert(pTempFile->cMappings == 1);
8454
8455 pvRet = pTempFile->paSegs[0].pbData;
8456 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8457 break;
8458 }
8459
8460 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8461 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8462 kwSandboxHandlePut(pHandle);
8463 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8464 return NULL;
8465 }
8466
8467 /*
8468 * This is simple in comparison to the above temporary file code.
8469 */
8470 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8471 {
8472 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8473 if ( dwDesiredAccess == FILE_MAP_READ
8474 && offFileHigh == 0
8475 && offFileLow == 0
8476 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8477 {
8478 pvRet = pCachedFile->pbCached;
8479 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8480 break;
8481 }
8482 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8483 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8484 kwSandboxHandlePut(pHandle);
8485 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8486 return NULL;
8487 }
8488 }
8489
8490 /*
8491 * Insert into the mapping tracking table. This is common
8492 * and we should only get here with a non-NULL pvRet.
8493 *
8494 * Note! We could look for duplicates and do ref counting, but it's
8495 * easier to just append for now.
8496 */
8497 kHlpAssert(pvRet != NULL);
8498 idxMapping = g_Sandbox.cMemMappings;
8499 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8500
8501 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8502 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8503 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8504 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8505 g_Sandbox.cMemMappings++;
8506
8507 kwSandboxHandlePut(pHandle);
8508 return pvRet;
8509 }
8510
8511 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8512 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8513 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8514 return pvRet;
8515}
8516
8517
8518/** Kernel32 - MapViewOfFileEx */
8519static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8520 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8521{
8522 PVOID pvRet;
8523 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8524 if (pHandle != NULL)
8525 {
8526 switch (pHandle->enmType)
8527 {
8528 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8529 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8530 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8531 if (!pvMapAddr)
8532 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8533 else
8534 {
8535 kHlpAssertFailed();
8536 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8537 }
8538 kwSandboxHandlePut(pHandle);
8539 return NULL;
8540
8541 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8542 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8543 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8544 if (!pvMapAddr)
8545 {
8546 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8547 kwSandboxHandlePut(pHandle);
8548 return pvRet;
8549 }
8550 /* We can use fallback here as the handle is an actual section handle. */
8551 break;
8552
8553 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8554 case KWHANDLETYPE_TEMP_FILE:
8555 case KWHANDLETYPE_OUTPUT_BUF:
8556 default:
8557 kHlpAssertFailed();
8558 kwSandboxHandlePut(pHandle);
8559 SetLastError(ERROR_INVALID_OPERATION);
8560 return NULL;
8561 }
8562 kwSandboxHandlePut(pHandle);
8563 }
8564
8565 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8566 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8567 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8568 return pvRet;
8569
8570}
8571
8572/** Kernel32 - UnmapViewOfFile */
8573static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8574{
8575 /*
8576 * Consult the memory mapping tracker.
8577 */
8578 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8579 KU32 idxMapping = g_Sandbox.cMemMappings;
8580 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8581 while (idxMapping-- > 0)
8582 if (paMemMappings[idxMapping].pvMapping == pvBase)
8583 {
8584 /* Type specific stuff. */
8585 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8586 {
8587 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8588 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8589 }
8590 else
8591 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8592
8593 /* Deref and probably free it. */
8594 if (--paMemMappings[idxMapping].cRefs == 0)
8595 {
8596 g_Sandbox.cMemMappings--;
8597 if (idxMapping != g_Sandbox.cMemMappings)
8598 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8599 }
8600 return TRUE;
8601 }
8602
8603 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8604 return UnmapViewOfFile(pvBase);
8605}
8606
8607/** @todo UnmapViewOfFileEx */
8608
8609#endif /* WITH_TEMP_MEMORY_FILES */
8610
8611
8612/** Kernel32 - DuplicateHandle */
8613static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8614 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8615{
8616 BOOL fRet;
8617
8618 /*
8619 * We must catch our handles being duplicated.
8620 */
8621 if (hSrcProc == GetCurrentProcess())
8622 {
8623 PKWHANDLE pHandle = kwSandboxHandleGet(hSrc);
8624 if (pHandle)
8625 {
8626 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8627 if (fRet)
8628 {
8629 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8630 {
8631 pHandle->cRefs++;
8632 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8633 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8634 pHandle->enmType, pHandle->cRefs));
8635 }
8636 else
8637 {
8638 fRet = FALSE;
8639 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8640 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8641 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8642 }
8643 }
8644 else
8645 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8646 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8647 kwSandboxHandlePut(pHandle);
8648 return fRet;
8649 }
8650 }
8651
8652 /*
8653 * Not one of ours, just do what the caller asks and log it.
8654 */
8655 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8656 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8657 fInheritHandle, dwOptions, fRet, *phNew));
8658 return fRet;
8659}
8660
8661
8662/** Kernel32 - CloseHandle */
8663static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8664{
8665 BOOL fRet;
8666 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8667 PKWHANDLE pHandle = kwSandboxHandleGet(hObject);
8668 if (pHandle)
8669 {
8670 /* Prevent the closing of the standard output and error handles. */
8671 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8672 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) /* why this?!? */)
8673 {
8674 fRet = CloseHandle(hObject);
8675 if (fRet)
8676 {
8677 EnterCriticalSection(&g_Sandbox.HandlesLock);
8678 pHandle = g_Sandbox.papHandles[idxHandle];
8679 g_Sandbox.papHandles[idxHandle] = NULL;
8680 g_Sandbox.cActiveHandles--;
8681 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8682 if (--pHandle->cRefs == 0)
8683 {
8684#ifdef WITH_TEMP_MEMORY_FILES
8685 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8686 {
8687 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8688 pHandle->u.pTempFile->cActiveHandles--;
8689 }
8690#endif
8691 kHlpFree(pHandle);
8692 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8693 }
8694 else
8695 {
8696 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8697 kwSandboxHandlePut(pHandle);
8698 }
8699 LeaveCriticalSection(&g_Sandbox.HandlesLock);
8700 return fRet;
8701 }
8702 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8703 }
8704 else
8705 {
8706#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8707 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8708 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8709#else
8710 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of stdXXX!\n", hObject));
8711#endif
8712 fRet = TRUE;
8713 }
8714 kwSandboxHandlePut(pHandle);
8715 return fRet;
8716 }
8717
8718 fRet = CloseHandle(hObject);
8719 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8720 return fRet;
8721}
8722
8723
8724/** Kernel32 - GetFileAttributesA. */
8725static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8726{
8727 DWORD fRet;
8728 const char *pszExt = kHlpGetExt(pszFilename);
8729 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8730 {
8731 KFSLOOKUPERROR enmError;
8732 PKFSOBJ pFsObj;
8733 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8734
8735 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8736 if (pFsObj)
8737 {
8738 kHlpAssert(pFsObj->fHaveStats);
8739 fRet = pFsObj->Stats.st_attribs;
8740 kFsCacheObjRelease(g_pFsCache, pFsObj);
8741 }
8742 else
8743 {
8744 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8745 fRet = INVALID_FILE_ATTRIBUTES;
8746 }
8747
8748 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8749 return fRet;
8750 }
8751
8752 fRet = GetFileAttributesA(pszFilename);
8753 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8754 return fRet;
8755}
8756
8757
8758/** Kernel32 - GetFileAttributesW. */
8759static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8760{
8761 DWORD fRet;
8762 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8763 {
8764 KFSLOOKUPERROR enmError;
8765 PKFSOBJ pFsObj;
8766 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8767
8768 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8769 if (pFsObj)
8770 {
8771 kHlpAssert(pFsObj->fHaveStats);
8772 fRet = pFsObj->Stats.st_attribs;
8773 kFsCacheObjRelease(g_pFsCache, pFsObj);
8774 }
8775 else
8776 {
8777 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8778 fRet = INVALID_FILE_ATTRIBUTES;
8779 }
8780#ifndef NDEBUG
8781 {
8782 DWORD fCheck = GetFileAttributesW(pwszFilename);
8783 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8784 }
8785#endif
8786 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8787 return fRet;
8788 }
8789
8790 fRet = GetFileAttributesW(pwszFilename);
8791 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8792 return fRet;
8793}
8794
8795
8796/** Kernel32 - GetFileAttributesExA. */
8797static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8798 WIN32_FILE_ATTRIBUTE_DATA *pData)
8799{
8800 BOOL fRet;
8801 const char *pszExt = kHlpGetExt(pszFilename);
8802 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8803 {
8804 KFSLOOKUPERROR enmError;
8805 PKFSOBJ pFsObj;
8806 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8807
8808 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8809 if (pFsObj)
8810 {
8811 kHlpAssert(pFsObj->fHaveStats);
8812 if (enmLevel == GetFileExInfoStandard)
8813 {
8814 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8815 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8816 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8817 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8818 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8819 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8820 kFsCacheObjRelease(g_pFsCache, pFsObj);
8821 fRet = TRUE;
8822 }
8823 else
8824 {
8825 kFsCacheObjRelease(g_pFsCache, pFsObj);
8826 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8827 }
8828 }
8829 else
8830 {
8831 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8832 fRet = FALSE;
8833 }
8834
8835#ifdef K_STRICT
8836 {
8837 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8838 DWORD const dwErrSaved = GetLastError();
8839 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8840 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8841 if (fRetCheck && fRet)
8842 {
8843# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8844 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8845 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8846 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8847 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8848 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8849 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8850 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8851 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8852 }
8853 else
8854 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8855 SetLastError(dwErrSaved);
8856 }
8857#endif
8858 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8859 return fRet;
8860 }
8861
8862 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8863 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8864 return fRet;
8865}
8866
8867
8868/** Kernel32 - GetFileAttributesExW. */
8869static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8870 WIN32_FILE_ATTRIBUTE_DATA *pData)
8871{
8872 BOOL fRet;
8873 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8874 {
8875 KFSLOOKUPERROR enmError;
8876 PKFSOBJ pFsObj;
8877 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8878
8879 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8880 if (pFsObj)
8881 {
8882 kHlpAssert(pFsObj->fHaveStats);
8883 if (enmLevel == GetFileExInfoStandard)
8884 {
8885 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8886 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8887 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8888 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8889 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8890 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8891 kFsCacheObjRelease(g_pFsCache, pFsObj);
8892 fRet = TRUE;
8893 }
8894 else
8895 {
8896 kFsCacheObjRelease(g_pFsCache, pFsObj);
8897 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8898 }
8899 }
8900 else
8901 {
8902 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8903 fRet = FALSE;
8904 }
8905
8906#ifdef K_STRICT
8907 {
8908 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8909 DWORD const dwErrSaved = GetLastError();
8910 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8911 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8912 if (fRetCheck && fRet)
8913 {
8914# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8915 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8916 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8917 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8918 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8919 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8920 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8921 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8922 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8923 }
8924 else
8925 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8926 SetLastError(dwErrSaved);
8927 }
8928#endif
8929 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8930 return fRet;
8931 }
8932
8933 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8934 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8935 return fRet;
8936}
8937
8938
8939/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8940 * directory containing each include file. We cache the result to speed
8941 * things up a little. */
8942static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8943{
8944 DWORD cwcRet;
8945 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8946 {
8947 KFSLOOKUPERROR enmError;
8948 PKFSOBJ pObj;
8949 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8950
8951 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8952 if (pObj)
8953 {
8954 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8955 {
8956 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8957 {
8958 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8959
8960 /* Should preserve trailing slash on directory paths. */
8961 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8962 {
8963 if ( cwcRet + 1 < cwcShortPath
8964 && pwszShortPath[cwcRet - 1] != '\\')
8965 {
8966 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8967 if ( cwcIn > 0
8968 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8969 {
8970 pwszShortPath[cwcRet++] = '\\';
8971 pwszShortPath[cwcRet] = '\0';
8972 }
8973 }
8974 }
8975
8976 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8977 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8978 kFsCacheObjRelease(g_pFsCache, pObj);
8979 return cwcRet;
8980 }
8981
8982 /* fall back for complicated cases. */
8983 }
8984 kFsCacheObjRelease(g_pFsCache, pObj);
8985 }
8986 }
8987 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8988 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8989 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8990 return cwcRet;
8991}
8992
8993
8994#ifdef WITH_TEMP_MEMORY_FILES
8995/** Kernel32 - DeleteFileW
8996 * Skip deleting the in-memory files. */
8997static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8998{
8999 BOOL fRc;
9000 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9001 && kwFsIsClTempFileW(pwszFilename))
9002 {
9003 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9004 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
9005 fRc = TRUE;
9006 }
9007 else
9008 {
9009 fRc = DeleteFileW(pwszFilename);
9010 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
9011 }
9012 return fRc;
9013}
9014#endif /* WITH_TEMP_MEMORY_FILES */
9015
9016
9017
9018#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
9019
9020/*
9021 *
9022 * Console output buffering.
9023 * Console output buffering.
9024 * Console output buffering.
9025 *
9026 */
9027
9028
9029/**
9030 * Write a wide char string to the console.
9031 *
9032 * @param pSandbox The sandbox which output buffer to flush.
9033 */
9034static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
9035{
9036 if (cwcToWrite > 0)
9037 {
9038 DWORD cwcWritten = 0;
9039 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
9040 {
9041 if (cwcWritten == cwcToWrite)
9042 { /* likely */ }
9043 else
9044 {
9045 DWORD off = 0;
9046 do
9047 {
9048 off += cwcWritten;
9049 cwcWritten = 0;
9050 } while ( off < cwcToWrite
9051 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
9052 kHlpAssert(off == cwcWritten);
9053 }
9054 }
9055 else
9056 kHlpAssertFailed();
9057 pSandbox->Combined.cFlushes++;
9058 }
9059}
9060
9061
9062/**
9063 * Flushes the combined console output buffer.
9064 *
9065 * @param pSandbox The sandbox which output buffer to flush.
9066 */
9067static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
9068{
9069 if (pSandbox->Combined.cwcBuf > 0)
9070 {
9071 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
9072 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
9073 pSandbox->Combined.cwcBuf = 0;
9074 }
9075}
9076
9077
9078/**
9079 * For handling combined buffer overflow cases line by line.
9080 *
9081 * @param pSandbox The sandbox.
9082 * @param pwcBuf What to add to the combined buffer. Usually a
9083 * line, unless we're really low on buffer space.
9084 * @param cwcBuf The length of what to add.
9085 * @param fBrokenLine Whether this is a broken line.
9086 */
9087static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
9088{
9089 if (fBrokenLine)
9090 kwSandboxConsoleFlushCombined(pSandbox);
9091 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
9092 {
9093 kwSandboxConsoleFlushCombined(pSandbox);
9094 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
9095 }
9096 else
9097 {
9098 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
9099 pSandbox->Combined.cwcBuf += cwcBuf;
9100 }
9101}
9102
9103
9104/**
9105 * Called to final flush a line buffer via the combined buffer (if applicable).
9106 *
9107 * @param pSandbox The sandbox.
9108 * @param pLineBuf The line buffer.
9109 * @param pszName The line buffer name (for logging)
9110 */
9111static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
9112{
9113 if (pLineBuf->fIsConsole)
9114 {
9115 if (pLineBuf->u.Con.cwcBuf > 0)
9116 {
9117 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
9118
9119 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
9120 {
9121 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
9122 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
9123 }
9124 else
9125 {
9126 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
9127 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
9128 }
9129 pLineBuf->u.Con.cwcBuf = 0;
9130 }
9131 }
9132#ifdef WITH_STD_OUT_ERR_BUFFERING
9133 else if (pLineBuf->u.Fully.cchBuf > 0)
9134 {
9135 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
9136
9137 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
9138 pLineBuf->u.Fully.cchBuf = 0;
9139 }
9140#endif
9141}
9142
9143
9144/**
9145 * Called at the end of sandboxed execution to flush both stream buffers.
9146 *
9147 * @param pSandbox The sandbox.
9148 */
9149static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
9150{
9151 /*
9152 * First do the cl.exe source file supression trick, if applicable.
9153 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
9154 * handle.
9155 */
9156 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9157 && pSandbox->Combined.cFlushes == 0)
9158 {
9159 if ( pSandbox->StdOut.fIsConsole
9160 || pSandbox->StdErr.fIsConsole)
9161 {
9162 if ( pSandbox->Combined.cwcBuf >= 3
9163 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
9164 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
9165 {
9166 KI32 off = pSandbox->Combined.cwcBuf - 1;
9167 if (pSandbox->Combined.wszBuf[off] == '\n')
9168 {
9169 KBOOL fOk = K_TRUE;
9170 while (off-- > 0)
9171 {
9172 wchar_t const wc = pSandbox->Combined.wszBuf[off];
9173 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
9174 { /* likely */ }
9175 else
9176 {
9177 fOk = K_FALSE;
9178 break;
9179 }
9180 }
9181 if (fOk)
9182 {
9183 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
9184 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9185 pSandbox->Combined.cwcBuf = 0;
9186 return;
9187 }
9188 }
9189 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
9190 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9191 }
9192 }
9193#ifdef WITH_STD_OUT_ERR_BUFFERING
9194 /*
9195 * Otherwise, it goes to standard output (redirected).
9196 */
9197 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
9198 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
9199 {
9200 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
9201 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
9202 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
9203
9204 if (pchBuf[off] == '\n')
9205 {
9206 KBOOL fOk = K_TRUE;
9207 if (pchBuf[off - 1] == '\r')
9208 off--;
9209 while (off-- > 0)
9210 {
9211 char const ch = pchBuf[off];
9212 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
9213 { /* likely */ }
9214 else
9215 {
9216 fOk = K_FALSE;
9217 break;
9218 }
9219 }
9220 if (fOk)
9221 {
9222 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
9223 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9224 pSandbox->StdOut.u.Fully.cchBuf = 0;
9225 return;
9226 }
9227 }
9228 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
9229 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9230 }
9231#endif
9232 }
9233
9234 /*
9235 * Flush the two line buffer, then the combined buffer.
9236 */
9237 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
9238 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
9239 kwSandboxConsoleFlushCombined(pSandbox);
9240}
9241
9242
9243/**
9244 * Writes a string to the given output stream.
9245 *
9246 * @param pSandbox The sandbox.
9247 * @param pLineBuf The line buffer for the output stream.
9248 * @param pwcBuffer The buffer to write.
9249 * @param cwcToWrite The number of wchar_t's in the buffer.
9250 */
9251static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
9252{
9253 kHlpAssert(pLineBuf->fIsConsole);
9254 if (cwcToWrite > 0)
9255 {
9256 /*
9257 * First, find the start of the last incomplete line so we can figure
9258 * out how much line buffering we need to do.
9259 */
9260 KU32 cchLastIncompleteLine;
9261 KU32 offLastIncompleteLine = cwcToWrite;
9262 while ( offLastIncompleteLine > 0
9263 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
9264 offLastIncompleteLine--;
9265 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
9266
9267 /* Was there anything to line buffer? */
9268 if (offLastIncompleteLine < cwcToWrite)
9269 {
9270 /* Need to grow the line buffer? */
9271 KU32 cwcNeeded = offLastIncompleteLine == 0
9272 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
9273 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
9274 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
9275 {
9276 void *pvNew;
9277 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
9278 while (cwcNew < cwcNeeded)
9279 cwcNew *= 2;
9280 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
9281 if (pvNew)
9282 {
9283 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9284 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
9285 }
9286 else
9287 {
9288 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
9289 if (pvNew)
9290 {
9291 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9292 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
9293 }
9294 else
9295 {
9296 /* This isn't perfect, but it will have to do for now. */
9297 if (pLineBuf->u.Con.cwcBuf > 0)
9298 {
9299 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9300 K_TRUE /*fBrokenLine*/);
9301 pLineBuf->u.Con.cwcBuf = 0;
9302 }
9303 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
9304 return;
9305 }
9306 }
9307 }
9308
9309 /*
9310 * Handle the case where we only add to the line buffer.
9311 */
9312 if (offLastIncompleteLine == 0)
9313 {
9314 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
9315 pLineBuf->u.Con.cwcBuf += cwcToWrite;
9316 return;
9317 }
9318 }
9319
9320 /*
9321 * If there is sufficient combined buffer to handle this request, this is rather simple.
9322 */
9323 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
9324 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9325 {
9326 if (pLineBuf->u.Con.cwcBuf > 0)
9327 {
9328 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9329 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9330 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9331 pLineBuf->u.Con.cwcBuf = 0;
9332 }
9333
9334 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9335 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9336 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9337 }
9338 else
9339 {
9340 /*
9341 * Do line-by-line processing of the input, flusing the combined buffer
9342 * when it becomes necessary. We may have to write lines
9343 */
9344 KU32 off = 0;
9345 KU32 offNextLine = 0;
9346
9347 /* If there are buffered chars, we handle the first line outside the
9348 main loop. We must try our best outputting it as a complete line. */
9349 if (pLineBuf->u.Con.cwcBuf > 0)
9350 {
9351 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9352 offNextLine++;
9353 offNextLine++;
9354 kHlpAssert(offNextLine <= offLastIncompleteLine);
9355
9356 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9357 {
9358 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9359 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9360 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9361 pLineBuf->u.Con.cwcBuf = 0;
9362
9363 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9364 pSandbox->Combined.cwcBuf += offNextLine;
9365 }
9366 else
9367 {
9368 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9369 if (cwcLeft > 0)
9370 {
9371 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9372 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9373 pLineBuf->u.Con.cwcBuf += cwcCopy;
9374 off += cwcCopy;
9375 }
9376 if (pLineBuf->u.Con.cwcBuf > 0)
9377 {
9378 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9379 K_TRUE /*fBrokenLine*/);
9380 pLineBuf->u.Con.cwcBuf = 0;
9381 }
9382 if (off < offNextLine)
9383 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9384 }
9385 off = offNextLine;
9386 }
9387
9388 /* Deal with the remaining lines */
9389 while (off < offLastIncompleteLine)
9390 {
9391 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9392 offNextLine++;
9393 offNextLine++;
9394 kHlpAssert(offNextLine <= offLastIncompleteLine);
9395 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9396 off = offNextLine;
9397 }
9398 }
9399
9400 /*
9401 * Buffer any remaining incomplete line chars.
9402 */
9403 if (cchLastIncompleteLine)
9404 {
9405 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9406 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9407 }
9408 }
9409}
9410
9411
9412/**
9413 * Worker for WriteConsoleA and WriteFile.
9414 *
9415 * @param pSandbox The sandbox.
9416 * @param pLineBuf The line buffer.
9417 * @param pchBuffer What to write.
9418 * @param cchToWrite How much to write.
9419 */
9420static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9421{
9422 /*
9423 * Convert it to wide char and use the 'W' to do the work.
9424 */
9425 int cwcRet;
9426 KU32 cwcBuf = cchToWrite * 2 + 1;
9427 wchar_t *pwcBufFree = NULL;
9428 wchar_t *pwcBuf;
9429 kHlpAssert(pLineBuf->fIsConsole);
9430
9431 if (cwcBuf <= 4096)
9432 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9433 else
9434 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9435
9436 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9437 if (cwcRet > 0)
9438 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9439 else
9440 {
9441 DWORD cchWritten;
9442 kHlpAssertFailed();
9443
9444 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9445 if (pLineBuf->u.Con.cwcBuf > 0)
9446 {
9447 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9448 pLineBuf->u.Con.cwcBuf = 0;
9449 }
9450 kwSandboxConsoleFlushCombined(pSandbox);
9451
9452 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9453 {
9454 if (cchWritten >= cchToWrite)
9455 { /* likely */ }
9456 else
9457 {
9458 KU32 off = 0;
9459 do
9460 {
9461 off += cchWritten;
9462 cchWritten = 0;
9463 } while ( off < cchToWrite
9464 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9465 }
9466 }
9467 }
9468
9469 if (pwcBufFree)
9470 kHlpFree(pwcBufFree);
9471}
9472
9473
9474/** Kernel32 - WriteConsoleA */
9475BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9476 PVOID pvReserved)
9477{
9478 BOOL fRc;
9479 PKWOUTPUTSTREAMBUF pLineBuf;
9480 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9481
9482 if (hConOutput == g_Sandbox.StdErr.hOutput)
9483 pLineBuf = &g_Sandbox.StdErr;
9484 else
9485 pLineBuf = &g_Sandbox.StdOut;
9486 if (pLineBuf->fIsConsole)
9487 {
9488 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9489
9490 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9491 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9492 if (pcbWritten)
9493 *pcbWritten = cbToWrite;
9494 fRc = TRUE;
9495 }
9496 else
9497 {
9498 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9499 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9500 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9501 }
9502 return fRc;
9503}
9504
9505
9506/** Kernel32 - WriteConsoleW */
9507BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9508 PVOID pvReserved)
9509{
9510 BOOL fRc;
9511 PKWOUTPUTSTREAMBUF pLineBuf;
9512 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9513
9514 if (hConOutput == g_Sandbox.StdErr.hOutput)
9515 pLineBuf = &g_Sandbox.StdErr;
9516 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9517 pLineBuf = &g_Sandbox.StdOut;
9518 else
9519 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9520 if (pLineBuf->fIsConsole)
9521 {
9522 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9523
9524 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9525 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9526 if (pcwcWritten)
9527 *pcwcWritten = cwcToWrite;
9528 fRc = TRUE;
9529 }
9530 else
9531 {
9532 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9533 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9534 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9535 }
9536 return fRc;
9537}
9538
9539#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9540
9541
9542
9543/*
9544 *
9545 * Virtual memory leak prevension.
9546 * Virtual memory leak prevension.
9547 * Virtual memory leak prevension.
9548 *
9549 */
9550
9551#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9552
9553/** For debug logging. */
9554# ifndef NDEBUG
9555static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9556{
9557 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9558 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9559 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9560 if (cbMemInfo != 0)
9561 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9562 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9563 MemInfo.BaseAddress,
9564 MemInfo.AllocationBase,
9565 MemInfo.RegionSize,
9566 MemInfo.State,
9567 MemInfo.Protect,
9568 MemInfo.Type));
9569}
9570# else
9571# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9572# endif
9573
9574/**
9575 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9576 *
9577 * @param idxFixed The fixed allocation index to "free".
9578 */
9579static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9580{
9581 BOOL fRc;
9582 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9583 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9584 kHlpAssert(fRc); K_NOREF(fRc);
9585 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9586 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9587}
9588
9589#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9590
9591
9592/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9593 * location (~78MB in 32-bit 2010 compiler). */
9594static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9595{
9596 PVOID pvMem;
9597 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9598 {
9599 KU32 idxPreAllocated = KU32_MAX;
9600
9601#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9602 /*
9603 * Look for a pre-reserved CL.exe heap allocation.
9604 */
9605 pvMem = NULL;
9606 if ( pvAddr != 0
9607 && (fAllocType & MEM_RESERVE))
9608 {
9609 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9610 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9611 while (idxFixed-- > 0)
9612 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9613 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9614 {
9615 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9616 {
9617 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9618 {
9619 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9620 pvMem = pvAddr;
9621 idxPreAllocated = idxFixed;
9622 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9623 pvAddr, cb, fAllocType, fProt, pvMem));
9624 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9625 SetLastError(NO_ERROR);
9626 break;
9627 }
9628 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9629 }
9630 else
9631 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9632 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9633 }
9634 }
9635 if (!pvMem)
9636#endif
9637 {
9638 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9639 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9640 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9641 if ( pvAddr
9642 && pvAddr != pvMem
9643 && !( fAllocType == MEM_RESERVE /* After mapping the PCH, VS2019 ends up here (happens */
9644 && fProt == PAGE_READWRITE /* in real cl.exe runs too). Just shut it up to avoid confusion. */
9645#if K_ARCH_BITS >= 64
9646 && cb > 0x10000000 /* seen 67c00000, 33e00000, ++ */
9647#else
9648 && cb > 0x04000000 /* no idea */
9649#endif
9650 )
9651 )
9652 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9653 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9654 }
9655
9656 if (pvMem)
9657 {
9658 /*
9659 * Track it.
9660 */
9661 PKWVIRTALLOC pTracker;
9662
9663 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9664 pTracker = g_Sandbox.pVirtualAllocHead;
9665 while ( pTracker
9666 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9667 pTracker = pTracker->pNext;
9668 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9669 if (!pTracker)
9670 {
9671 DWORD dwErr = GetLastError();
9672 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9673 if (pTracker)
9674 {
9675 pTracker->pvAlloc = pvMem;
9676 pTracker->cbAlloc = cb;
9677 pTracker->idxPreAllocated = idxPreAllocated;
9678 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9679 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9680 g_Sandbox.pVirtualAllocHead = pTracker;
9681 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9682 }
9683 SetLastError(dwErr);
9684 }
9685 }
9686 }
9687 else
9688 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9689 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9690 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9691 return pvMem;
9692}
9693
9694
9695/** Kernel32 - VirtualFree. */
9696static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9697{
9698 BOOL fRc;
9699 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9700 {
9701 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9702 if (dwFreeType & MEM_RELEASE)
9703 {
9704 PKWVIRTALLOC pTracker;
9705 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9706 pTracker = g_Sandbox.pVirtualAllocHead;
9707 if (pTracker)
9708 {
9709 if (pTracker->pvAlloc == pvAddr)
9710 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9711 else
9712 {
9713 PKWVIRTALLOC pPrev;
9714 do
9715 {
9716 pPrev = pTracker;
9717 pTracker = pTracker->pNext;
9718 } while (pTracker && pTracker->pvAlloc != pvAddr);
9719 if (pTracker)
9720 pPrev->pNext = pTracker->pNext;
9721 }
9722 if (pTracker)
9723 {
9724#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9725 if (pTracker->idxPreAllocated != KU32_MAX)
9726 {
9727 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9728 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9729 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9730 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9731 kHlpFree(pTracker);
9732 return TRUE;
9733 }
9734#endif
9735
9736 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9737 if (fRc)
9738 kHlpFree(pTracker);
9739 else
9740 {
9741 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9742 g_Sandbox.pVirtualAllocHead = pTracker;
9743 }
9744 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9745 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9746 return fRc;
9747 }
9748
9749 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9750 }
9751 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9752 }
9753 }
9754
9755#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9756 /*
9757 * Protect our fixed allocations (this isn't just paranoia, btw.).
9758 */
9759 if (dwFreeType & MEM_RELEASE)
9760 {
9761 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9762 while (idxFixed-- > 0)
9763 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9764 {
9765 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9766 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9767 return TRUE;
9768 }
9769 }
9770#endif
9771
9772 /*
9773 * Not tracker or not actually free the virtual range.
9774 */
9775 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9776 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9777 return fRc;
9778}
9779
9780
9781/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9782HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9783{
9784 HANDLE hHeap;
9785 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9786
9787 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9788 if (hHeap != NULL)
9789 {
9790 DWORD dwErr = GetLastError();
9791 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9792 if (pTracker)
9793 {
9794 pTracker->hHeap = hHeap;
9795 pTracker->pNext = g_Sandbox.pHeapHead;
9796 g_Sandbox.pHeapHead = pTracker;
9797 }
9798
9799 SetLastError(dwErr);
9800 }
9801 return hHeap;
9802
9803}
9804
9805
9806/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9807BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9808{
9809 BOOL fRc = HeapDestroy(hHeap);
9810 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9811 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9812 if (fRc)
9813 {
9814 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9815 if (pTracker)
9816 {
9817 if (pTracker->hHeap == hHeap)
9818 g_Sandbox.pHeapHead = pTracker->pNext;
9819 else
9820 {
9821 PKWHEAP pPrev;
9822 do
9823 {
9824 pPrev = pTracker;
9825 pTracker = pTracker->pNext;
9826 } while (pTracker && pTracker->hHeap == hHeap);
9827 if (pTracker)
9828 pPrev->pNext = pTracker->pNext;
9829 }
9830 if (pTracker)
9831 kHlpFree(pTracker);
9832 else
9833 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9834 }
9835 }
9836
9837 return fRc;
9838}
9839
9840
9841
9842/*
9843 *
9844 * Thread/Fiber local storage leak prevention.
9845 * Thread/Fiber local storage leak prevention.
9846 * Thread/Fiber local storage leak prevention.
9847 *
9848 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9849 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9850 * we're leaking these indexes, but more importantely we crash during
9851 * worker exit since the callback is triggered multiple times.
9852 */
9853
9854
9855/** Kernel32 - FlsAlloc */
9856DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9857{
9858 DWORD idxFls = FlsAlloc(pfnCallback);
9859 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9860 if (idxFls != FLS_OUT_OF_INDEXES)
9861 {
9862 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9863 if (pTracker)
9864 {
9865 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9866 pTracker->idx = idxFls;
9867 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9868 g_Sandbox.pFlsAllocHead = pTracker;
9869 }
9870 }
9871
9872 return idxFls;
9873}
9874
9875/** Kernel32 - FlsFree */
9876BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9877{
9878 BOOL fRc = FlsFree(idxFls);
9879 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9880 if (fRc)
9881 {
9882 PKWLOCALSTORAGE pTracker;
9883 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9884
9885 pTracker = g_Sandbox.pFlsAllocHead;
9886 if (pTracker)
9887 {
9888 if (pTracker->idx == idxFls)
9889 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9890 else
9891 {
9892 PKWLOCALSTORAGE pPrev;
9893 do
9894 {
9895 pPrev = pTracker;
9896 pTracker = pTracker->pNext;
9897 } while (pTracker && pTracker->idx != idxFls);
9898 if (pTracker)
9899 pPrev->pNext = pTracker->pNext;
9900 }
9901 if (pTracker)
9902 {
9903 pTracker->idx = FLS_OUT_OF_INDEXES;
9904 pTracker->pNext = NULL;
9905 kHlpFree(pTracker);
9906 }
9907 }
9908 }
9909 return fRc;
9910}
9911
9912
9913/** Kernel32 - TlsAlloc */
9914DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9915{
9916 DWORD idxTls = TlsAlloc();
9917 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9918 if (idxTls != TLS_OUT_OF_INDEXES)
9919 {
9920 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9921 if (pTracker)
9922 {
9923 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9924 pTracker->idx = idxTls;
9925 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9926 g_Sandbox.pTlsAllocHead = pTracker;
9927 }
9928 }
9929
9930 return idxTls;
9931}
9932
9933/** Kernel32 - TlsFree */
9934BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9935{
9936 BOOL fRc = TlsFree(idxTls);
9937 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9938 if (fRc)
9939 {
9940 PKWLOCALSTORAGE pTracker;
9941 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9942
9943 pTracker = g_Sandbox.pTlsAllocHead;
9944 if (pTracker)
9945 {
9946 if (pTracker->idx == idxTls)
9947 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9948 else
9949 {
9950 PKWLOCALSTORAGE pPrev;
9951 do
9952 {
9953 pPrev = pTracker;
9954 pTracker = pTracker->pNext;
9955 } while (pTracker && pTracker->idx != idxTls);
9956 if (pTracker)
9957 pPrev->pNext = pTracker->pNext;
9958 }
9959 if (pTracker)
9960 {
9961 pTracker->idx = TLS_OUT_OF_INDEXES;
9962 pTracker->pNext = NULL;
9963 kHlpFree(pTracker);
9964 }
9965 }
9966 }
9967 return fRc;
9968}
9969
9970
9971
9972/*
9973 *
9974 * Header file hashing.
9975 * Header file hashing.
9976 * Header file hashing.
9977 *
9978 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9979 * indicated that ~12% of the time was spent doing MD5 caluclation when
9980 * rebuiling openssl. The hashing it done right after reading the source
9981 * via ReadFile, same buffers and sizes.
9982 */
9983
9984#ifdef WITH_HASH_CACHE
9985
9986/**
9987 * Gets our crypto provider context/instance, creating it if needed.
9988 */
9989static HCRYPTPROV kwSandboxGetCryptoProvider(ALG_ID idAlg)
9990{
9991 DWORD dwProvider;
9992 HCRYPTPROV *phCryptProv;
9993 HCRYPTPROV hCryptProv;
9994 if ( idAlg == CALG_SHA_256
9995 || idAlg == CALG_SHA_512)
9996 {
9997 phCryptProv = &g_Sandbox.hCryptProvAes;
9998 dwProvider = PROV_RSA_AES;
9999 }
10000 else
10001 {
10002 phCryptProv = &g_Sandbox.hCryptProvRsa;
10003 dwProvider = PROV_RSA_FULL;
10004 }
10005 hCryptProv = *phCryptProv;
10006 if (hCryptProv)
10007 return hCryptProv;
10008
10009 /* Create it. */
10010 if (CryptAcquireContextW(&hCryptProv, NULL, NULL, dwProvider, CRYPT_VERIFYCONTEXT))
10011 {
10012 kHlpAssert(hCryptProv != 0);
10013 kHlpAssert(hCryptProv != KUPTR_MAX);
10014 *phCryptProv = hCryptProv;
10015 return hCryptProv;
10016 }
10017
10018 kwErrPrintf("kwSandboxGetCryptoProvider: CryptAcquireContext(,,,%#x, CRYPT_VERIFYCONTEXT) failed! %u\n",
10019 dwProvider, GetLastError());
10020 return (HCRYPTPROV)NULL;
10021}
10022
10023/** AdvApi32 - CryptCreateHash */
10024static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
10025 HCRYPTHASH *phHash)
10026{
10027 BOOL fRc;
10028
10029 /*
10030 * Only do this for cl.exe when it request normal MD5.
10031 */
10032 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
10033 {
10034 KU32 cbDigest;
10035 const char *pszName;
10036 switch (idAlg)
10037 {
10038 case CALG_MD5:
10039 cbDigest = 128/8;
10040 pszName = "MD5";
10041 g_cHashesMd5++;
10042 break;
10043 case CALG_SHA1:
10044 cbDigest = 160/8;
10045 pszName = "SHA1";
10046 g_cHashesSha1++;
10047 break;
10048 case CALG_SHA_256:
10049 cbDigest = 256/8;
10050 pszName = "SHA-256";
10051 g_cHashesSha256++;
10052 break;
10053 case CALG_SHA_512:
10054 cbDigest = 512/8;
10055 pszName = "SHA-512";
10056 g_cHashesSha512++;
10057 break;
10058 default:
10059 cbDigest = 0;
10060 pszName = NULL;
10061 break;
10062 }
10063
10064 if (cbDigest)
10065 {
10066 if (hKey == 0)
10067 {
10068 if (dwFlags == 0)
10069 {
10070 PKWCRYPTHASH pHash = (PKWCRYPTHASH)kHlpAllocZ(sizeof(*pHash));
10071 if (pHash)
10072 {
10073 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10074 pHash->uMagic = KWCRYPTHASH_MAGIC;
10075 pHash->cbHashed = 0;
10076 pHash->fGoneBad = K_FALSE;
10077 pHash->fFinal = K_FALSE;
10078 pHash->hFallback = KUPTR_MAX;
10079 pHash->idAlg = idAlg;
10080 pHash->pszAlgName = pszName;
10081 pHash->cbDigest = cbDigest;
10082
10083 /* link it. */
10084 pHash->pNext = g_Sandbox.pHashHead;
10085 g_Sandbox.pHashHead = pHash;
10086
10087 *phHash = (KUPTR)pHash;
10088 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%s, 0, 0, *phHash=%p) -> %d [cached]\n",
10089 hProv, pszName, *phHash, TRUE));
10090 return TRUE;
10091 }
10092
10093 kwErrPrintf("CryptCreateHash: out of memory!\n");
10094 }
10095 else
10096 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with %s\n", hKey, pszName);
10097 }
10098 else
10099 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with %s\n", hKey, pszName);
10100 }
10101 else
10102 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
10103 }
10104
10105 /*
10106 * Fallback.
10107 */
10108 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
10109 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
10110 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
10111 return fRc;
10112}
10113
10114
10115/** AdvApi32 - CryptHashData */
10116static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
10117{
10118 BOOL fRc;
10119 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10120 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10121 while (pHash && (KUPTR)pHash != hHash)
10122 pHash = pHash->pNext;
10123 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
10124 hHash, pHash, pbData, cbData, dwFlags));
10125 if (pHash)
10126 {
10127 /*
10128 * Validate the state.
10129 */
10130 if ( pHash->uMagic == KWCRYPTHASH_MAGIC
10131 && !pHash->fFinal
10132 && !pHash->fGoneBad)
10133 {
10134 if (pHash->hFallback == KUPTR_MAX)
10135 {
10136 /*
10137 * Does this match the previous ReadFile call to a cached file?
10138 * If it doesn't, try falling back.
10139 */
10140 if ( g_Sandbox.LastHashRead.cbRead == cbData
10141 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
10142 {
10143 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
10144 if ( pCachedFile
10145 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
10146 {
10147
10148 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
10149 {
10150 if ( pHash->pCachedFile == NULL
10151 && pHash->cbHashed == 0)
10152 pHash->pCachedFile = pCachedFile;
10153 if (pHash->pCachedFile == pCachedFile)
10154 {
10155 pHash->cbHashed += cbData;
10156 g_Sandbox.LastHashRead.pCachedFile = NULL;
10157 g_Sandbox.LastHashRead.pvRead = NULL;
10158 g_Sandbox.LastHashRead.cbRead = 0;
10159 g_Sandbox.LastHashRead.offRead = 0;
10160 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
10161 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
10162 return TRUE;
10163 }
10164
10165 /* Note! it's possible to fall back here too, if necessary. */
10166 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
10167 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
10168 }
10169 else
10170 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
10171 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
10172 }
10173 else if (!pCachedFile)
10174 KWCRYPT_LOG(("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n"));
10175 else
10176 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
10177 }
10178 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
10179 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
10180 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
10181 if (pHash->cbHashed == 0)
10182 {
10183 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
10184 HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(pHash->idAlg);
10185 if (hCryptProv)
10186 {
10187 HCRYPTHASH hCryptHash = KUPTR_MAX;
10188 if (CryptCreateHash(hCryptProv, pHash->idAlg, 0, 0, &hCryptHash))
10189 {
10190 kHlpAssert(hCryptHash != KUPTR_MAX);
10191 pHash->hFallback = hCryptHash;
10192 fRc = CryptHashData(hCryptHash, pbData, cbData, dwFlags);
10193 if (fRc)
10194 pHash->cbHashed = cbData;
10195 g_cHashesFallbacks++;
10196 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d (%u) [fallback!]\n",
10197 hHash, pbData, cbData, dwFlags, fRc, GetLastError()));
10198 }
10199 else
10200 {
10201 kwErrPrintf("kwSandbox_Advapi32_CryptHashData: Fallback CryptCreateHash(%u) failed: %u\n",
10202 pHash->idAlg, GetLastError());
10203 fRc = FALSE;
10204 }
10205 return fRc;
10206 }
10207 }
10208 pHash->fGoneBad = K_TRUE;
10209 SetLastError(ERROR_INVALID_PARAMETER);
10210 fRc = FALSE;
10211 }
10212 else
10213 {
10214 /* fallback. */
10215 fRc = CryptHashData(pHash->hFallback, pbData, cbData, dwFlags);
10216 if (fRc)
10217 pHash->cbHashed += cbData;
10218 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d [fallback]\n",
10219 hHash, pbData, cbData, dwFlags, fRc));
10220 }
10221 }
10222 /*
10223 * Bad handle state.
10224 */
10225 else
10226 {
10227 if (pHash->uMagic != KWCRYPTHASH_MAGIC)
10228 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
10229 else
10230 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
10231 SetLastError((DWORD)NTE_BAD_HASH);
10232 fRc = FALSE;
10233 }
10234 }
10235 else
10236 {
10237
10238 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
10239 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
10240 }
10241 return fRc;
10242}
10243
10244
10245/** Helper for simpe data hashing. */
10246static BOOL kwSandboxCalcHash(ALG_ID idAlg, void const *pvData, KSIZE cbData, KU8 *pbDigest, KSIZE cbDigest)
10247{
10248 BOOL fRet = FALSE;
10249 if (idAlg == CALG_MD5)
10250 {
10251 struct MD5Context Ctx;
10252 MD5Init(&Ctx);
10253 MD5Update(&Ctx, (unsigned char const *)pvData, (unsigned)cbData);
10254 MD5Final(pbDigest, &Ctx);
10255 fRet = TRUE;
10256 }
10257 else
10258 {
10259 HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(idAlg);
10260 if (hCryptProv)
10261 {
10262 HCRYPTHASH hCryptHash = KUPTR_MAX;
10263 if (CryptCreateHash(hCryptProv, idAlg, 0, 0, &hCryptHash))
10264 {
10265 if (CryptHashData(hCryptHash, (const BYTE *)pvData, (DWORD)cbData, 0))
10266 {
10267 DWORD cbActual = (DWORD)cbDigest;
10268 if (CryptGetHashParam(hCryptHash, HP_HASHVAL, pbDigest, &cbActual, 0))
10269 {
10270 fRet = TRUE;
10271 kHlpAssert(cbActual == cbDigest);
10272 }
10273 else
10274 kwErrPrintf("CryptGetHashParam([%#x],HP_HASHVAL,%p,%#x,0) failed: %u\n",
10275 idAlg, pbDigest, cbDigest, GetLastError());
10276 }
10277 else
10278 kwErrPrintf("CryptHashData([%#x],%p,%#x,0) failed: %u\n", idAlg, pvData, cbData, GetLastError());
10279 CryptDestroyHash(hCryptHash);
10280 }
10281 else
10282 kwErrPrintf("CryptCreateHash(%#x) failed: %u\n", idAlg, GetLastError());
10283 }
10284 }
10285 return fRet;
10286}
10287
10288
10289/** AdvApi32 - CryptGetHashParam */
10290static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
10291 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
10292{
10293 BOOL fRc;
10294 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10295 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10296 while (pHash && (KUPTR)pHash != hHash)
10297 pHash = pHash->pNext;
10298 if (pHash)
10299 {
10300 if (pHash->uMagic == KWCRYPTHASH_MAGIC)
10301 {
10302 if (dwFlags == 0)
10303 {
10304 DWORD cbRet;
10305 void *pvRet;
10306 union
10307 {
10308 DWORD dw;
10309 } uBuf;
10310
10311 switch (dwParam)
10312 {
10313 case HP_HASHVAL:
10314 {
10315 /* Check the hash progress. */
10316 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
10317 g_cHashes++;
10318 if (pCachedFile)
10319 {
10320 if ( pCachedFile->cbCached == pHash->cbHashed
10321 && !pHash->fGoneBad)
10322 {
10323 KBOOL *pfValid;
10324 switch (pHash->idAlg)
10325 {
10326 case CALG_MD5:
10327 pfValid = &pCachedFile->fValidMd5;
10328 pvRet = pCachedFile->abMd5Digest;
10329 break;
10330 case CALG_SHA1:
10331 pfValid = &pCachedFile->fValidSha1;
10332 pvRet = pCachedFile->abSha1Digest;
10333 break;
10334 case CALG_SHA_256:
10335 pfValid = &pCachedFile->fValidSha256;
10336 pvRet = pCachedFile->abSha256Digest;
10337 break;
10338 case CALG_SHA_512:
10339 pfValid = &pCachedFile->fValidSha512;
10340 pvRet = pCachedFile->abSha512Digest;
10341 break;
10342 default:
10343 kwErrPrintf("Invalid idAlg value: %#x\n", pHash->idAlg);
10344 SetLastError(ERROR_INVALID_SERVER_STATE);
10345 return FALSE;
10346 }
10347
10348 if (*pfValid)
10349 {
10350 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
10351 g_cHashesCached++;
10352 }
10353 else
10354 {
10355 fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pCachedFile->cbCached,
10356 pvRet, pHash->cbDigest);
10357 if (!fRc)
10358 return FALSE;
10359 *pfValid = K_TRUE;
10360 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
10361 }
10362 }
10363 else
10364 {
10365 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
10366 from what I can tell, so just deal with it. */
10367 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
10368 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
10369 pHash, pCachedFile, pCachedFile->szPath));
10370 g_cHashesPartial++;
10371 pHash->pCachedFile = NULL;
10372 pvRet = pHash->abDigest;
10373 fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pHash->cbHashed,
10374 pvRet, pHash->cbDigest);
10375 if (!fRc)
10376 {
10377 pHash->fGoneBad = K_TRUE;
10378 return FALSE;
10379 }
10380 }
10381 pHash->fFinal = K_TRUE;
10382 cbRet = pHash->cbDigest;
10383 break;
10384 }
10385
10386 pvRet = pHash->abDigest;
10387 cbRet = pHash->cbDigest;
10388 if (pHash->fFinal)
10389 break;
10390 if (pHash->hFallback != KUPTR_MAX)
10391 {
10392 DWORD cbActual = (DWORD)pHash->cbDigest;
10393 if (CryptGetHashParam(pHash->hFallback, HP_HASHVAL, pHash->abDigest, &cbActual, 0))
10394 {
10395 kHlpAssert(cbActual == pHash->cbDigest);
10396 pHash->fFinal = K_TRUE;
10397 break;
10398 }
10399 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: Fallback CryptGetHashParam failed: %u!!\n", GetLastError());
10400 }
10401 else
10402 {
10403 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
10404 SetLastError(ERROR_INVALID_SERVER_STATE);
10405 }
10406 return FALSE;
10407 }
10408
10409 case HP_HASHSIZE:
10410 uBuf.dw = pHash->cbDigest;
10411 pvRet = &uBuf;
10412 cbRet = sizeof(DWORD);
10413 break;
10414
10415 case HP_ALGID:
10416 uBuf.dw = pHash->idAlg;
10417 pvRet = &uBuf;
10418 cbRet = sizeof(DWORD);
10419 break;
10420
10421 default:
10422 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
10423 SetLastError((DWORD)NTE_BAD_TYPE);
10424 return FALSE;
10425 }
10426
10427 /*
10428 * Copy out cbRet from pvRet.
10429 */
10430 if (pbData)
10431 {
10432 if (*pcbData >= cbRet)
10433 {
10434 *pcbData = cbRet;
10435 kHlpMemCopy(pbData, pvRet, cbRet);
10436 if (cbRet == 4)
10437 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
10438 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
10439 else if (cbRet == 16)
10440 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",
10441 dwParam, pHash, pHash->pCachedFile, cbRet,
10442 pbData[0], pbData[1], pbData[2], pbData[3],
10443 pbData[4], pbData[5], pbData[6], pbData[7],
10444 pbData[8], pbData[9], pbData[10], pbData[11],
10445 pbData[12], pbData[13], pbData[14], pbData[15]));
10446 else if (cbRet == 20)
10447 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",
10448 dwParam, pHash, pHash->pCachedFile, cbRet,
10449 pbData[0], pbData[1], pbData[2], pbData[3],
10450 pbData[4], pbData[5], pbData[6], pbData[7],
10451 pbData[8], pbData[9], pbData[10], pbData[11],
10452 pbData[12], pbData[13], pbData[14], pbData[15],
10453 pbData[16], pbData[17], pbData[18], pbData[19] ));
10454 else if (cbRet >= 32)
10455 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",
10456 dwParam, pHash, pHash->pCachedFile, cbRet,
10457 pbData[0], pbData[1], pbData[2], pbData[3],
10458 pbData[4], pbData[5], pbData[6], pbData[7],
10459 pbData[8], pbData[9], pbData[10], pbData[11],
10460 pbData[12], pbData[13], pbData[14], pbData[15],
10461 pbData[16], pbData[17], pbData[18], pbData[19],
10462 pbData[20], pbData[21], pbData[22], pbData[23],
10463 pbData[24], pbData[25], pbData[26], pbData[27],
10464 pbData[28], pbData[29], pbData[30], pbData[31], cbRet > 32 ? "..." : ""));
10465 else
10466 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
10467 dwParam, pHash, pHash->pCachedFile, cbRet));
10468 return TRUE;
10469 }
10470
10471 kHlpMemCopy(pbData, pvRet, *pcbData);
10472 }
10473 SetLastError(ERROR_MORE_DATA);
10474 *pcbData = cbRet;
10475 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
10476 }
10477 else
10478 {
10479 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
10480 SetLastError((DWORD)NTE_BAD_FLAGS);
10481 }
10482 }
10483 else
10484 {
10485 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
10486 SetLastError((DWORD)NTE_BAD_HASH);
10487 }
10488 fRc = FALSE;
10489 }
10490 /*
10491 * Regular handle.
10492 */
10493 else
10494 {
10495 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
10496 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
10497 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
10498 }
10499
10500 return fRc;
10501}
10502
10503
10504/** AdvApi32 - CryptDestroyHash */
10505static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
10506{
10507 BOOL fRc;
10508 PKWCRYPTHASH pPrev = NULL;
10509 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10510 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10511 while (pHash && (KUPTR)pHash != hHash)
10512 {
10513 pPrev = pHash;
10514 pHash = pHash->pNext;
10515 }
10516 if (pHash)
10517 {
10518 if (pHash->uMagic == KWCRYPTHASH_MAGIC)
10519 {
10520 pHash->uMagic = 0;
10521 if (!pPrev)
10522 g_Sandbox.pHashHead = pHash->pNext;
10523 else
10524 pPrev->pNext = pHash->pNext;
10525 kHlpFree(pHash);
10526 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
10527 fRc = TRUE;
10528 }
10529 else
10530 {
10531 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10532 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10533 SetLastError(ERROR_INVALID_HANDLE);
10534 fRc = FALSE;
10535 }
10536 }
10537 /*
10538 * Regular handle.
10539 */
10540 else
10541 {
10542 fRc = CryptDestroyHash(hHash);
10543 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10544 }
10545 return fRc;
10546}
10547
10548#endif /* WITH_HASH_CACHE */
10549
10550
10551/*
10552 *
10553 * Reuse crypt context.
10554 * Reuse crypt context.
10555 * Reuse crypt context.
10556 *
10557 *
10558 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10559 *
10560 */
10561
10562#ifdef WITH_CRYPT_CTX_REUSE
10563
10564/** AdvApi32 - CryptAcquireContextW. */
10565static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10566 DWORD dwProvType, DWORD dwFlags)
10567{
10568 BOOL fRet;
10569
10570 /*
10571 * Lookup reusable context based on the input.
10572 */
10573 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10574 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10575 KU32 iCtx = g_Sandbox.cCryptCtxs;
10576 while (iCtx-- > 0)
10577 {
10578 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10579 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10580 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10581 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10582 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10583 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10584 {
10585 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10586 {
10587 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10588 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10589 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10590 return TRUE;
10591 }
10592 }
10593 }
10594
10595 /*
10596 * Create it and enter it into the reused array if possible.
10597 */
10598 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10599 if (fRet)
10600 {
10601 iCtx = g_Sandbox.cCryptCtxs;
10602 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10603 {
10604 /* Try duplicate the input strings. */
10605 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10606 (cwcContainer + 1) * sizeof(wchar_t));
10607 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10608 {
10609 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10610 (cwcProvider + 1) * sizeof(wchar_t));
10611 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10612 {
10613 /* Add a couple of references just to be on the safe side and all that. */
10614 HCRYPTPROV hProv = *phProv;
10615 if (CryptContextAddRef(hProv, NULL, 0))
10616 {
10617 if (CryptContextAddRef(hProv, NULL, 0))
10618 {
10619 /* Okay, finish the entry and return success */
10620 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10621 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10622 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10623 g_Sandbox.cCryptCtxs = iCtx + 1;
10624
10625 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10626 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10627 return TRUE;
10628 }
10629 CryptReleaseContext(hProv, 0);
10630 }
10631 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10632
10633 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10634 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10635 }
10636 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10637 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10638 }
10639 }
10640 else
10641 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10642 }
10643
10644 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10645 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10646 return fRet;
10647}
10648
10649
10650/** AdvApi32 - CryptReleaseContext */
10651static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10652{
10653 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10654 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10655 return fRet;
10656}
10657
10658
10659/** AdvApi32 - CryptContextAddRef */
10660static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10661{
10662 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10663 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10664 return fRet;
10665}
10666
10667#endif /* WITH_CRYPT_CTX_REUSE */
10668
10669/*
10670 *
10671 * Structured exception handling.
10672 * Structured exception handling.
10673 * Structured exception handling.
10674 *
10675 */
10676#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10677
10678# define EH_NONCONTINUABLE KU32_C(0x00000001)
10679# define EH_UNWINDING KU32_C(0x00000002)
10680# define EH_EXIT_UNWIND KU32_C(0x00000004)
10681# define EH_STACK_INVALID KU32_C(0x00000008)
10682# define EH_NESTED_CALL KU32_C(0x00000010)
10683
10684typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10685 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10686typedef struct _EXCEPTION_REGISTRATION_RECORD
10687{
10688 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10689 PFNXCPTHANDLER pfnXcptHandler;
10690};
10691
10692
10693/**
10694 * Calls @a pfnHandler.
10695 */
10696static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10697 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10698 PFNXCPTHANDLER pfnHandler)
10699{
10700# if 1
10701 /* This is a more robust version that isn't subject to calling
10702 convension cleanup disputes and such. */
10703 KU32 uSavedEdi;
10704 KU32 uSavedEsi;
10705 KU32 uSavedEbx;
10706 KU32 rcHandler;
10707
10708 __asm
10709 {
10710 mov [uSavedEdi], edi
10711 mov [uSavedEsi], esi
10712 mov [uSavedEbx], ebx
10713 mov esi, esp
10714 mov edi, esp
10715 mov edi, [pXcptRec]
10716 mov edx, [pRegRec]
10717 mov eax, [pXcptCtx]
10718 mov ebx, [ppRegRec]
10719 mov ecx, [pfnHandler]
10720 sub esp, 16
10721 and esp, 0fffffff0h
10722 mov [esp ], edi
10723 mov [esp + 4], edx
10724 mov [esp + 8], eax
10725 mov [esp + 12], ebx
10726 mov edi, esi
10727 call ecx
10728 mov esp, esi
10729 cmp esp, edi
10730 je stack_ok
10731 int 3
10732 stack_ok:
10733 mov edi, [uSavedEdi]
10734 mov esi, [uSavedEsi]
10735 mov ebx, [uSavedEbx]
10736 mov [rcHandler], eax
10737 }
10738 return rcHandler;
10739# else
10740 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10741# endif
10742}
10743
10744
10745/**
10746 * Vectored exception handler that emulates x86 chained exception handler.
10747 *
10748 * This is necessary because the RtlIsValidHandler check fails for self loaded
10749 * code and prevents cl.exe from working. (On AMD64 we can register function
10750 * tables, but on X86 cooking your own handling seems to be the only viabke
10751 * alternative.)
10752 *
10753 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10754 * @param pXcptPtrs The exception details.
10755 */
10756static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10757{
10758 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10759 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10760 if (g_Sandbox.fRunning)
10761 {
10762 HANDLE const hCurProc = GetCurrentProcess();
10763 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10764 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10765 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10766 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10767 {
10768 /* Read the exception record in a safe manner. */
10769 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10770 DWORD cbActuallyRead = 0;
10771 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10772 && cbActuallyRead == sizeof(RegRec))
10773 {
10774 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10775 KU32 rcHandler;
10776 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10777 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10778 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10779 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10780 if (rcHandler == ExceptionContinueExecution)
10781 {
10782 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10783 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10784 return EXCEPTION_CONTINUE_EXECUTION;
10785 }
10786
10787 if (rcHandler == ExceptionContinueSearch)
10788 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10789 else if (rcHandler == ExceptionNestedException)
10790 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10791 else
10792 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10793 }
10794 else
10795 {
10796 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10797 break;
10798 }
10799
10800 /*
10801 * Next.
10802 */
10803 pRegRec = RegRec.pPrevRegRec;
10804 }
10805 }
10806 return EXCEPTION_CONTINUE_SEARCH;
10807}
10808
10809
10810/** NtDll,Kernel32 - RtlUnwind */
10811static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10812 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10813{
10814 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10815 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10816 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10817 if (g_Sandbox.fRunning)
10818 {
10819 HANDLE const hCurProc = GetCurrentProcess();
10820 PCONTEXT pXcptCtx = NULL;
10821 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10822
10823 /*
10824 * Update / create an exception record.
10825 */
10826 if (pXcptRec)
10827 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10828 else
10829 {
10830 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10831 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10832 pXcptRec->ExceptionCode = (DWORD)STATUS_UNWIND;
10833 pXcptRec->ExceptionFlags = EH_UNWINDING;
10834 }
10835 if (!pStopXcptRec)
10836 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10837
10838 /*
10839 * Walk the chain till we find pStopXctpRec.
10840 */
10841 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10842 && pRegRec != NULL
10843 && pRegRec != pStopXcptRec)
10844 {
10845 /* Read the exception record in a safe manner. */
10846 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10847 DWORD cbActuallyRead = 0;
10848 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10849 && cbActuallyRead == sizeof(RegRec))
10850 {
10851 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10852 KU32 rcHandler;
10853 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10854 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10855 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10856 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10857
10858 if (rcHandler == ExceptionContinueSearch)
10859 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10860 else if (rcHandler == ExceptionCollidedUnwind)
10861 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10862 else
10863 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10864 }
10865 else
10866 {
10867 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10868 break;
10869 }
10870
10871 /*
10872 * Pop next.
10873 */
10874 pTib->ExceptionList = RegRec.pPrevRegRec;
10875 pRegRec = RegRec.pPrevRegRec;
10876 }
10877 return;
10878 }
10879
10880 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10881}
10882
10883#endif /* WINDOWS + X86 */
10884
10885
10886/*
10887 *
10888 * Misc function only intercepted while debugging.
10889 * Misc function only intercepted while debugging.
10890 * Misc function only intercepted while debugging.
10891 *
10892 */
10893
10894#ifndef NDEBUG
10895
10896/** CRT - memcpy */
10897static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10898{
10899 KU8 const *pbSrc = (KU8 const *)pvSrc;
10900 KU8 *pbDst = (KU8 *)pvDst;
10901 KSIZE cbLeft = cb;
10902 while (cbLeft-- > 0)
10903 *pbDst++ = *pbSrc++;
10904 return pvDst;
10905}
10906
10907
10908/** CRT - memset */
10909static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10910{
10911 KU8 *pbDst = (KU8 *)pvDst;
10912 KSIZE cbLeft = cb;
10913 while (cbLeft-- > 0)
10914 *pbDst++ = (KU8)bFiller;
10915 return pvDst;
10916}
10917
10918#endif /* NDEBUG */
10919
10920
10921/** @todo consider hooking NtQueryDirectoryFile as c1xx.dll/c1.dll in 2019
10922 * uses it directly to read the content of include directories, however
10923 * they do it one file at the time. We already have the info in the
10924 * cache (where we do bulk reads). There are a lot of calls for the
10925 * SDK include directories, as one can imagine. */
10926
10927/**
10928 * Functions that needs replacing for sandboxed execution.
10929 */
10930KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10931{
10932 /*
10933 * Kernel32.dll and friends.
10934 */
10935 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10936 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10937
10938 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10939 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10940 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10941 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10942 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10943 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10944 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10945 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10946 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10947 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10948 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10949
10950 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10951 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10952 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10953 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10954
10955 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10956
10957 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10958 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10959 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10960 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10961 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10962 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10963 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10964 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10965 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10966 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10967 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10968
10969 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10970 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10971 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10972 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10973#ifdef WITH_TEMP_MEMORY_FILES
10974 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10975 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10976 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10977 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10978 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10979 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10980 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10981 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10982 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10983 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10984#endif
10985 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10986 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10987 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10988 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10989 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10990 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10991 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10992 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10993 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10994#ifdef WITH_TEMP_MEMORY_FILES
10995 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10996#endif
10997
10998#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10999 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
11000 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
11001#endif
11002
11003 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
11004 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
11005
11006 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
11007 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
11008
11009 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
11010 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
11011 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
11012 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
11013
11014 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
11015
11016#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
11017 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
11018#endif
11019
11020#ifdef WITH_HASH_CACHE
11021 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
11022 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
11023 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
11024 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
11025#endif
11026
11027#ifdef WITH_CRYPT_CTX_REUSE
11028 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
11029 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
11030 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
11031#endif
11032
11033 /*
11034 * MS Visual C++ CRTs.
11035 */
11036 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
11037 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
11038 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
11039 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
11040 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
11041 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
11042
11043 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
11044 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
11045 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
11046
11047 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
11048 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
11049 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
11050
11051 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
11052 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
11053 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
11054 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
11055 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
11056 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
11057 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
11058 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
11059 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
11060 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
11061 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
11062 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
11063 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
11064 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
11065 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
11066 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
11067 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
11068 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
11069 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
11070 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
11071
11072 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
11073 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
11074 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
11075 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
11076 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
11077 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
11078 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
11079 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
11080 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
11081 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
11082 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
11083 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
11084 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
11085 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
11086
11087#ifndef NDEBUG
11088 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
11089 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
11090#endif
11091};
11092/** Number of entries in g_aReplacements. */
11093KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
11094
11095
11096/**
11097 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
11098 * execution.
11099 */
11100KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
11101{
11102 /*
11103 * Kernel32.dll and friends.
11104 */
11105 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
11106 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
11107
11108 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
11109 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
11110
11111#if 0
11112 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
11113#endif
11114
11115 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
11116 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
11117 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
11118 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
11119#ifdef WITH_TEMP_MEMORY_FILES
11120 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
11121 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
11122 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
11123 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
11124 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
11125 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
11126 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
11127 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
11128 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
11129 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
11130#endif
11131 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
11132 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
11133 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
11134 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
11135 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
11136 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
11137 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
11138 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
11139 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
11140#ifdef WITH_TEMP_MEMORY_FILES
11141 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
11142#endif
11143 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
11144 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
11145 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExW },
11146#ifndef NDEBUG
11147 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_Native_GetProcAddress },
11148#endif
11149
11150#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11151 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
11152 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
11153#endif
11154
11155#ifdef WITH_HASH_CACHE
11156 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
11157 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
11158 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
11159 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
11160#endif
11161
11162 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
11163
11164#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
11165 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
11166#endif
11167
11168 /*
11169 * MS Visual C++ CRTs.
11170 */
11171 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
11172 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
11173 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
11174 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
11175 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
11176 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
11177 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
11178
11179#if 0 /* used by mspdbXXX.dll */
11180 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
11181 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
11182#endif
11183};
11184/** Number of entries in g_aSandboxNativeReplacements. */
11185KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
11186
11187
11188/**
11189 * Functions that needs replacing when queried by GetProcAddress.
11190 */
11191KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
11192{
11193 /*
11194 * Kernel32.dll and friends.
11195 */
11196 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
11197 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
11198 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
11199 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
11200};
11201/** Number of entries in g_aSandboxGetProcReplacements. */
11202KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
11203
11204
11205/**
11206 * Control handler.
11207 *
11208 * @returns TRUE if handled, FALSE if not.
11209 * @param dwCtrlType The signal.
11210 */
11211static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
11212{
11213 DWORD cbIgn;
11214 int volatile rc; /* volatile for debugging */
11215 int volatile rcPrev;
11216 const char *pszMsg;
11217 switch (dwCtrlType)
11218 {
11219 case CTRL_C_EVENT:
11220 rc = 9;
11221 pszMsg = "kWorker: Ctrl-C\r\n";
11222 break;
11223
11224 case CTRL_BREAK_EVENT:
11225 rc = 10;
11226 pszMsg = "kWorker: Ctrl-Break\r\n";
11227 break;
11228
11229 case CTRL_CLOSE_EVENT:
11230 rc = 11;
11231 pszMsg = "kWorker: console closed\r\n";
11232 break;
11233
11234 case CTRL_LOGOFF_EVENT:
11235 rc = 11;
11236 pszMsg = "kWorker: logoff event\r\n";
11237 break;
11238
11239 case CTRL_SHUTDOWN_EVENT:
11240 rc = 11;
11241 pszMsg = "kWorker: shutdown event\r\n";
11242 break;
11243
11244 default:
11245 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
11246 return TRUE;
11247 }
11248
11249 /*
11250 * Terminate the process after 5 seconds.
11251 * If we get here a second time we just terminate the process ourselves.
11252 *
11253 * Note! We do no try call exit() here as it turned out to deadlock a lot
11254 * flusing file descriptors (stderr back when we first wrote to it).
11255 */
11256 rcPrev = g_rcCtrlC;
11257 g_rcCtrlC = rc;
11258 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
11259 if (rcPrev == 0)
11260 {
11261 int i;
11262 for (i = 0; i < 10; i++)
11263 {
11264 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
11265 Sleep(500);
11266 }
11267 }
11268 TerminateProcess(GetCurrentProcess(), rc);
11269 return TRUE;
11270}
11271
11272
11273#if 0
11274/**
11275 * Resets the KWMODULE::fVisited flag for _all_ known modules.
11276 */
11277static void kwSandboxResetModuleVisited(void)
11278{
11279 PKWMODULE pMod = g_pModuleHead;
11280 while (pMod)
11281 {
11282 pMod->fVisited = K_FALSE;
11283 pMod = pMod->pNextList;
11284 }
11285}
11286
11287
11288/**
11289 * Used by kwSandboxExec to reset the state of the module tree.
11290 *
11291 * This is done recursively.
11292 *
11293 * @param pMod The root of the tree to consider.
11294 */
11295static void kwSandboxResetModuleState(PKWMODULE pMod)
11296{
11297 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
11298 if (!pMod->fNative)
11299 {
11300 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
11301 if (!pMod->fVisited) /* Avoid loops. */
11302 {
11303 KSIZE iImp;
11304 pMod->fVisited = K_TRUE;
11305 iImp = pMod->u.Manual.cImpMods;
11306 while (iImp-- > 0)
11307 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
11308 }
11309 }
11310 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11311 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
11312 {
11313 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11314 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11315 {
11316 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11317 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11318 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11319 pMod->pszPath, pszValue ? pszValue : "<null>"));
11320 }
11321 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11322 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11323 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11324 pMod->pszPath, pszValue ? pszValue : "<null>"));
11325 else
11326 {
11327 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11328 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11329 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11330 if (pszValue != NULL)
11331 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11332 else
11333 pMod->pszMsPdbSrvEndpoint = NULL;
11334 pMod->fNeedReInit = K_TRUE;
11335 }
11336 }
11337}
11338#else
11339/**
11340 * Used by kwSandboxExec to reset the state of the module tree.
11341 */
11342static void kwSandboxResetModuleState(void)
11343{
11344 PKWMODULE pMod = g_pModuleHead;
11345 while (pMod)
11346 {
11347 if (!pMod->fNative)
11348 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
11349 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11350 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
11351 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
11352 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
11353 {
11354 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11355 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11356 {
11357 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11358 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11359 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11360 pMod->pszPath, pszValue ? pszValue : "<null>"));
11361 }
11362 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11363 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11364 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11365 pMod->pszPath, pszValue ? pszValue : "<null>"));
11366 else
11367 {
11368 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11369 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11370 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11371 if (pszValue != NULL)
11372 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11373 else
11374 pMod->pszMsPdbSrvEndpoint = NULL;
11375 pMod->fNeedReInit = K_TRUE;
11376 }
11377 }
11378
11379 pMod = pMod->pNextList;
11380 }
11381}
11382#endif
11383
11384static PPEB kwSandboxGetProcessEnvironmentBlock(void)
11385{
11386#if K_ARCH == K_ARCH_X86_32
11387 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
11388#elif K_ARCH == K_ARCH_AMD64
11389 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
11390#else
11391# error "Port me!"
11392#endif
11393}
11394
11395
11396/**
11397 * Enters the given handle into the handle table.
11398 *
11399 * @returns K_TRUE on success, K_FALSE on failure.
11400 * @param pSandbox The sandbox.
11401 * @param pHandle The handle.
11402 * @param hHandle The handle value to enter it under (for the
11403 * duplicate handle API).
11404 */
11405static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
11406{
11407 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
11408 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
11409
11410 EnterCriticalSection(&g_Sandbox.HandlesLock);
11411
11412 /*
11413 * Grow handle table.
11414 */
11415 if (idxHandle >= pSandbox->cHandles)
11416 {
11417 void *pvNew;
11418 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
11419 while (cHandles <= idxHandle)
11420 cHandles *= 2;
11421 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
11422 if (!pvNew)
11423 {
11424 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11425 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
11426 return K_FALSE;
11427 }
11428 pSandbox->papHandles = (PKWHANDLE *)pvNew;
11429 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
11430 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
11431 pSandbox->cHandles = cHandles;
11432 }
11433
11434 /*
11435 * Check that the entry is unused then insert it.
11436 */
11437 kHlpAssertStmtReturn(pSandbox->papHandles[idxHandle] == NULL, LeaveCriticalSection(&g_Sandbox.HandlesLock), K_FALSE);
11438 pSandbox->papHandles[idxHandle] = pHandle;
11439 pSandbox->cActiveHandles++;
11440 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11441 return K_TRUE;
11442}
11443
11444
11445/**
11446 * Safely looks up a handle, does not get it and it must not be 'put'.
11447 *
11448 * @returns Pointer to the handle structure if found, otherwise NULL.
11449 * @param hFile The handle to resolve.
11450 */
11451static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile)
11452{
11453 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11454 EnterCriticalSection(&g_Sandbox.HandlesLock);
11455 if (idxHandle < g_Sandbox.cHandles)
11456 {
11457 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11458 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11459 return pHandle;
11460 }
11461 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11462 return NULL;
11463}
11464
11465
11466/**
11467 * Safely gets a handle, must be "put" when done with it.
11468 *
11469 * @returns Pointer to the handle structure if found, otherwise NULL.
11470 * @param hFile The handle to resolve.
11471 */
11472static PKWHANDLE kwSandboxHandleGet(HANDLE hFile)
11473{
11474 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11475 EnterCriticalSection(&g_Sandbox.HandlesLock);
11476 if (idxHandle < g_Sandbox.cHandles)
11477 {
11478 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11479 if (pHandle)
11480 {
11481 kHlpAssertMsg(pHandle->tidOwner == KU32_MAX, ("hFile=%p tidOwner=%#x\n", hFile, pHandle->tidOwner));
11482 pHandle->tidOwner = GetCurrentThreadId();
11483 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11484 return pHandle;
11485 }
11486 }
11487 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11488 return NULL;
11489}
11490
11491
11492/**
11493 * Puts a handle returned by kwSandboxHandleGet.
11494 *
11495 * @param pHandle The handle to "put".
11496 */
11497K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle)
11498{
11499 kHlpAssertMsg(pHandle->tidOwner == GetCurrentThreadId(),
11500 ("hFile tidOwner=%#x tidMe=%#x\n", pHandle->hHandle, pHandle->tidOwner, GetCurrentThreadId()));
11501 pHandle->tidOwner = KU32_MAX;
11502}
11503
11504
11505/**
11506 * Creates a correctly quoted ANSI command line string from the given argv.
11507 *
11508 * @returns Pointer to the command line.
11509 * @param cArgs Number of arguments.
11510 * @param papszArgs The argument vector.
11511 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11512 * @param pcbCmdLine Where to return the command line length,
11513 * including one terminator.
11514 */
11515static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
11516{
11517 KU32 i;
11518 KSIZE cbCmdLine;
11519 char *pszCmdLine;
11520
11521 /* Make a copy of the argument vector that we'll be quoting. */
11522 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
11523 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
11524
11525 /* Quote the arguments that need it. */
11526 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
11527
11528 /* figure out cmd line length. */
11529 cbCmdLine = 0;
11530 for (i = 0; i < cArgs; i++)
11531 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
11532 *pcbCmdLine = cbCmdLine;
11533
11534 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
11535 if (pszCmdLine)
11536 {
11537 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
11538 if (papszQuotedArgs[0] != papszArgs[0])
11539 free(papszQuotedArgs[0]);
11540
11541 for (i = 1; i < cArgs; i++)
11542 {
11543 *psz++ = ' ';
11544 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
11545 if (papszQuotedArgs[i] != papszArgs[i])
11546 free(papszQuotedArgs[i]);
11547 }
11548 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
11549
11550 *psz++ = '\0';
11551 *psz++ = '\0';
11552 }
11553
11554 return pszCmdLine;
11555}
11556
11557
11558
11559static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
11560 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11561 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11562{
11563 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11564 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11565 wchar_t *pwcPool;
11566 KSIZE cbStrings;
11567 KSIZE cwc;
11568 KSIZE cbCmdLine;
11569 KU32 i;
11570
11571 /* Simple stuff. */
11572 pSandbox->rcExitCode = 256;
11573 pSandbox->pTool = pTool;
11574 pSandbox->idMainThread = GetCurrentThreadId();
11575 pSandbox->pgmptr = (char *)pTool->pszPath;
11576 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
11577#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11578 if (pSandbox->StdOut.fIsConsole)
11579 pSandbox->StdOut.u.Con.cwcBuf = 0;
11580 else
11581 pSandbox->StdOut.u.Fully.cchBuf = 0;
11582 if (pSandbox->StdErr.fIsConsole)
11583 pSandbox->StdErr.u.Con.cwcBuf = 0;
11584 else
11585 pSandbox->StdErr.u.Fully.cchBuf = 0;
11586 pSandbox->Combined.cwcBuf = 0;
11587 pSandbox->Combined.cFlushes = 0;
11588#endif
11589 pSandbox->fNoPchCaching = fNoPchCaching;
11590 pSandbox->cArgs = cArgs;
11591 pSandbox->papszArgs = (char **)papszArgs;
11592 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
11593 if (!pSandbox->pszCmdLine)
11594 return KERR_NO_MEMORY;
11595
11596 /*
11597 * Convert command line and argv to UTF-16.
11598 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
11599 */
11600 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
11601 if (!pSandbox->papwszArgs)
11602 return KERR_NO_MEMORY;
11603 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
11604 for (i = 0; i < cArgs; i++)
11605 {
11606 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
11607 pSandbox->papwszArgs[i] = pwcPool;
11608 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11609 pwcPool++;
11610 }
11611 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11612 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11613
11614 /*
11615 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11616 */
11617 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11618 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11619 if (!pSandbox->pwszCmdLine)
11620 return KERR_NO_MEMORY;
11621 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11622
11623 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11624 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11625 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11626
11627 /*
11628 * Setup the environment.
11629 */
11630 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11631 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11632 {
11633 KU32 iDst = 0;
11634 for (i = 0; i < cEnvVars; i++)
11635 {
11636 const char *pszVar = papszEnvVars[i];
11637 KSIZE cchVar = kHlpStrLen(pszVar);
11638 const char *pszEqual;
11639 if ( cchVar > 0
11640 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11641 {
11642 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11643 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11644 if (pszCopy && pwszCopy)
11645 {
11646 pSandbox->papszEnvVars[iDst] = pszCopy;
11647 pSandbox->environ[iDst] = pszCopy;
11648 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11649 pSandbox->wenviron[iDst] = pwszCopy;
11650
11651 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11652 if ( (pszEqual - pszVar) == 4
11653 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11654 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11655 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11656 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11657 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11658 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11659
11660 iDst++;
11661 }
11662 else
11663 {
11664 kHlpFree(pszCopy);
11665 kHlpFree(pwszCopy);
11666 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11667 }
11668 }
11669 else
11670 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11671 }
11672 pSandbox->papszEnvVars[iDst] = NULL;
11673 pSandbox->environ[iDst] = NULL;
11674 pSandbox->papwszEnvVars[iDst] = NULL;
11675 pSandbox->wenviron[iDst] = NULL;
11676 }
11677 else
11678 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11679
11680 /*
11681 * Invalidate the volatile parts of cache (kBuild output directory,
11682 * temporary directory, whatever).
11683 */
11684 kFsCacheInvalidateCustomBoth(g_pFsCache);
11685
11686#ifdef WITH_HISTORY
11687 /*
11688 * Record command line in debug history.
11689 */
11690 kHlpFree(g_apszHistory[g_iHistoryNext]);
11691 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11692 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11693#endif
11694
11695 return 0;
11696}
11697
11698
11699/**
11700 * Does sandbox cleanup between jobs.
11701 *
11702 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11703 * influence the result, so that kmk can get on with things ASAP.
11704 *
11705 * @param pSandbox The sandbox.
11706 */
11707static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11708{
11709 PROCESS_MEMORY_COUNTERS MemInfo;
11710 PKWVIRTALLOC pTracker;
11711 PKWHEAP pHeap;
11712 PKWLOCALSTORAGE pLocalStorage;
11713#ifdef WITH_HASH_CACHE
11714 PKWCRYPTHASH pHash;
11715#endif
11716#ifdef WITH_TEMP_MEMORY_FILES
11717 PKWFSTEMPFILE pTempFile;
11718#endif
11719 PKWEXITCALLACK pExitCallback;
11720
11721 /*
11722 * First stuff that may cause code to run.
11723 */
11724
11725 /* Do exit callback first. */
11726 pExitCallback = g_Sandbox.pExitCallbackHead;
11727 g_Sandbox.pExitCallbackHead = NULL;
11728 while (pExitCallback)
11729 {
11730 PKWEXITCALLACK pNext = pExitCallback->pNext;
11731 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11732 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11733 __try
11734 {
11735 pExitCallback->pfnCallback();
11736 }
11737 __except (EXCEPTION_EXECUTE_HANDLER)
11738 {
11739 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11740 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11741 kHlpAssertFailed();
11742 }
11743 kHlpFree(pExitCallback);
11744 pExitCallback = pNext;
11745 }
11746
11747 /* Free left behind FlsAlloc leaks. */
11748 pLocalStorage = g_Sandbox.pFlsAllocHead;
11749 g_Sandbox.pFlsAllocHead = NULL;
11750 while (pLocalStorage)
11751 {
11752 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11753 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11754 FlsFree(pLocalStorage->idx);
11755 kHlpFree(pLocalStorage);
11756 pLocalStorage = pNext;
11757 }
11758
11759 /* Free left behind TlsAlloc leaks. */
11760 pLocalStorage = g_Sandbox.pTlsAllocHead;
11761 g_Sandbox.pTlsAllocHead = NULL;
11762 while (pLocalStorage)
11763 {
11764 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11765 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11766 TlsFree(pLocalStorage->idx);
11767 kHlpFree(pLocalStorage);
11768 pLocalStorage = pNext;
11769 }
11770
11771
11772 /*
11773 * Then free resources associated with the sandbox run.
11774 */
11775
11776 /* Open handles, except fixed handles (stdout and stderr). */
11777 EnterCriticalSection(&pSandbox->HandlesLock);
11778 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11779 {
11780 KU32 idxHandle = pSandbox->cHandles;
11781 while (idxHandle-- > 0)
11782 if (pSandbox->papHandles[idxHandle] == NULL)
11783 { /* likely */ }
11784 else
11785 {
11786 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11787 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11788 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11789 {
11790 pSandbox->papHandles[idxHandle] = NULL;
11791 pSandbox->cLeakedHandles++;
11792
11793 switch (pHandle->enmType)
11794 {
11795 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11796 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11797 idxHandle, pHandle->hHandle, pHandle->cRefs));
11798 break;
11799 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11800 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11801 idxHandle, pHandle->hHandle, pHandle->cRefs));
11802 break;
11803 case KWHANDLETYPE_OUTPUT_BUF:
11804 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11805 idxHandle, pHandle->hHandle, pHandle->cRefs));
11806 break;
11807#ifdef WITH_TEMP_MEMORY_FILES
11808 case KWHANDLETYPE_TEMP_FILE:
11809 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11810 idxHandle, pHandle->hHandle, pHandle->cRefs));
11811 pHandle->u.pTempFile->cActiveHandles--;
11812 break;
11813 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11814 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11815 idxHandle, pHandle->hHandle, pHandle->cRefs));
11816 pHandle->u.pTempFile->cActiveHandles--;
11817 break;
11818#endif
11819 default:
11820 kHlpAssertFailed();
11821 }
11822 if (--pHandle->cRefs == 0)
11823 kHlpFree(pHandle);
11824 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11825 break;
11826 }
11827 }
11828 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11829 }
11830 LeaveCriticalSection(&pSandbox->HandlesLock);
11831
11832 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11833 g_Sandbox.cMemMappings = 0;
11834
11835#ifdef WITH_TEMP_MEMORY_FILES
11836 /* The temporary files aren't externally visible, they're all in memory. */
11837 pTempFile = pSandbox->pTempFileHead;
11838 pSandbox->pTempFileHead = NULL;
11839 while (pTempFile)
11840 {
11841 PKWFSTEMPFILE pNext = pTempFile->pNext;
11842 KU32 iSeg = pTempFile->cSegs;
11843 while (iSeg-- > 0)
11844 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11845 kHlpFree(pTempFile->paSegs);
11846 pTempFile->pNext = NULL;
11847 kHlpFree(pTempFile);
11848
11849 pTempFile = pNext;
11850 }
11851#endif
11852
11853 /* Free left behind HeapCreate leaks. */
11854 pHeap = g_Sandbox.pHeapHead;
11855 g_Sandbox.pHeapHead = NULL;
11856 while (pHeap != NULL)
11857 {
11858 PKWHEAP pNext = pHeap->pNext;
11859 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11860 HeapDestroy(pHeap->hHeap);
11861 pHeap = pNext;
11862 }
11863
11864 /* Free left behind VirtualAlloc leaks. */
11865 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11866 pTracker = g_Sandbox.pVirtualAllocHead;
11867 g_Sandbox.pVirtualAllocHead = NULL;
11868 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11869 while (pTracker)
11870 {
11871 PKWVIRTALLOC pNext = pTracker->pNext;
11872 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11873
11874#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11875 if (pTracker->idxPreAllocated != KU32_MAX)
11876 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11877 else
11878#endif
11879 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11880 kHlpFree(pTracker);
11881 pTracker = pNext;
11882 }
11883
11884 /* Free the environment. */
11885 if (pSandbox->papszEnvVars)
11886 {
11887 KU32 i;
11888 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11889 kHlpFree(pSandbox->papszEnvVars[i]);
11890 pSandbox->environ[0] = NULL;
11891 pSandbox->papszEnvVars[0] = NULL;
11892
11893 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11894 kHlpFree(pSandbox->papwszEnvVars[i]);
11895 pSandbox->wenviron[0] = NULL;
11896 pSandbox->papwszEnvVars[0] = NULL;
11897 }
11898
11899#ifdef WITH_HASH_CACHE
11900 /*
11901 * Hash handles.
11902 */
11903 pHash = pSandbox->pHashHead;
11904 pSandbox->pHashHead = NULL;
11905 while (pHash)
11906 {
11907 PKWCRYPTHASH pNext = pHash->pNext;
11908 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11909 if (pHash->hFallback != KUPTR_MAX)
11910 CryptDestroyHash(pHash->hFallback);
11911 kHlpFree(pHash);
11912 pHash = pNext;
11913 }
11914#endif
11915
11916 /*
11917 * Check the memory usage. If it's getting high, trigger a respawn
11918 * after the next job.
11919 */
11920 MemInfo.WorkingSetSize = 0;
11921 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11922 {
11923 /* The first time thru, we figure out approximately when to restart
11924 based on installed RAM and CPU threads. */
11925 static KU64 s_cbMaxWorkingSet = 0;
11926 if (s_cbMaxWorkingSet != 0)
11927 { /* likely */ }
11928 else
11929 {
11930 SYSTEM_INFO SysInfo;
11931 MEMORYSTATUSEX GlobalMemInfo;
11932 const char *pszValue;
11933
11934 /* Calculate a reasonable estimate. */
11935 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11936 GetNativeSystemInfo(&SysInfo);
11937
11938 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11939 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11940 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11941#if K_ARCH_BITS >= 64
11942 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11943#else
11944 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11945#endif
11946 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11947 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11948
11949 /* User limit. */
11950 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11951 if (pszValue != NULL)
11952 {
11953 char *pszNext;
11954 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11955 if (*pszNext == '\0' || *pszNext == 'M')
11956 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11957 else if (*pszNext == 'K')
11958 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11959 else if (*pszNext == 'G')
11960 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11961 else
11962 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11963 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11964 }
11965
11966 /* Clamp it a little. */
11967 if (s_cbMaxWorkingSet < 168*1024*1024)
11968 s_cbMaxWorkingSet = 168*1024*1024;
11969#if K_ARCH_BITS < 64
11970 else
11971 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11972 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11973 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11974 : 1536*1024*1024 /* got 4GB VA */);
11975#endif
11976 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11977 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11978 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11979 }
11980
11981 /* Finally the check. */
11982 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11983 {
11984 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11985 g_fRestart = K_TRUE;
11986 }
11987 }
11988
11989 /*
11990 * The CRT has a max of 8192 handles, so we better restart after a while if
11991 * someone is leaking handles or we risk running out of descriptors.
11992 *
11993 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11994 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11995 */
11996 if (pSandbox->cLeakedHandles > 6000)
11997 {
11998 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11999 g_fRestart = K_TRUE;
12000 }
12001}
12002
12003
12004/**
12005 * Does essential cleanups and restoring, anything externally visible.
12006 *
12007 * All cleanups that aren't externally visible are postponed till after we've
12008 * informed kmk of the result, so it can be done in the dead time between jobs.
12009 *
12010 * @param pSandbox The sandbox.
12011 */
12012static void kwSandboxCleanup(PKWSANDBOX pSandbox)
12013{
12014 /*
12015 * Restore the parent command line string.
12016 */
12017 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12018 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12019 pProcParams->CommandLine = pSandbox->SavedCommandLine;
12020#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12021 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
12022 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
12023#endif
12024}
12025
12026
12027static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12028 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
12029{
12030 int rcExit = 42;
12031 int rc;
12032
12033 /*
12034 * Initialize the sandbox environment.
12035 */
12036 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
12037 if (rc == 0)
12038 {
12039 if (g_cVerbose > 2)
12040 fprintf(stderr, "kWorker: info: Executing (sandboxed): %s\n", g_Sandbox.pszCmdLine);
12041
12042 /*
12043 * Do module initialization.
12044 */
12045#if 0
12046 //kwSandboxResetModuleVisited();
12047 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
12048#else
12049 kwSandboxResetModuleState();
12050#endif
12051 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
12052 if (rc == 0)
12053 {
12054 /*
12055 * Call the main function.
12056 */
12057#if K_ARCH == K_ARCH_AMD64
12058 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
12059#elif K_ARCH == K_ARCH_X86_32
12060 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
12061#else
12062# error "Port me!"
12063#endif
12064
12065 /* Save the NT TIB first (should do that here, not in some other function). */
12066 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
12067 pSandbox->TibMainThread = *pTib;
12068
12069 /* Make the call in a guarded fashion. */
12070#if K_ARCH == K_ARCH_AMD64
12071 /* AMD64 */
12072 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
12073 __try
12074 {
12075 pSandbox->pOutXcptListHead = pTib->ExceptionList;
12076 if (setjmp(pSandbox->JmpBuf) == 0)
12077 {
12078 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
12079 pSandbox->fRunning = K_TRUE;
12080 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
12081 pSandbox->fRunning = K_FALSE;
12082 }
12083 else
12084 rcExit = pSandbox->rcExitCode;
12085 }
12086#elif K_ARCH == K_ARCH_X86_32
12087 /* x86 (see _tmainCRTStartup) */
12088 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
12089 __try
12090 {
12091 pSandbox->pOutXcptListHead = pTib->ExceptionList;
12092 if (setjmp(pSandbox->JmpBuf) == 0)
12093 {
12094 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
12095 pSandbox->fRunning = K_TRUE;
12096 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
12097 pSandbox->fRunning = K_FALSE;
12098 }
12099 else
12100 rcExit = pSandbox->rcExitCode;
12101 }
12102#endif
12103 __except (EXCEPTION_EXECUTE_HANDLER)
12104 {
12105 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
12106#ifdef WITH_HISTORY
12107 {
12108 KU32 cPrinted = 0;
12109 while (cPrinted++ < 5)
12110 {
12111 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
12112 if (g_apszHistory[idx])
12113 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
12114 }
12115 }
12116#endif
12117 rcExit = 512;
12118 }
12119 pSandbox->fRunning = K_FALSE;
12120
12121 /* Now, restore the NT TIB. */
12122 *pTib = pSandbox->TibMainThread;
12123 }
12124 else
12125 rcExit = 42 + 4;
12126
12127 /*
12128 * Flush and clean up the essential bits only, postpone whatever we
12129 * can till after we've replied to kmk.
12130 */
12131#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12132 kwSandboxConsoleFlushAll(&g_Sandbox);
12133#endif
12134 kwSandboxCleanup(&g_Sandbox);
12135 /** @todo Flush sandboxed native CRTs too. */
12136 }
12137 else
12138 rcExit = 42 + 3;
12139
12140 return rcExit;
12141}
12142
12143
12144/**
12145 * Does the post command part of a job (optional).
12146 *
12147 * @returns The exit code of the job.
12148 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12149 * @param papszPostCmdArgs The post command and its argument.
12150 */
12151static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12152{
12153 const char *pszCmd = papszPostCmdArgs[0];
12154
12155 /* Allow the kmk builtin prefix. */
12156 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
12157 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
12158 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
12159
12160 /* Command switch. */
12161 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
12162 {
12163 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
12164 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
12165 }
12166
12167 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
12168}
12169
12170
12171/**
12172 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
12173 */
12174static unsigned kwGetCurrentProcessorGroup(void)
12175{
12176 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
12177 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
12178 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
12179 if (pfnGetter)
12180 {
12181 GROUP_AFFINITY GroupAffinity;
12182 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
12183 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
12184 return GroupAffinity.Group;
12185 }
12186 return 0;
12187}
12188
12189
12190/**
12191 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
12192 */
12193static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
12194{
12195 KSIZE cchRet = 0;
12196 HANDLE hToken;
12197 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
12198 {
12199 DWORD cbRet;
12200 TOKEN_STATISTICS TokenStats;
12201 memset(&TokenStats, 0, sizeof(TokenStats));
12202 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
12203 cchRet = sprintf(pszValue, "%" KX64_PRI,
12204 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
12205 else
12206 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
12207 CloseHandle(hToken);
12208 }
12209 else
12210 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
12211 return cchRet;
12212}
12213
12214
12215/**
12216 * Look for and expand the special environment variable.
12217 *
12218 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
12219 * couldn't accuratly determine. Currently the following variables are
12220 * implemented:
12221 * - "@@PROCESSOR_GROUP@@" - The processor group number.
12222 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
12223 * - "@@PID@@" - The kWorker process ID.
12224 * - "@@@@" - Escaped "@@".
12225 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
12226 */
12227static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
12228{
12229 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
12230 KU32 i = cEnvVars;
12231 while (i-- > 0)
12232 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
12233 && papszEnvVars[i][cchSpecialEnv] == '=')
12234 {
12235 /* We will expand stuff like @@NAME@@ */
12236 const char *pszValue = papszEnvVars[i];
12237 KSIZE offDst = 0;
12238 char szTmp[1024];
12239 for (;;)
12240 {
12241 const char *pszAt = kHlpStrChr(pszValue, '@');
12242 while (pszAt && pszAt[1] != '@')
12243 pszAt = kHlpStrChr(pszAt + 1, '@');
12244 if (pszAt)
12245 {
12246 KSIZE cchSrc = pszAt - pszValue;
12247 if (offDst + cchSrc < sizeof(szTmp))
12248 {
12249 char szSrc[64];
12250
12251 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12252 offDst += cchSrc;
12253 pszValue = pszAt + 2;
12254
12255 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
12256 {
12257 pszValue += 15;
12258 if (g_iProcessGroup == -1)
12259 g_iProcessGroup = kwGetCurrentProcessorGroup();
12260 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
12261 }
12262 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
12263 {
12264 pszValue += 19;
12265 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
12266 }
12267 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
12268 {
12269 pszValue += 5;
12270 cchSrc = sprintf(szSrc, "%d", getpid());
12271 }
12272 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
12273 {
12274 pszValue += 2;
12275 szSrc[0] = '@';
12276 szSrc[1] = '@';
12277 szSrc[2] = '\0';
12278 cchSrc = 2;
12279 }
12280 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
12281 {
12282 static unsigned int s_iCounter = 0;
12283 pszValue += 15;
12284 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
12285 }
12286 else
12287 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
12288 pszValue - 2);
12289 if (offDst + cchSrc < sizeof(szTmp))
12290 {
12291 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
12292 offDst += cchSrc;
12293 continue;
12294 }
12295 }
12296 }
12297 else
12298 {
12299 KSIZE cchSrc = kHlpStrLen(pszValue);
12300 if (offDst + cchSrc < sizeof(szTmp))
12301 {
12302 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12303 offDst += cchSrc;
12304 break;
12305 }
12306 }
12307 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
12308 }
12309 szTmp[offDst] = '\0';
12310
12311 /* Return a copy of it: */
12312 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
12313 if (papszEnvVars[i])
12314 {
12315 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
12316 return 0;
12317 }
12318 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
12319 }
12320
12321 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
12322}
12323
12324
12325/**
12326 * Part 2 of the "JOB" command handler.
12327 *
12328 * @returns The exit code of the job.
12329 * @param pszExecutable The executable to execute.
12330 * @param pszCwd The current working directory of the job.
12331 * @param cArgs The number of arguments.
12332 * @param papszArgs The argument vector.
12333 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
12334 * @param cEnvVars The number of environment variables.
12335 * @param papszEnvVars The environment vector.
12336 * @param pszSpecialEnv Name of special environment variable that
12337 * requires selective expansion here.
12338 * @param fNoPchCaching Whether to disable precompiled header file
12339 * caching. Avoid trouble when creating them.
12340 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12341 * @param papszPostCmdArgs The post command and its argument.
12342 */
12343static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
12344 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12345 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
12346 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12347{
12348 int rcExit;
12349 PKWTOOL pTool;
12350 char *pszSpecialEnvFree = NULL;
12351
12352 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
12353 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
12354#ifdef KW_LOG_ENABLED
12355 {
12356 KU32 i;
12357 for (i = 0; i < cArgs; i++)
12358 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
12359 for (i = 0; i < cPostCmdArgs; i++)
12360 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
12361 }
12362#endif
12363 g_cJobs++;
12364
12365 /*
12366 * Expand pszSpecialEnv if present.
12367 */
12368 if (pszSpecialEnv && *pszSpecialEnv)
12369 {
12370 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
12371 if (!rcExit)
12372 { /* likely */ }
12373 else
12374 return rcExit;
12375 }
12376
12377 /*
12378 * Lookup the tool.
12379 */
12380 g_Sandbox.pTool = NULL; /* Avoid confusion between the SetDllDirectoryW hacks. */
12381 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
12382 if (pTool)
12383 {
12384 /*
12385 * Change the directory if we're going to execute the job inside
12386 * this process. Then invoke the tool type specific handler.
12387 */
12388 switch (pTool->enmType)
12389 {
12390 case KWTOOLTYPE_SANDBOXED:
12391 case KWTOOLTYPE_WATCOM:
12392 {
12393 /* Change dir. */
12394 KFSLOOKUPERROR enmError;
12395 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
12396 if ( pNewCurDir == g_pCurDirObj
12397 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
12398 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12399 else if (SetCurrentDirectoryA(pszCwd))
12400 {
12401 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
12402 g_pCurDirObj = pNewCurDir;
12403 }
12404 else
12405 {
12406 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
12407 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12408 rcExit = 42 + 1;
12409 break;
12410 }
12411
12412 /* Call specific handler. */
12413 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
12414 {
12415 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
12416 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
12417 cEnvVars, papszEnvVars, fNoPchCaching);
12418 }
12419 else
12420 {
12421 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
12422 rcExit = 42 + 2;
12423 }
12424 break;
12425 }
12426
12427 case KWTOOLTYPE_EXEC:
12428 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
12429 rcExit = 42 + 2;
12430 break;
12431
12432 default:
12433 kHlpAssertFailed();
12434 kwErrPrintf("Internal tool type corruption!!\n");
12435 rcExit = 42 + 2;
12436 g_fRestart = K_TRUE;
12437 break;
12438 }
12439
12440 /*
12441 * Do the post command, if present.
12442 */
12443 if (cPostCmdArgs && rcExit == 0)
12444 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
12445 }
12446 else
12447 rcExit = 42 + 1;
12448 if (pszSpecialEnvFree)
12449 {
12450 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
12451 kHlpFree(pszSpecialEnvFree);
12452 }
12453 return rcExit;
12454}
12455
12456
12457/**
12458 * Handles a "JOB" command.
12459 *
12460 * @returns The exit code of the job.
12461 * @param pszMsg Points to the "JOB" command part of the message.
12462 * @param cbMsg Number of message bytes at @a pszMsg. There are
12463 * 4 more zero bytes after the message body to
12464 * simplify parsing.
12465 */
12466static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
12467{
12468 int rcExit = 42;
12469
12470 /*
12471 * Unpack the message.
12472 */
12473 const char *pszExecutable;
12474 KSIZE cbTmp;
12475
12476 pszMsg += sizeof("JOB");
12477 cbMsg -= sizeof("JOB");
12478
12479 /* Executable name. */
12480 pszExecutable = pszMsg;
12481 cbTmp = kHlpStrLen(pszMsg) + 1;
12482 pszMsg += cbTmp;
12483 if ( cbTmp < cbMsg
12484 && cbTmp > 2)
12485 {
12486 const char *pszCwd;
12487 cbMsg -= cbTmp;
12488
12489 /* Current working directory. */
12490 pszCwd = pszMsg;
12491 cbTmp = kHlpStrLen(pszMsg) + 1;
12492 pszMsg += cbTmp;
12493 if ( cbTmp + sizeof(KU32) < cbMsg
12494 && cbTmp >= 2)
12495 {
12496 KU32 cArgs;
12497 cbMsg -= cbTmp;
12498
12499 /* Argument count. */
12500 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
12501 pszMsg += sizeof(cArgs);
12502 cbMsg -= sizeof(cArgs);
12503
12504 if (cArgs > 0 && cArgs < 4096)
12505 {
12506 /* The argument vector. */
12507 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
12508 if (papszArgs)
12509 {
12510 KU32 i;
12511 for (i = 0; i < cArgs; i++)
12512 {
12513 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
12514 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
12515 pszMsg += cbTmp;
12516 if (cbTmp < cbMsg)
12517 cbMsg -= cbTmp;
12518 else
12519 {
12520 cbMsg = 0;
12521 break;
12522 }
12523
12524 }
12525 papszArgs[cArgs] = 0;
12526
12527 /* Environment variable count. */
12528 if (cbMsg > sizeof(KU32))
12529 {
12530 KU32 cEnvVars;
12531 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
12532 pszMsg += sizeof(cEnvVars);
12533 cbMsg -= sizeof(cEnvVars);
12534
12535 if (cEnvVars >= 0 && cEnvVars < 4096)
12536 {
12537 /* The argument vector. */
12538 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
12539 if (papszEnvVars)
12540 {
12541 for (i = 0; i < cEnvVars; i++)
12542 {
12543 papszEnvVars[i] = pszMsg;
12544 cbTmp = kHlpStrLen(pszMsg) + 1;
12545 pszMsg += cbTmp;
12546 if (cbTmp < cbMsg)
12547 cbMsg -= cbTmp;
12548 else
12549 {
12550 cbMsg = 0;
12551 break;
12552 }
12553 }
12554 papszEnvVars[cEnvVars] = 0;
12555
12556 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
12557 if (cbMsg >= sizeof(KU8) * 2)
12558 {
12559 KBOOL fWatcomBrainDamange = *pszMsg++;
12560 KBOOL fNoPchCaching = *pszMsg++;
12561 cbMsg -= 2;
12562
12563 /* Name of special enviornment variable requiring selective expansion. */
12564 if (cbMsg >= 1)
12565 {
12566 const char *pszSpecialEnv = pszMsg;
12567 cbTmp = kHlpStrLen(pszMsg);
12568 pszMsg += cbTmp + 1;
12569 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
12570
12571 /* Post command argument count (can be zero). */
12572 if (cbMsg >= sizeof(KU32))
12573 {
12574 KU32 cPostCmdArgs;
12575 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
12576 pszMsg += sizeof(cPostCmdArgs);
12577 cbMsg -= sizeof(cPostCmdArgs);
12578
12579 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
12580 {
12581 char const *apszPostCmdArgs[32+1];
12582 for (i = 0; i < cPostCmdArgs; i++)
12583 {
12584 apszPostCmdArgs[i] = pszMsg;
12585 cbTmp = kHlpStrLen(pszMsg) + 1;
12586 pszMsg += cbTmp;
12587 if ( cbTmp < cbMsg
12588 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
12589 cbMsg -= cbTmp;
12590 else
12591 {
12592 cbMsg = KSIZE_MAX;
12593 break;
12594 }
12595 }
12596 if (cbMsg == 0)
12597 {
12598 apszPostCmdArgs[cPostCmdArgs] = NULL;
12599
12600 /*
12601 * The next step.
12602 */
12603 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
12604 cArgs, papszArgs, fWatcomBrainDamange,
12605 cEnvVars, papszEnvVars, pszSpecialEnv,
12606 fNoPchCaching,
12607 cPostCmdArgs, apszPostCmdArgs);
12608 }
12609 else if (cbMsg == KSIZE_MAX)
12610 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
12611 else
12612 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
12613 }
12614 else
12615 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
12616 }
12617 else
12618 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
12619 }
12620 else
12621 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12622 }
12623 else
12624 kwErrPrintf("Detected bogus message unpacking flags!\n");
12625 kHlpFree((void *)papszEnvVars);
12626 }
12627 else
12628 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12629 }
12630 else
12631 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12632 }
12633 else
12634 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12635 kHlpFree((void *)papszArgs);
12636 }
12637 else
12638 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12639 }
12640 else
12641 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12642 }
12643 else
12644 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12645 }
12646 else
12647 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12648 return rcExit;
12649}
12650
12651
12652/**
12653 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12654 *
12655 * @retval 0 on success.
12656 * @retval -1 on error (fully bitched).
12657 *
12658 * @param hPipe The pipe handle.
12659 * @param pvBuf The buffer to write out out.
12660 * @param cbToWrite The number of bytes to write.
12661 */
12662static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12663{
12664 KU8 const *pbBuf = (KU8 const *)pvBuf;
12665 KU32 cbLeft = cbToWrite;
12666 while (g_rcCtrlC == 0)
12667 {
12668 DWORD cbActuallyWritten = 0;
12669 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12670 {
12671 cbLeft -= cbActuallyWritten;
12672 if (!cbLeft)
12673 return 0;
12674 pbBuf += cbActuallyWritten;
12675 }
12676 else
12677 {
12678 DWORD dwErr = GetLastError();
12679 if (cbLeft == cbToWrite)
12680 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12681 else
12682 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12683 return -1;
12684 }
12685 }
12686 return -1;
12687}
12688
12689
12690/**
12691 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12692 *
12693 * @retval 0 on success.
12694 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12695 * @retval -1 on error (fully bitched).
12696 * @param hPipe The pipe handle.
12697 * @param pvBuf The buffer to read into.
12698 * @param cbToRead The number of bytes to read.
12699 * @param fShutdownOkay Whether connection shutdown while reading the
12700 * first byte is okay or not.
12701 */
12702static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12703{
12704 KU8 *pbBuf = (KU8 *)pvBuf;
12705 KU32 cbLeft = cbToRead;
12706 while (g_rcCtrlC == 0)
12707 {
12708 DWORD cbActuallyRead = 0;
12709 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12710 {
12711 cbLeft -= cbActuallyRead;
12712 if (!cbLeft)
12713 return 0;
12714 pbBuf += cbActuallyRead;
12715 }
12716 else
12717 {
12718 DWORD dwErr = GetLastError();
12719 if (cbLeft == cbToRead)
12720 {
12721 if ( fMayShutdown
12722 && dwErr == ERROR_BROKEN_PIPE)
12723 return 1;
12724 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12725 }
12726 else
12727 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12728 return -1;
12729 }
12730 }
12731 return -1;
12732}
12733
12734
12735/**
12736 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12737 *
12738 * @returns pszBuf
12739 * @param pszBuf The buffer (sufficiently large).
12740 * @param uValue The value.
12741 */
12742static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12743{
12744 char szTmp[64];
12745 char *psz = &szTmp[63];
12746 int cch = 4;
12747
12748 *psz-- = '\0';
12749 do
12750 {
12751 if (--cch == 0)
12752 {
12753 *psz-- = ' ';
12754 cch = 3;
12755 }
12756 *psz-- = (uValue % 10) + '0';
12757 uValue /= 10;
12758 } while (uValue != 0);
12759
12760 return strcpy(pszBuf, psz + 1);
12761}
12762
12763
12764/**
12765 * Prints statistics.
12766 */
12767static void kwPrintStats(void)
12768{
12769 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12770 MEMORYSTATUSEX MemStatus;
12771 IO_COUNTERS IoCounters;
12772 DWORD cHandles;
12773 KSIZE cMisses;
12774 char szBuf[16*1024];
12775 int off = 0;
12776 char szPrf[24];
12777 char sz1[64];
12778 char sz2[64];
12779 char sz3[64];
12780 char sz4[64];
12781
12782 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12783
12784 szBuf[off++] = '\n';
12785
12786 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12787 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12788 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12789 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12790 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12791
12792 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12793 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12794
12795 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12796 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12797 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12798
12799 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12800 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12801 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12802
12803 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12804 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12805 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12806 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12807 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12808 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12809 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12810
12811 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12812 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12813 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12814 kwFmtU64(sz1, g_pFsCache->cObjects),
12815 kwFmtU64(sz2, g_pFsCache->cbObjects),
12816 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12817 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12818 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12819 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12820 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12821 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12822#ifdef KFSCACHE_CFG_UTF16
12823 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12824 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12825 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12826 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12827 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12828#endif
12829 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12830 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12831 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12832 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12833 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12834
12835 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12836 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12837 kwFmtU64(sz1, g_pFsCache->cLookups),
12838 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12839 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12840 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12841 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12842 kwFmtU64(sz4, cMisses),
12843 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12844
12845 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12846 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12847 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12848 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12849 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12850 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12851 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12852 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12853
12854#ifdef WITH_HASH_CACHE
12855 off += sprintf(&szBuf[off], "%s %14s hashes calculated, %s cache hits (%u%%), %s fallbacks, %s partial\n", szPrf,
12856 kwFmtU64(sz1, g_cHashes),
12857 kwFmtU64(sz2, g_cHashesCached),
12858 (unsigned)(g_cHashesCached * 100 / K_MAX(g_cHashes, 1)),
12859 kwFmtU64(sz3, g_cHashesFallbacks),
12860 kwFmtU64(sz4, g_cHashesPartial));
12861 off += sprintf(&szBuf[off], "%s %14s MD5: %s SHA-1: %s SHA-256: %s SHA-512: %s\n", szPrf, "", kwFmtU64(sz1, g_cHashesMd5),
12862 kwFmtU64(sz2, g_cHashesSha1), kwFmtU64(sz3, g_cHashesSha256), kwFmtU64(sz4, g_cHashesSha512));
12863#endif
12864
12865 /*
12866 * Process & Memory details.
12867 */
12868 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12869 cHandles = 0;
12870 MemInfo.cb = sizeof(MemInfo);
12871 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12872 memset(&MemInfo, 0, sizeof(MemInfo));
12873 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12874 kwFmtU64(sz1, cHandles),
12875 kwFmtU64(sz2, MemInfo.PageFaultCount),
12876 kwFmtU64(sz3, MemInfo.PagefileUsage),
12877 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12878 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12879 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12880 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12881 kwFmtU64(sz3, MemInfo.PrivateUsage));
12882 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12883 szPrf,
12884 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12885 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12886 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12887 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12888
12889 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12890 memset(&IoCounters, 0, sizeof(IoCounters));
12891 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12892 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12893 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12894 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12895 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12896 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12897 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12898 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12899 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12900
12901 MemStatus.dwLength = sizeof(MemStatus);
12902 if (!GlobalMemoryStatusEx(&MemStatus))
12903 memset(&MemStatus, 0, sizeof(MemStatus));
12904 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12905 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12906 MemStatus.ullAvailVirtual);
12907 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12908
12909 maybe_con_fwrite(szBuf, off, 1, stdout);
12910 fflush(stdout);
12911}
12912
12913
12914/**
12915 * Handles what comes after --test.
12916 *
12917 * @returns Exit code.
12918 * @param argc Number of arguments after --test.
12919 * @param argv Arguments after --test.
12920 */
12921static int kwTestRun(int argc, char **argv)
12922{
12923 int i;
12924 int j;
12925 int rcExit;
12926 int cRepeats;
12927 char szCwd[MAX_PATH];
12928 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12929 KU32 cEnvVars;
12930 char **papszEnvVars;
12931 const char *pszSpecialEnv = "";
12932 const char *pszSpecialEnvFull = NULL;
12933 KBOOL fWatcomBrainDamange = K_FALSE;
12934 KBOOL fNoPchCaching = K_FALSE;
12935
12936 /*
12937 * Parse arguments.
12938 */
12939 /* Repeat count. */
12940 i = 0;
12941 if (i >= argc)
12942 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12943 if (strcmp(argv[i], "--") != 0)
12944 {
12945 cRepeats = atoi(argv[i]);
12946 if (cRepeats <= 0)
12947 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12948 i++;
12949
12950 /* Optional directory change. */
12951 if ( i < argc
12952 && ( strcmp(argv[i], "--chdir") == 0
12953 || strcmp(argv[i], "-C") == 0 ) )
12954 {
12955 i++;
12956 if (i >= argc)
12957 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12958 pszCwd = argv[i++];
12959 }
12960
12961 /* Optional watcom flag directory change. */
12962 if ( i < argc
12963 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12964 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12965 {
12966 fWatcomBrainDamange = K_TRUE;
12967 i++;
12968 }
12969
12970 /* Optional watcom flag directory change. */
12971 if ( i < argc
12972 && strcmp(argv[i], "--no-pch-caching") == 0)
12973 {
12974 fNoPchCaching = K_TRUE;
12975 i++;
12976 }
12977
12978 /* Optional directory change. */
12979 if ( i < argc
12980 && ( strcmp(argv[i], "--set-special") == 0
12981 || strcmp(argv[i], "-s") == 0 ) )
12982 {
12983 i++;
12984 if (i >= argc)
12985 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12986 pszSpecialEnvFull = argv[i++];
12987 putenv(pszSpecialEnvFull);
12988 pszSpecialEnv = strdup(pszSpecialEnvFull);
12989 *strchr(pszSpecialEnv, '=') = '\0';
12990 }
12991
12992 /* Trigger breakpoint */
12993 if ( i < argc
12994 && strcmp(argv[i], "--breakpoint") == 0)
12995 {
12996 __debugbreak();
12997 i++;
12998 }
12999
13000 /* Check for '--'. */
13001 if (i >= argc)
13002 return kwErrPrintfRc(2, "Missing '--'\n");
13003 if (strcmp(argv[i], "--") != 0)
13004 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
13005 i++;
13006 }
13007 else
13008 {
13009 cRepeats = 1;
13010 i++;
13011 }
13012 if (i >= argc)
13013 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
13014
13015 /*
13016 * Duplicate the environment.
13017 */
13018 cEnvVars = 0;
13019 while (environ[cEnvVars] != NULL)
13020 cEnvVars++;
13021 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
13022
13023 /*
13024 * Do the job.
13025 */
13026 rcExit = 0;
13027 for (j = 0; j < cRepeats; j++)
13028 {
13029 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
13030 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
13031 argc - i, &argv[i], fWatcomBrainDamange,
13032 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
13033 0, NULL);
13034 KW_LOG(("rcExit=%d\n", rcExit));
13035 kwSandboxCleanupLate(&g_Sandbox);
13036 }
13037
13038 if (getenv("KWORKER_STATS") != NULL)
13039 kwPrintStats();
13040
13041# ifdef WITH_LOG_FILE
13042 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13043 CloseHandle(g_hLogFile);
13044# endif
13045 return rcExit;
13046}
13047
13048
13049/**
13050 * Reads @a pszFile into memory and chops it up into an argument vector.
13051 *
13052 * @returns Pointer to the argument vector on success, NULL on failure.
13053 * @param pszFile The file to load.
13054 * @param pcArgs Where to return the number of arguments.
13055 * @param ppszFileContent Where to return the allocation.
13056 */
13057static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
13058{
13059 char **papszArgs = NULL;
13060 FILE *pFile = fopen(pszFile, "r");
13061 if (pFile)
13062 {
13063 long cbFile;
13064 if ( fseek(pFile, 0, SEEK_END) == 0
13065 && (cbFile = ftell(pFile)) >= 0
13066 && fseek(pFile, 0, SEEK_SET) == 0)
13067 {
13068 char *pszFile = kHlpAllocZ(cbFile + 3);
13069 if (pszFile)
13070 {
13071 size_t cbRead = fread(pszFile, 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 pszFile[cbRead] = '\0';
13081 pszFile[cbRead + 1] = '\0';
13082 pszFile[cbRead + 2] = '\0';
13083
13084 while ((ch = pszFile[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 (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
13098 {
13099 off += 2;
13100 continue;
13101 }
13102 /* fall thru */
13103 default:
13104 pszArg = &pszFile[off];
13105 do
13106 ch = pszFile[++off];
13107 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
13108 pszFile[off++] = '\0';
13109 break;
13110
13111 case '\'':
13112 pszArg = &pszFile[++off];
13113 while ((ch = pszFile[off]) != '\0' && ch != '\'')
13114 off++;
13115 pszFile[off++] = '\0';
13116 break;
13117
13118 case '\"': /** @todo escape sequences */
13119 pszArg = &pszFile[++off];
13120 while ((ch = pszFile[off]) != '\0' && ch != '"')
13121 off++;
13122 pszFile[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 i;
13252 pCur->cEnvVars = 0;
13253 for (i = 0; environ[i] && rc == 0; i++)
13254 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* 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 KSIZE cbMsgBuf = 0;
13490 KU8 *pbMsgBuf = NULL;
13491 int i;
13492 HANDLE hPipe = INVALID_HANDLE_VALUE;
13493 const char *pszTmp;
13494 KFSLOOKUPERROR enmIgnored;
13495 DWORD dwType;
13496#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
13497 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
13498 kwSandboxVecXcptEmulateChained);
13499#endif
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
13506
13507#ifdef WITH_FIXED_VIRTUAL_ALLOCS
13508 /*
13509 * Reserve memory for cl.exe
13510 */
13511 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
13512 {
13513 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
13514 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
13515 g_aFixedVirtualAllocs[i].cbFixed,
13516 MEM_RESERVE, PAGE_READWRITE);
13517 if ( !g_aFixedVirtualAllocs[i].pvReserved
13518 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
13519 {
13520 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
13521 GetLastError());
13522 if (g_aFixedVirtualAllocs[i].pvReserved)
13523 {
13524 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
13525 g_aFixedVirtualAllocs[i].pvReserved = NULL;
13526 }
13527 }
13528 }
13529#endif
13530
13531 /*
13532 * Register our Control-C and Control-Break handlers.
13533 */
13534 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
13535 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
13536
13537 /*
13538 * Create the cache and mark the temporary directory as using the custom revision.
13539 */
13540 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
13541 if (!g_pFsCache)
13542 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
13543
13544 pszTmp = getenv("TEMP");
13545 if (pszTmp && *pszTmp != '\0')
13546 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13547 pszTmp = getenv("TMP");
13548 if (pszTmp && *pszTmp != '\0')
13549 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13550 pszTmp = getenv("TMPDIR");
13551 if (pszTmp && *pszTmp != '\0')
13552 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13553
13554 /*
13555 * Make g_abDefLdBuf executable.
13556 */
13557 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
13558 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
13559 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
13560 InitializeCriticalSection(&g_Sandbox.HandlesLock);
13561 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
13562
13563#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13564 /*
13565 * Get and duplicate the console handles.
13566 */
13567 /* Standard output. */
13568 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
13569 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
13570 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13571 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
13572 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
13573 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
13574 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13575 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13576 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
13577 g_Sandbox.HandleStdOut.cRefs = 0x10001;
13578 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
13579 g_Sandbox.HandleStdOut.tidOwner = KU32_MAX;
13580 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
13581 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
13582 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
13583 {
13584 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
13585 g_Sandbox.cFixedHandles++;
13586 else
13587 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
13588 }
13589 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13590 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
13591
13592 /* Standard error. */
13593 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
13594 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
13595 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13596 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
13597 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
13598 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
13599 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13600 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13601 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
13602 g_Sandbox.HandleStdErr.cRefs = 0x10001;
13603 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
13604 g_Sandbox.HandleStdErr.tidOwner = KU32_MAX;
13605 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
13606 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
13607 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
13608 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
13609 {
13610 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
13611 g_Sandbox.cFixedHandles++;
13612 else
13613 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
13614 }
13615 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13616 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
13617
13618 /* Combined console buffer. */
13619 if (g_Sandbox.StdErr.fIsConsole)
13620 {
13621 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
13622 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13623 }
13624 else if (g_Sandbox.StdOut.fIsConsole)
13625 {
13626 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
13627 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13628 }
13629 else
13630 {
13631 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
13632 g_Sandbox.Combined.uCodepage = CP_ACP;
13633 }
13634 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
13635#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
13636
13637
13638 /*
13639 * Parse arguments.
13640 */
13641 for (i = 1; i < argc; i++)
13642 {
13643 if (strcmp(argv[i], "--pipe") == 0)
13644 {
13645 i++;
13646 if (i < argc)
13647 {
13648 char *pszEnd = NULL;
13649 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13650 if ( *argv[i]
13651 && pszEnd != NULL
13652 && *pszEnd == '\0'
13653 && u64Value != 0
13654 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13655 && (uintptr_t)u64Value == u64Value)
13656 hPipe = (HANDLE)(uintptr_t)u64Value;
13657 else
13658 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13659 }
13660 else
13661 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13662 }
13663 else if (strcmp(argv[i], "--volatile") == 0)
13664 {
13665 i++;
13666 if (i < argc)
13667 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13668 else
13669 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13670 }
13671 else if (strcmp(argv[i], "--test") == 0)
13672 return kwTestRun(argc - i - 1, &argv[i + 1]);
13673 else if (strcmp(argv[i], "--full-test") == 0)
13674 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13675 else if (strcmp(argv[i], "--priority") == 0)
13676 {
13677 i++;
13678 if (i < argc)
13679 {
13680 char *pszEnd = NULL;
13681 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13682 if ( *argv[i]
13683 && pszEnd != NULL
13684 && *pszEnd == '\0'
13685 && uValue >= 1
13686 && uValue <= 5)
13687 {
13688 DWORD dwClass;
13689 int dwPriority;
13690 switch (uValue)
13691 {
13692 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13693 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13694 default:
13695 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13696 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13697 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13698 }
13699 SetPriorityClass(GetCurrentProcess(), dwClass);
13700 if (dwPriority != INT_MAX)
13701 SetThreadPriority(GetCurrentThread(), dwPriority);
13702 }
13703 else
13704 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13705 }
13706 else
13707 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13708 }
13709 else if (strcmp(argv[i], "--group") == 0)
13710 {
13711 i++;
13712 if (i < argc)
13713 {
13714 char *pszEnd = NULL;
13715 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13716 if ( *argv[i]
13717 && pszEnd != NULL
13718 && *pszEnd == '\0'
13719 && uValue == (WORD)uValue)
13720 kwSetProcessorGroup(uValue);
13721 else
13722 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13723 }
13724 else
13725 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13726 }
13727 else if ( strcmp(argv[i], "--verbose") == 0
13728 || strcmp(argv[i], "-v") == 0)
13729 g_cVerbose++;
13730 else if ( strcmp(argv[i], "--help") == 0
13731 || strcmp(argv[i], "-h") == 0
13732 || strcmp(argv[i], "-?") == 0)
13733 {
13734 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13735 "usage: kWorker <--help|-h>\n"
13736 "usage: kWorker <--version|-V>\n"
13737 "usage: kWorker [--volatile dir] --full-test kSubmit ...\n"
13738 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13739 "\n"
13740 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13741 return 0;
13742 }
13743 else if ( strcmp(argv[i], "--version") == 0
13744 || strcmp(argv[i], "-V") == 0)
13745 return kbuild_version(argv[0]);
13746 else
13747 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13748 }
13749
13750 /*
13751 * If no --pipe argument, then assume its standard input.
13752 * We need to carefully replace the CRT stdin with a handle to "nul".
13753 */
13754 if (hPipe == INVALID_HANDLE_VALUE)
13755 {
13756 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13757 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13758 {
13759 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13760 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13761 {
13762 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13763 if (fdNul >= 0)
13764 {
13765 if (_dup2(fdNul, 0) >= 0)
13766 {
13767 close(fdNul);
13768 hPipe = hDuplicate;
13769 }
13770 else
13771 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13772 }
13773 else
13774 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13775 }
13776 else
13777 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13778 }
13779 else
13780 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13781 GetFileType(hPipe), GetLastError());
13782 }
13783 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13784 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13785 GetFileType(hPipe), GetLastError());
13786 g_hPipe = hPipe;
13787
13788 /*
13789 * Serve the pipe.
13790 */
13791 for (;;)
13792 {
13793 KU32 cbMsg = 0;
13794 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13795 if (rc == 0)
13796 {
13797 /* Make sure the message length is within sane bounds. */
13798 if ( cbMsg > 4
13799 && cbMsg <= 256*1024*1024)
13800 {
13801 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13802 if (cbMsg + 4 <= cbMsgBuf)
13803 { /* likely */ }
13804 else
13805 {
13806 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13807 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13808 if (!pbMsgBuf)
13809 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13810 }
13811
13812 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13813 *(KU32 *)pbMsgBuf = cbMsg;
13814 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13815 if (rc == 0)
13816 {
13817 const char *psz;
13818
13819 pbMsgBuf[cbMsg] = '\0';
13820 pbMsgBuf[cbMsg + 1] = '\0';
13821 pbMsgBuf[cbMsg + 2] = '\0';
13822 pbMsgBuf[cbMsg + 3] = '\0';
13823
13824 /* The first string after the header is the command. */
13825 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13826 if ( strcmp(psz, "JOB") == 0
13827 && g_rcCtrlC == 0)
13828 {
13829 struct
13830 {
13831 KI32 rcExitCode;
13832 KU8 bExiting;
13833 KU8 abZero[3];
13834 } Reply;
13835 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13836 Reply.bExiting = g_fRestart;
13837 Reply.abZero[0] = 0;
13838 Reply.abZero[1] = 0;
13839 Reply.abZero[2] = 0;
13840 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13841 if ( rc == 0
13842 && !g_fRestart)
13843 {
13844 kwSandboxCleanupLate(&g_Sandbox);
13845 if (g_rcCtrlC == 0)
13846 continue;
13847 }
13848 }
13849 else
13850 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13851 }
13852 }
13853 else
13854 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13855 }
13856
13857 /*
13858 * If we're exitting because we're restarting, we need to delay till
13859 * kmk/kSubmit has read the result. Windows documentation says it
13860 * immediately discards pipe buffers once the pipe is broken by the
13861 * server (us). So, We flush the buffer and queues a 1 byte read
13862 * waiting for kSubmit to close the pipe when it receives the
13863 * bExiting = K_TRUE result.
13864 */
13865 if (g_fRestart)
13866 {
13867 DWORD cbIgnored = 1;
13868 KU8 b;
13869 FlushFileBuffers(hPipe);
13870 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13871 }
13872
13873 CloseHandle(hPipe);
13874#ifdef WITH_LOG_FILE
13875 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13876 CloseHandle(g_hLogFile);
13877#endif
13878 if (getenv("KWORKER_STATS") != NULL)
13879 kwPrintStats();
13880 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13881 }
13882}
13883
13884
13885/** @page pg_kWorker kSubmit / kWorker
13886 *
13887 * @section sec_kWorker_Motivation Motivation / Inspiration
13888 *
13889 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13890 * builds on machines "infested" by Anti Virus protection and disk encryption
13891 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13892 * got "infected".
13893 *
13894 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13895 * mainly a bunch of tiny assembly and C files being compiler a million times.
13896 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13897 * own toolchain from within the same process, saving a lot of process creation
13898 * and teardown overhead.
13899 *
13900 *
13901 * @section sec_kWorker_kSubmit About kSubmit
13902 *
13903 * When wanting to execute a job in a kWorker instance, it must be submitted
13904 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13905 * built into kmk and does not exist as an external program. The reason for
13906 * this is that it keep track of the kWorker instances.
13907 *
13908 * The kSubmit command has the --32-bit and --64-bit options for selecting
13909 * between 32-bit and 64-bit worker instance. We generally assume the user of
13910 * the command knows which bit count the executable has, so kSubmit is spared
13911 * the extra work of finding out.
13912 *
13913 * The kSubmit command shares a environment and current directory manipulation
13914 * with the kRedirect command, but not the file redirection. So long no file
13915 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13916 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13917 * and/or current directory changes to work.
13918 *
13919 * Unlike the kRedirect command, the kSubmit command can also specify an
13920 * internall post command to be executed after the main command succeeds.
13921 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13922 * information from Microsoft COFF object files and Watcom OMF object files and
13923 * is scheduled to replace kDepIDB.
13924 *
13925 *
13926 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13927 *
13928 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13929 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13930 * it. When the job is completed, kWorker writes a short reply with the exit
13931 * code and an internal status indicating whether it is going to restart.
13932 *
13933 * The kWorker intance will reply to kSubmit before completing all the internal
13934 * cleanup work, so as not to delay the next job execution unnecessarily. This
13935 * includes checking its own memory consumption and checking whether it needs
13936 * restarting. So, a decision to restart unfortunately have to wait till after
13937 * the next job has completed. This is a little bit unfortunate if the next job
13938 * requires a lot of memory and kWorker has already leaked/used a lot.
13939 *
13940 *
13941 * @section sec_kWorker_How_Works How kWorker Works
13942 *
13943 * kWorker will load the executable specified by kSubmit into memory and call
13944 * it's entrypoint in a lightly sandbox'ed environment.
13945 *
13946 *
13947 * @subsection ssec_kWorker_Loaing Image loading
13948 *
13949 * kWorker will manually load all the executable images into memory, fix them
13950 * up, and make a copy of the virgin image so it can be restored using memcpy
13951 * the next time it is used.
13952 *
13953 * Imported functions are monitored and replacements used for a few of them.
13954 * These replacements are serve the following purposes:
13955 * - Provide a different command line.
13956 * - Provide a different environment.
13957 * - Intercept process termination.
13958 * - Intercept thread creation (only linker is allowed to create threads).
13959 * - Intercept file reading for caching (header files, ++) as file system
13960 * access is made even slower by anti-virus software.
13961 * - Intercept crypto hash APIs to cache MD5 digests of header files
13962 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13963 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13964 * in memory as writing files grows expensive with encryption and
13965 * anti-virus software active.
13966 * - Intercept some file system queries to use the kFsCache instead of
13967 * going to the kernel and slowly worm thru the AV filter driver.
13968 * - Intercept standard output/error and console writes to aggressivly
13969 * buffer the output. The MS CRT does not buffer either when it goes to
13970 * the console, resulting in terrible performance and mixing up output
13971 * with other compile jobs.
13972 * This also allows us to filter out the annoying source file announcements
13973 * by cl.exe.
13974 * - Intercept VirtualAlloc and VirtualFree to prevent
13975 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13976 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13977 * the callbacks run after each job.
13978 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13979 * executables and tools using custom heaps (like the microsoft linker).
13980 * [exectuable images only]
13981 * - Intercept atexit and _onexit registration to be able run them after
13982 * each job instead of crashing as kWorker exits. This also helps avoid
13983 * some leaks. [executable image only]
13984 *
13985 * DLLs falls into two categories, system DLLs which we always load using the
13986 * native loader, and tool DLLs which can be handled like the executable or
13987 * optionally using the native loader. We maintain a hardcoded white listing of
13988 * tool DLLs we trust to load using the native loader.
13989 *
13990 * Imports of natively loaded DLLs are processed too, but we only replace a
13991 * subset of the functions compared to natively loaded excutable and DLL images.
13992 *
13993 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13994 * This is to speed up job execution.
13995 *
13996 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13997 * for each job run, but so far this hasn't been necessary.
13998 *
13999 *
14000 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
14001 *
14002 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
14003 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
14004 * intermediate representation between the first (c1/c1xx.dll) and second pass
14005 * (c2.dll).
14006 *
14007 * kWorker helps the compiler as best as it can. Given a little knowledge about
14008 * stable and volatile file system areas, it can do a lot of caching that a
14009 * normal compiler driver cannot easily do when given a single file.
14010 *
14011 *
14012 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
14013 *
14014 * The preprocessor part will open and process header files exactly as they are
14015 * encountered in the source files. If string.h is included by the main source
14016 * and five other header files, it will be searched for (include path), opened,
14017 * read, MD5-summed, and pre-processed six times. The last five times is just a
14018 * waste of time because of the guards or \#pragma once. A smart compiler would
14019 * make a little extra effort and realize this.
14020 *
14021 * kWorker will cache help the preprocessor by remembering places where the
14022 * header was not found with help of kFsCache, and cache the file in memory when
14023 * found. The first part is taken care of by intercepting GetFileAttributesW,
14024 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
14025 * cached, the file is kept open and the CreateFileW call returns a duplicate of
14026 * that handle. An internal handle table is used by ReadFile and CloseFile to
14027 * keep track of intercepted handles (also used for temporary file, temporary
14028 * file mappings, console buffering, and standard out/err buffering).
14029 *
14030 * PS. The header search optimization also comes in handy when cl.exe goes on
14031 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
14032 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
14033 * optionally compile the three pass DLLs as executables during development
14034 * and problem analysis.
14035 *
14036 *
14037 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
14038 *
14039 * The issues of the temporary files is pretty severe on the Dell machine used
14040 * for benchmarking with full AV and encryption. The synthetic benchmark
14041 * improved by 30% when kWorker implemented measures to keep them entirely in
14042 * memory.
14043 *
14044 * kWorker implement these by recognizing the filename pattern in CreateFileW
14045 * and creating/opening the given file as needed. The handle returned is a
14046 * duplicate of the current process, thus giving us a good chance of catching
14047 * API calls we're not intercepting.
14048 *
14049 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
14050 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
14051 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
14052 * UnmapViewOfFile.
14053 *
14054 *
14055 * @section sec_kWorker_Numbers Some measurements.
14056 *
14057 * - r2881 building src/VBox/Runtime:
14058 * - without: 2m01.016388s = 120.016388 s
14059 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
14060 * - r2884 building vbox/debug (r110512):
14061 * - without: 11m14.446609s = 674.446609 s
14062 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
14063 * - r2896 building vbox/debug (r110577):
14064 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
14065 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
14066 * MS Defender as AV):
14067 * - without: 10m24.990389s = 624.990389s
14068 * - with: 08m04.738184s = 484.738184s
14069 * - delta: 624.99s - 484.74s = 140.25s
14070 * - saved: 140.25/624.99 = 22% faster
14071 *
14072 *
14073 * @subsection subsec_kWorker_Early_Numbers Early Experiments
14074 *
14075 * These are some early experiments doing 1024 compilations of
14076 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
14077 * main function:
14078 *
14079 * Skylake (W10/amd64, only stdandard MS defender):
14080 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
14081 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
14082 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
14083 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
14084 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
14085 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
14086 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
14087 *
14088 * Dell (W7/amd64, infected by mcafee):
14089 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
14090 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
14091 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
14092 *
14093 * The command line:
14094 * @code{.cpp}
14095 "\"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"
14096 * @endcode
14097 */
14098
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