VirtualBox

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

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

kWorker: The missing SetDllDirectoryW hack (for the tool/exe). Better error reporting should an .exe import fail to load. Shut up weird assertion on changing back an .idata page from readwrite to readonly, failed with NOACCESS on a 17763 build box.

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

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