VirtualBox

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

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

kWorker: Set g_Sandbox.pTool to NULL when creating (looking up) the tool for the next job, otherwise the innermost SetDllDirectoryW hack will pick up the directory of the previous tool and we won't necessarily find all the DLLs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 532.8 KB
Line 
1/* $Id: kWorker.c 3378 2020-06-11 21:26:51Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <[email protected]>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33//#undef NDEBUG
34//#define K_STRICT 1
35//#define KW_LOG_ENABLED
36
37#define PSAPI_VERSION 1
38#include <k/kHlp.h>
39#include <k/kLdr.h>
40
41#include <stdio.h>
42#include <intrin.h>
43#include <setjmp.h>
44#include <ctype.h>
45#include <errno.h>
46#include <process.h>
47
48#include "nt/ntstat.h"
49#include "kbuild_version.h"
50
51#include "nt/ntstuff.h"
52#include "nt/nthlp.h"
53#include <psapi.h>
54
55#include "nt/kFsCache.h"
56#include "nt_fullpath.h"
57#include "win_get_processor_group_active_mask.h"
58#include "quote_argv.h"
59#include "md5.h"
60#include "console.h"
61
62#include "../kmk/kmkbuiltin.h"
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68/** @def WITH_TEMP_MEMORY_FILES
69 * Enables temporary memory files for cl.exe. */
70#define WITH_TEMP_MEMORY_FILES
71
72/** @def WITH_HASH_CACHE
73 * Enables caching of MD5, SHA-1, SHA-256 and SHA-512 hashes for cl.exe.
74 * This prevents wasting time on rehashing common headers each time
75 * they are included. */
76#define WITH_HASH_CACHE
77
78/** @def WITH_CRYPT_CTX_REUSE
79 * Enables reusing crypt contexts. The Visual C++ compiler always creates a
80 * context which is only used for MD5 and maybe some random bytes (VS 2010).
81 * So, only create it once and add a reference to it instead of creating new
82 * ones. Saves registry access among other things. */
83#define WITH_CRYPT_CTX_REUSE
84
85/** @def WITH_CONSOLE_OUTPUT_BUFFERING
86 * Enables buffering of all console output as well as removal of annoying
87 * source file echo by cl.exe. */
88#define WITH_CONSOLE_OUTPUT_BUFFERING
89
90/** @def WITH_STD_OUT_ERR_BUFFERING
91 * Enables buffering of standard output and standard error buffer as well as
92 * removal of annoying source file echo by cl.exe. */
93#define WITH_STD_OUT_ERR_BUFFERING
94
95/** @def WITH_LOG_FILE
96 * Log to file instead of stderr. */
97#define WITH_LOG_FILE
98
99/** @def WITH_HISTORY
100 * Keep history of the last jobs. For debugging. */
101#define WITH_HISTORY
102
103/** @def WITH_FIXED_VIRTUAL_ALLOCS
104 * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
105 * there is only one, but an important one, from cl.exe).
106 */
107#if K_ARCH == K_ARCH_X86_32
108# define WITH_FIXED_VIRTUAL_ALLOCS
109#endif
110
111/** @def WITH_PCH_CACHING
112 * Enables read caching of precompiled header files. */
113#if K_ARCH_BITS >= 64
114# define WITH_PCH_CACHING
115#endif
116
117
118#ifndef NDEBUG
119# define KW_LOG_ENABLED
120#endif
121
122/** @def KW_LOG
123 * Generic logging.
124 * @param a Argument list for kwDbgPrintf */
125#ifdef KW_LOG_ENABLED
126# define KW_LOG(a) kwDbgPrintf a
127#else
128# define KW_LOG(a) do { } while (0)
129#endif
130
131/** @def KWLDR_LOG
132 * Loader related logging.
133 * @param a Argument list for kwDbgPrintf */
134#ifdef KW_LOG_ENABLED
135# define KWLDR_LOG(a) kwDbgPrintf a
136#else
137# define KWLDR_LOG(a) do { } while (0)
138#endif
139
140
141/** @def KWFS_LOG
142 * FS cache logging.
143 * @param a Argument list for kwDbgPrintf */
144#ifdef KW_LOG_ENABLED
145# define KWFS_LOG(a) kwDbgPrintf a
146#else
147# define KWFS_LOG(a) do { } while (0)
148#endif
149
150/** @def KWOUT_LOG
151 * Output related logging.
152 * @param a Argument list for kwDbgPrintf */
153#ifdef KW_LOG_ENABLED
154# define KWOUT_LOG(a) kwDbgPrintf a
155#else
156# define KWOUT_LOG(a) do { } while (0)
157#endif
158
159/** @def KWCRYPT_LOG
160 * FS cache logging.
161 * @param a Argument list for kwDbgPrintf */
162#ifdef KW_LOG_ENABLED
163# define KWCRYPT_LOG(a) kwDbgPrintf a
164#else
165# define KWCRYPT_LOG(a) do { } while (0)
166#endif
167
168/** Converts a windows handle to a handle table index.
169 * @note We currently just mask off the 31th bit, and do no shifting or anything
170 * else to create an index of the handle.
171 * @todo consider shifting by 2 or 3. */
172#define KW_HANDLE_TO_INDEX(a_hHandle) ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
173/** Maximum handle value we can deal with. */
174#define KW_HANDLE_MAX 0x20000
175
176/** Max temporary file size (memory backed). */
177#if K_ARCH_BITS >= 64
178# define KWFS_TEMP_FILE_MAX (256*1024*1024)
179#else
180# define KWFS_TEMP_FILE_MAX (64*1024*1024)
181#endif
182
183/** Marks unfinished code. */
184#if 1
185# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
186#else
187# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
188#endif
189
190/** User data key for tools. */
191#define KW_DATA_KEY_TOOL (~(KUPTR)16381)
192/** User data key for a cached file. */
193#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
194
195/** String constant comma length. */
196#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
197
198
199/**
200 * Generate CRT slot wrapper functions.
201 */
202#define CRT_SLOT_FUNCTION_WRAPPER(a_RetTypeAndCallConv, a_FnName, a_aArgsDecl, a_aArgCall) \
203 static a_RetTypeAndCallConv a_FnName##00 a_aArgsDecl { const unsigned iCrtSlot = 0; return a_FnName##_wrapped a_aArgCall; } \
204 static a_RetTypeAndCallConv a_FnName##01 a_aArgsDecl { const unsigned iCrtSlot = 1; return a_FnName##_wrapped a_aArgCall; } \
205 static a_RetTypeAndCallConv a_FnName##02 a_aArgsDecl { const unsigned iCrtSlot = 2; return a_FnName##_wrapped a_aArgCall; } \
206 static a_RetTypeAndCallConv a_FnName##03 a_aArgsDecl { const unsigned iCrtSlot = 3; return a_FnName##_wrapped a_aArgCall; } \
207 static a_RetTypeAndCallConv a_FnName##04 a_aArgsDecl { const unsigned iCrtSlot = 4; return a_FnName##_wrapped a_aArgCall; } \
208 static a_RetTypeAndCallConv a_FnName##05 a_aArgsDecl { const unsigned iCrtSlot = 5; return a_FnName##_wrapped a_aArgCall; } \
209 static a_RetTypeAndCallConv a_FnName##06 a_aArgsDecl { const unsigned iCrtSlot = 6; return a_FnName##_wrapped a_aArgCall; } \
210 static a_RetTypeAndCallConv a_FnName##07 a_aArgsDecl { const unsigned iCrtSlot = 7; return a_FnName##_wrapped a_aArgCall; } \
211 static a_RetTypeAndCallConv a_FnName##08 a_aArgsDecl { const unsigned iCrtSlot = 8; return a_FnName##_wrapped a_aArgCall; } \
212 static a_RetTypeAndCallConv a_FnName##09 a_aArgsDecl { const unsigned iCrtSlot = 9; return a_FnName##_wrapped a_aArgCall; } \
213 static a_RetTypeAndCallConv a_FnName##10 a_aArgsDecl { const unsigned iCrtSlot = 10; return a_FnName##_wrapped a_aArgCall; } \
214 static a_RetTypeAndCallConv a_FnName##11 a_aArgsDecl { const unsigned iCrtSlot = 11; return a_FnName##_wrapped a_aArgCall; } \
215 static a_RetTypeAndCallConv a_FnName##12 a_aArgsDecl { const unsigned iCrtSlot = 12; return a_FnName##_wrapped a_aArgCall; } \
216 static a_RetTypeAndCallConv a_FnName##13 a_aArgsDecl { const unsigned iCrtSlot = 13; return a_FnName##_wrapped a_aArgCall; } \
217 static a_RetTypeAndCallConv a_FnName##14 a_aArgsDecl { const unsigned iCrtSlot = 14; return a_FnName##_wrapped a_aArgCall; } \
218 static a_RetTypeAndCallConv a_FnName##15 a_aArgsDecl { const unsigned iCrtSlot = 15; return a_FnName##_wrapped a_aArgCall; } \
219 static a_RetTypeAndCallConv a_FnName##16 a_aArgsDecl { const unsigned iCrtSlot = 16; return a_FnName##_wrapped a_aArgCall; } \
220 static a_RetTypeAndCallConv a_FnName##17 a_aArgsDecl { const unsigned iCrtSlot = 17; return a_FnName##_wrapped a_aArgCall; } \
221 static a_RetTypeAndCallConv a_FnName##18 a_aArgsDecl { const unsigned iCrtSlot = 18; return a_FnName##_wrapped a_aArgCall; } \
222 static a_RetTypeAndCallConv a_FnName##19 a_aArgsDecl { const unsigned iCrtSlot = 19; return a_FnName##_wrapped a_aArgCall; } \
223 static a_RetTypeAndCallConv a_FnName##20 a_aArgsDecl { const unsigned iCrtSlot = 20; return a_FnName##_wrapped a_aArgCall; } \
224 static a_RetTypeAndCallConv a_FnName##21 a_aArgsDecl { const unsigned iCrtSlot = 21; return a_FnName##_wrapped a_aArgCall; } \
225 static a_RetTypeAndCallConv a_FnName##22 a_aArgsDecl { const unsigned iCrtSlot = 22; return a_FnName##_wrapped a_aArgCall; } \
226 static a_RetTypeAndCallConv a_FnName##23 a_aArgsDecl { const unsigned iCrtSlot = 23; return a_FnName##_wrapped a_aArgCall; } \
227 static a_RetTypeAndCallConv a_FnName##24 a_aArgsDecl { const unsigned iCrtSlot = 24; return a_FnName##_wrapped a_aArgCall; } \
228 static a_RetTypeAndCallConv a_FnName##25 a_aArgsDecl { const unsigned iCrtSlot = 25; return a_FnName##_wrapped a_aArgCall; } \
229 static a_RetTypeAndCallConv a_FnName##26 a_aArgsDecl { const unsigned iCrtSlot = 26; return a_FnName##_wrapped a_aArgCall; } \
230 static a_RetTypeAndCallConv a_FnName##27 a_aArgsDecl { const unsigned iCrtSlot = 27; return a_FnName##_wrapped a_aArgCall; } \
231 static a_RetTypeAndCallConv a_FnName##28 a_aArgsDecl { const unsigned iCrtSlot = 28; return a_FnName##_wrapped a_aArgCall; } \
232 static a_RetTypeAndCallConv a_FnName##29 a_aArgsDecl { const unsigned iCrtSlot = 29; return a_FnName##_wrapped a_aArgCall; } \
233 static a_RetTypeAndCallConv a_FnName##30 a_aArgsDecl { const unsigned iCrtSlot = 30; return a_FnName##_wrapped a_aArgCall; } \
234 static a_RetTypeAndCallConv a_FnName##31 a_aArgsDecl { const unsigned iCrtSlot = 31; return a_FnName##_wrapped a_aArgCall; } \
235 static const KUPTR a_FnName[] = \
236 { \
237 (KUPTR)a_FnName##00, \
238 (KUPTR)a_FnName##01, \
239 (KUPTR)a_FnName##02, \
240 (KUPTR)a_FnName##03, \
241 (KUPTR)a_FnName##04, \
242 (KUPTR)a_FnName##05, \
243 (KUPTR)a_FnName##06, \
244 (KUPTR)a_FnName##07, \
245 (KUPTR)a_FnName##08, \
246 (KUPTR)a_FnName##09, \
247 (KUPTR)a_FnName##10, \
248 (KUPTR)a_FnName##11, \
249 (KUPTR)a_FnName##12, \
250 (KUPTR)a_FnName##13, \
251 (KUPTR)a_FnName##14, \
252 (KUPTR)a_FnName##15, \
253 (KUPTR)a_FnName##16, \
254 (KUPTR)a_FnName##17, \
255 (KUPTR)a_FnName##18, \
256 (KUPTR)a_FnName##19, \
257 (KUPTR)a_FnName##20, \
258 (KUPTR)a_FnName##21, \
259 (KUPTR)a_FnName##22, \
260 (KUPTR)a_FnName##23, \
261 (KUPTR)a_FnName##24, \
262 (KUPTR)a_FnName##25, \
263 (KUPTR)a_FnName##26, \
264 (KUPTR)a_FnName##27, \
265 (KUPTR)a_FnName##28, \
266 (KUPTR)a_FnName##29, \
267 (KUPTR)a_FnName##30, \
268 (KUPTR)a_FnName##31, \
269 }
270
271
272/*********************************************************************************************************************************
273* Structures and Typedefs *
274*********************************************************************************************************************************/
275typedef enum KWLOCATION
276{
277 KWLOCATION_INVALID = 0,
278 KWLOCATION_EXE_DIR,
279 KWLOCATION_IMPORTER_DIR,
280 KWLOCATION_SYSTEM32,
281 KWLOCATION_UNKNOWN_NATIVE,
282 KWLOCATION_UNKNOWN,
283} KWLOCATION;
284
285typedef enum KWMODSTATE
286{
287 KWMODSTATE_INVALID = 0,
288 KWMODSTATE_NEEDS_BITS,
289 KWMODSTATE_NEEDS_INIT,
290 KWMODSTATE_BEING_INITED,
291 KWMODSTATE_INIT_FAILED,
292 KWMODSTATE_READY,
293} KWMODSTATE;
294
295typedef struct KWMODULE *PKWMODULE;
296typedef struct KWMODULE
297{
298 /** Pointer to the next image withe the same hash. */
299 PKWMODULE pNextHash;
300 /** Pointer to the next image in the global list. */
301 PKWMODULE pNextList;
302 /** The normalized path to the image. */
303 const char *pszPath;
304 /** The hash of the program path. */
305 KU32 uHashPath;
306 /** Number of references. */
307 KU32 cRefs;
308 /** UTF-16 version of pszPath. */
309 const wchar_t *pwszPath;
310 /** The offset of the filename in pszPath. */
311 KU16 offFilename;
312 /** The offset of the filename in pwszPath. */
313 KU16 offFilenameW;
314 /** Set if executable. */
315 KBOOL fExe;
316 /** Set if native module entry. */
317 KBOOL fNative;
318 /** Loader module handle. */
319 PKLDRMOD pLdrMod;
320 /** The windows module handle. */
321 HMODULE hOurMod;
322 /** Parent (real) module if this is a virtual API module (api-ms-*.dll or
323 * ext-ms-*.dll). Referenced. */
324 PKWMODULE pVirtualApiMod;
325 /** The of the loaded image bits. */
326 KSIZE cbImage;
327 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
328 KU8 iCrtSlot;
329 /** Loop prevention when working the tree. */
330 KBOOL fVisited;
331 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
332 KBOOL fNeedReInit;
333 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
334 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
335 KU8 fReInitOnMsPdbSrvEndpointChange;
336 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
337 char *pszMsPdbSrvEndpoint;
338
339 union
340 {
341 /** Data for a manually loaded image. */
342 struct
343 {
344 /** Where we load the image. */
345 KU8 *pbLoad;
346 /** Virgin copy of the image. */
347 KU8 *pbCopy;
348 /** Ldr pvBits argument. This is NULL till we've successfully resolved
349 * the imports. */
350 void *pvBits;
351 /** The state. */
352 KWMODSTATE enmState;
353 /** The re-init state. */
354 KWMODSTATE enmReInitState;
355#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
356 /** The number of entries in the table. */
357 KU32 cFunctions;
358 /** The function table address (in the copy). */
359 PRUNTIME_FUNCTION paFunctions;
360 /** Set if we've already registered a function table already. */
361 KBOOL fRegisteredFunctionTable;
362#endif
363 /** Set if we share memory with other executables. */
364 KBOOL fUseLdBuf;
365 /** Set after the first whole image copy is done. */
366 KBOOL fCanDoQuick;
367 /** Number of quick copy chunks. */
368 KU8 cQuickCopyChunks;
369 /** Number of quick zero chunks. */
370 KU8 cQuickZeroChunks;
371 /** Quicker image copy instructions that skips non-writable parts when
372 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
373 * image. */
374 struct
375 {
376 /** The copy destination. */
377 KU8 *pbDst;
378 /** The copy source. */
379 KU8 const *pbSrc;
380 /** How much to copy. */
381 KSIZE cbToCopy;
382 } aQuickCopyChunks[3];
383 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
384 struct
385 {
386 /** Where to start zeroing. */
387 KU8 *pbDst;
388 /** How much to zero. */
389 KSIZE cbToZero;
390 } aQuickZeroChunks[3];
391
392 /** Pointer to g_abInitData of the kWorkerTlsXxxK.c instance.
393 * This member is set by kwLdrTlsAllocationHook. */
394 KU8 *pabTlsInitData;
395 /** Pointer to the g_pvWorkerModule variable in kWorkerTlsXxxK.c (our instance
396 * of it). This member is set by kwLdrTlsAllocationHook. Used by our
397 * destructor to prevent after-free references. */
398 PKWMODULE *ppTlsWorkerModuleVar;
399 /** TLS index if one was allocated, otherwise KU32_MAX.
400 * This member is set by kwLdrTlsAllocationHook. */
401 KU32 idxTls;
402 /** Offset (RVA) of the TLS initialization data. */
403 KU32 offTlsInitData;
404 /** Number of bytes of TLS initialization data. */
405 KU32 cbTlsInitData;
406 /** Number of allocated bytes for TLS. */
407 KU32 cbTlsAlloc;
408 /** Number of TLS callbacks. */
409 KU32 cTlsCallbacks;
410 /** Offset (RVA) of the TLS callback table. */
411 KU32 offTlsCallbacks;
412
413 /** Number of imported modules. */
414 KSIZE cImpMods;
415 /** Import array (variable size). */
416 PKWMODULE apImpMods[1];
417 } Manual;
418 } u;
419} KWMODULE;
420
421
422typedef struct KWDYNLOAD *PKWDYNLOAD;
423typedef struct KWDYNLOAD
424{
425 /** Pointer to the next in the list. */
426 PKWDYNLOAD pNext;
427
428 /** The module handle we present to the application.
429 * This is the LoadLibraryEx return value for special modules and the
430 * KWMODULE.hOurMod value for the others. */
431 HMODULE hmod;
432
433 /** The module for non-special resource stuff, NULL if special. */
434 PKWMODULE pMod;
435
436 /** The length of the LoadLibary filename. */
437 KSIZE cchRequest;
438 /** The LoadLibrary filename. */
439 char szRequest[1];
440} KWDYNLOAD;
441
442
443/**
444 * GetModuleHandle cache for system modules frequently queried.
445 */
446typedef struct KWGETMODULEHANDLECACHE
447{
448 const char *pszName;
449 const wchar_t *pwszName;
450 KU8 cchName;
451 KU8 cwcName;
452 KBOOL fAlwaysPresent;
453 HANDLE hmod;
454} KWGETMODULEHANDLECACHE;
455typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
456
457
458/** One TLS DLL. */
459typedef struct KWTLSDLL
460{
461 const wchar_t *pwszName; /**< The DLL name. */
462 KBOOL fUsed; /**< Set if used, clear if not. */
463} KWTLSDLL;
464typedef KWTLSDLL *PKWTLSDLL;
465
466/**
467 * TLS DLL tracker.
468 */
469typedef struct KWTLSDLLENTRY
470{
471 KU32 cbTls; /**< Max TLS size. */
472 KU32 cDlls; /**< Number of DLLs we ship (paDlls). */
473 PKWTLSDLL paDlls; /**< Array of DLLs we ship. */
474} KWTLSDLLENTRY;
475typedef KWTLSDLLENTRY *PKWTLSDLLENTRY;
476
477
478/**
479 * A cached file.
480 */
481typedef struct KFSWCACHEDFILE
482{
483 /** The user data core. */
484 KFSUSERDATA Core;
485
486 /** Cached file handle. */
487 HANDLE hCached;
488 /** Cached file section handle. */
489 HANDLE hSection;
490 /** Cached file content. */
491 KU8 *pbCached;
492 /** The file size. */
493 KU32 cbCached;
494#ifdef WITH_HASH_CACHE
495 /** Set if we've got a valid MD5 hash in abMd5Digest. */
496 KBOOL fValidMd5;
497 /** Set if we've got a valid SHA-1 hash in abMd5Digest. */
498 KBOOL fValidSha1;
499 /** Set if we've got a valid SHA-256 hash in abMd5Digest. */
500 KBOOL fValidSha256;
501 /** Set if we've got a valid SHA-512 hash in abMd5Digest. */
502 KBOOL fValidSha512;
503 /** The MD5 digest if fValidMd5 is set. */
504 KU8 abMd5Digest[16];
505 /** The SHA-1 digest if fValidSha1 is set. */
506 KU8 abSha1Digest[20];
507 /** The SHA-256 digest if fValidSha256 is set. */
508 KU8 abSha256Digest[32];
509 /** The SHA-512 digest if fValidSha256 is set. */
510 KU8 abSha512Digest[64];
511#endif
512
513 /** Circular self reference. Prevents the object from ever going away and
514 * keeps it handy for debugging. */
515 PKFSOBJ pFsObj;
516 /** The file path (for debugging). */
517 char szPath[1];
518} KFSWCACHEDFILE;
519/** Pointer to a cached filed. */
520typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
521
522#ifdef WITH_HASH_CACHE
523
524/** Pointer to a MD5 hash instance. */
525typedef struct KWCRYPTHASH *PKWCRYPTHASH;
526/**
527 * A MD5 hash instance.
528 */
529typedef struct KWCRYPTHASH
530{
531 /** The magic value. */
532 KUPTR uMagic;
533 /** Pointer to the next hash handle. */
534 PKWCRYPTHASH pNext;
535 /** The cached file we've associated this handle with. */
536 PKFSWCACHEDFILE pCachedFile;
537 /** The number of bytes we've hashed. */
538 KU32 cbHashed;
539 /** Set if this has gone wrong. */
540 KBOOL fGoneBad;
541 /** Set if we've already finalized the digest. */
542 KBOOL fFinal;
543 /** If in fallback mode. */
544 HCRYPTHASH hFallback;
545 /** The algorithm. */
546 ALG_ID idAlg;
547 /** The hash name. */
548 const char *pszAlgName;
549 /** The digest size. */
550 KU32 cbDigest;
551 /** The finalized digest. */
552 KU8 abDigest[64];
553} KWCRYPTHASH;
554/** Magic value for KWCRYPTHASH::uMagic (Les McCann). */
555# define KWCRYPTHASH_MAGIC KUPTR_C(0x19350923)
556
557#endif /* WITH_HASH_CACHE */
558#ifdef WITH_TEMP_MEMORY_FILES
559
560typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
561typedef struct KWFSTEMPFILESEG
562{
563 /** File offset of data. */
564 KU32 offData;
565 /** The size of the buffer pbData points to. */
566 KU32 cbDataAlloc;
567 /** The segment data. */
568 KU8 *pbData;
569} KWFSTEMPFILESEG;
570
571typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
572typedef struct KWFSTEMPFILE
573{
574 /** Pointer to the next temporary file for this run. */
575 PKWFSTEMPFILE pNext;
576 /** The UTF-16 path. (Allocated after this structure.) */
577 const wchar_t *pwszPath;
578 /** The path length. */
579 KU16 cwcPath;
580 /** Number of active handles using this file/mapping (<= 2). */
581 KU8 cActiveHandles;
582 /** Number of active mappings (mapped views) (0 or 1). */
583 KU8 cMappings;
584 /** The amount of space allocated in the segments. */
585 KU32 cbFileAllocated;
586 /** The current file size. */
587 KU32 cbFile;
588 /** The number of segments. */
589 KU32 cSegs;
590 /** Segments making up the file. */
591 PKWFSTEMPFILESEG paSegs;
592} KWFSTEMPFILE;
593
594#endif /* WITH_TEMP_MEMORY_FILES */
595#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
596
597/**
598 * Console line buffer or output full buffer.
599 */
600typedef struct KWOUTPUTSTREAMBUF
601{
602 /** The main output handle. */
603 HANDLE hOutput;
604 /** Our backup handle. */
605 HANDLE hBackup;
606 /** Set if this is a console handle and we're in line buffered mode.
607 * When clear, we may buffer multiple lines, though try flush on line
608 * boundraries when ever possible. */
609 KBOOL fIsConsole;
610 /** Compressed GetFileType result. */
611 KU8 fFileType;
612 KU8 abPadding[2];
613 union
614 {
615 /** Line buffer mode (fIsConsole == K_TRUE). */
616 struct
617 {
618 /** Amount of pending console output in wchar_t's. */
619 KU32 cwcBuf;
620 /** The allocated buffer size. */
621 KU32 cwcBufAlloc;
622 /** Pending console output. */
623 wchar_t *pwcBuf;
624 } Con;
625 /** Fully buffered mode (fIsConsole == K_FALSE). */
626 struct
627 {
628 /** Amount of pending output (in chars). */
629 KU32 cchBuf;
630#ifdef WITH_STD_OUT_ERR_BUFFERING
631 /** The allocated buffer size (in chars). */
632 KU32 cchBufAlloc;
633 /** Pending output. */
634 char *pchBuf;
635#endif
636 } Fully;
637 } u;
638} KWOUTPUTSTREAMBUF;
639/** Pointer to a console line buffer. */
640typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
641
642/**
643 * Combined console buffer of complete lines.
644 */
645typedef struct KWCONSOLEOUTPUT
646{
647 /** The console output handle.
648 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
649 * combined output buffering. */
650 HANDLE hOutput;
651 /** The current code page for the console. */
652 KU32 uCodepage;
653 /** Amount of pending console output in wchar_t's. */
654 KU32 cwcBuf;
655 /** Number of times we've flushed it in any way (for cl.exe hack). */
656 KU32 cFlushes;
657 /** Pending console output. */
658 wchar_t wszBuf[8192];
659} KWCONSOLEOUTPUT;
660/** Pointer to a combined console buffer. */
661typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
662
663#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
664
665/** Handle type. */
666typedef enum KWHANDLETYPE
667{
668 KWHANDLETYPE_INVALID = 0,
669 KWHANDLETYPE_FSOBJ_READ_CACHE,
670 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
671#ifdef WITH_TEMP_MEMORY_FILES
672 KWHANDLETYPE_TEMP_FILE,
673 KWHANDLETYPE_TEMP_FILE_MAPPING,
674#endif
675 KWHANDLETYPE_OUTPUT_BUF
676} KWHANDLETYPE;
677
678/** Handle data. */
679typedef struct KWHANDLE
680{
681 KWHANDLETYPE enmType;
682 /** Number of references */
683 KU32 cRefs;
684 /** The current file offset. */
685 KU32 offFile;
686 /** Handle access. */
687 KU32 dwDesiredAccess;
688 /** The handle. */
689 HANDLE hHandle;
690 /** The current owner (GetCurrentThreadId). */
691 KU32 tidOwner;
692
693 /** Type specific data. */
694 union
695 {
696 /** The file system object. */
697 PKFSWCACHEDFILE pCachedFile;
698#ifdef WITH_TEMP_MEMORY_FILES
699 /** Temporary file handle or mapping handle. */
700 PKWFSTEMPFILE pTempFile;
701#endif
702#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
703 /** Buffered output stream. */
704 PKWOUTPUTSTREAMBUF pOutBuf;
705#endif
706 } u;
707} KWHANDLE;
708typedef KWHANDLE *PKWHANDLE;
709
710/**
711 * Tracking one of our memory mappings.
712 */
713typedef struct KWMEMMAPPING
714{
715 /** Number of references. */
716 KU32 cRefs;
717 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
718 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
719 KWHANDLETYPE enmType;
720 /** The mapping address. */
721 PVOID pvMapping;
722 /** Type specific data. */
723 union
724 {
725 /** The file system object. */
726 PKFSWCACHEDFILE pCachedFile;
727#ifdef WITH_TEMP_MEMORY_FILES
728 /** Temporary file handle or mapping handle. */
729 PKWFSTEMPFILE pTempFile;
730#endif
731 } u;
732} KWMEMMAPPING;
733/** Pointer to a memory mapping tracker. */
734typedef KWMEMMAPPING *PKWMEMMAPPING;
735
736
737/** Pointer to a VirtualAlloc tracker entry. */
738typedef struct KWVIRTALLOC *PKWVIRTALLOC;
739/**
740 * Tracking an VirtualAlloc allocation.
741 */
742typedef struct KWVIRTALLOC
743{
744 PKWVIRTALLOC pNext;
745 void *pvAlloc;
746 KSIZE cbAlloc;
747 /** This is KU32_MAX if not a preallocated chunk. */
748 KU32 idxPreAllocated;
749} KWVIRTALLOC;
750
751
752/** Pointer to a heap (HeapCreate) tracker entry. */
753typedef struct KWHEAP *PKWHEAP;
754/**
755 * Tracking an heap (HeapCreate)
756 */
757typedef struct KWHEAP
758{
759 PKWHEAP pNext;
760 HANDLE hHeap;
761} KWHEAP;
762
763
764/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
765typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
766/**
767 * Tracking an FlsAlloc/TlsAlloc index.
768 */
769typedef struct KWLOCALSTORAGE
770{
771 PKWLOCALSTORAGE pNext;
772 KU32 idx;
773} KWLOCALSTORAGE;
774
775
776/** Pointer to an at exit callback record */
777typedef struct KWEXITCALLACK *PKWEXITCALLACK;
778/**
779 * At exit callback record.
780 */
781typedef struct KWEXITCALLACK
782{
783 PKWEXITCALLACK pNext;
784 _onexit_t pfnCallback;
785 /** At exit doesn't have an exit code. */
786 KBOOL fAtExit;
787} KWEXITCALLACK;
788
789
790typedef enum KWTOOLTYPE
791{
792 KWTOOLTYPE_INVALID = 0,
793 KWTOOLTYPE_SANDBOXED,
794 KWTOOLTYPE_WATCOM,
795 KWTOOLTYPE_EXEC,
796 KWTOOLTYPE_END
797} KWTOOLTYPE;
798
799typedef enum KWTOOLHINT
800{
801 KWTOOLHINT_INVALID = 0,
802 KWTOOLHINT_NONE,
803 KWTOOLHINT_VISUAL_CPP_CL,
804 KWTOOLHINT_VISUAL_CPP_LINK,
805 KWTOOLHINT_END
806} KWTOOLHINT;
807
808
809/**
810 * A kWorker tool.
811 */
812typedef struct KWTOOL
813{
814 /** The user data core structure. */
815 KFSUSERDATA Core;
816
817 /** The normalized path to the program. */
818 const char *pszPath;
819 /** UTF-16 version of pszPath. */
820 wchar_t const *pwszPath;
821 /** The kind of tool. */
822 KWTOOLTYPE enmType;
823
824 union
825 {
826 struct
827 {
828 /** The main entry point. */
829 KUPTR uMainAddr;
830 /** The executable. */
831 PKWMODULE pExe;
832 /** List of dynamically loaded modules.
833 * These will be kept loaded till the tool is destroyed (if we ever do that). */
834 PKWDYNLOAD pDynLoadHead;
835 /** Module array sorted by hOurMod. */
836 PKWMODULE *papModules;
837 /** Number of entries in papModules. */
838 KU32 cModules;
839
840 /** Tool hint (for hacks and such). */
841 KWTOOLHINT enmHint;
842 } Sandboxed;
843 } u;
844} KWTOOL;
845/** Pointer to a tool. */
846typedef struct KWTOOL *PKWTOOL;
847
848
849typedef struct KWSANDBOX *PKWSANDBOX;
850typedef struct KWSANDBOX
851{
852 /** Jump buffer (first for alignment reasons). */
853 jmp_buf JmpBuf;
854 /** The tool currently running in the sandbox. */
855 PKWTOOL pTool;
856 /** The thread ID of the main thread (owner of JmpBuf). */
857 DWORD idMainThread;
858 /** Copy of the NT TIB of the main thread. */
859 NT_TIB TibMainThread;
860 /** The NT_TIB::ExceptionList value inside the try case.
861 * We restore this prior to the longjmp. */
862 void *pOutXcptListHead;
863 /** The exit code in case of longjmp. */
864 int rcExitCode;
865 /** Set if we're running. */
866 KBOOL fRunning;
867 /** Whether to disable caching of ".pch" files. */
868 KBOOL fNoPchCaching;
869
870 /** The command line. */
871 char *pszCmdLine;
872 /** The UTF-16 command line. */
873 wchar_t *pwszCmdLine;
874 /** Number of arguments in papszArgs. */
875 int cArgs;
876 /** The argument vector. */
877 char **papszArgs;
878 /** The argument vector. */
879 wchar_t **papwszArgs;
880
881 /** The _pgmptr msvcrt variable. */
882 char *pgmptr;
883 /** The _wpgmptr msvcrt variable. */
884 wchar_t *wpgmptr;
885
886 /** The _initenv msvcrt variable. */
887 char **initenv;
888 /** The _winitenv msvcrt variable. */
889 wchar_t **winitenv;
890
891 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
892 KSIZE cEnvVarsAllocated;
893 /** The _environ msvcrt variable. */
894 char **environ;
895 /** The _wenviron msvcrt variable. */
896 wchar_t **wenviron;
897 /** The shadow _environ msvcrt variable. */
898 char **papszEnvVars;
899 /** The shadow _wenviron msvcrt variable. */
900 wchar_t **papwszEnvVars;
901
902
903 /** Critical section protecting the below handle members below.
904 * @note Does not protect the individual handles. */
905 CRITICAL_SECTION HandlesLock;
906 /** Handle table. */
907 PKWHANDLE *papHandles;
908 /** Size of the handle table. */
909 KU32 cHandles;
910 /** Number of active handles in the table. */
911 KU32 cActiveHandles;
912 /** Number of handles in the handle table that will not be freed. */
913 KU32 cFixedHandles;
914 /** Total number of leaked handles. */
915 KU32 cLeakedHandles;
916
917 /** Number of active memory mappings in paMemMappings. */
918 KU32 cMemMappings;
919 /** The allocated size of paMemMappings. */
920 KU32 cMemMappingsAlloc;
921 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
922 PKWMEMMAPPING paMemMappings;
923
924#ifdef WITH_TEMP_MEMORY_FILES
925 /** Head of the list of temporary file. */
926 PKWFSTEMPFILE pTempFileHead;
927#endif
928
929 /** Critical section protecting pVirtualAllocHead. */
930 CRITICAL_SECTION VirtualAllocLock;
931 /** Head of the virtual alloc allocations. */
932 PKWVIRTALLOC pVirtualAllocHead;
933 /** Head of the heap list (HeapCreate).
934 * This is only done from images we forcibly restore. */
935 PKWHEAP pHeapHead;
936 /** Head of the FlsAlloc indexes. */
937 PKWLOCALSTORAGE pFlsAllocHead;
938 /** Head of the TlsAlloc indexes. */
939 PKWLOCALSTORAGE pTlsAllocHead;
940
941 /** The at exit callback head.
942 * This is only done from images we forcibly restore. */
943 PKWEXITCALLACK pExitCallbackHead;
944
945 MY_UNICODE_STRING SavedCommandLine;
946
947#ifdef WITH_HASH_CACHE
948 /** The crypto provider instance we use for hashes. */
949 HCRYPTPROV hCryptProvRsa;
950 /** The crypto provider instance we use for hashes. */
951 HCRYPTPROV hCryptProvAes;
952 /** List of crypto hash instances. */
953 PKWCRYPTHASH pHashHead;
954 /** ReadFile sets these while CryptHashData claims and clears them.
955 *
956 * This is part of the heuristics we use for MD5/SHA1/SHA256 caching for header
957 * files. The observed pattern is that c1.dll/c1xx.dll first reads a chunk of a
958 * source or header, then passes the same buffer and read byte count to
959 * CryptHashData.
960 */
961 struct
962 {
963 /** The cached file last read from. */
964 PKFSWCACHEDFILE pCachedFile;
965 /** The file offset of the last cached read. */
966 KU32 offRead;
967 /** The number of bytes read last. */
968 KU32 cbRead;
969 /** The buffer pointer of the last read. */
970 void *pvRead;
971 } LastHashRead;
972#endif
973
974#ifdef WITH_CRYPT_CTX_REUSE
975 /** Reusable crypt contexts. */
976 struct
977 {
978 /** The creation provider type. */
979 KU32 dwProvType;
980 /** The creation flags. */
981 KU32 dwFlags;
982 /** The length of the container name. */
983 KU32 cwcContainer;
984 /** The length of the provider name. */
985 KU32 cwcProvider;
986 /** The container name string. */
987 wchar_t *pwszContainer;
988 /** The provider name string. */
989 wchar_t *pwszProvider;
990 /** The context handle. */
991 HCRYPTPROV hProv;
992 } aCryptCtxs[4];
993 /** Number of reusable crypt conexts in aCryptCtxs. */
994 KU32 cCryptCtxs;
995#endif
996
997
998#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
999 /** The internal standard output handle. */
1000 KWHANDLE HandleStdOut;
1001 /** The internal standard error handle. */
1002 KWHANDLE HandleStdErr;
1003 /** Standard output (and whatever else) buffer. */
1004 KWOUTPUTSTREAMBUF StdOut;
1005 /** Standard error buffer. */
1006 KWOUTPUTSTREAMBUF StdErr;
1007 /** Combined buffer of completed lines. */
1008 KWCONSOLEOUTPUT Combined;
1009#endif
1010} KWSANDBOX;
1011
1012
1013/** A CRT slot. */
1014typedef struct KWCRTSLOT
1015{
1016 KU32 iSlot;
1017
1018 /** The CRT module data. */
1019 PKWMODULE pModule;
1020 /** Pointer to the malloc function. */
1021 void * (__cdecl *pfnMalloc)(size_t);
1022 /** Pointer to the beginthreadex function. */
1023 uintptr_t (__cdecl *pfnBeginThreadEx)(void *, unsigned, unsigned (__stdcall *)(void *), void *, unsigned, unsigned *);
1024
1025} KWCRTSLOT;
1026typedef KWCRTSLOT *PKWCRTSLOT;
1027
1028
1029/** Replacement function entry. */
1030typedef struct KWREPLACEMENTFUNCTION
1031{
1032 /** The function name. */
1033 const char *pszFunction;
1034 /** The length of the function name. */
1035 KSIZE cchFunction;
1036 /** The module name (optional). */
1037 const char *pszModule;
1038 /** The replacement function, data address or CRT slot function array. */
1039 KUPTR pfnReplacement;
1040 /** Only replace in the executable.
1041 * @todo fix the reinitialization of non-native DLLs! */
1042 KBOOL fOnlyExe;
1043 /** Set if pfnReplacement points to a CRT slot function array. */
1044 KBOOL fCrtSlotArray;
1045} KWREPLACEMENTFUNCTION;
1046typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
1047
1048#if 0
1049/** Replacement function entry. */
1050typedef struct KWREPLACEMENTDATA
1051{
1052 /** The function name. */
1053 const char *pszFunction;
1054 /** The length of the function name. */
1055 KSIZE cchFunction;
1056 /** The module name (optional). */
1057 const char *pszModule;
1058 /** Function providing the replacement. */
1059 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
1060} KWREPLACEMENTDATA;
1061typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
1062#endif
1063
1064/**
1065 * One test job (--full-test).
1066 */
1067typedef struct KWONETEST
1068{
1069 /** Where this job originated. */
1070 const char *pszJobSrc;
1071 /** The argument number it started with. */
1072 unsigned iJobSrc;
1073 /** Set if virgin, clear if modified. */
1074 KBOOL fVirgin;
1075
1076 /** Number of runs to give it. */
1077 unsigned cRuns;
1078
1079 /** @name kSubmitHandleJobUnpacked arguments
1080 * @{ */
1081 const char *pszExecutable;
1082 const char *pszCwd;
1083 KU32 cArgs;
1084 const char **papszArgs;
1085 KU32 cEnvVars;
1086 const char **papszEnvVars;
1087 const char *pszSpecialEnv;
1088 KBOOL fWatcomBrainDamange;
1089 KBOOL fNoPchCaching;
1090 KU32 cPostCmdArgs;
1091 const char **papszPostCmdArgs;
1092 /** @} */
1093
1094 /** Pointer to the next one. */
1095 struct KWONETEST *pNext;
1096} KWONETEST;
1097/** Pointer to one test job. */
1098typedef KWONETEST *PKWONETEST;
1099
1100
1101/*********************************************************************************************************************************
1102* Global Variables *
1103*********************************************************************************************************************************/
1104/** The sandbox data. */
1105static KWSANDBOX g_Sandbox;
1106
1107/** The module currently occupying g_abDefLdBuf. */
1108static PKWMODULE g_pModInLdBuf = NULL;
1109
1110/** The module that previuosly occupied g_abDefLdBuf. */
1111static PKWMODULE g_pModPrevInLdBuf = NULL;
1112
1113/** Module list head. */
1114static PKWMODULE g_pModuleHead = NULL;
1115/** Where to insert the next module. */
1116static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1117
1118/** Module hash table. */
1119static PKWMODULE g_apModules[127];
1120
1121/** GetModuleHandle cache. */
1122static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1123{
1124#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1125 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1126#if 1
1127 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1128 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1129#endif
1130 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1131};
1132
1133/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1134static PKWMODULE g_pModPendingTlsAlloc = NULL;
1135
1136/** The 1KB TLS DLLs. */
1137static KWTLSDLL g_aTls1KDlls[] =
1138{
1139 { L"kWorkerTls1K.dll", K_FALSE },
1140 { L"kWorkerTls1K01.dll", K_FALSE },
1141 { L"kWorkerTls1K02.dll", K_FALSE },
1142 { L"kWorkerTls1K03.dll", K_FALSE },
1143 { L"kWorkerTls1K04.dll", K_FALSE },
1144 { L"kWorkerTls1K05.dll", K_FALSE },
1145 { L"kWorkerTls1K06.dll", K_FALSE },
1146 { L"kWorkerTls1K07.dll", K_FALSE },
1147 { L"kWorkerTls1K08.dll", K_FALSE },
1148 { L"kWorkerTls1K09.dll", K_FALSE },
1149 { L"kWorkerTls1K10.dll", K_FALSE },
1150 { L"kWorkerTls1K11.dll", K_FALSE },
1151 { L"kWorkerTls1K12.dll", K_FALSE },
1152 { L"kWorkerTls1K13.dll", K_FALSE },
1153 { L"kWorkerTls1K14.dll", K_FALSE },
1154 { L"kWorkerTls1K15.dll", K_FALSE },
1155};
1156
1157/** The 64KB TLS DLLs. */
1158static KWTLSDLL g_aTls64KDlls[] =
1159{
1160 { L"kWorkerTls64K.dll", K_FALSE },
1161 { L"kWorkerTls64K01.dll", K_FALSE },
1162 { L"kWorkerTls64K02.dll", K_FALSE },
1163 { L"kWorkerTls64K03.dll", K_FALSE },
1164 { L"kWorkerTls64K04.dll", K_FALSE },
1165 { L"kWorkerTls64K05.dll", K_FALSE },
1166 { L"kWorkerTls64K06.dll", K_FALSE },
1167 { L"kWorkerTls64K07.dll", K_FALSE },
1168};
1169
1170/** The 128KB TLS DLLs. */
1171static KWTLSDLL g_aTls128KDlls[] =
1172{
1173 { L"kWorkerTls128K.dll", K_FALSE },
1174 { L"kWorkerTls128K01.dll", K_FALSE },
1175 { L"kWorkerTls128K02.dll", K_FALSE },
1176 { L"kWorkerTls128K03.dll", K_FALSE },
1177 { L"kWorkerTls128K04.dll", K_FALSE },
1178 { L"kWorkerTls128K05.dll", K_FALSE },
1179 { L"kWorkerTls128K06.dll", K_FALSE },
1180 { L"kWorkerTls128K07.dll", K_FALSE },
1181};
1182
1183/** The 512KB TLS DLLs. */
1184static KWTLSDLL g_aTls512KDlls[] =
1185{
1186 { L"kWorkerTls512K.dll", K_FALSE },
1187 { L"kWorkerTls512K01.dll", K_FALSE },
1188 { L"kWorkerTls512K02.dll", K_FALSE },
1189 { L"kWorkerTls512K03.dll", K_FALSE },
1190 { L"kWorkerTls512K04.dll", K_FALSE },
1191 { L"kWorkerTls512K05.dll", K_FALSE },
1192 { L"kWorkerTls512K06.dll", K_FALSE },
1193 { L"kWorkerTls512K07.dll", K_FALSE },
1194};
1195
1196/** The TLS DLLs grouped by size. */
1197static KWTLSDLLENTRY const g_aTlsDlls[] =
1198{
1199 { 1024, K_ELEMENTS(g_aTls1KDlls), g_aTls1KDlls },
1200 { 64*1024, K_ELEMENTS(g_aTls64KDlls), g_aTls64KDlls },
1201 { 128*1024, K_ELEMENTS(g_aTls128KDlls), g_aTls128KDlls },
1202 { 512*1024, K_ELEMENTS(g_aTls512KDlls), g_aTls512KDlls },
1203};
1204
1205/** CRT slots.
1206 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1207static KWCRTSLOT g_aCrtSlots[32];
1208
1209/** windbg .reload statements. vs */
1210char g_szReloads[4096];
1211/** Current offset into g_szReloads. */
1212KU32 volatile g_cchReloads;
1213
1214/** The file system cache. */
1215static PKFSCACHE g_pFsCache;
1216/** The current directory (referenced). */
1217static PKFSOBJ g_pCurDirObj = NULL;
1218#ifdef KBUILD_OS_WINDOWS
1219/** The windows system32 directory (referenced). */
1220static PKFSDIR g_pWinSys32 = NULL;
1221#endif
1222
1223/** Verbosity level. */
1224static int g_cVerbose = 2;
1225
1226/** Whether we should restart the worker. */
1227static KBOOL g_fRestart = K_FALSE;
1228
1229/** The process group this worker is tied to (--group option), -1 if none. */
1230static KI32 g_iProcessGroup = -1;
1231
1232/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1233static int volatile g_rcCtrlC = 0;
1234
1235/** The communication pipe handle. We break this when we see Ctrl-C such. */
1236#ifdef KBUILD_OS_WINDOWS
1237static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1238#else
1239static int g_hPipe = -1;
1240#endif
1241
1242
1243/* Further down. */
1244extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1245extern KU32 const g_cSandboxReplacements;
1246
1247extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1248extern KU32 const g_cSandboxNativeReplacements;
1249
1250extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1251extern KU32 const g_cSandboxGetProcReplacements;
1252
1253
1254/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1255 * cover the default executable link address of 0x400000.
1256 * @remarks Early main() makes it read+write+executable. Attempts as having
1257 * it as a separate section failed because the linker insists on
1258 * writing out every zero in the uninitialized section, resulting in
1259 * really big binaries. */
1260__declspec(align(0x1000))
1261static KU8 g_abDefLdBuf[16*1024*1024];
1262
1263#ifdef WITH_LOG_FILE
1264/** Log file handle. */
1265static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1266#endif
1267
1268
1269#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1270/** Virtual address space reserved for CL.EXE heap manager.
1271 *
1272 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1273 * address. It's among other things used for precompiled headers, which
1274 * seemingly have addresses hardcoded into them and won't work if mapped
1275 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1276 * for it. (The /Zm option may affect this allocation.)
1277 */
1278static struct
1279{
1280 /** The memory address we need. */
1281 KUPTR const uFixed;
1282 /** How much we need to fix. */
1283 KSIZE const cbFixed;
1284 /** What we actually got, NULL if given back. */
1285 void *pvReserved;
1286 /** Whether it is in use or not. */
1287 KBOOL fInUse;
1288} g_aFixedVirtualAllocs[] =
1289{
1290# if K_ARCH == K_ARCH_X86_32
1291 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1292 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1293 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1294# else
1295 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1296# endif
1297};
1298#endif
1299
1300
1301#ifdef WITH_HISTORY
1302/** The job history. */
1303static char *g_apszHistory[32];
1304/** Index of the next history entry. */
1305static unsigned g_iHistoryNext = 0;
1306#endif
1307
1308
1309/** Number of jobs executed. */
1310static KU32 g_cJobs;
1311/** Number of tools. */
1312static KU32 g_cTools;
1313/** Number of modules. */
1314static KU32 g_cModules;
1315/** Number of non-native modules. */
1316static KU32 g_cNonNativeModules;
1317/** Number of read-cached files. */
1318static KU32 g_cReadCachedFiles;
1319/** Total size of read-cached files. */
1320static KSIZE g_cbReadCachedFiles;
1321
1322/** Total number of ReadFile calls. */
1323static KSIZE g_cReadFileCalls;
1324/** Total bytes read via ReadFile. */
1325static KSIZE g_cbReadFileTotal;
1326/** Total number of read from read-cached files. */
1327static KSIZE g_cReadFileFromReadCached;
1328/** Total bytes read from read-cached files. */
1329static KSIZE g_cbReadFileFromReadCached;
1330/** Total number of read from in-memory temporary files. */
1331static KSIZE g_cReadFileFromInMemTemp;
1332/** Total bytes read from in-memory temporary files. */
1333static KSIZE g_cbReadFileFromInMemTemp;
1334
1335/** Total number of WriteFile calls. */
1336static KSIZE g_cWriteFileCalls;
1337/** Total bytes written via WriteFile. */
1338static KSIZE g_cbWriteFileTotal;
1339/** Total number of written to from in-memory temporary files. */
1340static KSIZE g_cWriteFileToInMemTemp;
1341/** Total bytes written to in-memory temporary files. */
1342static KSIZE g_cbWriteFileToInMemTemp;
1343
1344#ifdef WITH_HASH_CACHE
1345/** Total number of hashes. */
1346static KSIZE g_cHashes;
1347/** Number of cached hash hits. */
1348static KSIZE g_cHashesCached;
1349/** Number of fallbacks. */
1350static KSIZE g_cHashesFallbacks;
1351/** Number of partial cached file hashes. */
1352static KSIZE g_cHashesPartial;
1353/** Total number of MD5 hashes. */
1354static KSIZE g_cHashesMd5;
1355/** Total number of SHA-1 hashes. */
1356static KSIZE g_cHashesSha1;
1357/** Total number of SHA-256 hashes. */
1358static KSIZE g_cHashesSha256;
1359/** Total number of SHA-512 hashes. */
1360static KSIZE g_cHashesSha512;
1361#endif
1362
1363
1364/*********************************************************************************************************************************
1365* Internal Functions *
1366*********************************************************************************************************************************/
1367static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1368static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1369 const char *pszSearchPath, PKWMODULE *ppMod);
1370static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1371static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName);
1372static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule);
1373static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod);
1374static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1375static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1376static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile);
1377static PKWHANDLE kwSandboxHandleGet(HANDLE hFile);
1378K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle);
1379#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1380static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1381#endif
1382static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1383
1384
1385
1386
1387/**
1388 * Debug printing.
1389 * @param pszFormat Debug format string.
1390 * @param ... Format argument.
1391 */
1392static void kwDbgPrintfV(const char *pszFormat, va_list va)
1393{
1394 if (g_cVerbose >= 2)
1395 {
1396 DWORD const dwSavedErr = GetLastError();
1397#ifdef WITH_LOG_FILE
1398 DWORD dwIgnored;
1399 char szTmp[2048];
1400 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1401 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1402 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1403 cch += cchPrefix;
1404 else
1405 {
1406 cch = sizeof(szTmp) - 1;
1407 szTmp[cch] = '\0';
1408 }
1409
1410 if (g_hLogFile == INVALID_HANDLE_VALUE)
1411 {
1412 wchar_t wszFilename[128];
1413 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1414 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1415 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1416 }
1417
1418 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1419#else
1420 fprintf(stderr, "debug: ");
1421 vfprintf(stderr, pszFormat, va);
1422#endif
1423
1424 SetLastError(dwSavedErr);
1425 }
1426}
1427
1428
1429/**
1430 * Debug printing.
1431 * @param pszFormat Debug format string.
1432 * @param ... Format argument.
1433 */
1434static void kwDbgPrintf(const char *pszFormat, ...)
1435{
1436 if (g_cVerbose >= 2)
1437 {
1438 va_list va;
1439 va_start(va, pszFormat);
1440 kwDbgPrintfV(pszFormat, va);
1441 va_end(va);
1442 }
1443}
1444
1445
1446/**
1447 * Debugger printing.
1448 * @param pszFormat Debug format string.
1449 * @param ... Format argument.
1450 */
1451static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1452{
1453 if (IsDebuggerPresent())
1454 {
1455 DWORD const dwSavedErr = GetLastError();
1456 char szTmp[2048];
1457
1458 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1459 OutputDebugStringA(szTmp);
1460
1461 SetLastError(dwSavedErr);
1462 }
1463}
1464
1465
1466/**
1467 * Debugger printing.
1468 * @param pszFormat Debug format string.
1469 * @param ... Format argument.
1470 */
1471static void kwDebuggerPrintf(const char *pszFormat, ...)
1472{
1473 va_list va;
1474 va_start(va, pszFormat);
1475 kwDebuggerPrintfV(pszFormat, va);
1476 va_end(va);
1477}
1478
1479
1480
1481/**
1482 * Error printing.
1483 * @param pszFormat Message format string.
1484 * @param ... Format argument.
1485 */
1486static void kwErrPrintfV(const char *pszFormat, va_list va)
1487{
1488 DWORD const dwSavedErr = GetLastError();
1489
1490#if defined(KW_LOG_ENABLED) && defined(WITH_LOG_FILE)
1491 va_list vaCopy;
1492# if defined(va_copy) || !defined(_MSC_VER) || _MSC_VER >= 1700 /*??*/
1493 va_copy(vaCopy, va);
1494# else
1495 vaCopy = va;
1496# endif
1497 kwDebuggerPrintf("kWorker: error: ");
1498 kwDebuggerPrintfV(pszFormat, vaCopy);
1499#endif
1500
1501 fprintf(stderr, "kWorker: error: ");
1502 vfprintf(stderr, pszFormat, va);
1503 fflush(stderr); /* In case it's a pipe. */
1504
1505 SetLastError(dwSavedErr);
1506}
1507
1508
1509/**
1510 * Error printing.
1511 * @param pszFormat Message format string.
1512 * @param ... Format argument.
1513 */
1514static void kwErrPrintf(const char *pszFormat, ...)
1515{
1516 va_list va;
1517 va_start(va, pszFormat);
1518 kwErrPrintfV(pszFormat, va);
1519 va_end(va);
1520}
1521
1522
1523/**
1524 * Error printing.
1525 * @return rc;
1526 * @param rc Return value
1527 * @param pszFormat Message format string.
1528 * @param ... Format argument.
1529 */
1530static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1531{
1532 va_list va;
1533 va_start(va, pszFormat);
1534 kwErrPrintfV(pszFormat, va);
1535 va_end(va);
1536 return rc;
1537}
1538
1539
1540#ifdef K_STRICT
1541
1542KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1543{
1544 DWORD const dwSavedErr = GetLastError();
1545
1546 fprintf(stderr,
1547 "\n"
1548 "!!Assertion failed!!\n"
1549 "Expression: %s\n"
1550 "Function : %s\n"
1551 "File: %s\n"
1552 "Line: %d\n"
1553 , pszExpr, pszFunction, pszFile, iLine);
1554
1555 SetLastError(dwSavedErr);
1556}
1557
1558
1559KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1560{
1561 DWORD const dwSavedErr = GetLastError();
1562 va_list va;
1563
1564 va_start(va, pszFormat);
1565 fprintf(stderr, pszFormat, va);
1566 va_end(va);
1567
1568 SetLastError(dwSavedErr);
1569}
1570
1571#endif /* K_STRICT */
1572
1573
1574/**
1575 * Hashes a string.
1576 *
1577 * @returns 32-bit string hash.
1578 * @param pszString String to hash.
1579 */
1580static KU32 kwStrHash(const char *pszString)
1581{
1582 /* This algorithm was created for sdbm (a public-domain reimplementation of
1583 ndbm) database library. it was found to do well in scrambling bits,
1584 causing better distribution of the keys and fewer splits. it also happens
1585 to be a good general hashing function with good distribution. the actual
1586 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1587 is the faster version used in gawk. [there is even a faster, duff-device
1588 version] the magic constant 65599 was picked out of thin air while
1589 experimenting with different constants, and turns out to be a prime.
1590 this is one of the algorithms used in berkeley db (see sleepycat) and
1591 elsewhere. */
1592 KU32 uHash = 0;
1593 KU32 uChar;
1594 while ((uChar = (unsigned char)*pszString++) != 0)
1595 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1596 return uHash;
1597}
1598
1599
1600/**
1601 * Hashes a string.
1602 *
1603 * @returns The string length.
1604 * @param pszString String to hash.
1605 * @param puHash Where to return the 32-bit string hash.
1606 */
1607static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1608{
1609 const char * const pszStart = pszString;
1610 KU32 uHash = 0;
1611 KU32 uChar;
1612 while ((uChar = (unsigned char)*pszString) != 0)
1613 {
1614 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1615 pszString++;
1616 }
1617 *puHash = uHash;
1618 return pszString - pszStart;
1619}
1620
1621
1622/**
1623 * Hashes a string.
1624 *
1625 * @returns The string length in wchar_t units.
1626 * @param pwszString String to hash.
1627 * @param puHash Where to return the 32-bit string hash.
1628 */
1629static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1630{
1631 const wchar_t * const pwszStart = pwszString;
1632 KU32 uHash = 0;
1633 KU32 uChar;
1634 while ((uChar = *pwszString) != 0)
1635 {
1636 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1637 pwszString++;
1638 }
1639 *puHash = uHash;
1640 return pwszString - pwszStart;
1641}
1642
1643
1644/**
1645 * Converts the given string to unicode.
1646 *
1647 * @returns Length of the resulting string in wchar_t's.
1648 * @param pszSrc The source string.
1649 * @param pwszDst The destination buffer.
1650 * @param cwcDst The size of the destination buffer in wchar_t's.
1651 */
1652static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1653{
1654 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1655 KSIZE offDst = 0;
1656 while (offDst < cwcDst)
1657 {
1658 char ch = *pszSrc++;
1659 pwszDst[offDst++] = ch;
1660 if (!ch)
1661 return offDst - 1;
1662 kHlpAssert((unsigned)ch < 127);
1663 }
1664
1665 pwszDst[offDst - 1] = '\0';
1666 return offDst;
1667}
1668
1669
1670/**
1671 * Converts the given string to UTF-16, allocating the buffer.
1672 *
1673 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1674 * the source string.
1675 * @param pchSrc The source string.
1676 * @param cchSrc The length of the source string.
1677 */
1678static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1679{
1680 DWORD const dwErrSaved = GetLastError();
1681 KSIZE cwcBuf = cchSrc + 1;
1682 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1683 if (pwszBuf)
1684 {
1685 if (cchSrc > 0)
1686 {
1687 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1688 if (cwcRet > 0)
1689 {
1690 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1691 pwszBuf[cwcRet] = '\0';
1692 }
1693 else
1694 {
1695 kHlpFree(pwszBuf);
1696
1697 /* Figure the length and allocate the right buffer size. */
1698 SetLastError(NO_ERROR);
1699 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1700 if (cwcRet)
1701 {
1702 cwcBuf = cwcRet + 2;
1703 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1704 if (pwszBuf)
1705 {
1706 SetLastError(NO_ERROR);
1707 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1708 if (cwcRet)
1709 {
1710 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1711 pwszBuf[cwcRet] = '\0';
1712 }
1713 else
1714 {
1715 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1716 kHlpFree(pwszBuf);
1717 pwszBuf = NULL;
1718 }
1719 }
1720 }
1721 else
1722 {
1723 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1724 pwszBuf = NULL;
1725 }
1726 }
1727 }
1728 else
1729 pwszBuf[0] = '\0';
1730 }
1731 SetLastError(dwErrSaved);
1732 return pwszBuf;
1733}
1734
1735
1736/**
1737 * Converts the given UTF-16 to a normal string.
1738 *
1739 * @returns Length of the resulting string.
1740 * @param pwszSrc The source UTF-16 string.
1741 * @param pszDst The destination buffer.
1742 * @param cbDst The size of the destination buffer in bytes.
1743 */
1744static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1745{
1746 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1747 KSIZE offDst = 0;
1748 while (offDst < cbDst)
1749 {
1750 wchar_t wc = *pwszSrc++;
1751 pszDst[offDst++] = (char)wc;
1752 if (!wc)
1753 return offDst - 1;
1754 kHlpAssert((unsigned)wc < 127);
1755 }
1756
1757 pszDst[offDst - 1] = '\0';
1758 return offDst;
1759}
1760
1761
1762/**
1763 * Converts the given UTF-16 to ASSI, allocating the buffer.
1764 *
1765 * @returns Pointer to the new heap allocation containing the ANSI version of
1766 * the source string.
1767 * @param pwcSrc The source string.
1768 * @param cwcSrc The length of the source string.
1769 */
1770static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1771{
1772 DWORD const dwErrSaved = GetLastError();
1773 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1774 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1775 if (pszBuf)
1776 {
1777 if (cwcSrc > 0)
1778 {
1779 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1780 if (cchRet > 0)
1781 {
1782 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1783 pszBuf[cchRet] = '\0';
1784 }
1785 else
1786 {
1787 kHlpFree(pszBuf);
1788
1789 /* Figure the length and allocate the right buffer size. */
1790 SetLastError(NO_ERROR);
1791 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1792 if (cchRet)
1793 {
1794 cbBuf = cchRet + 2;
1795 pszBuf = (char *)kHlpAlloc(cbBuf);
1796 if (pszBuf)
1797 {
1798 SetLastError(NO_ERROR);
1799 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1800 if (cchRet)
1801 {
1802 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1803 pszBuf[cchRet] = '\0';
1804 }
1805 else
1806 {
1807 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1808 kHlpFree(pszBuf);
1809 pszBuf = NULL;
1810 }
1811 }
1812 }
1813 else
1814 {
1815 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1816 pszBuf = NULL;
1817 }
1818 }
1819 }
1820 else
1821 pszBuf[0] = '\0';
1822 }
1823 SetLastError(dwErrSaved);
1824 return pszBuf;
1825}
1826
1827
1828
1829/** UTF-16 string length. */
1830static KSIZE kwUtf16Len(wchar_t const *pwsz)
1831{
1832 KSIZE cwc = 0;
1833 while (*pwsz != '\0')
1834 cwc++, pwsz++;
1835 return cwc;
1836}
1837
1838/**
1839 * Copy out the UTF-16 string following the convension of GetModuleFileName
1840 */
1841static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1842{
1843 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1844 if (cwcSrc + 1 <= cwcDst)
1845 {
1846 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1847 return (DWORD)cwcSrc;
1848 }
1849 if (cwcDst > 0)
1850 {
1851 KSIZE cwcDstTmp = cwcDst - 1;
1852 pwszDst[cwcDstTmp] = '\0';
1853 if (cwcDstTmp > 0)
1854 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1855 }
1856 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1857 return (DWORD)cwcDst;
1858}
1859
1860
1861/**
1862 * Copy out the ANSI string following the convension of GetModuleFileName
1863 */
1864static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1865{
1866 KSIZE cchSrc = kHlpStrLen(pszSrc);
1867 if (cchSrc + 1 <= cbDst)
1868 {
1869 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1870 return (DWORD)cchSrc;
1871 }
1872 if (cbDst > 0)
1873 {
1874 KSIZE cbDstTmp = cbDst - 1;
1875 pszDst[cbDstTmp] = '\0';
1876 if (cbDstTmp > 0)
1877 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1878 }
1879 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1880 return (DWORD)cbDst;
1881}
1882
1883
1884/**
1885 * Normalizes the path so we get a consistent hash.
1886 *
1887 * @returns status code.
1888 * @param pszPath The path.
1889 * @param pszNormPath The output buffer.
1890 * @param cbNormPath The size of the output buffer.
1891 */
1892static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1893{
1894 KFSLOOKUPERROR enmError;
1895 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1896 if (pFsObj)
1897 {
1898 KBOOL fRc;
1899 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1900 kFsCacheObjRelease(g_pFsCache, pFsObj);
1901 if (fRc)
1902 return 0;
1903 return KERR_BUFFER_OVERFLOW;
1904 }
1905 return KERR_FILE_NOT_FOUND;
1906}
1907
1908
1909/**
1910 * Get the pointer to the filename part of the path.
1911 *
1912 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1913 * @returns Pointer to the terminator char if no filename.
1914 * @param pszPath The path to parse.
1915 */
1916static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1917{
1918 const wchar_t *pwszLast = NULL;
1919 for (;;)
1920 {
1921 wchar_t wc = *pwszPath;
1922#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1923 if (wc == '/' || wc == '\\' || wc == ':')
1924 {
1925 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1926 /* nothing */;
1927 pwszLast = pwszPath;
1928 }
1929#else
1930 if (wc == '/')
1931 {
1932 while ((wc = *++pszFilename) == '/')
1933 /* betsuni */;
1934 pwszLast = pwszPath;
1935 }
1936#endif
1937 if (!wc)
1938 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1939 pwszPath++;
1940 }
1941}
1942
1943
1944
1945/**
1946 * Retains a new reference to the given module
1947 * @returns pMod
1948 * @param pMod The module to retain.
1949 */
1950static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1951{
1952 kHlpAssert(pMod->cRefs > 0);
1953 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1954 pMod->cRefs++;
1955 return pMod;
1956}
1957
1958
1959/**
1960 * Releases a module reference.
1961 *
1962 * @param pMod The module to release.
1963 */
1964static void kwLdrModuleRelease(PKWMODULE pMod)
1965{
1966 if (--pMod->cRefs == 0)
1967 {
1968 /* Make sure it doesn't receive any more native TLS callbacks.if non-native. */
1969 if (!pMod->fNative && pMod->u.Manual.ppTlsWorkerModuleVar)
1970 {
1971 *pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1972 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1973 }
1974
1975 /* Unlink it from the hash table. */
1976 if (!pMod->fExe)
1977 {
1978 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1979 if (g_apModules[idx] == pMod)
1980 g_apModules[idx] = pMod->pNextHash;
1981 else
1982 {
1983 PKWMODULE pPrev = g_apModules[idx];
1984 kHlpAssert(pPrev != NULL);
1985 while (pPrev->pNextHash != pMod)
1986 {
1987 pPrev = pPrev->pNextHash;
1988 kHlpAssert(pPrev != NULL);
1989 }
1990 pPrev->pNextHash = pMod->pNextHash;
1991 }
1992 }
1993
1994 /* Unlink it from the list. */
1995 if (pMod != g_pModuleHead)
1996 {
1997 PKWMODULE pPrev = g_pModuleHead;
1998 while (pPrev)
1999 {
2000 if (pPrev->pNextList == pMod)
2001 {
2002 pPrev->pNextList = pMod->pNextList;
2003 if (!pMod->pNextList)
2004 g_ppModuleNext = &pPrev->pNextList;
2005 break;
2006 }
2007 pPrev = pPrev->pNextList;
2008 }
2009 kHlpAssert(pPrev != NULL);
2010 }
2011 else
2012 {
2013 g_pModuleHead = pMod->pNextList;
2014 if (!pMod->pNextList)
2015 g_ppModuleNext = &g_pModuleHead;
2016 }
2017
2018 /* Release import modules. */
2019 if (!pMod->fNative)
2020 {
2021 KSIZE idx = pMod->u.Manual.cImpMods;
2022 while (idx-- > 0)
2023 if (pMod->u.Manual.apImpMods[idx])
2024 {
2025 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
2026 pMod->u.Manual.apImpMods[idx] = NULL;
2027 }
2028 }
2029
2030 /* Free our resources. */
2031 kLdrModClose(pMod->pLdrMod);
2032 pMod->pLdrMod = NULL;
2033
2034 if (!pMod->fNative)
2035 {
2036 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
2037 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2038 }
2039
2040 if (pMod->iCrtSlot != KU8_MAX)
2041 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
2042
2043 if (pMod->pszMsPdbSrvEndpoint)
2044 {
2045 kHlpFree(pMod->pszMsPdbSrvEndpoint);
2046 pMod->pszMsPdbSrvEndpoint = NULL;
2047 }
2048
2049 kHlpFree(pMod);
2050 }
2051 else
2052 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
2053}
2054
2055
2056/**
2057 * Links the module into the module hash table.
2058 *
2059 * @returns pMod
2060 * @param pMod The module to link.
2061 */
2062static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
2063{
2064 if (!pMod->fExe)
2065 {
2066 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
2067 pMod->pNextHash = g_apModules[idx];
2068 g_apModules[idx] = pMod;
2069 }
2070
2071 pMod->pNextList = NULL;
2072 *g_ppModuleNext = pMod;
2073 g_ppModuleNext = &pMod->pNextList;
2074
2075 return pMod;
2076}
2077
2078
2079/**
2080 * Replaces imports for this module according to g_aSandboxNativeReplacements.
2081 *
2082 * @param pMod The natively loaded module to process.
2083 */
2084static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
2085{
2086 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
2087 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
2088 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
2089 IMAGE_NT_HEADERS const *pNtHdrs;
2090 IMAGE_DATA_DIRECTORY const *pDirEnt;
2091
2092 kHlpAssert(pMod->fNative);
2093
2094 /*
2095 * Locate the export descriptors.
2096 */
2097 /* MZ header. */
2098 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
2099 {
2100 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
2101 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
2102 }
2103 else
2104 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
2105
2106 /* Check PE header. */
2107 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2108 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
2109
2110 /* Locate the import descriptor array. */
2111 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2112 if ( pDirEnt->Size > 0
2113 && pDirEnt->VirtualAddress != 0)
2114 {
2115 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
2116 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
2117 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
2118 KU8 *pbProtRange = NULL;
2119 SIZE_T cbProtRange = 0;
2120 DWORD fOldProt = 0;
2121 KU32 const cbPage = 0x1000;
2122 BOOL fRc;
2123
2124
2125 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
2126 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
2127 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
2128
2129 /*
2130 * Walk the import descriptor array.
2131 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
2132 */
2133 while ( cLeft-- > 0
2134 && pImpDesc->Name > 0
2135 && pImpDesc->FirstThunk > 0)
2136 {
2137 KU32 iThunk;
2138 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
2139 PKWMODULE pImportMod = NULL;
2140 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
2141 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
2142 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
2143 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
2144 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
2145 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
2146 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
2147
2148 /* Iterate the thunks. */
2149 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
2150 {
2151 KUPTR const off = paOrgThunks[iThunk].u1.Function;
2152 kHlpAssertReturnVoid(off < cbImage);
2153 if (!IMAGE_SNAP_BY_ORDINAL(off))
2154 {
2155 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
2156 KSIZE const cchSymbol = kHlpStrLen((const char *)&pName->Name[0]);
2157 KU32 i = g_cSandboxNativeReplacements;
2158 while (i-- > 0)
2159 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
2160 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
2161 {
2162 if ( !g_aSandboxNativeReplacements[i].pszModule
2163 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
2164 {
2165 KWLDR_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
2166
2167 /* The .rdata section is normally read-only, so we need to make it writable first. */
2168 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
2169 {
2170 /* Restore previous .rdata page. */
2171 if (fOldProt)
2172 {
2173 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
2174 kHlpAssert(fRc || GetLastError() == ERROR_NOACCESS /*tinderwin2*/);
2175 fOldProt = 0;
2176 }
2177
2178 /* Query attributes for the current .rdata page. */
2179 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
2180 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
2181 kHlpAssert(cbProtRange);
2182 if (cbProtRange)
2183 {
2184 switch (ProtInfo.Protect)
2185 {
2186 case PAGE_READWRITE:
2187 case PAGE_WRITECOPY:
2188 case PAGE_EXECUTE_READWRITE:
2189 case PAGE_EXECUTE_WRITECOPY:
2190 /* Already writable, nothing to do. */
2191 fRc = TRUE;
2192 break;
2193
2194 default:
2195 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2196 case PAGE_READONLY:
2197 cbProtRange = cbPage;
2198 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2199 break;
2200
2201 case PAGE_EXECUTE:
2202 case PAGE_EXECUTE_READ:
2203 cbProtRange = cbPage;
2204 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2205 break;
2206 }
2207 kHlpAssertStmt(fRc, fOldProt = 0);
2208 }
2209 }
2210
2211 /*
2212 * Unslotted replacements are simple.
2213 */
2214 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2215 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2216 else
2217 {
2218 /*
2219 * Must find our module entry for this module, possibly creating one.
2220 */
2221 if (!pImportMod)
2222 {
2223 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2224 K_TRUE /*fAlwaysPresent*/);
2225 if (!pImportMod)
2226 {
2227 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2228 pszImport, pMod->pszPath);
2229 break;
2230 }
2231 }
2232 paThunks[iThunk].u1.AddressOfData
2233 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2234 }
2235 break;
2236 }
2237 }
2238 }
2239 }
2240
2241
2242 /* Next import descriptor. */
2243 pImpDesc++;
2244 }
2245
2246
2247 if (fOldProt)
2248 {
2249 DWORD fIgnore = 0;
2250 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2251 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2252 }
2253 }
2254
2255}
2256
2257
2258/**
2259 * Creates a module from a native kLdr module handle.
2260 *
2261 * @returns Module w/ 1 reference on success, NULL on failure.
2262 * @param pLdrMod The native kLdr module.
2263 * @param pszPath The normalized path to the module.
2264 * @param cbPath The module path length with terminator.
2265 * @param uHashPath The module path hash.
2266 * @param fDoReplacements Whether to do import replacements on this
2267 * module.
2268 */
2269static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2270 KBOOL fDoReplacements, PKWMODULE pVirtualApiMod)
2271{
2272 /*
2273 * Create the entry.
2274 */
2275 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2276 if (pMod)
2277 {
2278 pMod->pwszPath = (wchar_t *)(pMod + 1);
2279 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2280 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2281 pMod->uHashPath = uHashPath;
2282 pMod->cRefs = 1;
2283 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2284 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2285 pMod->fExe = K_FALSE;
2286 pMod->fNative = K_TRUE;
2287 pMod->pLdrMod = pLdrMod;
2288 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2289 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2290 pMod->iCrtSlot = KU8_MAX;
2291 pMod->fNeedReInit = K_FALSE;
2292 pMod->pszMsPdbSrvEndpoint = NULL;
2293 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2294 pMod->pVirtualApiMod = pVirtualApiMod;
2295 if (pVirtualApiMod)
2296 kwLdrModuleRetain(pVirtualApiMod);
2297
2298 if (fDoReplacements)
2299 {
2300 DWORD const dwSavedErr = GetLastError();
2301 kwLdrModuleDoNativeImportReplacements(pMod);
2302 SetLastError(dwSavedErr);
2303 }
2304
2305 KWLDR_LOG(("New module: %p LB %#010x %s (native%s%s)\n",
2306 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath,
2307 pVirtualApiMod ? ", virtual api => " : "", pVirtualApiMod ? pVirtualApiMod->pszPath : ""));
2308 g_cModules++;
2309 return kwLdrModuleLink(pMod);
2310 }
2311 return NULL;
2312}
2313
2314
2315
2316/**
2317 * Creates a module using the native loader.
2318 *
2319 * @returns Module w/ 1 reference on success, NULL on failure.
2320 * @param pszPath The normalized path to the module.
2321 * @param uHashPath The module path hash.
2322 * @param fDoReplacements Whether to do import replacements on this
2323 * module.
2324 */
2325static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2326{
2327 PKLDRMOD pLdrMod;
2328 int rc;
2329
2330 /*
2331 * HACK ALERT! Make sure the application path is searched when looking for
2332 * imports in the module we're loading.
2333 */
2334 /** @todo improve on this hack! */
2335 PKWMODULE pExe = g_Sandbox.pTool ? g_Sandbox.pTool->u.Sandboxed.pExe : NULL;
2336 if (pExe)
2337 {
2338 /* HACK ALERT! */
2339 wchar_t *pwzFilename = (wchar_t *)&pExe->pwszPath[pExe->offFilenameW];
2340 wchar_t wcSaved = pExe->pwszPath[pExe->offFilenameW];
2341 *pwzFilename = '\0';
2342 if (!SetDllDirectoryW(pExe->pwszPath))
2343 kwErrPrintf("SetDllDirectoryW failed: %u\n", GetLastError());
2344 KW_LOG(("kwLdrModuleCreateNative: Applied SetDllDirectoryW hack (%ls)\n", pExe->pwszPath));
2345 *pwzFilename = wcSaved;
2346 }
2347 else
2348 KW_LOG(("kwLdrModuleCreateNative: Warning! Too early for SetDllDirectoryW hack\n"));
2349
2350
2351 /*
2352 * Load the library and create a module structure for it.
2353 */
2354 rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2355 if (rc == 0)
2356 {
2357 KSIZE cchPath = kHlpStrLen(pszPath);
2358 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, cchPath + 1, uHashPath,
2359 fDoReplacements, NULL /*pVirtualApiMod*/);
2360 if (pMod)
2361 return pMod;
2362 kLdrModClose(pLdrMod);
2363 }
2364 return NULL;
2365}
2366
2367
2368/**
2369 * Checks if the given name could be a virtual API module or not.
2370 */
2371static KBOOL kwLdrIsVirtualApiModule(const char *pszName, KSIZE cchName)
2372{
2373 if (cchName <= 7)
2374 return K_FALSE;
2375 switch (*pszName)
2376 {
2377 default:
2378 return K_FALSE;
2379 case 'a':
2380 case 'A':
2381 if (pszName[1] != 'p' && pszName[1] != 'P')
2382 return K_FALSE;
2383 if (pszName[2] != 'i' && pszName[2] != 'I')
2384 return K_FALSE;
2385 break;
2386 case 'e':
2387 case 'E':
2388 if (pszName[1] != 'x' && pszName[1] != 'X')
2389 return K_FALSE;
2390 if (pszName[2] != 't' && pszName[2] != 'T')
2391 return K_FALSE;
2392 break;
2393 }
2394 if (pszName[3] != '-')
2395 return K_FALSE;
2396 if (pszName[4] != 'm' && pszName[4] != 'M')
2397 return K_FALSE;
2398 if (pszName[5] != 's' && pszName[5] != 'S')
2399 return K_FALSE;
2400 if (pszName[6] != '-')
2401 return K_FALSE;
2402 return K_TRUE;
2403}
2404
2405
2406/**
2407 * Try load what seems to be a virtual API DLL.
2408 *
2409 * This is a worker for kwLdrModuleResolveAndLookup and
2410 * kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule.
2411 *
2412 * @returns Pointer to module on success, NULL on failure.
2413 * @param pszName The name of the module. This must be
2414 * normalized already!
2415 * @param cchName The length of the name.
2416 */
2417static PKWMODULE kwLdrModuleTryLoadVirtualDll(const char *pszName, KSIZE cchName)
2418{
2419 HMODULE hModule;
2420
2421 /*
2422 * Look it up in the hash table.
2423 */
2424 KU32 const uHashPath = kwStrHash(pszName);
2425 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2426 PKWMODULE pMod = g_apModules[idxHash];
2427 if (pMod)
2428 {
2429 do
2430 {
2431 if ( pMod->uHashPath == uHashPath
2432 && kHlpStrComp(pMod->pszPath, pszName) == 0)
2433 return kwLdrModuleRetain(pMod);
2434 pMod = pMod->pNextHash;
2435 } while (pMod);
2436 }
2437
2438 /*
2439 * Not found. Try load it.
2440 */
2441 hModule = LoadLibraryA(pszName);
2442 if (!hModule)
2443 {
2444 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s failed (%u)\n", pszName, GetLastError()));
2445 return NULL;
2446 }
2447
2448 /*
2449 * Loaded successfully. Create a module for the real module.
2450 */
2451 pMod = kwLdrModuleForLoadedNativeByHandle(hModule, K_FALSE /*fEnsureCrtSlot*/, pszName);
2452 if (pMod)
2453 {
2454 /* Create a module for the virtual API name too, unless it is actually a real DLL. */
2455 if (stricmp(&pMod->pszPath[pMod->offFilename], pszName) != 0)
2456 {
2457 PKLDRMOD pLdrMod;
2458 int rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2459 if (rc == 0)
2460 {
2461 PKWMODULE pVirtMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszName, cchName + 1, kwStrHash(pszName),
2462 K_FALSE /*fDoReplacements*/, pMod /*pVirtualApiMod*/);
2463 if (pVirtMod)
2464 {
2465 kwLdrModuleRelease(pMod);
2466 pMod = pVirtMod;
2467 }
2468 else
2469 {
2470 kLdrModClose(pLdrMod);
2471 kwErrPrintf("out of memory\n");
2472 }
2473 }
2474 else
2475 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
2476 }
2477 else
2478 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 (%u)\n",
2991 szName, pMod->pszPath, rc, GetLastError());
2992 break;
2993 }
2994
2995 if (rc == 0)
2996 {
2997 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2998 kwLdrModuleGetImportCallback, pMod);
2999 if (rc == 0)
3000 {
3001#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3002 /*
3003 * Find the function table. No validation here because the
3004 * loader did that already, right...
3005 */
3006 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
3007 IMAGE_NT_HEADERS const *pNtHdrs;
3008 IMAGE_DATA_DIRECTORY const *pXcptDir;
3009 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
3010 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
3011 else
3012 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
3013 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
3014 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
3015 if (pXcptDir->Size > 0)
3016 {
3017 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
3018 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
3019 == pXcptDir->Size);
3020 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
3021 }
3022 else
3023 {
3024 pMod->u.Manual.cFunctions = 0;
3025 pMod->u.Manual.paFunctions = NULL;
3026 }
3027#endif
3028
3029 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
3030
3031 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
3032 if (rc == 0)
3033 {
3034 /*
3035 * Final finish.
3036 */
3037 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
3038 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
3039 pMod->u.Manual.enmReInitState = KWMODSTATE_NEEDS_BITS;
3040 if ( g_Sandbox.pTool
3041 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
3042 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3043 && !pMod->fExe)
3044 pMod->u.Manual.enmReInitState = KWMODSTATE_READY;
3045 g_cModules++;
3046 g_cNonNativeModules++;
3047 return pMod;
3048 }
3049 kwErrPrintf("kwLdrModuleCreateNonNativeSetupTls failed with %d for %s\n", rc, pMod->pszPath);
3050 }
3051 else
3052 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
3053 }
3054
3055 kwLdrModuleRelease(pMod);
3056 return NULL;
3057 }
3058
3059 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
3060 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3061 }
3062 else if (fFixed)
3063 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
3064 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
3065 else
3066 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3067 }
3068 else
3069 kwErrPrintf("kwLdrModuleCreateNonNative: out of memory!\n");
3070 }
3071 else
3072 kwErrPrintf("kwLdrModuleCreateNonNative: kLdrModNumberOfImports failed for '%s'\n", pszPath);
3073 }
3074 kLdrModClose(pLdrMod);
3075 }
3076 else
3077 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
3078 return NULL;
3079}
3080
3081
3082/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
3083static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
3084 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
3085{
3086 PKWMODULE pCurMod = (PKWMODULE)pvUser;
3087 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
3088 int rc;
3089 K_NOREF(pMod);
3090
3091 if (pImpMod->fNative)
3092 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
3093 iSymbol, pchSymbol, cchSymbol, pszVersion,
3094 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3095 puValue, pfKind);
3096 else
3097 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
3098 iSymbol, pchSymbol, cchSymbol, pszVersion,
3099 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3100 puValue, pfKind);
3101 if (rc == 0)
3102 {
3103 KU32 i = g_cSandboxReplacements;
3104 while (i-- > 0)
3105 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
3106 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
3107 {
3108 if ( !g_aSandboxReplacements[i].pszModule
3109 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
3110 {
3111 if ( pCurMod->fExe
3112 || !g_aSandboxReplacements[i].fOnlyExe)
3113 {
3114 KWLDR_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
3115 if (!g_aSandboxReplacements[i].fCrtSlotArray)
3116 *puValue = g_aSandboxReplacements[i].pfnReplacement;
3117 else
3118 {
3119 if (pImpMod->iCrtSlot == KU8_MAX)
3120 {
3121 rc = kwLdrModuleCreateCrtSlot(pImpMod);
3122 if (rc)
3123 KWLDR_LOG(("kwLdrModuleGetImportCallback: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
3124 }
3125 *puValue = ((KUPTR *)g_aSandboxReplacements[i].pfnReplacement)[pImpMod->iCrtSlot];
3126 }
3127 }
3128 break;
3129 }
3130 }
3131 }
3132
3133 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
3134 KWLDR_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
3135 return rc;
3136
3137}
3138
3139
3140/**
3141 * Gets the main entrypoint for a module.
3142 *
3143 * @returns 0 on success, KERR on failure
3144 * @param pMod The module.
3145 * @param puAddrMain Where to return the address.
3146 */
3147static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
3148{
3149 KLDRADDR uLdrAddrMain;
3150 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
3151 if (rc == 0)
3152 {
3153 *puAddrMain = (KUPTR)uLdrAddrMain;
3154 return 0;
3155 }
3156 return rc;
3157}
3158
3159
3160/**
3161 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
3162 *
3163 * @returns K_TRUE/K_FALSE.
3164 * @param pszFilename The filename (no path).
3165 * @param enmLocation The location.
3166 */
3167static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
3168{
3169 if (enmLocation != KWLOCATION_SYSTEM32)
3170 return K_TRUE;
3171 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3172 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3173 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3174 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3175 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3176 || kHlpStrNICompAscii(pszFilename, TUPLE("api-ms-win-crt-")) == 0
3177#if 0 /* for debugging, only for debugging. */
3178 || kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3179 || kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3180 || kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3181#endif
3182 ;
3183}
3184
3185
3186/**
3187 * Lazily initializes the g_pWinSys32 variable.
3188 */
3189static PKFSDIR kwLdrResolveWinSys32(void)
3190{
3191 KFSLOOKUPERROR enmError;
3192 PKFSDIR pWinSys32;
3193
3194 /* Get the path first. */
3195 char szSystem32[MAX_PATH];
3196 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
3197 {
3198 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
3199 strcpy(szSystem32, "C:\\Windows\\System32");
3200 }
3201
3202 /* Look it up and verify it. */
3203 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
3204 if (pWinSys32)
3205 {
3206 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
3207 {
3208 g_pWinSys32 = pWinSys32;
3209 return pWinSys32;
3210 }
3211
3212 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
3213 }
3214 else
3215 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
3216 return NULL;
3217}
3218
3219
3220/**
3221 * Whether we can load this DLL natively or not.
3222 *
3223 * @returns K_TRUE/K_FALSE.
3224 * @param pszFilename The filename (no path).
3225 * @param enmLocation The location.
3226 * @param pszFullPath The full filename and path.
3227 */
3228static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
3229{
3230 if (enmLocation == KWLOCATION_SYSTEM32)
3231 return K_TRUE;
3232 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
3233 return K_TRUE;
3234 if ( enmLocation == KWLOCATION_UNKNOWN
3235 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3236 return K_TRUE;
3237
3238 /* If the location is unknown, we must check if it's some dynamic loading
3239 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
3240 if (enmLocation == KWLOCATION_UNKNOWN)
3241 {
3242 PKFSDIR pWinSys32 = g_pWinSys32;
3243 if (!pWinSys32)
3244 pWinSys32 = kwLdrResolveWinSys32();
3245 if (pWinSys32)
3246 {
3247 KFSLOOKUPERROR enmError;
3248 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
3249 if (pFsObj)
3250 {
3251 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
3252 kFsCacheObjRelease(g_pFsCache, pFsObj);
3253 if (fInWinSys32)
3254 return K_TRUE;
3255 }
3256 }
3257 }
3258
3259 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3260 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3261 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3262 || kHlpStrNICompAscii(pszFilename, TUPLE("tbbmalloc")) == 0
3263 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3264 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3265 || ( enmLocation != KWLOCATION_UNKNOWN
3266 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3267#if 0 /* for debugging, only for debugging. */
3268 //|| kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3269 //|| kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3270 //|| kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3271#endif
3272 ;
3273}
3274
3275
3276/**
3277 * Check if the path leads to a regular file (that exists).
3278 *
3279 * @returns K_TRUE / K_FALSE
3280 * @param pszPath Path to the file to check out.
3281 */
3282static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
3283{
3284 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
3285 KSIZE cchPath = kHlpStrLen(pszPath);
3286 if ( cchPath > 3
3287 && pszPath[cchPath - 4] == '.'
3288 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
3289 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
3290 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
3291 {
3292 KFSLOOKUPERROR enmError;
3293 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3294 if (pFsObj)
3295 {
3296 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
3297 kFsCacheObjRelease(g_pFsCache, pFsObj);
3298 return fRc;
3299 }
3300 }
3301 else
3302 {
3303 BirdStat_T Stat;
3304 int rc = birdStatFollowLink(pszPath, &Stat);
3305 if (rc == 0)
3306 {
3307 if (S_ISREG(Stat.st_mode))
3308 return K_TRUE;
3309 }
3310 }
3311 return K_FALSE;
3312}
3313
3314
3315/**
3316 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
3317 *
3318 * If the file exists, we consult the module hash table before trying to load it
3319 * off the disk.
3320 *
3321 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
3322 * failure.
3323 * @param pszPath The name of the import module.
3324 * @param enmLocation The location we're searching. This is used in
3325 * the heuristics for determining if we can use the
3326 * native loader or need to sandbox the DLL.
3327 * @param pExe The executable (optional).
3328 * @param pszSearchPath The PATH to search (optional).
3329 */
3330static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
3331{
3332 /*
3333 * Does the file exists and is it a regular file?
3334 */
3335 if (kwLdrModuleIsRegularFile(pszPath))
3336 {
3337 /*
3338 * Yes! Normalize it and look it up in the hash table.
3339 */
3340 char szNormPath[1024];
3341 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
3342 if (rc == 0)
3343 {
3344 const char *pszName;
3345 KU32 const uHashPath = kwStrHash(szNormPath);
3346 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3347 PKWMODULE pMod = g_apModules[idxHash];
3348 if (pMod)
3349 {
3350 do
3351 {
3352 if ( pMod->uHashPath == uHashPath
3353 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3354 return kwLdrModuleRetain(pMod);
3355 pMod = pMod->pNextHash;
3356 } while (pMod);
3357 }
3358
3359 /*
3360 * Not in the hash table, so we have to load it from scratch.
3361 */
3362 pszName = kHlpGetFilename(szNormPath);
3363 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
3364 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
3365 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
3366 else
3367 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
3368 if (pMod)
3369 return pMod;
3370 return (PKWMODULE)~(KUPTR)0;
3371 }
3372 }
3373 return NULL;
3374}
3375
3376
3377/**
3378 * Gets a reference to the module by the given name.
3379 *
3380 * We must do the search path thing, as our hash table may multiple DLLs with
3381 * the same base name due to different tools version and similar. We'll use a
3382 * modified search sequence, though. No point in searching the current
3383 * directory for instance.
3384 *
3385 * @returns 0 on success, KERR on failure.
3386 * @param pszName The name of the import module.
3387 * @param pExe The executable (optional).
3388 * @param pImporter The module doing the importing (optional).
3389 * @param pszSearchPath The PATH to search (optional).
3390 * @param ppMod Where to return the module pointer w/ reference.
3391 */
3392static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
3393 const char *pszSearchPath, PKWMODULE *ppMod)
3394{
3395 KSIZE const cchName = kHlpStrLen(pszName);
3396 char szPath[1024];
3397 char *psz;
3398 PKWMODULE pMod = NULL;
3399 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
3400 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
3401
3402 /* Virtual API module. Normalize and try load it. */
3403 if (pMod == NULL && cchName > 7 && kwLdrIsVirtualApiModule(pszName, cchName))
3404 {
3405 if (cchName + cchSuffix >= sizeof(szPath))
3406 return KERR_BUFFER_OVERFLOW;
3407 kHlpMemCopy(szPath, pszName, cchName);
3408 if (fNeedSuffix)
3409 kHlpMemCopy(&szPath[cchName], ".dll", sizeof(".dll"));
3410 szPath[cchName + cchSuffix] = '\0';
3411 _strlwr(szPath);
3412 pMod = kwLdrModuleTryLoadVirtualDll(szPath, cchName + cchSuffix);
3413 }
3414
3415 /* The import path. */
3416 if (pMod == NULL && pImporter != NULL)
3417 {
3418 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
3419 return KERR_BUFFER_OVERFLOW;
3420
3421 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
3422 if (fNeedSuffix)
3423 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3424 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
3425 }
3426
3427 /* Application directory first. */
3428 if (pMod == NULL && pExe != NULL && pExe != pImporter)
3429 {
3430 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
3431 return KERR_BUFFER_OVERFLOW;
3432 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
3433 if (fNeedSuffix)
3434 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3435 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
3436 }
3437
3438 /* The windows directory. */
3439 if (pMod == NULL)
3440 {
3441 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
3442 if ( cchDir <= 2
3443 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
3444 return KERR_BUFFER_OVERFLOW;
3445 szPath[cchDir++] = '\\';
3446 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
3447 if (fNeedSuffix)
3448 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3449 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3450 }
3451
3452 /* The path. */
3453 if ( pMod == NULL
3454 && pszSearchPath)
3455 {
3456 const char *pszCur = pszSearchPath;
3457 while (*pszCur != '\0')
3458 {
3459 /* Find the end of the component */
3460 KSIZE cch = 0;
3461 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
3462 cch++;
3463
3464 if ( cch > 0 /* wrong, but whatever */
3465 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
3466 {
3467 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
3468 if ( szPath[cch - 1] != ':'
3469 && szPath[cch - 1] != '/'
3470 && szPath[cch - 1] != '\\')
3471 *pszDst++ = '\\';
3472 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
3473 if (fNeedSuffix)
3474 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
3475 *pszDst = '\0';
3476
3477 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3478 if (pMod)
3479 break;
3480 }
3481
3482 /* Advance */
3483 pszCur += cch;
3484 while (*pszCur == ';')
3485 pszCur++;
3486 }
3487 }
3488
3489 /* Return. */
3490 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3491 {
3492 *ppMod = pMod;
3493 return 0;
3494 }
3495 *ppMod = NULL;
3496 return KERR_GENERAL_FAILURE;
3497}
3498
3499
3500/**
3501 * Creates a CRT slot for the given module.
3502 *
3503 * @returns 0 on success, non-zero on failure.
3504 * @param pModule The module.
3505 */
3506static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3507{
3508 KSIZE iSlot;
3509 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3510 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3511 if (g_aCrtSlots[iSlot].pModule == NULL)
3512 {
3513 KLDRADDR uAddr;
3514 int rc;
3515
3516 /* Do the linking: */
3517 g_aCrtSlots[iSlot].pModule = pModule;
3518 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3519 pModule->iCrtSlot = (KU8)iSlot;
3520
3521 /* resolve symbols: */
3522 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3523 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3524 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3525 if (rc != 0)
3526 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3527
3528 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "_beginthreadex", 14,
3529 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3530 *(KUPTR *)&g_aCrtSlots[iSlot].pfnBeginThreadEx = rc == 0 ? (KUPTR)uAddr : 0;
3531 //if (rc != 0)
3532 // kwErrPrintf("Failed to resolved '_beginthreadex' in '%s': %d\n", pModule->pszPath, rc);
3533
3534 return 0;
3535 }
3536 kwErrPrintf("Out of CRT slots!\n");
3537 return KERR_NO_MEMORY;
3538}
3539
3540
3541/**
3542 * Locates the module structure for an already loaded native module.
3543 *
3544 * This will create a module structure if needed.
3545 *
3546 * @returns Pointer to the module structure on success, NULL on failure.
3547 * @param hModule The native module handle.
3548 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3549 * @param pszLogName The name to use for logging/errors.
3550 */
3551static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName)
3552{
3553 /*
3554 * Get a normalized path for it.
3555 */
3556 char szModPath[1024];
3557 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3558 {
3559 char szNormPath[1024];
3560 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3561 if (rc == 0)
3562 {
3563 /*
3564 * Hash the path and look it up.
3565 */
3566 KU32 uHashPath;
3567 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3568 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3569 PKWMODULE pMod = g_apModules[idxHash];
3570 if (pMod)
3571 {
3572 do
3573 {
3574 if ( pMod->uHashPath == uHashPath
3575 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3576 {
3577 kwLdrModuleRetain(pMod);
3578 break;
3579 }
3580 pMod = pMod->pNextHash;
3581 } while (pMod);
3582 }
3583
3584 /*
3585 * If not in the hash table, so create a module entry.
3586 */
3587 if (!pMod)
3588 {
3589 PKLDRMOD pLdrMod;
3590 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3591 if (rc == 0)
3592 {
3593 /** @todo more accurately determine location */
3594 const char *pszFilename = kHlpGetFilename(szNormPath);
3595 KBOOL fDoReplacements = kwLdrModuleShouldDoNativeReplacements(pszFilename, KWLOCATION_SYSTEM32);
3596 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3597 fDoReplacements, NULL /*pVirtualApiMod*/);
3598 if (!pMod)
3599 {
3600 kLdrModClose(pLdrMod);
3601 kwErrPrintf("out of memory\n");
3602 }
3603 }
3604 else
3605 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszLogName, rc);
3606 }
3607 if (pMod)
3608 {
3609 /*
3610 * Create a CRT slot for the module if necessary.
3611 */
3612 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3613 return pMod;
3614 rc = kwLdrModuleCreateCrtSlot(pMod);
3615 if (rc == 0)
3616 return pMod;
3617 kwLdrModuleRelease(pMod);
3618 }
3619 }
3620 else
3621 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszLogName, GetLastError());
3622 }
3623 else
3624 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszLogName, GetLastError());
3625 return NULL;
3626}
3627
3628
3629/**
3630 * Locates the module structure for an already loaded native module.
3631 *
3632 * This will create a module structure if needed.
3633 *
3634 * @returns Pointer to the module structure on success, NULL on failure.
3635 * @param pszName The name of the module.
3636 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3637 * @param fAlwaysPresent Whether the module is expected to always be present,
3638 * or not. If not, complain less.
3639 */
3640static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3641{
3642 /*
3643 * Locate the module handle and pass it to kwLdrModuleForLoadedNativeByHandle.
3644 */
3645 HANDLE hModule = GetModuleHandleA(pszName);
3646 if (hModule)
3647 return kwLdrModuleForLoadedNativeByHandle(hModule, fEnsureCrtSlot, pszName);
3648 if (fAlwaysPresent)
3649 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3650 return NULL;
3651}
3652
3653
3654/**
3655 * Does the TLS memory initialization for a module on the current thread.
3656 *
3657 * @returns 0 on success, error on failure.
3658 * @param pMod The module.
3659 */
3660static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3661{
3662 if (pMod->u.Manual.idxTls != KU32_MAX)
3663 {
3664 PTEB pTeb = NtCurrentTeb();
3665 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3666 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3667 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3668 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3669 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3670 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3671 if (pMod->u.Manual.cbTlsInitData)
3672 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3673 }
3674 return 0;
3675}
3676
3677
3678/**
3679 * Does the TLS callbacks for a module.
3680 *
3681 * @param pMod The module.
3682 * @param dwReason The callback reason.
3683 */
3684static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3685{
3686 if (pMod->u.Manual.cTlsCallbacks)
3687 {
3688 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3689 do
3690 {
3691 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *ppfnCallback, pMod->hOurMod, dwReason));
3692 (*ppfnCallback)(pMod->hOurMod, dwReason, 0);
3693 } while (*++ppfnCallback);
3694 }
3695}
3696
3697
3698/**
3699 * Does module initialization starting at @a pMod.
3700 *
3701 * This is initially used on the executable. Later it is used by the
3702 * LoadLibrary interceptor.
3703 *
3704 * @returns 0 on success, error on failure.
3705 * @param pMod The module to initialize.
3706 */
3707static int kwLdrModuleInitTree(PKWMODULE pMod)
3708{
3709 int rc = 0;
3710 if (!pMod->fNative)
3711 {
3712 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3713 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3714
3715 /*
3716 * Need to copy bits?
3717 */
3718 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3719 {
3720 if (pMod->u.Manual.fUseLdBuf)
3721 {
3722#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3723 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3724 {
3725 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3726 kHlpAssert(fRc); K_NOREF(fRc);
3727 }
3728#endif
3729 g_pModPrevInLdBuf = g_pModInLdBuf;
3730 g_pModInLdBuf = pMod;
3731 }
3732
3733 /* Do quick zeroing and copying when we can. */
3734 pMod->u.Manual.fCanDoQuick = K_FALSE;
3735 if ( pMod->u.Manual.fCanDoQuick
3736 && ( !pMod->u.Manual.fUseLdBuf
3737 || g_pModPrevInLdBuf == pMod))
3738 {
3739 /* Zero first. */
3740 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3741 switch (pMod->u.Manual.cQuickZeroChunks)
3742 {
3743 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3744 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3745 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3746 case 0: break;
3747 }
3748
3749 /* Then copy. */
3750 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3751 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3752 switch (pMod->u.Manual.cQuickCopyChunks)
3753 {
3754 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3755 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3756 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3757 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3758 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3759 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3760 case 0: break;
3761 }
3762 }
3763 /* Must copy the whole image. */
3764 else
3765 {
3766 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3767 pMod->u.Manual.fCanDoQuick = K_TRUE;
3768 }
3769 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3770 }
3771
3772#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3773 /*
3774 * Need to register function table?
3775 */
3776 if ( !pMod->u.Manual.fRegisteredFunctionTable
3777 && pMod->u.Manual.cFunctions > 0)
3778 {
3779 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3780 pMod->u.Manual.cFunctions,
3781 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3782 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3783 }
3784#endif
3785
3786
3787 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3788 {
3789 /*
3790 * Must do imports first, but mark our module as being initialized to avoid
3791 * endless recursion should there be a dependency loop.
3792 */
3793 KSIZE iImp;
3794 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3795
3796 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3797 {
3798 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3799 if (rc != 0)
3800 return rc;
3801 }
3802
3803 /* Do TLS allocations for module init? */
3804 rc = kwLdrCallTlsAllocateAndInit(pMod);
3805 if (rc != 0)
3806 return rc;
3807 if (pMod->u.Manual.cTlsCallbacks > 0)
3808 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3809
3810 /* Finally call the entry point. */
3811 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3812 if (rc == 0)
3813 pMod->u.Manual.enmState = KWMODSTATE_READY;
3814 else
3815 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3816 }
3817 }
3818 /*
3819 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3820 * _MSPDBSRV_ENDPOINT_ changes value.
3821 */
3822 else if (pMod->fNeedReInit)
3823 {
3824 int rc2;
3825 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3826 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3827 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3828 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3829 if (!rc && !rc2)
3830 { /* likely */ }
3831 else
3832 {
3833 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3834 if (rc2 && !rc)
3835 rc = rc2;
3836 }
3837 pMod->fNeedReInit = K_FALSE;
3838 }
3839 return rc;
3840}
3841
3842
3843/**
3844 * Looks up a module handle for a tool.
3845 *
3846 * @returns Referenced loader module on success, NULL on if not found.
3847 * @param pTool The tool.
3848 * @param hmod The module handle.
3849 */
3850static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3851{
3852 KUPTR const uHMod = (KUPTR)hmod;
3853 PKWMODULE *papMods;
3854 KU32 iEnd;
3855 KU32 i;
3856 PKWDYNLOAD pDynLoad;
3857
3858 if (pTool)
3859 { /* likely */ }
3860 else
3861 return NULL;
3862
3863 /* The executable. */
3864 if ( hmod == NULL
3865 || (pTool->u.Sandboxed.pExe && pTool->u.Sandboxed.pExe->hOurMod == hmod))
3866 {
3867 if (pTool->u.Sandboxed.pExe)
3868 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3869 return NULL;
3870 }
3871
3872 /*
3873 * Binary lookup using the module table.
3874 */
3875 papMods = pTool->u.Sandboxed.papModules;
3876 iEnd = pTool->u.Sandboxed.cModules;
3877 if (iEnd)
3878 {
3879 KU32 iStart = 0;
3880 i = iEnd / 2;
3881 for (;;)
3882 {
3883 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
3884 if (uHMod < uHModCur)
3885 {
3886 iEnd = i--;
3887 if (iStart <= i)
3888 { }
3889 else
3890 break;
3891 }
3892 else if (uHMod != uHModCur)
3893 {
3894 iStart = ++i;
3895 if (i < iEnd)
3896 { }
3897 else
3898 break;
3899 }
3900 /* We've got a match. Always return the non-virtual module (first) when there is one. */
3901 else if (!papMods[i]->pVirtualApiMod)
3902 return kwLdrModuleRetain(papMods[i]);
3903 else
3904 {
3905 while (i > 0 && papMods[i - 1]->pVirtualApiMod && papMods[i - 1]->hOurMod == hmod)
3906 i--;
3907 return kwLdrModuleRetain(papMods[i]);
3908 }
3909
3910 i = iStart + (iEnd - iStart) / 2;
3911 }
3912
3913#ifndef NDEBUG
3914 iStart = pTool->u.Sandboxed.cModules;
3915 while (--iStart > 0)
3916 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3917 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3918#endif
3919 }
3920
3921 /*
3922 * Dynamically loaded images.
3923 */
3924 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3925 if (pDynLoad->hmod == hmod)
3926 {
3927 if (pDynLoad->pMod)
3928 return kwLdrModuleRetain(pDynLoad->pMod);
3929 KWFS_TODO();
3930 return NULL;
3931 }
3932
3933 return NULL;
3934}
3935
3936/**
3937 * Adds the given module to the tool import table.
3938 *
3939 * @returns 0 on success, non-zero on failure.
3940 * @param pTool The tool.
3941 * @param pMod The module.
3942 */
3943static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3944{
3945 /*
3946 * Binary lookup. Locating the right slot for it, return if already there.
3947 */
3948 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3949 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3950 KU32 iEnd = pTool->u.Sandboxed.cModules;
3951 KU32 i;
3952 if (iEnd)
3953 {
3954 KU32 iStart = 0;
3955 i = iEnd / 2;
3956 for (;;)
3957 {
3958 PKWMODULE pCurMod = papMods[i];
3959 KUPTR const uHModCur = (KUPTR)pCurMod->hOurMod;
3960 if (uHMod < uHModCur)
3961 {
3962 iEnd = i;
3963 if (iStart < i)
3964 { }
3965 else
3966 break;
3967 }
3968 else if (uHMod != uHModCur)
3969 {
3970 iStart = ++i;
3971 if (i < iEnd)
3972 { }
3973 else
3974 break;
3975 }
3976 else
3977 {
3978 /* Already there in the table. The non-virtual module must be the first
3979 entry if we've got duplicate hmod values because of virtual modules. */
3980 if (pMod != pCurMod)
3981 {
3982 /* Skip to the last module with the same hmod. */
3983 while (i + 1 < iEnd && (KUPTR)(pCurMod = papMods[i + 1])->hOurMod == uHMod)
3984 {
3985 if (pMod == pCurMod)
3986 return 0;
3987 i++;
3988 }
3989
3990 /* Then scan backwards till the first one. */
3991 while (i > 0 && (KUPTR)(pCurMod = papMods[i - 1])->hOurMod == uHMod)
3992 {
3993 if (pMod == pCurMod)
3994 return 0;
3995 i--;
3996 }
3997 pCurMod = papMods[i];
3998 if (pMod != pCurMod)
3999 {
4000 if (pMod->pVirtualApiMod && !pCurMod->pVirtualApiMod)
4001 i++;
4002 break;
4003 }
4004 }
4005 return 0;
4006 }
4007
4008 i = iStart + (iEnd - iStart) / 2;
4009 }
4010#ifndef NDEBUG
4011 iStart = pTool->u.Sandboxed.cModules;
4012 while (--iStart > 0)
4013 {
4014 kHlpAssert(papMods[iStart] != pMod);
4015 kHlpAssert( (KUPTR)papMods[iStart]->hOurMod != uHMod
4016 || pMod->pVirtualApiMod
4017 || papMods[iStart]->pVirtualApiMod);
4018 }
4019 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod <= uHMod);
4020 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod >= uHMod);
4021#endif
4022 }
4023 else
4024 i = 0;
4025
4026 /*
4027 * Grow the table?
4028 */
4029 if ((pTool->u.Sandboxed.cModules % 16) == 0)
4030 {
4031 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
4032 if (!pvNew)
4033 return KERR_NO_MEMORY;
4034 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
4035 }
4036
4037 /* Insert it. */
4038 if (i != pTool->u.Sandboxed.cModules)
4039 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
4040 papMods[i] = kwLdrModuleRetain(pMod);
4041 pTool->u.Sandboxed.cModules++;
4042 KWLDR_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
4043 return 0;
4044}
4045
4046
4047/**
4048 * Adds the given module and all its imports to the
4049 *
4050 * @returns 0 on success, non-zero on failure.
4051 * @param pTool The tool.
4052 * @param pMod The module.
4053 */
4054static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
4055{
4056 int rc = kwToolAddModule(pTool, pMod);
4057 if (pMod->pVirtualApiMod && rc == 0)
4058 rc = kwToolAddModule(pTool, pMod->pVirtualApiMod);
4059 if (!pMod->fNative && rc == 0)
4060 {
4061 KSIZE iImp = pMod->u.Manual.cImpMods;
4062 while (iImp-- > 0)
4063 {
4064 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
4065 if (rc == 0)
4066 { }
4067 else
4068 break;
4069 }
4070 }
4071
4072 return 0;
4073}
4074
4075
4076/**
4077 * Creates a tool entry and inserts it.
4078 *
4079 * @returns Pointer to the tool entry. NULL on failure.
4080 * @param pToolFsObj The file object of the tool. The created tool
4081 * will be associated with it.
4082 *
4083 * A reference is donated by the caller and must be
4084 * released.
4085 * @param pszSearchPath The PATH environment variable value, or NULL.
4086 */
4087static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
4088{
4089 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
4090 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
4091 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
4092 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
4093 if (pTool)
4094 {
4095 KBOOL fRc;
4096 wchar_t wcSaved;
4097 wchar_t *pwcEnd;
4098 pTool->pwszPath = (wchar_t const *)(pTool + 1);
4099 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
4100 kHlpAssert(fRc); K_NOREF(fRc);
4101
4102 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
4103 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
4104 kHlpAssert(fRc);
4105
4106 /* HACK ALERT! This is to help the loader search the application directory. */
4107 pwcEnd = (wchar_t *)&pTool->pwszPath[pToolFsObj->cwcParent];
4108 wcSaved = *pwcEnd;
4109 *pwcEnd = '\0';
4110 if (!SetDllDirectoryW(pTool->pwszPath))
4111 kwErrPrintf("SetDllDirectoryW(tool) failed: %u\n", GetLastError());
4112 *pwcEnd = wcSaved;
4113
4114 pTool->enmType = KWTOOLTYPE_SANDBOXED;
4115 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
4116 NULL /*pExeMod*/, pszSearchPath);
4117 if (pTool->u.Sandboxed.pExe)
4118 {
4119 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
4120 if (rc == 0)
4121 {
4122 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
4123 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
4124 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
4125 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
4126 else
4127 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
4128 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
4129 }
4130 else
4131 {
4132 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
4133 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
4134 pTool->u.Sandboxed.pExe = NULL;
4135 pTool->enmType = KWTOOLTYPE_EXEC;
4136 }
4137 }
4138 else
4139 {
4140 kwErrPrintf("kwLdrModuleCreateNonNative failed!\n");
4141 pTool->enmType = KWTOOLTYPE_EXEC;
4142 }
4143
4144 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4145 g_cTools++;
4146 return pTool;
4147 }
4148 kwErrPrintf("kFsCacheObjAddUserData failed!\n");
4149 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4150 return NULL;
4151}
4152
4153
4154/**
4155 * Looks up the given tool, creating a new tool table entry if necessary.
4156 *
4157 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
4158 * @param pszExe The executable for the tool (not normalized).
4159 * @param cEnvVars Number of environment varibles.
4160 * @param papszEnvVars Environment variables. For getting the PATH.
4161 */
4162static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
4163{
4164 /*
4165 * We associate the tools instances with the file system objects.
4166 *
4167 * We'd like to do the lookup without invaliding the volatile parts of the
4168 * cache, thus the double lookup here. The cache gets invalidate later on.
4169 */
4170 KFSLOOKUPERROR enmError;
4171 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4172 if ( !pToolFsObj
4173 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
4174 {
4175 kFsCacheInvalidateCustomBoth(g_pFsCache);
4176 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4177 }
4178 if (pToolFsObj)
4179 {
4180 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
4181 {
4182 const char *pszSearchPath;
4183 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
4184 if (pTool)
4185 {
4186 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4187 return pTool;
4188 }
4189
4190 /*
4191 * Need to create a new tool.
4192 */
4193 pszSearchPath = NULL;
4194 while (cEnvVars-- > 0)
4195 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
4196 {
4197 pszSearchPath = &papszEnvVars[cEnvVars][5];
4198 break;
4199 }
4200
4201 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
4202 if (pTool)
4203 return pTool;
4204
4205 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
4206 }
4207 else
4208 {
4209 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4210 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
4211 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
4212 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
4213 }
4214 }
4215 else
4216 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
4217 return NULL;
4218}
4219
4220
4221
4222/*
4223 *
4224 * File system cache.
4225 * File system cache.
4226 * File system cache.
4227 *
4228 */
4229
4230
4231/**
4232 * This is for kDep.
4233 */
4234int kwFsPathExists(const char *pszPath)
4235{
4236 BirdTimeSpec_T TsIgnored;
4237 KFSLOOKUPERROR enmError;
4238 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
4239 if (pFsObj)
4240 {
4241 kFsCacheObjRelease(g_pFsCache, pFsObj);
4242 return 1;
4243 }
4244 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
4245}
4246
4247
4248/* duplicated in dir-nt-bird.c */
4249void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
4250{
4251 KFSLOOKUPERROR enmError;
4252 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
4253 if (pPathObj)
4254 {
4255 KSIZE off = pPathObj->cchParent;
4256 if (off > 0)
4257 {
4258 KSIZE offEnd = off + pPathObj->cchName;
4259 if (offEnd < cbFull)
4260 {
4261 PKFSDIR pAncestor;
4262
4263 pszFull[off + pPathObj->cchName] = '\0';
4264 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
4265
4266 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4267 {
4268 kHlpAssert(off > 1);
4269 kHlpAssert(pAncestor != NULL);
4270 kHlpAssert(pAncestor->Obj.cchName > 0);
4271 pszFull[--off] = '/';
4272 off -= pAncestor->Obj.cchName;
4273 kHlpAssert(pAncestor->Obj.cchParent == off);
4274 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4275 }
4276 kFsCacheObjRelease(g_pFsCache, pPathObj);
4277 return;
4278 }
4279 }
4280 else
4281 {
4282 if ((size_t)pPathObj->cchName + 1 < cbFull)
4283 {
4284 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
4285 pszFull[pPathObj->cchName] = '/';
4286 pszFull[pPathObj->cchName + 1] = '\0';
4287
4288 kFsCacheObjRelease(g_pFsCache, pPathObj);
4289 return;
4290 }
4291 }
4292
4293 /* do fallback. */
4294 kHlpAssertFailed();
4295 kFsCacheObjRelease(g_pFsCache, pPathObj);
4296 }
4297
4298 nt_fullpath(pszPath, pszFull, cbFull);
4299}
4300
4301
4302/**
4303 * Helper for getting the extension of a UTF-16 path.
4304 *
4305 * @returns Pointer to the extension or the terminator.
4306 * @param pwszPath The path.
4307 * @param pcwcExt Where to return the length of the extension.
4308 */
4309static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
4310{
4311 wchar_t const *pwszName = pwszPath;
4312 wchar_t const *pwszExt = NULL;
4313 for (;;)
4314 {
4315 wchar_t const wc = *pwszPath++;
4316 if (wc == '.')
4317 pwszExt = pwszPath;
4318 else if (wc == '/' || wc == '\\' || wc == ':')
4319 {
4320 pwszName = pwszPath;
4321 pwszExt = NULL;
4322 }
4323 else if (wc == '\0')
4324 {
4325 if (pwszExt)
4326 {
4327 *pcwcExt = pwszPath - pwszExt - 1;
4328 return pwszExt;
4329 }
4330 *pcwcExt = 0;
4331 return pwszPath - 1;
4332 }
4333 }
4334}
4335
4336
4337
4338/**
4339 * Parses the argument string passed in as pszSrc.
4340 *
4341 * @returns size of the processed arguments.
4342 * @param pszSrc Pointer to the commandline that's to be parsed.
4343 * @param pcArgs Where to return the number of arguments.
4344 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
4345 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
4346 *
4347 * @remarks Lifted from startuphacks-win.c
4348 */
4349static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
4350{
4351 int bs;
4352 char chQuote;
4353 char *pfFlags;
4354 int cbArgs;
4355 int cArgs;
4356
4357#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
4358#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
4359#define WHITE(c) ((c) == ' ' || (c) == '\t')
4360
4361#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
4362#define _ARG_RESPONSE 0x02 /* Argument read from response file */
4363#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
4364#define _ARG_ENV 0x08 /* Argument from environment */
4365#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
4366
4367 cArgs = 0;
4368 cbArgs = 0;
4369
4370#if 0
4371 /* argv[0] */
4372 PUTC((char)_ARG_NONZERO);
4373 PUTV;
4374 for (;;)
4375 {
4376 PUTC(*pszSrc);
4377 if (*pszSrc == 0)
4378 break;
4379 ++pszSrc;
4380 }
4381 ++pszSrc;
4382#endif
4383
4384 for (;;)
4385 {
4386 while (WHITE(*pszSrc))
4387 ++pszSrc;
4388 if (*pszSrc == 0)
4389 break;
4390 pfFlags = pchPool;
4391 PUTC((unsigned char)_ARG_NONZERO);
4392 PUTV;
4393 bs = 0; chQuote = 0;
4394 for (;;)
4395 {
4396 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
4397 {
4398 while (bs >= 2)
4399 {
4400 PUTC('\\');
4401 bs -= 2;
4402 }
4403 if (bs & 1)
4404 PUTC(*pszSrc);
4405 else
4406 {
4407 chQuote = chQuote ? 0 : *pszSrc;
4408 if (pfFlags != NULL)
4409 *pfFlags |= _ARG_DQUOTE;
4410 }
4411 bs = 0;
4412 }
4413 else if (*pszSrc == '\\')
4414 ++bs;
4415 else
4416 {
4417 while (bs != 0)
4418 {
4419 PUTC('\\');
4420 --bs;
4421 }
4422 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
4423 break;
4424 PUTC(*pszSrc);
4425 }
4426 ++pszSrc;
4427 }
4428 PUTC(0);
4429 }
4430
4431 *pcArgs = cArgs;
4432 return cbArgs;
4433}
4434
4435
4436
4437
4438/*
4439 *
4440 * Process and thread related APIs.
4441 * Process and thread related APIs.
4442 * Process and thread related APIs.
4443 *
4444 */
4445
4446/** Common worker for ExitProcess(), exit() and friends. */
4447static void WINAPI kwSandboxDoExit(int uExitCode)
4448{
4449 if (g_Sandbox.idMainThread == GetCurrentThreadId())
4450 {
4451 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
4452
4453 g_Sandbox.rcExitCode = (int)uExitCode;
4454
4455 /* Before we jump, restore the TIB as we're not interested in any
4456 exception chain stuff installed by the sandboxed executable. */
4457 *pTib = g_Sandbox.TibMainThread;
4458 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
4459
4460 longjmp(g_Sandbox.JmpBuf, 1);
4461 }
4462 KWFS_TODO();
4463}
4464
4465
4466/** ExitProcess replacement. */
4467static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
4468{
4469 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
4470 kwSandboxDoExit((int)uExitCode);
4471}
4472
4473
4474/** ExitProcess replacement. */
4475static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
4476{
4477 if (hProcess == GetCurrentProcess())
4478 kwSandboxDoExit(uExitCode);
4479 KWFS_TODO();
4480 return TerminateProcess(hProcess, uExitCode);
4481}
4482
4483
4484/** Normal CRT exit(). */
4485static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
4486{
4487 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
4488 kwSandboxDoExit(rcExitCode);
4489}
4490
4491
4492/** Quick CRT _exit(). */
4493static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
4494{
4495 /* Quick. */
4496 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
4497 kwSandboxDoExit(rcExitCode);
4498}
4499
4500
4501/** Return to caller CRT _cexit(). */
4502static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
4503{
4504 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
4505 kwSandboxDoExit(rcExitCode);
4506}
4507
4508
4509/** Quick return to caller CRT _c_exit(). */
4510static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
4511{
4512 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
4513 kwSandboxDoExit(rcExitCode);
4514}
4515
4516
4517/** Runtime error and exit _amsg_exit(). */
4518static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
4519{
4520 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
4521 kwSandboxDoExit(255);
4522}
4523
4524
4525/** CRT - terminate(). */
4526static void __cdecl kwSandbox_msvcrt_terminate(void)
4527{
4528 KW_LOG(("\nRuntime - terminate!\n"));
4529 kwSandboxDoExit(254);
4530}
4531
4532
4533/** CRT - _onexit */
4534static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
4535{
4536 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4537 {
4538 PKWEXITCALLACK pCallback;
4539 KW_LOG(("_onexit(%p)\n", pfnFunc));
4540 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4541
4542 pCallback = kHlpAlloc(sizeof(*pCallback));
4543 if (pCallback)
4544 {
4545 pCallback->pfnCallback = pfnFunc;
4546 pCallback->fAtExit = K_FALSE;
4547 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4548 g_Sandbox.pExitCallbackHead = pCallback;
4549 return pfnFunc;
4550 }
4551 return NULL;
4552 }
4553 //KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
4554 //return pfnFunc;
4555}
4556
4557
4558/** CRT - atexit */
4559static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
4560{
4561 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4562 {
4563 PKWEXITCALLACK pCallback;
4564 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4565 KW_LOG(("atexit(%p)\n", pfnFunc));
4566
4567 pCallback = kHlpAlloc(sizeof(*pCallback));
4568 if (pCallback)
4569 {
4570 pCallback->pfnCallback = (_onexit_t)pfnFunc;
4571 pCallback->fAtExit = K_TRUE;
4572 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4573 g_Sandbox.pExitCallbackHead = pCallback;
4574 return 0;
4575 }
4576 return -1;
4577 }
4578 //KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4579 //return 0;
4580}
4581
4582
4583/** Kernel32 - SetConsoleCtrlHandler(). */
4584static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4585{
4586 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4587 return TRUE;
4588}
4589
4590
4591/** The CRT internal __getmainargs() API. */
4592static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4593 int dowildcard, int const *piNewMode)
4594{
4595 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4596 *pargc = g_Sandbox.cArgs;
4597 *pargv = g_Sandbox.papszArgs;
4598 *penvp = g_Sandbox.environ;
4599
4600 /** @todo startinfo points at a newmode (setmode) value. */
4601 return 0;
4602}
4603
4604
4605/** The CRT internal __wgetmainargs() API. */
4606static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4607 int dowildcard, int const *piNewMode)
4608{
4609 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4610 *pargc = g_Sandbox.cArgs;
4611 *pargv = g_Sandbox.papwszArgs;
4612 *penvp = g_Sandbox.wenviron;
4613
4614 /** @todo startinfo points at a newmode (setmode) value. */
4615 return 0;
4616}
4617
4618
4619
4620/** Kernel32 - GetCommandLineA() */
4621static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4622{
4623 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4624 return g_Sandbox.pszCmdLine;
4625}
4626
4627
4628/** Kernel32 - GetCommandLineW() */
4629static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4630{
4631 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4632 return g_Sandbox.pwszCmdLine;
4633}
4634
4635
4636/** Kernel32 - GetStartupInfoA() */
4637static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4638{
4639 KW_LOG(("GetStartupInfoA\n"));
4640 GetStartupInfoA(pStartupInfo);
4641 pStartupInfo->lpReserved = NULL;
4642 pStartupInfo->lpTitle = NULL;
4643 pStartupInfo->lpReserved2 = NULL;
4644 pStartupInfo->cbReserved2 = 0;
4645}
4646
4647
4648/** Kernel32 - GetStartupInfoW() */
4649static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4650{
4651 KW_LOG(("GetStartupInfoW\n"));
4652 GetStartupInfoW(pStartupInfo);
4653 pStartupInfo->lpReserved = NULL;
4654 pStartupInfo->lpTitle = NULL;
4655 pStartupInfo->lpReserved2 = NULL;
4656 pStartupInfo->cbReserved2 = 0;
4657}
4658
4659
4660/** CRT - __p___argc(). */
4661static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4662{
4663 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4664 return &g_Sandbox.cArgs;
4665}
4666
4667
4668/** CRT - __p___argv(). */
4669static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4670{
4671 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4672 return &g_Sandbox.papszArgs;
4673}
4674
4675
4676/** CRT - __p___sargv(). */
4677static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4678{
4679 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4680 return &g_Sandbox.papwszArgs;
4681}
4682
4683
4684/** CRT - __p__acmdln(). */
4685static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4686{
4687 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4688 return (char **)&g_Sandbox.pszCmdLine;
4689}
4690
4691
4692/** CRT - __p__acmdln(). */
4693static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4694{
4695 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4696 return &g_Sandbox.pwszCmdLine;
4697}
4698
4699
4700/** CRT - __p__pgmptr(). */
4701static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4702{
4703 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4704 return &g_Sandbox.pgmptr;
4705}
4706
4707
4708/** CRT - __p__wpgmptr(). */
4709static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4710{
4711 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4712 return &g_Sandbox.wpgmptr;
4713}
4714
4715
4716/** CRT - _get_pgmptr(). */
4717static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4718{
4719 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4720 *ppszValue = g_Sandbox.pgmptr;
4721 return 0;
4722}
4723
4724
4725/** CRT - _get_wpgmptr(). */
4726static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4727{
4728 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4729 *ppwszValue = g_Sandbox.wpgmptr;
4730 return 0;
4731}
4732
4733/** Just in case. */
4734static void kwSandbox_msvcrt__wincmdln(void)
4735{
4736 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4737 KWFS_TODO();
4738}
4739
4740
4741/** Just in case. */
4742static void kwSandbox_msvcrt__wwincmdln(void)
4743{
4744 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4745 KWFS_TODO();
4746}
4747
4748/** CreateThread interceptor. */
4749static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4750 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4751 DWORD fFlags, PDWORD pidThread)
4752{
4753 HANDLE hThread = NULL;
4754 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4755 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4756 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4757 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4758 {
4759 /* Allow link::DbgThread. */
4760 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4761 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4762 }
4763 else
4764 KWFS_TODO();
4765 return hThread;
4766}
4767
4768
4769/** _beginthread - create a new thread. */
4770static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4771{
4772 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4773 KWFS_TODO();
4774 return 0;
4775}
4776
4777
4778/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4779static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4780 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4781 unsigned fCreate, unsigned *pidThread)
4782{
4783 /*
4784 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4785 * whatever it needs to.
4786 */
4787 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4788 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4789 pfnThreadProc, pvUser, fCreate, pidThread));
4790 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4791 {
4792 uintptr_t rcRet;
4793 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4794 if (!s_pfnReal)
4795 {
4796 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4797 if (!s_pfnReal)
4798 {
4799 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4800 __debugbreak();
4801 }
4802 }
4803 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4804 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4805 return rcRet;
4806 }
4807
4808 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4809 KWFS_TODO();
4810 return 0;
4811}
4812
4813
4814/** _beginthreadex - create a new thread. */
4815static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex_wrapped(void *pvSecAttr, unsigned cbStack,
4816 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4817 unsigned fCreate, unsigned *pidThread, PKWCRTSLOT pSlot)
4818{
4819 /*
4820 * Since the VC++ 12 (VS 2013) compiler, the 2nd pass is now threaded.
4821 * Let it do whatever it needs to.
4822 */
4823 KW_LOG(("kwSandbox_msvcrt__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4824 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4825 pfnThreadProc, pvUser, fCreate, pidThread));
4826 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4827 && pSlot->pfnBeginThreadEx)
4828 {
4829 uintptr_t rcRet = pSlot->pfnBeginThreadEx(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4830 KW_LOG(("kwSandbox_msvcrt__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4831 return rcRet;
4832 }
4833
4834 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4835 KWFS_TODO();
4836 return 0;
4837}
4838
4839CRT_SLOT_FUNCTION_WRAPPER(uintptr_t __cdecl, kwSandbox_msvcrt__beginthreadex,
4840 (void *pvSecAttr, unsigned cbStack, unsigned (__stdcall *pfnThreadProc)(void *),
4841 void *pvUser, unsigned fCreate, unsigned *pidThread),
4842 (pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread, &g_aCrtSlots[iCrtSlot]));
4843
4844
4845
4846/*
4847 *
4848 * Environment related APIs.
4849 * Environment related APIs.
4850 * Environment related APIs.
4851 *
4852 */
4853
4854/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4855static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4856{
4857 char *pszzEnv;
4858 char *pszCur;
4859 KSIZE cbNeeded = 1;
4860 KSIZE iVar = 0;
4861
4862 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4863
4864 /* Figure how space much we need first. */
4865 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4866 cbNeeded += kHlpStrLen(pszCur) + 1;
4867
4868 /* Allocate it. */
4869 pszzEnv = kHlpAlloc(cbNeeded);
4870 if (pszzEnv)
4871 {
4872 char *psz = pszzEnv;
4873 iVar = 0;
4874 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4875 {
4876 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4877 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4878 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4879 }
4880 *psz++ = '\0';
4881 kHlpAssert((KUPTR)(psz - pszzEnv) == cbNeeded);
4882 }
4883
4884 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4885#if 0
4886 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4887 pszCur = pszzEnv;
4888 iVar = 0;
4889 while (*pszCur)
4890 {
4891 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4892 iVar++;
4893 pszCur += kHlpStrLen(pszCur) + 1;
4894 }
4895 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4896 pszCur++;
4897 fprintf(stderr, "ended at %p, after %u bytes (expected %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4898#endif
4899 return pszzEnv;
4900}
4901
4902
4903/** Kernel32 - GetEnvironmentStrings */
4904static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4905{
4906 KW_LOG(("GetEnvironmentStrings!\n"));
4907 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4908}
4909
4910
4911/** Kernel32 - GetEnvironmentStringsW */
4912static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4913{
4914 wchar_t *pwszzEnv;
4915 wchar_t *pwszCur;
4916 KSIZE cwcNeeded = 1;
4917 KSIZE iVar = 0;
4918
4919 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4920
4921 /* Figure how space much we need first. */
4922 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4923 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4924
4925 /* Allocate it. */
4926 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4927 if (pwszzEnv)
4928 {
4929 wchar_t *pwsz = pwszzEnv;
4930 iVar = 0;
4931 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4932 {
4933 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4934 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4935 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4936 }
4937 *pwsz++ = '\0';
4938 kHlpAssert((KUPTR)(pwsz - pwszzEnv) == cwcNeeded);
4939 }
4940
4941 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4942 return pwszzEnv;
4943}
4944
4945
4946/** Kernel32 - FreeEnvironmentStringsA */
4947static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4948{
4949 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4950 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4951 kHlpFree(pszzEnv);
4952 return TRUE;
4953}
4954
4955
4956/** Kernel32 - FreeEnvironmentStringsW */
4957static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4958{
4959 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4960 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4961 kHlpFree(pwszzEnv);
4962 return TRUE;
4963}
4964
4965
4966/**
4967 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4968 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4969 *
4970 * @returns 0 on success, non-zero on failure.
4971 * @param pSandbox The sandbox.
4972 * @param cMin Minimum size, including terminator.
4973 */
4974static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4975{
4976 void *pvNew;
4977 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4978 KSIZE cNew = cOld + 256;
4979 while (cNew < cMin)
4980 cNew += 256;
4981
4982 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4983 if (pvNew)
4984 {
4985 pSandbox->environ = (char **)pvNew;
4986 pSandbox->environ[cOld] = NULL;
4987
4988 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4989 if (pvNew)
4990 {
4991 pSandbox->papszEnvVars = (char **)pvNew;
4992 pSandbox->papszEnvVars[cOld] = NULL;
4993
4994 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4995 if (pvNew)
4996 {
4997 pSandbox->wenviron = (wchar_t **)pvNew;
4998 pSandbox->wenviron[cOld] = NULL;
4999
5000 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
5001 if (pvNew)
5002 {
5003 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
5004 pSandbox->papwszEnvVars[cOld] = NULL;
5005
5006 pSandbox->cEnvVarsAllocated = cNew;
5007 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
5008 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
5009 return 0;
5010 }
5011 }
5012 }
5013 }
5014 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
5015 return KERR_NO_MEMORY;
5016}
5017
5018
5019/**
5020 * Sets an environment variable, ANSI style.
5021 *
5022 * @returns 0 on success, non-zero on failure.
5023 * @param pSandbox The sandbox.
5024 * @param pchVar The variable name.
5025 * @param cchVar The length of the name.
5026 * @param pszValue The value.
5027 */
5028static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
5029{
5030 /* Allocate and construct the new strings. */
5031 KSIZE cchTmp = kHlpStrLen(pszValue);
5032 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
5033 if (pszNew)
5034 {
5035 wchar_t *pwszNew;
5036 kHlpMemCopy(pszNew, pchVar, cchVar);
5037 pszNew[cchVar] = '=';
5038 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
5039 cchTmp += cchVar + 1;
5040 pszNew[cchTmp] = '\0';
5041
5042 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
5043 if (pwszNew)
5044 {
5045 /* Look it up. */
5046 KSIZE iVar = 0;
5047 char *pszEnv;
5048 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5049 {
5050 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5051 && pszEnv[cchVar] == '=')
5052 {
5053 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5054 " iVar=%d: %p='%s' and %p='%ls'\n",
5055 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5056 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5057 iVar, pszNew, pszNew, pwszNew, pwszNew));
5058
5059 kHlpFree(pSandbox->papszEnvVars[iVar]);
5060 pSandbox->papszEnvVars[iVar] = pszNew;
5061 pSandbox->environ[iVar] = pszNew;
5062
5063 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5064 pSandbox->papwszEnvVars[iVar] = pwszNew;
5065 pSandbox->wenviron[iVar] = pwszNew;
5066 return 0;
5067 }
5068 iVar++;
5069 }
5070
5071 /* Not found, do we need to grow the table first? */
5072 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5073 kwSandboxGrowEnv(pSandbox, iVar + 2);
5074 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5075 {
5076 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5077
5078 pSandbox->papszEnvVars[iVar + 1] = NULL;
5079 pSandbox->papszEnvVars[iVar] = pszNew;
5080 pSandbox->environ[iVar + 1] = NULL;
5081 pSandbox->environ[iVar] = pszNew;
5082
5083 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5084 pSandbox->papwszEnvVars[iVar] = pwszNew;
5085 pSandbox->wenviron[iVar + 1] = NULL;
5086 pSandbox->wenviron[iVar] = pwszNew;
5087 return 0;
5088 }
5089
5090 kHlpFree(pwszNew);
5091 }
5092 kHlpFree(pszNew);
5093 }
5094 KW_LOG(("Out of memory!\n"));
5095 return 0;
5096}
5097
5098
5099/**
5100 * Sets an environment variable, UTF-16 style.
5101 *
5102 * @returns 0 on success, non-zero on failure.
5103 * @param pSandbox The sandbox.
5104 * @param pwcVar The variable name.
5105 * @param cwcVar The length of the name.
5106 * @param pwszValue The value.
5107 */
5108static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
5109{
5110 /* Allocate and construct the new strings. */
5111 KSIZE cwcTmp = kwUtf16Len(pwszValue);
5112 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
5113 if (pwszNew)
5114 {
5115 char *pszNew;
5116 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
5117 pwszNew[cwcVar] = '=';
5118 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
5119 cwcTmp += cwcVar + 1;
5120 pwszNew[cwcVar] = '\0';
5121
5122 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
5123 if (pszNew)
5124 {
5125 /* Look it up. */
5126 KSIZE iVar = 0;
5127 wchar_t *pwszEnv;
5128 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5129 {
5130 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
5131 && pwszEnv[cwcVar] == '=')
5132 {
5133 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5134 " iVar=%d: %p='%s' and %p='%ls'\n",
5135 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5136 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5137 iVar, pszNew, pszNew, pwszNew, pwszNew));
5138
5139 kHlpFree(pSandbox->papszEnvVars[iVar]);
5140 pSandbox->papszEnvVars[iVar] = pszNew;
5141 pSandbox->environ[iVar] = pszNew;
5142
5143 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5144 pSandbox->papwszEnvVars[iVar] = pwszNew;
5145 pSandbox->wenviron[iVar] = pwszNew;
5146 return 0;
5147 }
5148 iVar++;
5149 }
5150
5151 /* Not found, do we need to grow the table first? */
5152 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5153 kwSandboxGrowEnv(pSandbox, iVar + 2);
5154 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5155 {
5156 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5157
5158 pSandbox->papszEnvVars[iVar + 1] = NULL;
5159 pSandbox->papszEnvVars[iVar] = pszNew;
5160 pSandbox->environ[iVar + 1] = NULL;
5161 pSandbox->environ[iVar] = pszNew;
5162
5163 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5164 pSandbox->papwszEnvVars[iVar] = pwszNew;
5165 pSandbox->wenviron[iVar + 1] = NULL;
5166 pSandbox->wenviron[iVar] = pwszNew;
5167 return 0;
5168 }
5169
5170 kHlpFree(pwszNew);
5171 }
5172 kHlpFree(pszNew);
5173 }
5174 KW_LOG(("Out of memory!\n"));
5175 return 0;
5176}
5177
5178
5179/** ANSI unsetenv worker. */
5180static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5181{
5182 KSIZE iVar = 0;
5183 char *pszEnv;
5184 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5185 {
5186 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5187 && pszEnv[cchVar] == '=')
5188 {
5189 KSIZE cVars = iVar;
5190 while (pSandbox->papszEnvVars[cVars])
5191 cVars++;
5192 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
5193 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
5194
5195 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5196 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5197 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5198
5199 kHlpFree(pSandbox->papszEnvVars[iVar]);
5200 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5201 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5202 pSandbox->papszEnvVars[cVars] = NULL;
5203 pSandbox->environ[cVars] = NULL;
5204
5205 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5206 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5207 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5208 pSandbox->papwszEnvVars[cVars] = NULL;
5209 pSandbox->wenviron[cVars] = NULL;
5210 return 0;
5211 }
5212 iVar++;
5213 }
5214 return KERR_ENVVAR_NOT_FOUND;
5215}
5216
5217
5218/** UTF-16 unsetenv worker. */
5219static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5220{
5221 KSIZE iVar = 0;
5222 wchar_t *pwszEnv;
5223 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5224 {
5225 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5226 && pwszEnv[cwcVar] == '=')
5227 {
5228 KSIZE cVars = iVar;
5229 while (pSandbox->papwszEnvVars[cVars])
5230 cVars++;
5231 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
5232 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
5233
5234 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5235 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5236 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5237
5238 kHlpFree(pSandbox->papszEnvVars[iVar]);
5239 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5240 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5241 pSandbox->papszEnvVars[cVars] = NULL;
5242 pSandbox->environ[cVars] = NULL;
5243
5244 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5245 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5246 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5247 pSandbox->papwszEnvVars[cVars] = NULL;
5248 pSandbox->wenviron[cVars] = NULL;
5249 return 0;
5250 }
5251 iVar++;
5252 }
5253 return KERR_ENVVAR_NOT_FOUND;
5254}
5255
5256
5257
5258/** ANSI getenv worker. */
5259static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5260{
5261 KSIZE iVar = 0;
5262 char *pszEnv;
5263 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
5264 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5265 && pszEnv[cchVar] == '=')
5266 return &pszEnv[cchVar + 1];
5267 return NULL;
5268}
5269
5270
5271/** UTF-16 getenv worker. */
5272static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5273{
5274 KSIZE iVar = 0;
5275 wchar_t *pwszEnv;
5276 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
5277 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5278 && pwszEnv[cwcVar] == '=')
5279 return &pwszEnv[cwcVar + 1];
5280 return NULL;
5281}
5282
5283
5284/** Kernel32 - GetEnvironmentVariableA() */
5285static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
5286{
5287 char *pszFoundValue;
5288 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5289
5290 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5291 if (pszFoundValue)
5292 {
5293 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
5294 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
5295 return cchRet;
5296 }
5297 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
5298 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5299 return 0;
5300}
5301
5302
5303/** Kernel32 - GetEnvironmentVariableW() */
5304static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
5305{
5306 wchar_t *pwszFoundValue;
5307 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5308
5309 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5310 if (pwszFoundValue)
5311 {
5312 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
5313 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
5314 return cchRet;
5315 }
5316 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
5317 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5318 return 0;
5319}
5320
5321
5322/** Kernel32 - SetEnvironmentVariableA() */
5323static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
5324{
5325 int rc;
5326 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5327
5328 if (pszValue)
5329 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5330 else
5331 {
5332 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5333 rc = 0; //??
5334 }
5335 if (rc == 0)
5336 {
5337 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
5338 return TRUE;
5339 }
5340 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5341 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
5342 return FALSE;
5343}
5344
5345
5346/** Kernel32 - SetEnvironmentVariableW() */
5347static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
5348{
5349 int rc;
5350 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5351
5352 if (pwszValue)
5353 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5354 else
5355 {
5356 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5357 rc = 0; //??
5358 }
5359 if (rc == 0)
5360 {
5361 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
5362 return TRUE;
5363 }
5364 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5365 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
5366 return FALSE;
5367}
5368
5369
5370/** Kernel32 - ExpandEnvironmentStringsA() */
5371static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
5372{
5373 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5374 KWFS_TODO();
5375 return 0;
5376}
5377
5378
5379/** Kernel32 - ExpandEnvironmentStringsW() */
5380static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
5381{
5382 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5383 KWFS_TODO();
5384 return 0;
5385}
5386
5387
5388/** CRT - _putenv(). */
5389static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
5390{
5391 int rc;
5392 char const *pszEqual;
5393 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5394
5395 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
5396 if (pszEqual)
5397 {
5398 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
5399 if (rc == 0)
5400 { }
5401 else
5402 rc = -1;
5403 }
5404 else
5405 {
5406 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
5407 rc = 0;
5408 }
5409 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
5410 return rc;
5411}
5412
5413
5414/** CRT - _wputenv(). */
5415static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
5416{
5417 int rc;
5418 wchar_t const *pwszEqual;
5419 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5420
5421 pwszEqual = wcschr(pwszVarEqualValue, '=');
5422 if (pwszEqual)
5423 {
5424 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
5425 if (rc == 0)
5426 { }
5427 else
5428 rc = -1;
5429 }
5430 else
5431 {
5432 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
5433 rc = 0;
5434 }
5435 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
5436 return rc;
5437}
5438
5439
5440/** CRT - _putenv_s(). */
5441static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
5442{
5443 char const *pszEqual;
5444 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5445
5446 pszEqual = kHlpStrChr(pszVar, '=');
5447 if (pszEqual == NULL)
5448 {
5449 if (pszValue)
5450 {
5451 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5452 if (rc == 0)
5453 {
5454 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
5455 return 0;
5456 }
5457 }
5458 else
5459 {
5460 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5461 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
5462 return 0;
5463 }
5464 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
5465 return ENOMEM;
5466 }
5467 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
5468 return EINVAL;
5469}
5470
5471
5472/** CRT - _wputenv_s(). */
5473static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
5474{
5475 wchar_t const *pwszEqual;
5476 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5477
5478 pwszEqual = wcschr(pwszVar, '=');
5479 if (pwszEqual == NULL)
5480 {
5481 if (pwszValue)
5482 {
5483 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5484 if (rc == 0)
5485 {
5486 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
5487 return 0;
5488 }
5489 }
5490 else
5491 {
5492 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5493 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
5494 return 0;
5495 }
5496 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
5497 return ENOMEM;
5498 }
5499 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
5500 return EINVAL;
5501}
5502
5503
5504/** CRT - get pointer to the __initenv variable (initial environment). */
5505static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
5506{
5507 KW_LOG(("__p___initenv\n"));
5508 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5509 KWFS_TODO();
5510 return &g_Sandbox.initenv;
5511}
5512
5513
5514/** CRT - get pointer to the __winitenv variable (initial environment). */
5515static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
5516{
5517 KW_LOG(("__p___winitenv\n"));
5518 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5519 KWFS_TODO();
5520 return &g_Sandbox.winitenv;
5521}
5522
5523
5524/** CRT - get pointer to the _environ variable (current environment). */
5525static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
5526{
5527 KW_LOG(("__p__environ\n"));
5528 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5529 return &g_Sandbox.environ;
5530}
5531
5532
5533/** CRT - get pointer to the _wenviron variable (current environment). */
5534static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
5535{
5536 KW_LOG(("__p__wenviron\n"));
5537 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5538 return &g_Sandbox.wenviron;
5539}
5540
5541
5542/** CRT - get the _environ variable (current environment).
5543 * @remarks Not documented or prototyped? */
5544static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
5545{
5546 KWFS_TODO(); /** @todo check the callers expectations! */
5547 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5548 *ppapszEnviron = g_Sandbox.environ;
5549 return 0;
5550}
5551
5552
5553/** CRT - get the _wenviron variable (current environment).
5554 * @remarks Not documented or prototyped? */
5555static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
5556{
5557 KWFS_TODO(); /** @todo check the callers expectations! */
5558 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5559 *ppapwszEnviron = g_Sandbox.wenviron;
5560 return 0;
5561}
5562
5563
5564/** CRT - _wdupenv_s() (see _tdupenv_s(). */
5565static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
5566 PKWCRTSLOT pSlot)
5567{
5568 errno_t rc;
5569 wchar_t *pwszValue;
5570 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5571
5572 if (ppwszValue)
5573 {
5574 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
5575 if (pwszValue)
5576 {
5577 size_t cwcValue = wcslen(pwszValue);
5578 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
5579 if (pwszDst)
5580 {
5581 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
5582 pwszDst[cwcValue] = '\0';
5583 *ppwszValue = pwszDst;
5584 if (pcwcValue)
5585 *pcwcValue = cwcValue;
5586 rc = 0;
5587 }
5588 else
5589 {
5590 *ppwszValue = NULL;
5591 if (pcwcValue)
5592 *pcwcValue = 0;
5593 rc = ENOMEM;
5594 }
5595 }
5596 else
5597 {
5598 *ppwszValue = NULL;
5599 if (pcwcValue)
5600 *pcwcValue = 0;
5601 rc = 0;
5602 }
5603 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5604 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5605 }
5606 else
5607 {
5608 /*
5609 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5610 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5611 * was set to a value.
5612 */
5613 if (pcwcValue)
5614 *pcwcValue = 0;
5615 rc = EINVAL;
5616 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5617 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5618 }
5619 return rc;
5620}
5621CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5622 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5623 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5624
5625
5626
5627/*
5628 *
5629 * Loader related APIs
5630 * Loader related APIs
5631 * Loader related APIs
5632 *
5633 */
5634
5635/**
5636 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5637 */
5638static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5639{
5640 /* Load it first. */
5641 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5642 if (hmod)
5643 {
5644 pDynLoad->hmod = hmod;
5645 pDynLoad->pMod = NULL; /* indicates special */
5646
5647 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5648 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5649 KWLDR_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5650 }
5651 else
5652 kHlpFree(pDynLoad);
5653 return hmod;
5654}
5655
5656
5657/**
5658 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5659 */
5660static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5661{
5662 static const char s_szDll[] = ".dll";
5663 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5664 PKWMODULE pMod;
5665 char szNormPath[256];
5666
5667 /*
5668 * Lower case it and make sure it ends with .dll.
5669 */
5670 if (cbFilename > sizeof(szNormPath))
5671 {
5672 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5673 return NULL;
5674 }
5675 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5676 _strlwr(szNormPath);
5677 kHlpAssert(cbFilename > 7 /* api-ms- */ );
5678 if (strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5679 {
5680 if (cbFilename + sizeof(s_szDll) - 1 > sizeof(szNormPath))
5681 {
5682 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5683 return NULL;
5684 }
5685
5686 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5687 cbFilename += sizeof(s_szDll) - 1;
5688 }
5689
5690 /*
5691 * Try load it.
5692 */
5693 pMod = kwLdrModuleTryLoadVirtualDll(szNormPath, cbFilename - 1);
5694 if (pMod)
5695 {
5696 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5697
5698 pDynLoad->pMod = pMod;
5699 pDynLoad->hmod = pMod->hOurMod;
5700
5701 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5702 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5703 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5704 return pDynLoad->hmod;
5705 }
5706 kHlpFree(pDynLoad);
5707 return NULL;
5708}
5709
5710
5711/** Kernel32 - LoadLibraryExA() */
5712static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5713{
5714 KSIZE cchFilename = kHlpStrLen(pszFilename);
5715 const char *pszSearchPath;
5716 PKWDYNLOAD pDynLoad;
5717 PKWMODULE pMod;
5718 int rc;
5719 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5720 //fprintf(stderr, "LoadLibraryExA: %s, %#x\n", pszFilename, fFlags);
5721
5722 /*
5723 * Deal with a couple of extremely unlikely special cases right away.
5724 */
5725 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5726 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5727 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5728 { /* likely */ }
5729 else
5730 {
5731 KWFS_TODO();
5732 return LoadLibraryExA(pszFilename, hFile, fFlags);
5733 }
5734
5735 /*
5736 * Check if we've already got a dynload entry for this one.
5737 */
5738 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5739 if ( pDynLoad->cchRequest == cchFilename
5740 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5741 {
5742 if (pDynLoad->pMod)
5743 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5744 else
5745 rc = 0;
5746 if (rc == 0)
5747 {
5748 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5749 return pDynLoad->hmod;
5750 }
5751 SetLastError(ERROR_DLL_INIT_FAILED);
5752 return NULL;
5753 }
5754
5755 /*
5756 * Allocate a dynload entry for the request.
5757 */
5758 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5759 if (pDynLoad)
5760 {
5761 pDynLoad->cchRequest = cchFilename;
5762 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5763 }
5764 else
5765 {
5766 KWLDR_LOG(("LoadLibraryExA: Out of memory!\n"));
5767 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5768 return NULL;
5769 }
5770
5771 /*
5772 * Deal with resource / data DLLs.
5773 */
5774 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5775 | LOAD_LIBRARY_AS_DATAFILE
5776 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5777 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5778 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5779
5780 /*
5781 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5782 */
5783 if ( kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5784 && kHlpIsFilenameOnly(pszFilename))
5785 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5786
5787 /*
5788 * Normal library loading.
5789 * We start by being very lazy and reusing the code for resolving imports.
5790 */
5791 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5792 if (!kHlpIsFilenameOnly(pszFilename))
5793 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5794#if 1 /* HACK ALERT! We run into trouble with a 2nd mspdb140.dll instance (x64 + x86), so use the one already loaded. A call
5795 * to NdrClientCall2 at ConnectToServer+0x426 fails with E_INVALIDARG. Problems with multiple connections from same PID? */
5796 else if ( strcmp(pszFilename, "mspdb140.dll") == 0
5797 && GetModuleHandleA(pszFilename) != NULL)
5798 {
5799 pMod = kwLdrModuleForLoadedNativeByHandle(GetModuleHandleA(pszFilename), K_FALSE, pszFilename);
5800 KWLDR_LOG(("LoadLibraryExA: mspdb140 hack: pMod=%p\n", pMod));
5801 }
5802#endif
5803 else
5804 {
5805 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5806 if (rc != 0)
5807 pMod = NULL;
5808 }
5809 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5810 {
5811 /* Enter it into the tool module table and dynamic link request cache. */
5812 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5813
5814 pDynLoad->pMod = pMod;
5815 pDynLoad->hmod = pMod->hOurMod;
5816
5817 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5818 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5819
5820 /*
5821 * Make sure it's initialized (need to link it first since DllMain may
5822 * use loader APIs).
5823 */
5824 rc = kwLdrModuleInitTree(pMod);
5825 if (rc == 0)
5826 {
5827 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5828 return pDynLoad->hmod;
5829 }
5830
5831 SetLastError(ERROR_DLL_INIT_FAILED);
5832 }
5833 else
5834 {
5835 KWFS_TODO();
5836 kHlpFree(pDynLoad);
5837 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5838 }
5839 return NULL;
5840}
5841
5842
5843/** Kernel32 - LoadLibraryExA() for native overloads */
5844static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5845{
5846 char szPath[1024];
5847 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5848
5849 /*
5850 * We may have to help resolved unqualified DLLs living in the executable directory.
5851 */
5852 if ( kHlpIsFilenameOnly(pszFilename)
5853 && g_Sandbox.pTool
5854 && g_Sandbox.pTool->u.Sandboxed.pExe)
5855 {
5856 KSIZE const cchFilename = kHlpStrLen(pszFilename);
5857#define MY_IMATCH(a_szName) (cchFilename == sizeof(a_szName) - 1 && kHlpStrICompAscii(pszFilename, a_szName) == 0)
5858 if ( !kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5859 && !MY_IMATCH("ntdll")
5860 && !MY_IMATCH("kernel32")
5861 && !MY_IMATCH("ntdll.dll")
5862 && !MY_IMATCH("kernelbase")
5863 && !MY_IMATCH("kernel32.dll")
5864 && !MY_IMATCH("kernelbase.dll")
5865 )
5866#undef MY_IMATCH
5867 {
5868 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5869 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5870 {
5871 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5872 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5873 if (kwFsPathExists(szPath))
5874 {
5875 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5876 pszFilename = szPath;
5877 }
5878 }
5879
5880 if (pszFilename != szPath)
5881 {
5882 KSIZE cchSuffix = 0;
5883 KBOOL fNeedSuffix = K_FALSE;
5884 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5885 kHlpAssert(pszCur);
5886 if (pszCur)
5887 {
5888 while (*pszCur != '\0')
5889 {
5890 /* Find the end of the component */
5891 KSIZE cch = 0;
5892 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5893 cch++;
5894
5895 if ( cch > 0 /* wrong, but whatever */
5896 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5897 {
5898 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5899 if ( szPath[cch - 1] != ':'
5900 && szPath[cch - 1] != '/'
5901 && szPath[cch - 1] != '\\')
5902 *pszDst++ = '\\';
5903 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5904 if (fNeedSuffix)
5905 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5906 *pszDst = '\0';
5907
5908 if (kwFsPathExists(szPath))
5909 {
5910 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5911 pszFilename = szPath;
5912 break;
5913 }
5914 }
5915
5916 /* Advance */
5917 pszCur += cch;
5918 while (*pszCur == ';')
5919 pszCur++;
5920 }
5921 }
5922 }
5923 }
5924 }
5925
5926 return LoadLibraryExA(pszFilename, hFile, fFlags);
5927}
5928
5929
5930/** Kernel32 - LoadLibraryExW() for native overloads */
5931static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5932{
5933 char szTmp[4096];
5934 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5935 if (cchTmp < sizeof(szTmp))
5936 return kwSandbox_Kernel32_Native_LoadLibraryExA(szTmp, hFile, fFlags);
5937
5938 KWFS_TODO();
5939 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5940 return NULL;
5941}
5942
5943
5944/** Kernel32 - LoadLibraryExW() */
5945static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5946{
5947 char szTmp[4096];
5948 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5949 if (cchTmp < sizeof(szTmp))
5950 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5951
5952 KWFS_TODO();
5953 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5954 return NULL;
5955}
5956
5957/** Kernel32 - LoadLibraryA() */
5958static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5959{
5960 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5961}
5962
5963
5964/** Kernel32 - LoadLibraryW() */
5965static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5966{
5967 char szTmp[4096];
5968 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5969 if (cchTmp < sizeof(szTmp))
5970 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5971 KWFS_TODO();
5972 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5973 return NULL;
5974}
5975
5976
5977/** Kernel32 - FreeLibrary() */
5978static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5979{
5980 /* Ignored, we like to keep everything loaded. */
5981 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5982 return TRUE;
5983}
5984
5985
5986/** Worker for GetModuleHandleA/W for handling cached modules. */
5987static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5988{
5989 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5990 if (hmod)
5991 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5992 hmod, g_aGetModuleHandleCache[i].pszName));
5993 else
5994 {
5995 /*
5996 * The first time around we have to make sure we have a module table
5997 * entry for it, if not we add one. We need to add it to the tools
5998 * module list to for it to work.
5999 */
6000 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
6001 g_aGetModuleHandleCache[i].fAlwaysPresent);
6002 if (pMod)
6003 {
6004 hmod = pMod->hOurMod;
6005 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
6006 {
6007 kwToolAddModule(g_Sandbox.pTool, pMod);
6008 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
6009 hmod, g_aGetModuleHandleCache[i].pszName));
6010 }
6011 else
6012 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
6013 hmod, g_aGetModuleHandleCache[i].pszName));
6014
6015 }
6016 }
6017 return hmod;
6018}
6019
6020
6021/** Kernel32 - GetModuleHandleA() */
6022static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
6023{
6024 KSIZE i;
6025 KSIZE cchModule;
6026 PKWDYNLOAD pDynLoad;
6027 KSIZE cchSuffix;
6028 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6029 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6030
6031 /*
6032 * The executable.
6033 */
6034 if (pszModule == NULL)
6035 {
6036 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
6037 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
6038 }
6039
6040 /*
6041 * If no path of suffix, pretend it ends with .DLL.
6042 */
6043 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
6044
6045 /*
6046 * Cache of system modules we've seen queried.
6047 */
6048 cchModule = kHlpStrLen(pszModule);
6049 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6050 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
6051 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
6052 || ( cchSuffix > 0
6053 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
6054 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
6055 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
6056 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
6057
6058 /*
6059 * Modules we've dynamically loaded.
6060 */
6061 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6062 if (pDynLoad->pMod)
6063 {
6064 const char *pszPath = pDynLoad->pMod->pszPath;
6065 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
6066 if ( stricmp(pszPath, pszModule) == 0
6067 || stricmp(pszName, pszModule) == 0
6068 || ( cchSuffix > 0
6069 && strnicmp(pszName, pszModule, cchModule) == 0
6070 && stricmp(&pszName[cchModule], ".dll") == 0))
6071 {
6072 if ( pDynLoad->pMod->fNative
6073 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6074 {
6075 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
6076 return pDynLoad->hmod;
6077 }
6078 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
6079 SetLastError(ERROR_MOD_NOT_FOUND);
6080 return NULL;
6081 }
6082 }
6083
6084 /*
6085 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6086 * to and go via the g_aGetModuleHandleCache cache.
6087 */
6088/** @todo virtual api DLLs */
6089 if (kHlpStrNICompAscii(pszModule, "api-ms-win-", 11) == 0)
6090 {
6091 HMODULE hmod = GetModuleHandleA(pszModule);
6092 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
6093 if (hmod)
6094 {
6095 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6096 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
6097 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6098 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
6099 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6100 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
6101 if (hmod == GetModuleHandleW(L"UCRTBASE.DLL"))
6102 return kwSandbox_Kernel32_GetModuleHandleA("UCRTBASE.DLL");
6103 }
6104 else
6105 dwErr = GetLastError();
6106 }
6107
6108 kwErrPrintf("pszModule=%s\n", pszModule);
6109 KWFS_TODO();
6110 SetLastError(ERROR_MOD_NOT_FOUND);
6111 return NULL;
6112}
6113
6114
6115/** Kernel32 - GetModuleHandleW() */
6116static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
6117{
6118 KSIZE i;
6119 KSIZE cwcModule;
6120 PKWDYNLOAD pDynLoad;
6121 KSIZE cwcSuffix;
6122 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6123 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6124
6125 /*
6126 * The executable.
6127 */
6128 if (pwszModule == NULL)
6129 {
6130 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
6131 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
6132 }
6133
6134 /*
6135 * If no path of suffix, pretend it ends with .DLL.
6136 */
6137 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
6138
6139 /*
6140 * Cache of system modules we've seen queried.
6141 */
6142 cwcModule = kwUtf16Len(pwszModule);
6143 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6144 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
6145 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
6146 || ( cwcSuffix > 0
6147 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
6148 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
6149 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
6150 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
6151
6152 /*
6153 * Modules we've dynamically loaded.
6154 */
6155 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6156 if (pDynLoad->pMod)
6157 {
6158 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
6159 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
6160 if ( _wcsicmp(pwszPath, pwszModule) == 0
6161 || _wcsicmp(pwszName, pwszModule) == 0
6162 || ( cwcSuffix
6163 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
6164 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
6165 {
6166 if ( pDynLoad->pMod->fNative
6167 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6168 {
6169 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
6170 return pDynLoad->hmod;
6171 }
6172 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
6173 SetLastError(ERROR_MOD_NOT_FOUND);
6174 return NULL;
6175 }
6176 }
6177
6178 /*
6179 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6180 * to and go via the g_aGetModuleHandleCache cache.
6181 */
6182 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
6183 {
6184 HMODULE hmod = GetModuleHandleW(pwszModule);
6185 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
6186 if (hmod)
6187 {
6188 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6189 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
6190 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6191 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
6192 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6193 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
6194 }
6195 else
6196 dwErr = GetLastError();
6197 }
6198
6199 kwErrPrintf("pwszModule=%ls\n", pwszModule);
6200 KWFS_TODO();
6201 SetLastError(dwErr);
6202 return NULL;
6203}
6204
6205
6206/** Used to debug dynamically resolved procedures. */
6207static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
6208{
6209#ifdef _MSC_VER
6210 __debugbreak();
6211#else
6212 KWFS_TODO();
6213#endif
6214 return ~(UINT)0;
6215}
6216
6217
6218#ifndef NDEBUG
6219/*
6220 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
6221 */
6222# if K_ARCH == K_ARCH_X86_32
6223static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
6224# else
6225static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
6226# endif
6227typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
6228typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
6229typedef struct KWCXINTERCEPTORENTRY
6230{
6231 PFNINVOKECOMPILERPASSW pfnOrg;
6232 PKWMODULE pModule;
6233 PFNINVOKECOMPILERPASSW pfnWrap;
6234} KWCXINTERCEPTORENTRY;
6235
6236static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
6237 KWCXINTERCEPTORENTRY *pEntry)
6238{
6239 int i;
6240 KIPTR rcExit;
6241 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
6242 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
6243 for (i = 0; i < cArgs; i++)
6244 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
6245
6246 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
6247
6248 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
6249 return rcExit;
6250}
6251
6252static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
6253static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
6254static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
6255
6256static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
6257{
6258 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
6259 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
6260 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
6261};
6262
6263static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6264{
6265 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
6266}
6267
6268static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6269{
6270 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
6271}
6272
6273static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6274{
6275 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
6276}
6277
6278#endif /* !NDEBUG */
6279
6280
6281/** Kernel32 - GetProcAddress() */
6282static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6283{
6284 KSIZE i;
6285 PKWMODULE pMod;
6286 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6287
6288 /*
6289 * Try locate the module.
6290 */
6291 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6292 if (pMod)
6293 {
6294 KLDRADDR uValue;
6295 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
6296 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
6297 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
6298 KU32_MAX /*iSymbol*/,
6299 pszProc,
6300 kHlpStrLen(pszProc),
6301 NULL /*pszVersion*/,
6302 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
6303 &uValue,
6304 NULL /*pfKind*/);
6305 if (rc == 0)
6306 {
6307 //static int s_cDbgGets = 0;
6308 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
6309 KU32 i = g_cSandboxGetProcReplacements;
6310 while (i-- > 0)
6311 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
6312 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
6313 {
6314 if ( !g_aSandboxGetProcReplacements[i].pszModule
6315 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
6316 {
6317 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
6318 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
6319 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
6320 {
6321 if (!g_aSandboxReplacements[i].fCrtSlotArray)
6322 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
6323 else
6324 {
6325 if (pMod->iCrtSlot == KU8_MAX)
6326 {
6327 int rc = kwLdrModuleCreateCrtSlot(pMod);
6328 if (rc)
6329 {
6330 KW_LOG(("GetProcAddress: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
6331 SetLastError(ERROR_INTERNAL_ERROR);
6332 return NULL;
6333 }
6334 }
6335 uValue = ((KUPTR *)g_aSandboxGetProcReplacements[i].pfnReplacement)[pMod->iCrtSlot];
6336 }
6337
6338 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6339 }
6340 kwLdrModuleRelease(pMod);
6341 return (FARPROC)(KUPTR)uValue;
6342 }
6343 }
6344
6345#ifndef NDEBUG
6346 /* Intercept the compiler pass method, dumping arguments. */
6347 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
6348 {
6349 KU32 i;
6350 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6351 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6352 {
6353 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6354 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6355 break;
6356 }
6357 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6358 while (i-- > 0)
6359 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6360 {
6361 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6362 g_aCxInterceptorEntries[i].pModule = pMod;
6363 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6364 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6365 break;
6366 }
6367 }
6368#endif
6369 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6370 kwLdrModuleRelease(pMod);
6371 //s_cDbgGets++;
6372 //if (s_cGets >= 3)
6373 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6374 return (FARPROC)(KUPTR)uValue;
6375 }
6376
6377 KWFS_TODO();
6378 SetLastError(ERROR_PROC_NOT_FOUND);
6379 kwLdrModuleRelease(pMod);
6380 return NULL;
6381 }
6382
6383 /*
6384 * Hmm... could be a cached module-by-name.
6385 */
6386 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6387 if (g_aGetModuleHandleCache[i].hmod == hmod)
6388 return GetProcAddress(hmod, pszProc);
6389
6390 KWFS_TODO();
6391 return GetProcAddress(hmod, pszProc);
6392}
6393
6394
6395#ifndef NDEBUG
6396/** Kernel32 - GetProcAddress() - native replacement for debugging only. */
6397static FARPROC WINAPI kwSandbox_Kernel32_Native_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6398{
6399 FARPROC pfnRet = GetProcAddress(hmod, pszProc);
6400 KWLDR_LOG(("kwSandbox_Kernel32_Native_GetProcAddress(%p, %s) -> %p\n", hmod, pszProc, pfnRet));
6401 return pfnRet;
6402}
6403#endif
6404
6405
6406/** Kernel32 - GetModuleFileNameA() */
6407static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6408{
6409 PKWMODULE pMod;
6410 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6411
6412 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6413 if (pMod != NULL)
6414 {
6415 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6416 kwLdrModuleRelease(pMod);
6417 return cbRet;
6418 }
6419 KWFS_TODO();
6420 return 0;
6421}
6422
6423
6424/** Kernel32 - GetModuleFileNameW() */
6425static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6426{
6427 PKWMODULE pMod;
6428 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6429
6430 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6431 if (pMod)
6432 {
6433 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6434 kwLdrModuleRelease(pMod);
6435 return cwcRet;
6436 }
6437
6438 KWFS_TODO();
6439 return 0;
6440}
6441
6442
6443/** NtDll - RtlPcToFileHeader
6444 * This is necessary for msvcr100.dll!CxxThrowException. */
6445static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6446{
6447 PVOID pvRet;
6448
6449 /*
6450 * Do a binary lookup of the module table for the current tool.
6451 * This will give us a
6452 */
6453 if (g_Sandbox.fRunning)
6454 {
6455 KUPTR const uPC = (KUPTR)pvPC;
6456 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6457 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6458 KU32 i;
6459 if (iEnd)
6460 {
6461 KU32 iStart = 0;
6462 i = iEnd / 2;
6463 for (;;)
6464 {
6465 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6466 if (uPC < uHModCur)
6467 {
6468 iEnd = i;
6469 if (iStart < i)
6470 { }
6471 else
6472 break;
6473 }
6474 else if (uPC != uHModCur)
6475 {
6476 iStart = ++i;
6477 if (i < iEnd)
6478 { }
6479 else
6480 break;
6481 }
6482 else
6483 {
6484 /* This isn't supposed to happen. */
6485 break;
6486 }
6487
6488 i = iStart + (iEnd - iStart) / 2;
6489 }
6490
6491 /* For reasons of simplicity (= copy & paste), we end up with the
6492 module after the one we're interested in here. */
6493 i--;
6494 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6495 && papMods[i]->pLdrMod)
6496 {
6497 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6498 if (uRvaPC < papMods[i]->cbImage)
6499 {
6500 *ppvImageBase = papMods[i]->hOurMod;
6501 pvRet = papMods[i]->hOurMod;
6502 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6503 return pvRet;
6504 }
6505 }
6506 }
6507 else
6508 i = 0;
6509 }
6510
6511 /*
6512 * Call the regular API.
6513 */
6514 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6515 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6516 return pvRet;
6517}
6518
6519
6520/*
6521 *
6522 * File access APIs (for speeding them up).
6523 * File access APIs (for speeding them up).
6524 * File access APIs (for speeding them up).
6525 *
6526 */
6527
6528
6529/**
6530 * Converts a lookup error to a windows error code.
6531 *
6532 * @returns The windows error code.
6533 * @param enmError The lookup error.
6534 */
6535static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6536{
6537 switch (enmError)
6538 {
6539 case KFSLOOKUPERROR_NOT_FOUND:
6540 case KFSLOOKUPERROR_NOT_DIR:
6541 return ERROR_FILE_NOT_FOUND;
6542
6543 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6544 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6545 case KFSLOOKUPERROR_PATH_TOO_SHORT:
6546 return ERROR_PATH_NOT_FOUND;
6547
6548 case KFSLOOKUPERROR_PATH_TOO_LONG:
6549 return ERROR_FILENAME_EXCED_RANGE;
6550
6551 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6552 return ERROR_NOT_ENOUGH_MEMORY;
6553
6554 default:
6555 return ERROR_PATH_NOT_FOUND;
6556 }
6557}
6558
6559#ifdef WITH_TEMP_MEMORY_FILES
6560
6561/**
6562 * Checks for a cl.exe temporary file.
6563 *
6564 * There are quite a bunch of these. They seems to be passing data between the
6565 * first and second compiler pass. Since they're on disk, they get subjected to
6566 * AV software screening and normal file consistency rules. So, not necessarily
6567 * a very efficient way of handling reasonably small amounts of data.
6568 *
6569 * We make the files live in virtual memory by intercepting their opening,
6570 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6571 *
6572 * @returns K_TRUE / K_FALSE
6573 * @param pwszFilename The file name being accessed.
6574 */
6575static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6576{
6577 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6578 if (pwszName)
6579 {
6580 /* The name starts with _CL_... */
6581 if ( pwszName[0] == '_'
6582 && pwszName[1] == 'C'
6583 && pwszName[2] == 'L'
6584 && pwszName[3] == '_' )
6585 {
6586 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6587 this check by just checking that it's alpha numerical ascii from here on. */
6588 wchar_t wc;
6589 pwszName += 4;
6590 while ((wc = *pwszName++) != '\0')
6591 {
6592 if (wc < 127 && iswalnum(wc))
6593 { /* likely */ }
6594 else
6595 return K_FALSE;
6596 }
6597 return K_TRUE;
6598 }
6599
6600 /* In VC2019 there is also one {UUID} file in temp: */
6601 if (pwszName[0] == '{')
6602 {
6603 KSIZE cwcName = kwUtf16Len(pwszName);
6604 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6605 && pwszName[37] == '}'
6606 && iswalnum(pwszName[1]) // 4
6607 && iswalnum(pwszName[2]) // 4
6608 && iswalnum(pwszName[3]) // 6
6609 && iswalnum(pwszName[4]) // 5
6610 && iswalnum(pwszName[5]) // d
6611 && iswalnum(pwszName[6]) // d
6612 && iswalnum(pwszName[7]) // d
6613 && iswalnum(pwszName[8]) // 9
6614 && pwszName[9] == '-' // -
6615 && iswalnum(pwszName[10]) // e
6616 && iswalnum(pwszName[11]) // 4
6617 && iswalnum(pwszName[12]) // 9
6618 && iswalnum(pwszName[13]) // 4
6619 && pwszName[14] == '-' // -
6620 && iswalnum(pwszName[15]) // 4
6621 && iswalnum(pwszName[16]) // 7
6622 && iswalnum(pwszName[17]) // 1
6623 && iswalnum(pwszName[18]) // b
6624 && pwszName[19] == '-' // -
6625 && iswalnum(pwszName[20]) // 9
6626 && iswalnum(pwszName[21]) // 9
6627 && iswalnum(pwszName[22]) // 6
6628 && iswalnum(pwszName[23]) // b
6629 && pwszName[24] == '-' // -
6630 && iswalnum(pwszName[25]) // 9
6631 && iswalnum(pwszName[26]) // b
6632 && iswalnum(pwszName[27]) // 5
6633 && iswalnum(pwszName[28]) // 5
6634 && iswalnum(pwszName[29]) // 6
6635 && iswalnum(pwszName[30]) // e
6636 && iswalnum(pwszName[31]) // 2
6637 && iswalnum(pwszName[32]) // 5
6638 && iswalnum(pwszName[33]) // a
6639 && iswalnum(pwszName[34]) // 3
6640 && iswalnum(pwszName[35]) // f
6641 && iswalnum(pwszName[36])) // 8
6642 return K_TRUE;
6643 }
6644 }
6645 return K_FALSE;
6646}
6647
6648
6649/**
6650 * Creates a handle to a temporary file.
6651 *
6652 * @returns The handle on success.
6653 * INVALID_HANDLE_VALUE and SetLastError on failure.
6654 * @param pTempFile The temporary file.
6655 * @param dwDesiredAccess The desired access to the handle.
6656 * @param fMapping Whether this is a mapping (K_TRUE) or file
6657 * (K_FALSE) handle type.
6658 */
6659static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6660{
6661 /*
6662 * Create a handle to the temporary file.
6663 */
6664 HANDLE hFile = INVALID_HANDLE_VALUE;
6665 HANDLE hProcSelf = GetCurrentProcess();
6666 if (DuplicateHandle(hProcSelf, hProcSelf,
6667 hProcSelf, &hFile,
6668 SYNCHRONIZE, FALSE,
6669 0 /*dwOptions*/))
6670 {
6671 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6672 if (pHandle)
6673 {
6674 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6675 pHandle->cRefs = 1;
6676 pHandle->offFile = 0;
6677 pHandle->hHandle = hFile;
6678 pHandle->dwDesiredAccess = dwDesiredAccess;
6679 pHandle->tidOwner = KU32_MAX;
6680 pHandle->u.pTempFile = pTempFile;
6681 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6682 {
6683 pTempFile->cActiveHandles++;
6684 kHlpAssert(pTempFile->cActiveHandles >= 1);
6685 kHlpAssert(pTempFile->cActiveHandles <= 2);
6686 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6687 return hFile;
6688 }
6689
6690 kHlpFree(pHandle);
6691 }
6692 else
6693 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6694 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6695 }
6696 else
6697 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6698 return INVALID_HANDLE_VALUE;
6699}
6700
6701
6702static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6703 KBOOL *pfFallback)
6704{
6705 HANDLE hFile;
6706 DWORD dwErr;
6707
6708 /*
6709 * Check if we've got an existing temp file.
6710 * ASSUME exact same path for now.
6711 */
6712 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6713 PKWFSTEMPFILE pTempFile;
6714 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6715 {
6716 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6717 if ( pTempFile->cwcPath == cwcFilename
6718 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6719 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6720 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6721 break;
6722 }
6723
6724 /*
6725 * Create a new temporary file instance if not found.
6726 */
6727 *pfFallback = K_FALSE;
6728 if (pTempFile == NULL)
6729 {
6730 KSIZE cbFilename;
6731
6732 switch (dwCreationDisposition)
6733 {
6734 case CREATE_ALWAYS:
6735 case OPEN_ALWAYS:
6736 case CREATE_NEW:
6737 dwErr = NO_ERROR;
6738 break;
6739
6740 case OPEN_EXISTING:
6741 case TRUNCATE_EXISTING:
6742 *pfFallback = K_TRUE;
6743 kHlpAssertFailed();
6744 SetLastError(ERROR_FILE_NOT_FOUND);
6745 return INVALID_HANDLE_VALUE;
6746
6747 default:
6748 kHlpAssertFailed();
6749 SetLastError(ERROR_INVALID_PARAMETER);
6750 return INVALID_HANDLE_VALUE;
6751 }
6752
6753 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6754 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6755 if (pTempFile)
6756 {
6757 pTempFile->cwcPath = (KU16)cwcFilename;
6758 pTempFile->cbFile = 0;
6759 pTempFile->cbFileAllocated = 0;
6760 pTempFile->cActiveHandles = 0;
6761 pTempFile->cMappings = 0;
6762 pTempFile->cSegs = 0;
6763 pTempFile->paSegs = NULL;
6764 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6765
6766 pTempFile->pNext = g_Sandbox.pTempFileHead;
6767 g_Sandbox.pTempFileHead = pTempFile;
6768 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6769 }
6770 else
6771 {
6772 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6773 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6774 return INVALID_HANDLE_VALUE;
6775 }
6776 }
6777 else
6778 {
6779 switch (dwCreationDisposition)
6780 {
6781 case OPEN_EXISTING:
6782 dwErr = NO_ERROR;
6783 break;
6784 case OPEN_ALWAYS:
6785 dwErr = ERROR_ALREADY_EXISTS;
6786 break;
6787
6788 case TRUNCATE_EXISTING:
6789 case CREATE_ALWAYS:
6790 kHlpAssertFailed();
6791 pTempFile->cbFile = 0;
6792 dwErr = ERROR_ALREADY_EXISTS;
6793 break;
6794
6795 case CREATE_NEW:
6796 kHlpAssertFailed();
6797 SetLastError(ERROR_FILE_EXISTS);
6798 return INVALID_HANDLE_VALUE;
6799
6800 default:
6801 kHlpAssertFailed();
6802 SetLastError(ERROR_INVALID_PARAMETER);
6803 return INVALID_HANDLE_VALUE;
6804 }
6805 }
6806
6807 /*
6808 * Create a handle to the temporary file.
6809 */
6810 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6811 if (hFile != INVALID_HANDLE_VALUE)
6812 SetLastError(dwErr);
6813 return hFile;
6814}
6815
6816#endif /* WITH_TEMP_MEMORY_FILES */
6817
6818/**
6819 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6820 *
6821 * @returns K_TRUE if cacheable, K_FALSE if not.
6822 * @param wcFirst The first extension character.
6823 * @param wcSecond The second extension character.
6824 * @param wcThird The third extension character.
6825 * @param fAttrQuery Set if it's for an attribute query, clear if for
6826 * file creation.
6827 */
6828static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6829{
6830 /* C++ header without an extension or a directory. */
6831 if (wcFirst == '\0')
6832 {
6833 /** @todo exclude temporary files... */
6834 return K_TRUE;
6835 }
6836
6837 /* C Header: .h */
6838 if (wcFirst == 'h' || wcFirst == 'H')
6839 {
6840 if (wcSecond == '\0')
6841 return K_TRUE;
6842
6843 /* C++ Header: .hpp, .hxx */
6844 if ( (wcSecond == 'p' || wcSecond == 'P')
6845 && (wcThird == 'p' || wcThird == 'P'))
6846 return K_TRUE;
6847 if ( (wcSecond == 'x' || wcSecond == 'X')
6848 && (wcThird == 'x' || wcThird == 'X'))
6849 return K_TRUE;
6850 }
6851 /* Misc starting with i. */
6852 else if (wcFirst == 'i' || wcFirst == 'I')
6853 {
6854 if (wcSecond != '\0')
6855 {
6856 if (wcSecond == 'n' || wcSecond == 'N')
6857 {
6858 /* C++ inline header: .inl */
6859 if (wcThird == 'l' || wcThird == 'L')
6860 return K_TRUE;
6861
6862 /* Assembly include file: .inc */
6863 if (wcThird == 'c' || wcThird == 'C')
6864 return K_TRUE;
6865 }
6866 }
6867 }
6868 /* Assembly header: .mac */
6869 else if (wcFirst == 'm' || wcFirst == 'M')
6870 {
6871 if (wcSecond == 'a' || wcSecond == 'A')
6872 {
6873 if (wcThird == 'c' || wcThird == 'C')
6874 return K_TRUE;
6875 }
6876 }
6877#ifdef WITH_PCH_CACHING
6878 /* Precompiled header: .pch */
6879 else if (wcFirst == 'p' || wcFirst == 'P')
6880 {
6881 if (wcSecond == 'c' || wcSecond == 'C')
6882 {
6883 if (wcThird == 'h' || wcThird == 'H')
6884 return !g_Sandbox.fNoPchCaching;
6885 }
6886 }
6887#endif
6888#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6889 /* Linker - Object file: .obj */
6890 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6891 {
6892 if (wcSecond == 'b' || wcSecond == 'B')
6893 {
6894 if (wcThird == 'j' || wcThird == 'J')
6895 return K_TRUE;
6896 }
6897 }
6898#endif
6899 else if (fAttrQuery)
6900 {
6901 /* Dynamic link library: .dll */
6902 if (wcFirst == 'd' || wcFirst == 'D')
6903 {
6904 if (wcSecond == 'l' || wcSecond == 'L')
6905 {
6906 if (wcThird == 'l' || wcThird == 'L')
6907 return K_TRUE;
6908 }
6909 }
6910 /* Executable file: .exe */
6911 else if (wcFirst == 'e' || wcFirst == 'E')
6912 {
6913 if (wcSecond == 'x' || wcSecond == 'X')
6914 {
6915 if (wcThird == 'e' || wcThird == 'E')
6916 return K_TRUE;
6917 }
6918 }
6919 /* Response file: .rsp */
6920 else if (wcFirst == 'r' || wcFirst == 'R')
6921 {
6922 if (wcSecond == 's' || wcSecond == 'S')
6923 {
6924 if (wcThird == 'p' || wcThird == 'P')
6925 return !g_Sandbox.fNoPchCaching;
6926 }
6927 }
6928 /* Linker: */
6929 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6930 {
6931 /* Object file: .obj */
6932 if (wcFirst == 'o' || wcFirst == 'O')
6933 {
6934 if (wcSecond == 'b' || wcSecond == 'B')
6935 {
6936 if (wcThird == 'j' || wcThird == 'J')
6937 return K_TRUE;
6938 }
6939 }
6940 /* Library file: .lib */
6941 else if (wcFirst == 'l' || wcFirst == 'L')
6942 {
6943 if (wcSecond == 'i' || wcSecond == 'I')
6944 {
6945 if (wcThird == 'b' || wcThird == 'B')
6946 return K_TRUE;
6947 }
6948 }
6949 /* Linker definition file: .def */
6950 else if (wcFirst == 'd' || wcFirst == 'D')
6951 {
6952 if (wcSecond == 'e' || wcSecond == 'E')
6953 {
6954 if (wcThird == 'f' || wcThird == 'F')
6955 return K_TRUE;
6956 }
6957 }
6958 }
6959 }
6960
6961 return K_FALSE;
6962}
6963
6964
6965/**
6966 * Checks if the file extension indicates that the file/dir is something we
6967 * ought to cache.
6968 *
6969 * @returns K_TRUE if cachable, K_FALSE if not.
6970 * @param pszExt The kHlpGetExt result.
6971 * @param fAttrQuery Set if it's for an attribute query, clear if for
6972 * file creation.
6973 */
6974static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6975{
6976 wchar_t const wcFirst = *pszExt;
6977 if (wcFirst)
6978 {
6979 wchar_t const wcSecond = pszExt[1];
6980 if (wcSecond)
6981 {
6982 wchar_t const wcThird = pszExt[2];
6983 if (pszExt[3] == '\0')
6984 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6985 return K_FALSE;
6986 }
6987 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6988 }
6989 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6990}
6991
6992
6993/**
6994 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6995 * should be cached.
6996 *
6997 * @returns K_TRUE if cachable, K_FALSE if not.
6998 * @param pwszPath The UTF-16 path to examine.
6999 * @param fAttrQuery Set if it's for an attribute query, clear if for
7000 * file creation.
7001 */
7002static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
7003{
7004 KSIZE cwcExt;
7005 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
7006 switch (cwcExt)
7007 {
7008 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
7009 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
7010 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
7011 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
7012 }
7013 return K_FALSE;
7014}
7015
7016
7017
7018/**
7019 * Creates a new
7020 *
7021 * @returns
7022 * @param pFsObj .
7023 * @param pwszFilename .
7024 */
7025static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
7026{
7027 HANDLE hFile;
7028 MY_IO_STATUS_BLOCK Ios;
7029 MY_OBJECT_ATTRIBUTES ObjAttr;
7030 MY_UNICODE_STRING UniStr;
7031 MY_NTSTATUS rcNt;
7032 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7033
7034 /*
7035 * Open the file relative to the parent directory.
7036 */
7037 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
7038 kHlpAssert(pFsObj->pParent);
7039 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
7040
7041 Ios.Information = ~(ULONG_PTR)0;
7042 Ios.u.Status = -1;
7043
7044 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
7045 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
7046 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
7047
7048 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
7049
7050 rcNt = g_pfnNtCreateFile(&hFile,
7051 GENERIC_READ | SYNCHRONIZE,
7052 &ObjAttr,
7053 &Ios,
7054 NULL, /*cbFileInitialAlloc */
7055 FILE_ATTRIBUTE_NORMAL,
7056 FILE_SHARE_READ,
7057 FILE_OPEN,
7058 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
7059 NULL, /*pEaBuffer*/
7060 0); /*cbEaBuffer*/
7061 if (MY_NT_SUCCESS(rcNt))
7062 {
7063 /*
7064 * Read the whole file into memory.
7065 */
7066 LARGE_INTEGER cbFile;
7067 if (GetFileSizeEx(hFile, &cbFile))
7068 {
7069 if ( cbFile.QuadPart >= 0
7070#ifdef WITH_PCH_CACHING
7071 && ( cbFile.QuadPart < 16*1024*1024
7072 || ( cbFile.QuadPart < 96*1024*1024
7073 && pFsObj->cchName > 4
7074 && !g_Sandbox.fNoPchCaching
7075 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
7076#endif
7077 )
7078 {
7079 KU32 cbCache = (KU32)cbFile.QuadPart;
7080 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
7081 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
7082 if (hMapping != NULL)
7083 {
7084 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
7085 if (pbCache)
7086 {
7087 /*
7088 * Create the cached file object.
7089 */
7090 PKFSWCACHEDFILE pCachedFile;
7091 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
7092 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
7093 sizeof(*pCachedFile) + cbPath);
7094 if (pCachedFile)
7095 {
7096 pCachedFile->hCached = hFile;
7097 pCachedFile->hSection = hMapping;
7098 pCachedFile->cbCached = cbCache;
7099 pCachedFile->pbCached = pbCache;
7100 pCachedFile->pFsObj = pFsObj;
7101 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
7102 kFsCacheObjRetain(pFsObj);
7103
7104 g_cReadCachedFiles++;
7105 g_cbReadCachedFiles += cbCache;
7106
7107 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
7108 return pCachedFile;
7109 }
7110
7111 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
7112 }
7113 else
7114 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
7115 CloseHandle(hMapping);
7116 }
7117 else
7118 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
7119 }
7120 else
7121 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
7122 }
7123 else
7124 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
7125 g_pfnNtClose(hFile);
7126 }
7127 else
7128 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
7129 return NULL;
7130}
7131
7132
7133/**
7134 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
7135 */
7136static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
7137 KBOOL fIsFileHandle, HANDLE *phFile)
7138{
7139 HANDLE hProcSelf = GetCurrentProcess();
7140 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
7141 hProcSelf, phFile,
7142 dwDesiredAccess, fInheritHandle,
7143 0 /*dwOptions*/))
7144 {
7145 /*
7146 * Create handle table entry for the duplicate handle.
7147 */
7148 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
7149 if (pHandle)
7150 {
7151 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
7152 pHandle->cRefs = 1;
7153 pHandle->offFile = 0;
7154 pHandle->hHandle = *phFile;
7155 pHandle->dwDesiredAccess = dwDesiredAccess;
7156 pHandle->tidOwner = KU32_MAX;
7157 pHandle->u.pCachedFile = pCachedFile;
7158 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
7159 return K_TRUE;
7160
7161 kHlpFree(pHandle);
7162 }
7163 else
7164 KWFS_LOG(("Out of memory for handle!\n"));
7165
7166 CloseHandle(*phFile);
7167 *phFile = INVALID_HANDLE_VALUE;
7168 }
7169 else
7170 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
7171 return K_FALSE;
7172}
7173
7174
7175/**
7176 * Kernel32 - Common code for CreateFileW and CreateFileA.
7177 */
7178static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
7179{
7180 *phFile = INVALID_HANDLE_VALUE;
7181
7182 /*
7183 * At the moment we only handle existing files.
7184 */
7185 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
7186 {
7187 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
7188 kHlpAssert(pFsObj->fHaveStats);
7189 if ( pCachedFile != NULL
7190 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
7191 {
7192 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
7193 return K_TRUE;
7194 }
7195 }
7196 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
7197
7198 /* Do fallback, please. */
7199 return K_FALSE;
7200}
7201
7202
7203/** Kernel32 - CreateFileA */
7204static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7205 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7206 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7207{
7208 HANDLE hFile;
7209
7210 /*
7211 * Check for include files and similar that we do read-only caching of.
7212 */
7213 if (dwCreationDisposition == OPEN_EXISTING)
7214 {
7215 if ( dwDesiredAccess == GENERIC_READ
7216 || dwDesiredAccess == FILE_GENERIC_READ)
7217 {
7218 if (dwShareMode & FILE_SHARE_READ)
7219 {
7220 if ( !pSecAttrs
7221 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7222 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7223 {
7224 const char *pszExt = kHlpGetExt(pszFilename);
7225 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
7226 {
7227 KFSLOOKUPERROR enmError;
7228 PKFSOBJ pFsObj;
7229 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7230
7231 pFsObj = kFsCacheLookupA(g_pFsCache, pszFilename, &enmError);
7232 if (pFsObj)
7233 {
7234 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7235 {
7236 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7237 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7238 kFsCacheObjRelease(g_pFsCache, pFsObj);
7239 if (fRc)
7240 {
7241 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
7242 return hFile;
7243 }
7244 }
7245 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7246 {
7247 KWFS_LOG(("CreateFileA(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7248 SetLastError(ERROR_FILE_NOT_FOUND);
7249 return INVALID_HANDLE_VALUE;
7250 }
7251 /* Always fall back on missing files in volatile areas. */
7252 }
7253 /* These are for nasm and yasm header searching. Cache will already
7254 have checked the directories for the file, no need to call
7255 CreateFile to do it again. */
7256 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7257 {
7258 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7259 SetLastError(ERROR_FILE_NOT_FOUND);
7260 return INVALID_HANDLE_VALUE;
7261 }
7262 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7263 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7264 {
7265 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
7266 SetLastError(ERROR_PATH_NOT_FOUND);
7267 return INVALID_HANDLE_VALUE;
7268 }
7269
7270 /* fallback */
7271 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7272 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7273 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
7274 return hFile;
7275 }
7276 }
7277 }
7278 }
7279 }
7280
7281 /*
7282 * Okay, normal.
7283 */
7284 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7285 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7286 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7287
7288 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7289 return hFile;
7290}
7291
7292
7293/** Kernel32 - CreateFileW */
7294static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7295 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7296 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7297{
7298 HANDLE hFile;
7299
7300#ifdef WITH_TEMP_MEMORY_FILES
7301 /*
7302 * Check for temporary files (cl.exe only).
7303 */
7304 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7305 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7306 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7307 && kwFsIsClTempFileW(pwszFilename))
7308 {
7309 KBOOL fFallback = K_FALSE;
7310 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7311 if (!fFallback)
7312 {
7313 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7314 return hFile;
7315 }
7316 }
7317#endif
7318
7319 /*
7320 * Check for include files and similar that we do read-only caching of.
7321 */
7322 if (dwCreationDisposition == OPEN_EXISTING)
7323 {
7324 if ( dwDesiredAccess == GENERIC_READ
7325 || dwDesiredAccess == FILE_GENERIC_READ)
7326 {
7327 if (dwShareMode & FILE_SHARE_READ)
7328 {
7329 if ( !pSecAttrs
7330 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7331 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7332 {
7333 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7334 {
7335 KFSLOOKUPERROR enmError;
7336 PKFSOBJ pFsObj;
7337 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7338
7339 pFsObj = kFsCacheLookupW(g_pFsCache, pwszFilename, &enmError);
7340 if (pFsObj)
7341 {
7342 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7343 {
7344 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7345 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7346 kFsCacheObjRelease(g_pFsCache, pFsObj);
7347 if (fRc)
7348 {
7349 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7350 return hFile;
7351 }
7352 }
7353 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7354 {
7355 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7356 SetLastError(ERROR_FILE_NOT_FOUND);
7357#if 0
7358 if ( pFsObj->cchName > sizeof("generated.h")
7359 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - sizeof("generated.h") + 1], "generated.h") == 0)
7360 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; pFsObj->fFlags=%#x\n", pwszFilename, pFsObj->fFlags);
7361#endif
7362 return INVALID_HANDLE_VALUE;
7363 }
7364 /* Always fall back on missing files in volatile areas. */
7365 }
7366 /* These are for nasm and yasm style header searching. Cache will
7367 already have checked the directories for the file, no need to call
7368 CreateFile to do it again. */
7369 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7370 {
7371#if 0
7372 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7373 if ( cwcFilename > sizeof("generated.h")
7374 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7375 L"generated.h", sizeof(L"generated.h")) == 0)
7376 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; (KFSLOOKUPERROR_NOT_FOUND)\n", pwszFilename, pFsObj->fFlags);
7377#endif
7378 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7379 SetLastError(ERROR_FILE_NOT_FOUND);
7380 return INVALID_HANDLE_VALUE;
7381 }
7382 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7383 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7384 {
7385#if 0
7386 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7387 if ( cwcFilename > sizeof("generated.h")
7388 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7389 L"generated.h", sizeof(L"generated.h")) == 0)
7390 kwErrPrintf("CreateFileW(%ls) -> ERROR_PATH_NOT_FOUND; (%d)\n", pwszFilename, enmError);
7391#endif
7392 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7393 SetLastError(ERROR_PATH_NOT_FOUND);
7394 return INVALID_HANDLE_VALUE;
7395 }
7396
7397 /* fallback */
7398 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7399 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7400 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7401 return hFile;
7402 }
7403 }
7404 else
7405 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7406 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7407 }
7408 else
7409 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7410 }
7411 else
7412 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7413 }
7414 else
7415 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7416
7417 /*
7418 * Okay, normal.
7419 */
7420 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7421 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7422 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7423
7424 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7425 return hFile;
7426}
7427
7428
7429
7430/** Kernel32 - SetFilePointer */
7431static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7432{
7433 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7434 if (pHandle != NULL)
7435 {
7436 KU32 cbFile;
7437 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7438 switch (pHandle->enmType)
7439 {
7440 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7441 cbFile = pHandle->u.pCachedFile->cbCached;
7442 break;
7443#ifdef WITH_TEMP_MEMORY_FILES
7444 case KWHANDLETYPE_TEMP_FILE:
7445 cbFile = pHandle->u.pTempFile->cbFile;
7446 break;
7447 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7448#endif
7449 case KWHANDLETYPE_OUTPUT_BUF:
7450 default:
7451 kHlpAssertFailed();
7452 kwSandboxHandlePut(pHandle);
7453 SetLastError(ERROR_INVALID_FUNCTION);
7454 return INVALID_SET_FILE_POINTER;
7455 }
7456
7457 switch (dwMoveMethod)
7458 {
7459 case FILE_BEGIN:
7460 break;
7461 case FILE_CURRENT:
7462 offMove += pHandle->offFile;
7463 break;
7464 case FILE_END:
7465 offMove += cbFile;
7466 break;
7467 default:
7468 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7469 kwSandboxHandlePut(pHandle);
7470 SetLastError(ERROR_INVALID_PARAMETER);
7471 return INVALID_SET_FILE_POINTER;
7472 }
7473 if (offMove >= 0)
7474 {
7475 if (offMove >= (KSSIZE)cbFile)
7476 {
7477#ifdef WITH_TEMP_MEMORY_FILES
7478 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7479 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7480#endif
7481 offMove = (KSSIZE)cbFile;
7482#ifdef WITH_TEMP_MEMORY_FILES
7483 /* For writable files, seeking beyond the end is fine, but check that we've got
7484 the type range for the request. */
7485 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7486 {
7487 kHlpAssertMsgFailed(("%#llx\n", offMove));
7488 kwSandboxHandlePut(pHandle);
7489 SetLastError(ERROR_SEEK);
7490 return INVALID_SET_FILE_POINTER;
7491 }
7492#endif
7493 }
7494 pHandle->offFile = (KU32)offMove;
7495 }
7496 else
7497 {
7498 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7499 kwSandboxHandlePut(pHandle);
7500 SetLastError(ERROR_NEGATIVE_SEEK);
7501 return INVALID_SET_FILE_POINTER;
7502 }
7503 if (pcbMoveHi)
7504 *pcbMoveHi = (KU64)offMove >> 32;
7505 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7506 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7507 kwSandboxHandlePut(pHandle);
7508 SetLastError(NO_ERROR);
7509 return (KU32)offMove;
7510 }
7511
7512 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7513 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7514}
7515
7516
7517/** Kernel32 - SetFilePointerEx */
7518static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7519 DWORD dwMoveMethod)
7520{
7521 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7522 if (pHandle != NULL)
7523 {
7524 KI64 offMyMove = offMove.QuadPart;
7525 KU32 cbFile;
7526 switch (pHandle->enmType)
7527 {
7528 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7529 cbFile = pHandle->u.pCachedFile->cbCached;
7530 break;
7531#ifdef WITH_TEMP_MEMORY_FILES
7532 case KWHANDLETYPE_TEMP_FILE:
7533 cbFile = pHandle->u.pTempFile->cbFile;
7534 break;
7535 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7536#endif
7537 case KWHANDLETYPE_OUTPUT_BUF:
7538 default:
7539 kHlpAssertFailed();
7540 kwSandboxHandlePut(pHandle);
7541 SetLastError(ERROR_INVALID_FUNCTION);
7542 return FALSE;
7543 }
7544
7545 switch (dwMoveMethod)
7546 {
7547 case FILE_BEGIN:
7548 break;
7549 case FILE_CURRENT:
7550 offMyMove += pHandle->offFile;
7551 break;
7552 case FILE_END:
7553 offMyMove += cbFile;
7554 break;
7555 default:
7556 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7557 kwSandboxHandlePut(pHandle);
7558 SetLastError(ERROR_INVALID_PARAMETER);
7559 return FALSE;
7560 }
7561 if (offMyMove >= 0)
7562 {
7563 if (offMyMove >= (KSSIZE)cbFile)
7564 {
7565#ifdef WITH_TEMP_MEMORY_FILES
7566 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7567 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7568#endif
7569 offMyMove = (KSSIZE)cbFile;
7570#ifdef WITH_TEMP_MEMORY_FILES
7571 /* For writable files, seeking beyond the end is fine, but check that we've got
7572 the type range for the request. */
7573 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7574 {
7575 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7576 kwSandboxHandlePut(pHandle);
7577 SetLastError(ERROR_SEEK);
7578 return FALSE;
7579 }
7580#endif
7581 }
7582 pHandle->offFile = (KU32)offMyMove;
7583 }
7584 else
7585 {
7586 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7587 kwSandboxHandlePut(pHandle);
7588 SetLastError(ERROR_NEGATIVE_SEEK);
7589 return FALSE;
7590 }
7591 if (poffNew)
7592 poffNew->QuadPart = offMyMove;
7593 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7594 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7595 kwSandboxHandlePut(pHandle);
7596 return TRUE;
7597 }
7598 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7599 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7600}
7601
7602
7603/** Kernel32 - ReadFile */
7604static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7605 LPOVERLAPPED pOverlapped)
7606{
7607 BOOL fRet;
7608 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7609 g_cReadFileCalls++;
7610 if (pHandle != NULL)
7611 {
7612 switch (pHandle->enmType)
7613 {
7614 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7615 {
7616 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7617 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7618 if (cbActually > cbToRead)
7619 cbActually = cbToRead;
7620
7621#ifdef WITH_HASH_CACHE
7622 if (g_Sandbox.pHashHead)
7623 {
7624 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7625 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7626 g_Sandbox.LastHashRead.cbRead = cbActually;
7627 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7628 }
7629#endif
7630
7631 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7632 pHandle->offFile += cbActually;
7633
7634 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7635 *pcbActuallyRead = cbActually;
7636
7637 g_cbReadFileFromReadCached += cbActually;
7638 g_cbReadFileTotal += cbActually;
7639 g_cReadFileFromReadCached++;
7640
7641 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7642 kwSandboxHandlePut(pHandle);
7643 return TRUE;
7644 }
7645
7646#ifdef WITH_TEMP_MEMORY_FILES
7647 case KWHANDLETYPE_TEMP_FILE:
7648 {
7649 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7650 KU32 cbActually;
7651 if (pHandle->offFile < pTempFile->cbFile)
7652 {
7653 cbActually = pTempFile->cbFile - pHandle->offFile;
7654 if (cbActually > cbToRead)
7655 cbActually = cbToRead;
7656
7657 /* Copy the data. */
7658 if (cbActually > 0)
7659 {
7660 KU32 cbLeft;
7661 KU32 offSeg;
7662 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7663
7664 /* Locate the segment containing the byte at offFile. */
7665 KU32 iSeg = pTempFile->cSegs - 1;
7666 kHlpAssert(pTempFile->cSegs > 0);
7667 while (paSegs[iSeg].offData > pHandle->offFile)
7668 iSeg--;
7669
7670 /* Copy out the data. */
7671 cbLeft = cbActually;
7672 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7673 for (;;)
7674 {
7675 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7676 if (cbAvail >= cbLeft)
7677 {
7678 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7679 break;
7680 }
7681
7682 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7683 cbLeft -= cbAvail;
7684 offSeg = 0;
7685 iSeg++;
7686 kHlpAssert(iSeg < pTempFile->cSegs);
7687 }
7688
7689 /* Update the file offset. */
7690 pHandle->offFile += cbActually;
7691 }
7692 }
7693 /* Read does not commit file space, so return zero bytes. */
7694 else
7695 cbActually = 0;
7696
7697 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7698 *pcbActuallyRead = cbActually;
7699
7700 g_cbReadFileTotal += cbActually;
7701 g_cbReadFileFromInMemTemp += cbActually;
7702 g_cReadFileFromInMemTemp++;
7703
7704 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7705 kwSandboxHandlePut(pHandle);
7706 return TRUE;
7707 }
7708
7709 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7710#endif /* WITH_TEMP_MEMORY_FILES */
7711 case KWHANDLETYPE_OUTPUT_BUF:
7712 default:
7713 kHlpAssertFailed();
7714 kwSandboxHandlePut(pHandle);
7715 SetLastError(ERROR_INVALID_FUNCTION);
7716 *pcbActuallyRead = 0;
7717 return FALSE;
7718 }
7719 }
7720
7721 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7722 if (fRet && pcbActuallyRead)
7723 g_cbReadFileTotal += *pcbActuallyRead;
7724 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7725 return fRet;
7726}
7727
7728
7729/** Kernel32 - ReadFileEx */
7730static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7731 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7732{
7733 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7734
7735 KWFS_LOG(("ReadFile(%p)\n", hFile));
7736 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7737}
7738
7739#ifdef WITH_STD_OUT_ERR_BUFFERING
7740
7741/**
7742 * Write something to a handle, making sure everything is actually written.
7743 *
7744 * @param hHandle Where to write it to.
7745 * @param pchBuf What to write
7746 * @param cchToWrite How much to write.
7747 */
7748static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7749{
7750 if (cchToWrite > 0)
7751 {
7752 DWORD cchWritten = 0;
7753 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7754 {
7755 if (cchWritten == cchToWrite)
7756 { /* likely */ }
7757 else
7758 {
7759 do
7760 {
7761 pchBuf += cchWritten;
7762 cchToWrite -= cchWritten;
7763 cchWritten = 0;
7764 } while ( cchToWrite > 0
7765 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7766 }
7767 }
7768 else
7769 kHlpAssertFailed();
7770 }
7771}
7772
7773
7774/**
7775 * Worker for WriteFile when the output isn't going to the console.
7776 *
7777 * @param pSandbox The sandbox.
7778 * @param pOutBuf The output buffer.
7779 * @param pchBuffer What to write.
7780 * @param cchToWrite How much to write.
7781 */
7782static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7783{
7784 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7785 { /* likely */ }
7786 else
7787 {
7788 /* No realloc, max size is 64KB. */
7789 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7790 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7791 if (!pOutBuf->u.Fully.pchBuf)
7792 {
7793 while ( !pOutBuf->u.Fully.pchBuf
7794 && pOutBuf->u.Fully.cchBufAlloc > 64)
7795 {
7796 pOutBuf->u.Fully.cchBufAlloc /= 2;
7797 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7798 }
7799 if (!pOutBuf->u.Fully.pchBuf)
7800 {
7801 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7802 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7803 }
7804 }
7805 }
7806
7807 /*
7808 * Special case: Output ends with newline and fits in the buffer.
7809 */
7810 if ( cchToWrite > 1
7811 && pchBuffer[cchToWrite - 1] == '\n'
7812 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7813 {
7814 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7815 pOutBuf->u.Fully.cchBuf += cchToWrite;
7816 }
7817 else
7818 {
7819 /*
7820 * Work thru the text line by line, flushing the buffer when
7821 * appropriate. The buffer is not a line buffer here, it's a
7822 * full buffer.
7823 */
7824 while (cchToWrite > 0)
7825 {
7826 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7827 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7828 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7829 {
7830 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7831 pOutBuf->u.Fully.cchBuf += cchLine;
7832 }
7833 /*
7834 * Option one: Flush the buffer and the current line.
7835 *
7836 * We choose this one when the line won't ever fit, or when we have
7837 * an incomplete line in the buffer.
7838 */
7839 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7840 || pOutBuf->u.Fully.cchBuf == 0
7841 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7842 {
7843 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7844 if (pOutBuf->u.Fully.cchBuf > 0)
7845 {
7846 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7847 pOutBuf->u.Fully.cchBuf = 0;
7848 }
7849 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7850 }
7851 /*
7852 * Option two: Only flush the lines in the buffer.
7853 */
7854 else
7855 {
7856 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7857 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7858 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7859 pOutBuf->u.Fully.cchBuf = cchLine;
7860 }
7861
7862 /* advance */
7863 pchBuffer += cchLine;
7864 cchToWrite -= cchLine;
7865 }
7866 }
7867}
7868
7869#endif /* WITH_STD_OUT_ERR_BUFFERING */
7870
7871#ifdef WITH_TEMP_MEMORY_FILES
7872static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7873{
7874 KU32 cbMinFile = offFile + cbNeeded;
7875 if (cbMinFile >= offFile)
7876 {
7877 /* Calc how much space we've already allocated and */
7878 if (cbMinFile <= pTempFile->cbFileAllocated)
7879 return K_TRUE;
7880
7881 /* Grow the file. */
7882 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7883 {
7884 int rc;
7885 KU32 cSegs = pTempFile->cSegs;
7886 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7887 do
7888 {
7889 /* grow the segment array? */
7890 if ((cSegs % 16) == 0)
7891 {
7892 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7893 if (!pvNew)
7894 return K_FALSE;
7895 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7896 }
7897
7898 /* Use page alloc here to simplify mapping later. */
7899 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7900 if (rc == 0)
7901 { /* likely */ }
7902 else
7903 {
7904 cbNewSeg = 64*1024;
7905 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7906 if (rc != 0)
7907 {
7908 kHlpAssertFailed();
7909 return K_FALSE;
7910 }
7911 }
7912 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7913 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7914 pTempFile->cbFileAllocated += cbNewSeg;
7915 pTempFile->cSegs = ++cSegs;
7916
7917 } while (pTempFile->cbFileAllocated < cbMinFile);
7918
7919 return K_TRUE;
7920 }
7921 }
7922
7923 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7924 return K_FALSE;
7925}
7926#endif /* WITH_TEMP_MEMORY_FILES */
7927
7928
7929#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7930/** Kernel32 - WriteFile */
7931static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7932 LPOVERLAPPED pOverlapped)
7933{
7934 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7935 BOOL fRet;
7936 g_cWriteFileCalls++;
7937 if (pHandle != NULL)
7938 {
7939 switch (pHandle->enmType)
7940 {
7941# ifdef WITH_TEMP_MEMORY_FILES
7942 case KWHANDLETYPE_TEMP_FILE:
7943 {
7944 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7945
7946 kHlpAssert(!pOverlapped);
7947 kHlpAssert(pcbActuallyWritten);
7948
7949 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7950 {
7951 KU32 cbLeft;
7952 KU32 offSeg;
7953
7954 /* Locate the segment containing the byte at offFile. */
7955 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7956 KU32 iSeg = pTempFile->cSegs - 1;
7957 kHlpAssert(pTempFile->cSegs > 0);
7958 while (paSegs[iSeg].offData > pHandle->offFile)
7959 iSeg--;
7960
7961 /* Copy in the data. */
7962 cbLeft = cbToWrite;
7963 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7964 for (;;)
7965 {
7966 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7967 if (cbAvail >= cbLeft)
7968 {
7969 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7970 break;
7971 }
7972
7973 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7974 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7975 cbLeft -= cbAvail;
7976 offSeg = 0;
7977 iSeg++;
7978 kHlpAssert(iSeg < pTempFile->cSegs);
7979 }
7980
7981 /* Update the file offset. */
7982 pHandle->offFile += cbToWrite;
7983 if (pHandle->offFile > pTempFile->cbFile)
7984 pTempFile->cbFile = pHandle->offFile;
7985
7986 *pcbActuallyWritten = cbToWrite;
7987
7988 g_cbWriteFileTotal += cbToWrite;
7989 g_cbWriteFileToInMemTemp += cbToWrite;
7990 g_cWriteFileToInMemTemp++;
7991
7992 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7993 kwSandboxHandlePut(pHandle);
7994 return TRUE;
7995 }
7996
7997 kHlpAssertFailed();
7998 kwSandboxHandlePut(pHandle);
7999 *pcbActuallyWritten = 0;
8000 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8001 return FALSE;
8002 }
8003# endif
8004
8005 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8006 kHlpAssertFailed();
8007 kwSandboxHandlePut(pHandle);
8008 SetLastError(ERROR_ACCESS_DENIED);
8009 *pcbActuallyWritten = 0;
8010 return FALSE;
8011
8012# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
8013 /*
8014 * Standard output & error.
8015 */
8016 case KWHANDLETYPE_OUTPUT_BUF:
8017 {
8018 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8019 if (pOutBuf->fIsConsole)
8020 {
8021 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
8022 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
8023 }
8024 else
8025 {
8026# ifdef WITH_STD_OUT_ERR_BUFFERING
8027 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
8028 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
8029 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
8030# else
8031 kHlpAssertFailed();
8032# endif
8033 }
8034 if (pcbActuallyWritten)
8035 *pcbActuallyWritten = cbToWrite;
8036 g_cbWriteFileTotal += cbToWrite;
8037 kwSandboxHandlePut(pHandle);
8038 return TRUE;
8039 }
8040# endif
8041
8042 default:
8043#ifdef WITH_TEMP_MEMORY_FILES
8044 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8045#endif
8046 kHlpAssertFailed();
8047 kwSandboxHandlePut(pHandle);
8048 SetLastError(ERROR_INVALID_FUNCTION);
8049 *pcbActuallyWritten = 0;
8050 return FALSE;
8051 }
8052 }
8053
8054 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
8055 if (fRet && pcbActuallyWritten)
8056 g_cbWriteFileTotal += *pcbActuallyWritten;
8057 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
8058 return fRet;
8059}
8060
8061
8062/** Kernel32 - WriteFileEx */
8063static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
8064 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
8065{
8066 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
8067
8068 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
8069 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
8070}
8071
8072#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
8073
8074#ifdef WITH_TEMP_MEMORY_FILES
8075
8076/** Kernel32 - SetEndOfFile; */
8077static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
8078{
8079 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8080 if (pHandle != NULL)
8081 {
8082 switch (pHandle->enmType)
8083 {
8084 case KWHANDLETYPE_TEMP_FILE:
8085 {
8086 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8087 if ( pHandle->offFile > pTempFile->cbFile
8088 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
8089 {
8090 kHlpAssertFailed();
8091 kwSandboxHandlePut(pHandle);
8092 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8093 return FALSE;
8094 }
8095
8096 pTempFile->cbFile = pHandle->offFile;
8097 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
8098 kwSandboxHandlePut(pHandle);
8099 return TRUE;
8100 }
8101
8102 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8103 kHlpAssertFailed();
8104 kwSandboxHandlePut(pHandle);
8105 SetLastError(ERROR_ACCESS_DENIED);
8106 return FALSE;
8107
8108# ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8109 case KWHANDLETYPE_OUTPUT_BUF:
8110 kHlpAssertFailed();
8111 kwSandboxHandlePut(pHandle);
8112 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
8113 return FALSE;
8114# endif
8115
8116 default:
8117 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8118 kHlpAssertFailed();
8119 kwSandboxHandlePut(pHandle);
8120 SetLastError(ERROR_INVALID_FUNCTION);
8121 return FALSE;
8122 }
8123 }
8124
8125 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
8126 return SetEndOfFile(hFile);
8127}
8128
8129
8130/** Kernel32 - GetFileType */
8131static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
8132{
8133 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8134 if (pHandle != NULL)
8135 {
8136 switch (pHandle->enmType)
8137 {
8138 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8139 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
8140 kwSandboxHandlePut(pHandle);
8141 return FILE_TYPE_DISK;
8142
8143 case KWHANDLETYPE_TEMP_FILE:
8144 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
8145 kwSandboxHandlePut(pHandle);
8146 return FILE_TYPE_DISK;
8147
8148#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8149 case KWHANDLETYPE_OUTPUT_BUF:
8150 {
8151 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8152 DWORD fRet;
8153 if (pOutBuf->fFileType != KU8_MAX)
8154 {
8155 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
8156 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
8157 }
8158 else
8159 {
8160 fRet = GetFileType(hFile);
8161 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
8162 }
8163 kwSandboxHandlePut(pHandle);
8164 return fRet;
8165 }
8166#endif
8167 }
8168 kwSandboxHandlePut(pHandle);
8169 }
8170
8171 KWFS_LOG(("GetFileType(%p)\n", hFile));
8172 return GetFileType(hFile);
8173}
8174
8175
8176/** Kernel32 - GetFileSize */
8177static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
8178{
8179 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8180 if (pHandle != NULL)
8181 {
8182 if (pcbHighDword)
8183 *pcbHighDword = 0;
8184 SetLastError(NO_ERROR);
8185 switch (pHandle->enmType)
8186 {
8187 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8188 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8189 kwSandboxHandlePut(pHandle);
8190 return pHandle->u.pCachedFile->cbCached;
8191
8192 case KWHANDLETYPE_TEMP_FILE:
8193 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8194 kwSandboxHandlePut(pHandle);
8195 return pHandle->u.pTempFile->cbFile;
8196
8197 case KWHANDLETYPE_OUTPUT_BUF:
8198 /* do default */
8199 break;
8200
8201 default:
8202 kHlpAssertFailed();
8203 kwSandboxHandlePut(pHandle);
8204 SetLastError(ERROR_INVALID_FUNCTION);
8205 return INVALID_FILE_SIZE;
8206 }
8207 kwSandboxHandlePut(pHandle);
8208 }
8209
8210 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
8211 return GetFileSize(hFile, pcbHighDword);
8212}
8213
8214
8215/** Kernel32 - GetFileSizeEx */
8216static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
8217{
8218 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8219 if (pHandle != NULL)
8220 {
8221 switch (pHandle->enmType)
8222 {
8223 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8224 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8225 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
8226 kwSandboxHandlePut(pHandle);
8227 return TRUE;
8228
8229 case KWHANDLETYPE_TEMP_FILE:
8230 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8231 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
8232 kwSandboxHandlePut(pHandle);
8233 return TRUE;
8234
8235 case KWHANDLETYPE_OUTPUT_BUF:
8236 /* do default */
8237 break;
8238
8239 default:
8240 kHlpAssertFailed();
8241 kwSandboxHandlePut(pHandle);
8242 SetLastError(ERROR_INVALID_FUNCTION);
8243 return INVALID_FILE_SIZE;
8244 }
8245 kwSandboxHandlePut(pHandle);
8246 }
8247
8248 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
8249 return GetFileSizeEx(hFile, pcbFile);
8250}
8251
8252
8253/** Kernel32 - CreateFileMappingW */
8254static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
8255 DWORD fProtect, DWORD dwMaximumSizeHigh,
8256 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
8257{
8258 HANDLE hMapping;
8259 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8260 if (pHandle != NULL)
8261 {
8262 switch (pHandle->enmType)
8263 {
8264 case KWHANDLETYPE_TEMP_FILE:
8265 {
8266 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8267 if ( ( fProtect == PAGE_READONLY
8268 || fProtect == PAGE_EXECUTE_READ)
8269 && dwMaximumSizeHigh == 0
8270 && ( dwMaximumSizeLow == 0
8271 || dwMaximumSizeLow == pTempFile->cbFile)
8272 && pwszName == NULL)
8273 {
8274 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
8275 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
8276 kwSandboxHandlePut(pHandle);
8277 return hMapping;
8278 }
8279 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
8280 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8281 kwSandboxHandlePut(pHandle);
8282 SetLastError(ERROR_ACCESS_DENIED);
8283 return INVALID_HANDLE_VALUE;
8284 }
8285
8286 /* moc.exe benefits from this. */
8287 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8288 {
8289 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8290 if ( ( fProtect == PAGE_READONLY
8291 || fProtect == PAGE_EXECUTE_READ)
8292 && dwMaximumSizeHigh == 0
8293 && ( dwMaximumSizeLow == 0
8294 || dwMaximumSizeLow == pCachedFile->cbCached)
8295 && pwszName == NULL)
8296 {
8297 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
8298 K_FALSE /*fIsFileHandle*/, &hMapping))
8299 { /* likely */ }
8300 else
8301 hMapping = NULL;
8302 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8303 kwSandboxHandlePut(pHandle);
8304 return hMapping;
8305 }
8306
8307 /* Do fallback (for .pch). */
8308 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8309 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8310 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8311
8312 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8313 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8314 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8315 kwSandboxHandlePut(pHandle);
8316 return hMapping;
8317 }
8318
8319 /** @todo read cached memory mapped files too for moc. */
8320 }
8321 kwSandboxHandlePut(pHandle);
8322 }
8323
8324 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8325 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8326 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8327 return hMapping;
8328}
8329
8330
8331/** Kernel32 - MapViewOfFile */
8332static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8333 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8334{
8335 PVOID pvRet;
8336 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8337 if (pHandle != NULL)
8338 {
8339 KU32 idxMapping;
8340
8341 /*
8342 * Ensure one free entry in the mapping tracking table first,
8343 * since this is common to both temporary and cached files.
8344 */
8345 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8346 { /* likely */ }
8347 else
8348 {
8349 void *pvNew;
8350 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8351 if (cNew)
8352 cNew *= 2;
8353 else
8354 cNew = 32;
8355 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8356 if (pvNew)
8357 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8358 else
8359 {
8360 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8361 kwSandboxHandlePut(pHandle);
8362 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8363 return NULL;
8364 }
8365 g_Sandbox.cMemMappingsAlloc = cNew;
8366 }
8367
8368 /*
8369 * Type specific work.
8370 */
8371 switch (pHandle->enmType)
8372 {
8373 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8374 case KWHANDLETYPE_TEMP_FILE:
8375 case KWHANDLETYPE_OUTPUT_BUF:
8376 default:
8377 kHlpAssertFailed();
8378 kwSandboxHandlePut(pHandle);
8379 SetLastError(ERROR_INVALID_OPERATION);
8380 return NULL;
8381
8382 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8383 {
8384 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8385 if ( dwDesiredAccess == FILE_MAP_READ
8386 && offFileHigh == 0
8387 && offFileLow == 0
8388 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8389 {
8390 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8391 if (pTempFile->cSegs != 1)
8392 {
8393 KU32 iSeg;
8394 KU32 cbLeft;
8395 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8396 KU8 *pbAll = NULL;
8397 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8398 if (rc != 0)
8399 {
8400 kHlpAssertFailed();
8401 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8402 return NULL;
8403 }
8404
8405 cbLeft = pTempFile->cbFile;
8406 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8407 {
8408 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8409 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8410 cbLeft -= cbToCopy;
8411 }
8412
8413 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8414 {
8415 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8416 pTempFile->paSegs[iSeg].pbData = NULL;
8417 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8418 }
8419
8420 pTempFile->cSegs = 1;
8421 pTempFile->cbFileAllocated = cbAll;
8422 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8423 pTempFile->paSegs[0].pbData = pbAll;
8424 pTempFile->paSegs[0].offData = 0;
8425 }
8426
8427 pTempFile->cMappings++;
8428 kHlpAssert(pTempFile->cMappings == 1);
8429
8430 pvRet = pTempFile->paSegs[0].pbData;
8431 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8432 break;
8433 }
8434
8435 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8436 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8437 kwSandboxHandlePut(pHandle);
8438 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8439 return NULL;
8440 }
8441
8442 /*
8443 * This is simple in comparison to the above temporary file code.
8444 */
8445 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8446 {
8447 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8448 if ( dwDesiredAccess == FILE_MAP_READ
8449 && offFileHigh == 0
8450 && offFileLow == 0
8451 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8452 {
8453 pvRet = pCachedFile->pbCached;
8454 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8455 break;
8456 }
8457 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8458 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8459 kwSandboxHandlePut(pHandle);
8460 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8461 return NULL;
8462 }
8463 }
8464
8465 /*
8466 * Insert into the mapping tracking table. This is common
8467 * and we should only get here with a non-NULL pvRet.
8468 *
8469 * Note! We could look for duplicates and do ref counting, but it's
8470 * easier to just append for now.
8471 */
8472 kHlpAssert(pvRet != NULL);
8473 idxMapping = g_Sandbox.cMemMappings;
8474 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8475
8476 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8477 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8478 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8479 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8480 g_Sandbox.cMemMappings++;
8481
8482 kwSandboxHandlePut(pHandle);
8483 return pvRet;
8484 }
8485
8486 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8487 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8488 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8489 return pvRet;
8490}
8491
8492
8493/** Kernel32 - MapViewOfFileEx */
8494static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8495 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8496{
8497 PVOID pvRet;
8498 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8499 if (pHandle != NULL)
8500 {
8501 switch (pHandle->enmType)
8502 {
8503 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8504 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8505 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8506 if (!pvMapAddr)
8507 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8508 else
8509 {
8510 kHlpAssertFailed();
8511 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8512 }
8513 kwSandboxHandlePut(pHandle);
8514 return NULL;
8515
8516 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8517 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8518 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8519 if (!pvMapAddr)
8520 {
8521 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8522 kwSandboxHandlePut(pHandle);
8523 return pvRet;
8524 }
8525 /* We can use fallback here as the handle is an actual section handle. */
8526 break;
8527
8528 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8529 case KWHANDLETYPE_TEMP_FILE:
8530 case KWHANDLETYPE_OUTPUT_BUF:
8531 default:
8532 kHlpAssertFailed();
8533 kwSandboxHandlePut(pHandle);
8534 SetLastError(ERROR_INVALID_OPERATION);
8535 return NULL;
8536 }
8537 kwSandboxHandlePut(pHandle);
8538 }
8539
8540 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8541 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8542 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8543 return pvRet;
8544
8545}
8546
8547/** Kernel32 - UnmapViewOfFile */
8548static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8549{
8550 /*
8551 * Consult the memory mapping tracker.
8552 */
8553 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8554 KU32 idxMapping = g_Sandbox.cMemMappings;
8555 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8556 while (idxMapping-- > 0)
8557 if (paMemMappings[idxMapping].pvMapping == pvBase)
8558 {
8559 /* Type specific stuff. */
8560 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8561 {
8562 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8563 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8564 }
8565 else
8566 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8567
8568 /* Deref and probably free it. */
8569 if (--paMemMappings[idxMapping].cRefs == 0)
8570 {
8571 g_Sandbox.cMemMappings--;
8572 if (idxMapping != g_Sandbox.cMemMappings)
8573 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8574 }
8575 return TRUE;
8576 }
8577
8578 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8579 return UnmapViewOfFile(pvBase);
8580}
8581
8582/** @todo UnmapViewOfFileEx */
8583
8584#endif /* WITH_TEMP_MEMORY_FILES */
8585
8586
8587/** Kernel32 - DuplicateHandle */
8588static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8589 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8590{
8591 BOOL fRet;
8592
8593 /*
8594 * We must catch our handles being duplicated.
8595 */
8596 if (hSrcProc == GetCurrentProcess())
8597 {
8598 PKWHANDLE pHandle = kwSandboxHandleGet(hSrc);
8599 if (pHandle)
8600 {
8601 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8602 if (fRet)
8603 {
8604 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8605 {
8606 pHandle->cRefs++;
8607 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8608 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8609 pHandle->enmType, pHandle->cRefs));
8610 }
8611 else
8612 {
8613 fRet = FALSE;
8614 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8615 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8616 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8617 }
8618 }
8619 else
8620 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8621 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8622 kwSandboxHandlePut(pHandle);
8623 return fRet;
8624 }
8625 }
8626
8627 /*
8628 * Not one of ours, just do what the caller asks and log it.
8629 */
8630 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8631 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8632 fInheritHandle, dwOptions, fRet, *phNew));
8633 return fRet;
8634}
8635
8636
8637/** Kernel32 - CloseHandle */
8638static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8639{
8640 BOOL fRet;
8641 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8642 PKWHANDLE pHandle = kwSandboxHandleGet(hObject);
8643 if (pHandle)
8644 {
8645 /* Prevent the closing of the standard output and error handles. */
8646 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8647 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) /* why this?!? */)
8648 {
8649 fRet = CloseHandle(hObject);
8650 if (fRet)
8651 {
8652 EnterCriticalSection(&g_Sandbox.HandlesLock);
8653 pHandle = g_Sandbox.papHandles[idxHandle];
8654 g_Sandbox.papHandles[idxHandle] = NULL;
8655 g_Sandbox.cActiveHandles--;
8656 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8657 if (--pHandle->cRefs == 0)
8658 {
8659#ifdef WITH_TEMP_MEMORY_FILES
8660 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8661 {
8662 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8663 pHandle->u.pTempFile->cActiveHandles--;
8664 }
8665#endif
8666 kHlpFree(pHandle);
8667 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8668 }
8669 else
8670 {
8671 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8672 kwSandboxHandlePut(pHandle);
8673 }
8674 LeaveCriticalSection(&g_Sandbox.HandlesLock);
8675 return fRet;
8676 }
8677 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8678 }
8679 else
8680 {
8681#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8682 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8683 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8684#else
8685 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of stdXXX!\n", hObject));
8686#endif
8687 fRet = TRUE;
8688 }
8689 kwSandboxHandlePut(pHandle);
8690 return fRet;
8691 }
8692
8693 fRet = CloseHandle(hObject);
8694 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8695 return fRet;
8696}
8697
8698
8699/** Kernel32 - GetFileAttributesA. */
8700static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8701{
8702 DWORD fRet;
8703 const char *pszExt = kHlpGetExt(pszFilename);
8704 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8705 {
8706 KFSLOOKUPERROR enmError;
8707 PKFSOBJ pFsObj;
8708 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8709
8710 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8711 if (pFsObj)
8712 {
8713 kHlpAssert(pFsObj->fHaveStats);
8714 fRet = pFsObj->Stats.st_attribs;
8715 kFsCacheObjRelease(g_pFsCache, pFsObj);
8716 }
8717 else
8718 {
8719 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8720 fRet = INVALID_FILE_ATTRIBUTES;
8721 }
8722
8723 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8724 return fRet;
8725 }
8726
8727 fRet = GetFileAttributesA(pszFilename);
8728 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8729 return fRet;
8730}
8731
8732
8733/** Kernel32 - GetFileAttributesW. */
8734static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8735{
8736 DWORD fRet;
8737 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8738 {
8739 KFSLOOKUPERROR enmError;
8740 PKFSOBJ pFsObj;
8741 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8742
8743 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8744 if (pFsObj)
8745 {
8746 kHlpAssert(pFsObj->fHaveStats);
8747 fRet = pFsObj->Stats.st_attribs;
8748 kFsCacheObjRelease(g_pFsCache, pFsObj);
8749 }
8750 else
8751 {
8752 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8753 fRet = INVALID_FILE_ATTRIBUTES;
8754 }
8755#ifndef NDEBUG
8756 {
8757 DWORD fCheck = GetFileAttributesW(pwszFilename);
8758 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8759 }
8760#endif
8761 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8762 return fRet;
8763 }
8764
8765 fRet = GetFileAttributesW(pwszFilename);
8766 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8767 return fRet;
8768}
8769
8770
8771/** Kernel32 - GetFileAttributesExA. */
8772static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8773 WIN32_FILE_ATTRIBUTE_DATA *pData)
8774{
8775 BOOL fRet;
8776 const char *pszExt = kHlpGetExt(pszFilename);
8777 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8778 {
8779 KFSLOOKUPERROR enmError;
8780 PKFSOBJ pFsObj;
8781 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8782
8783 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8784 if (pFsObj)
8785 {
8786 kHlpAssert(pFsObj->fHaveStats);
8787 if (enmLevel == GetFileExInfoStandard)
8788 {
8789 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8790 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8791 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8792 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8793 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8794 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8795 kFsCacheObjRelease(g_pFsCache, pFsObj);
8796 fRet = TRUE;
8797 }
8798 else
8799 {
8800 kFsCacheObjRelease(g_pFsCache, pFsObj);
8801 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8802 }
8803 }
8804 else
8805 {
8806 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8807 fRet = FALSE;
8808 }
8809
8810#ifdef K_STRICT
8811 {
8812 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8813 DWORD const dwErrSaved = GetLastError();
8814 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8815 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8816 if (fRetCheck && fRet)
8817 {
8818# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8819 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8820 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8821 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8822 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8823 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8824 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8825 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8826 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8827 }
8828 else
8829 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8830 SetLastError(dwErrSaved);
8831 }
8832#endif
8833 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8834 return fRet;
8835 }
8836
8837 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8838 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8839 return fRet;
8840}
8841
8842
8843/** Kernel32 - GetFileAttributesExW. */
8844static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8845 WIN32_FILE_ATTRIBUTE_DATA *pData)
8846{
8847 BOOL fRet;
8848 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8849 {
8850 KFSLOOKUPERROR enmError;
8851 PKFSOBJ pFsObj;
8852 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8853
8854 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8855 if (pFsObj)
8856 {
8857 kHlpAssert(pFsObj->fHaveStats);
8858 if (enmLevel == GetFileExInfoStandard)
8859 {
8860 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8861 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8862 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8863 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8864 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8865 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8866 kFsCacheObjRelease(g_pFsCache, pFsObj);
8867 fRet = TRUE;
8868 }
8869 else
8870 {
8871 kFsCacheObjRelease(g_pFsCache, pFsObj);
8872 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8873 }
8874 }
8875 else
8876 {
8877 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8878 fRet = FALSE;
8879 }
8880
8881#ifdef K_STRICT
8882 {
8883 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8884 DWORD const dwErrSaved = GetLastError();
8885 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8886 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8887 if (fRetCheck && fRet)
8888 {
8889# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8890 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8891 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8892 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8893 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8894 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8895 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8896 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8897 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8898 }
8899 else
8900 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8901 SetLastError(dwErrSaved);
8902 }
8903#endif
8904 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8905 return fRet;
8906 }
8907
8908 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8909 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8910 return fRet;
8911}
8912
8913
8914/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8915 * directory containing each include file. We cache the result to speed
8916 * things up a little. */
8917static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8918{
8919 DWORD cwcRet;
8920 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8921 {
8922 KFSLOOKUPERROR enmError;
8923 PKFSOBJ pObj;
8924 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8925
8926 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8927 if (pObj)
8928 {
8929 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8930 {
8931 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8932 {
8933 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8934
8935 /* Should preserve trailing slash on directory paths. */
8936 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8937 {
8938 if ( cwcRet + 1 < cwcShortPath
8939 && pwszShortPath[cwcRet - 1] != '\\')
8940 {
8941 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8942 if ( cwcIn > 0
8943 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8944 {
8945 pwszShortPath[cwcRet++] = '\\';
8946 pwszShortPath[cwcRet] = '\0';
8947 }
8948 }
8949 }
8950
8951 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8952 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8953 kFsCacheObjRelease(g_pFsCache, pObj);
8954 return cwcRet;
8955 }
8956
8957 /* fall back for complicated cases. */
8958 }
8959 kFsCacheObjRelease(g_pFsCache, pObj);
8960 }
8961 }
8962 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8963 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8964 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8965 return cwcRet;
8966}
8967
8968
8969#ifdef WITH_TEMP_MEMORY_FILES
8970/** Kernel32 - DeleteFileW
8971 * Skip deleting the in-memory files. */
8972static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8973{
8974 BOOL fRc;
8975 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8976 && kwFsIsClTempFileW(pwszFilename))
8977 {
8978 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8979 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8980 fRc = TRUE;
8981 }
8982 else
8983 {
8984 fRc = DeleteFileW(pwszFilename);
8985 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8986 }
8987 return fRc;
8988}
8989#endif /* WITH_TEMP_MEMORY_FILES */
8990
8991
8992
8993#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8994
8995/*
8996 *
8997 * Console output buffering.
8998 * Console output buffering.
8999 * Console output buffering.
9000 *
9001 */
9002
9003
9004/**
9005 * Write a wide char string to the console.
9006 *
9007 * @param pSandbox The sandbox which output buffer to flush.
9008 */
9009static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
9010{
9011 if (cwcToWrite > 0)
9012 {
9013 DWORD cwcWritten = 0;
9014 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
9015 {
9016 if (cwcWritten == cwcToWrite)
9017 { /* likely */ }
9018 else
9019 {
9020 DWORD off = 0;
9021 do
9022 {
9023 off += cwcWritten;
9024 cwcWritten = 0;
9025 } while ( off < cwcToWrite
9026 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
9027 kHlpAssert(off == cwcWritten);
9028 }
9029 }
9030 else
9031 kHlpAssertFailed();
9032 pSandbox->Combined.cFlushes++;
9033 }
9034}
9035
9036
9037/**
9038 * Flushes the combined console output buffer.
9039 *
9040 * @param pSandbox The sandbox which output buffer to flush.
9041 */
9042static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
9043{
9044 if (pSandbox->Combined.cwcBuf > 0)
9045 {
9046 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
9047 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
9048 pSandbox->Combined.cwcBuf = 0;
9049 }
9050}
9051
9052
9053/**
9054 * For handling combined buffer overflow cases line by line.
9055 *
9056 * @param pSandbox The sandbox.
9057 * @param pwcBuf What to add to the combined buffer. Usually a
9058 * line, unless we're really low on buffer space.
9059 * @param cwcBuf The length of what to add.
9060 * @param fBrokenLine Whether this is a broken line.
9061 */
9062static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
9063{
9064 if (fBrokenLine)
9065 kwSandboxConsoleFlushCombined(pSandbox);
9066 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
9067 {
9068 kwSandboxConsoleFlushCombined(pSandbox);
9069 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
9070 }
9071 else
9072 {
9073 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
9074 pSandbox->Combined.cwcBuf += cwcBuf;
9075 }
9076}
9077
9078
9079/**
9080 * Called to final flush a line buffer via the combined buffer (if applicable).
9081 *
9082 * @param pSandbox The sandbox.
9083 * @param pLineBuf The line buffer.
9084 * @param pszName The line buffer name (for logging)
9085 */
9086static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
9087{
9088 if (pLineBuf->fIsConsole)
9089 {
9090 if (pLineBuf->u.Con.cwcBuf > 0)
9091 {
9092 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
9093
9094 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
9095 {
9096 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
9097 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
9098 }
9099 else
9100 {
9101 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
9102 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
9103 }
9104 pLineBuf->u.Con.cwcBuf = 0;
9105 }
9106 }
9107#ifdef WITH_STD_OUT_ERR_BUFFERING
9108 else if (pLineBuf->u.Fully.cchBuf > 0)
9109 {
9110 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
9111
9112 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
9113 pLineBuf->u.Fully.cchBuf = 0;
9114 }
9115#endif
9116}
9117
9118
9119/**
9120 * Called at the end of sandboxed execution to flush both stream buffers.
9121 *
9122 * @param pSandbox The sandbox.
9123 */
9124static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
9125{
9126 /*
9127 * First do the cl.exe source file supression trick, if applicable.
9128 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
9129 * handle.
9130 */
9131 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9132 && pSandbox->Combined.cFlushes == 0)
9133 {
9134 if ( pSandbox->StdOut.fIsConsole
9135 || pSandbox->StdErr.fIsConsole)
9136 {
9137 if ( pSandbox->Combined.cwcBuf >= 3
9138 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
9139 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
9140 {
9141 KI32 off = pSandbox->Combined.cwcBuf - 1;
9142 if (pSandbox->Combined.wszBuf[off] == '\n')
9143 {
9144 KBOOL fOk = K_TRUE;
9145 while (off-- > 0)
9146 {
9147 wchar_t const wc = pSandbox->Combined.wszBuf[off];
9148 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
9149 { /* likely */ }
9150 else
9151 {
9152 fOk = K_FALSE;
9153 break;
9154 }
9155 }
9156 if (fOk)
9157 {
9158 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
9159 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9160 pSandbox->Combined.cwcBuf = 0;
9161 return;
9162 }
9163 }
9164 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
9165 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9166 }
9167 }
9168#ifdef WITH_STD_OUT_ERR_BUFFERING
9169 /*
9170 * Otherwise, it goes to standard output (redirected).
9171 */
9172 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
9173 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
9174 {
9175 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
9176 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
9177 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
9178
9179 if (pchBuf[off] == '\n')
9180 {
9181 KBOOL fOk = K_TRUE;
9182 if (pchBuf[off - 1] == '\r')
9183 off--;
9184 while (off-- > 0)
9185 {
9186 char const ch = pchBuf[off];
9187 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
9188 { /* likely */ }
9189 else
9190 {
9191 fOk = K_FALSE;
9192 break;
9193 }
9194 }
9195 if (fOk)
9196 {
9197 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
9198 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9199 pSandbox->StdOut.u.Fully.cchBuf = 0;
9200 return;
9201 }
9202 }
9203 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
9204 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9205 }
9206#endif
9207 }
9208
9209 /*
9210 * Flush the two line buffer, then the combined buffer.
9211 */
9212 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
9213 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
9214 kwSandboxConsoleFlushCombined(pSandbox);
9215}
9216
9217
9218/**
9219 * Writes a string to the given output stream.
9220 *
9221 * @param pSandbox The sandbox.
9222 * @param pLineBuf The line buffer for the output stream.
9223 * @param pwcBuffer The buffer to write.
9224 * @param cwcToWrite The number of wchar_t's in the buffer.
9225 */
9226static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
9227{
9228 kHlpAssert(pLineBuf->fIsConsole);
9229 if (cwcToWrite > 0)
9230 {
9231 /*
9232 * First, find the start of the last incomplete line so we can figure
9233 * out how much line buffering we need to do.
9234 */
9235 KU32 cchLastIncompleteLine;
9236 KU32 offLastIncompleteLine = cwcToWrite;
9237 while ( offLastIncompleteLine > 0
9238 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
9239 offLastIncompleteLine--;
9240 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
9241
9242 /* Was there anything to line buffer? */
9243 if (offLastIncompleteLine < cwcToWrite)
9244 {
9245 /* Need to grow the line buffer? */
9246 KU32 cwcNeeded = offLastIncompleteLine == 0
9247 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
9248 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
9249 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
9250 {
9251 void *pvNew;
9252 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
9253 while (cwcNew < cwcNeeded)
9254 cwcNew *= 2;
9255 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
9256 if (pvNew)
9257 {
9258 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9259 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
9260 }
9261 else
9262 {
9263 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
9264 if (pvNew)
9265 {
9266 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9267 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
9268 }
9269 else
9270 {
9271 /* This isn't perfect, but it will have to do for now. */
9272 if (pLineBuf->u.Con.cwcBuf > 0)
9273 {
9274 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9275 K_TRUE /*fBrokenLine*/);
9276 pLineBuf->u.Con.cwcBuf = 0;
9277 }
9278 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
9279 return;
9280 }
9281 }
9282 }
9283
9284 /*
9285 * Handle the case where we only add to the line buffer.
9286 */
9287 if (offLastIncompleteLine == 0)
9288 {
9289 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
9290 pLineBuf->u.Con.cwcBuf += cwcToWrite;
9291 return;
9292 }
9293 }
9294
9295 /*
9296 * If there is sufficient combined buffer to handle this request, this is rather simple.
9297 */
9298 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
9299 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9300 {
9301 if (pLineBuf->u.Con.cwcBuf > 0)
9302 {
9303 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9304 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9305 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9306 pLineBuf->u.Con.cwcBuf = 0;
9307 }
9308
9309 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9310 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9311 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9312 }
9313 else
9314 {
9315 /*
9316 * Do line-by-line processing of the input, flusing the combined buffer
9317 * when it becomes necessary. We may have to write lines
9318 */
9319 KU32 off = 0;
9320 KU32 offNextLine = 0;
9321
9322 /* If there are buffered chars, we handle the first line outside the
9323 main loop. We must try our best outputting it as a complete line. */
9324 if (pLineBuf->u.Con.cwcBuf > 0)
9325 {
9326 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9327 offNextLine++;
9328 offNextLine++;
9329 kHlpAssert(offNextLine <= offLastIncompleteLine);
9330
9331 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9332 {
9333 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9334 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9335 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9336 pLineBuf->u.Con.cwcBuf = 0;
9337
9338 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9339 pSandbox->Combined.cwcBuf += offNextLine;
9340 }
9341 else
9342 {
9343 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9344 if (cwcLeft > 0)
9345 {
9346 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9347 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9348 pLineBuf->u.Con.cwcBuf += cwcCopy;
9349 off += cwcCopy;
9350 }
9351 if (pLineBuf->u.Con.cwcBuf > 0)
9352 {
9353 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9354 K_TRUE /*fBrokenLine*/);
9355 pLineBuf->u.Con.cwcBuf = 0;
9356 }
9357 if (off < offNextLine)
9358 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9359 }
9360 off = offNextLine;
9361 }
9362
9363 /* Deal with the remaining lines */
9364 while (off < offLastIncompleteLine)
9365 {
9366 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9367 offNextLine++;
9368 offNextLine++;
9369 kHlpAssert(offNextLine <= offLastIncompleteLine);
9370 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9371 off = offNextLine;
9372 }
9373 }
9374
9375 /*
9376 * Buffer any remaining incomplete line chars.
9377 */
9378 if (cchLastIncompleteLine)
9379 {
9380 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9381 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9382 }
9383 }
9384}
9385
9386
9387/**
9388 * Worker for WriteConsoleA and WriteFile.
9389 *
9390 * @param pSandbox The sandbox.
9391 * @param pLineBuf The line buffer.
9392 * @param pchBuffer What to write.
9393 * @param cchToWrite How much to write.
9394 */
9395static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9396{
9397 /*
9398 * Convert it to wide char and use the 'W' to do the work.
9399 */
9400 int cwcRet;
9401 KU32 cwcBuf = cchToWrite * 2 + 1;
9402 wchar_t *pwcBufFree = NULL;
9403 wchar_t *pwcBuf;
9404 kHlpAssert(pLineBuf->fIsConsole);
9405
9406 if (cwcBuf <= 4096)
9407 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9408 else
9409 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9410
9411 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9412 if (cwcRet > 0)
9413 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9414 else
9415 {
9416 DWORD cchWritten;
9417 kHlpAssertFailed();
9418
9419 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9420 if (pLineBuf->u.Con.cwcBuf > 0)
9421 {
9422 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9423 pLineBuf->u.Con.cwcBuf = 0;
9424 }
9425 kwSandboxConsoleFlushCombined(pSandbox);
9426
9427 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9428 {
9429 if (cchWritten >= cchToWrite)
9430 { /* likely */ }
9431 else
9432 {
9433 KU32 off = 0;
9434 do
9435 {
9436 off += cchWritten;
9437 cchWritten = 0;
9438 } while ( off < cchToWrite
9439 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9440 }
9441 }
9442 }
9443
9444 if (pwcBufFree)
9445 kHlpFree(pwcBufFree);
9446}
9447
9448
9449/** Kernel32 - WriteConsoleA */
9450BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9451 PVOID pvReserved)
9452{
9453 BOOL fRc;
9454 PKWOUTPUTSTREAMBUF pLineBuf;
9455 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9456
9457 if (hConOutput == g_Sandbox.StdErr.hOutput)
9458 pLineBuf = &g_Sandbox.StdErr;
9459 else
9460 pLineBuf = &g_Sandbox.StdOut;
9461 if (pLineBuf->fIsConsole)
9462 {
9463 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9464
9465 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9466 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9467 if (pcbWritten)
9468 *pcbWritten = cbToWrite;
9469 fRc = TRUE;
9470 }
9471 else
9472 {
9473 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9474 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9475 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9476 }
9477 return fRc;
9478}
9479
9480
9481/** Kernel32 - WriteConsoleW */
9482BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9483 PVOID pvReserved)
9484{
9485 BOOL fRc;
9486 PKWOUTPUTSTREAMBUF pLineBuf;
9487 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9488
9489 if (hConOutput == g_Sandbox.StdErr.hOutput)
9490 pLineBuf = &g_Sandbox.StdErr;
9491 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9492 pLineBuf = &g_Sandbox.StdOut;
9493 else
9494 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9495 if (pLineBuf->fIsConsole)
9496 {
9497 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9498
9499 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9500 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9501 if (pcwcWritten)
9502 *pcwcWritten = cwcToWrite;
9503 fRc = TRUE;
9504 }
9505 else
9506 {
9507 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9508 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9509 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9510 }
9511 return fRc;
9512}
9513
9514#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9515
9516
9517
9518/*
9519 *
9520 * Virtual memory leak prevension.
9521 * Virtual memory leak prevension.
9522 * Virtual memory leak prevension.
9523 *
9524 */
9525
9526#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9527
9528/** For debug logging. */
9529# ifndef NDEBUG
9530static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9531{
9532 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9533 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9534 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9535 if (cbMemInfo != 0)
9536 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9537 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9538 MemInfo.BaseAddress,
9539 MemInfo.AllocationBase,
9540 MemInfo.RegionSize,
9541 MemInfo.State,
9542 MemInfo.Protect,
9543 MemInfo.Type));
9544}
9545# else
9546# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9547# endif
9548
9549/**
9550 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9551 *
9552 * @param idxFixed The fixed allocation index to "free".
9553 */
9554static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9555{
9556 BOOL fRc;
9557 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9558 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9559 kHlpAssert(fRc); K_NOREF(fRc);
9560 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9561 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9562}
9563
9564#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9565
9566
9567/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9568 * location (~78MB in 32-bit 2010 compiler). */
9569static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9570{
9571 PVOID pvMem;
9572 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9573 {
9574 KU32 idxPreAllocated = KU32_MAX;
9575
9576#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9577 /*
9578 * Look for a pre-reserved CL.exe heap allocation.
9579 */
9580 pvMem = NULL;
9581 if ( pvAddr != 0
9582 && (fAllocType & MEM_RESERVE))
9583 {
9584 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9585 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9586 while (idxFixed-- > 0)
9587 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9588 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9589 {
9590 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9591 {
9592 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9593 {
9594 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9595 pvMem = pvAddr;
9596 idxPreAllocated = idxFixed;
9597 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9598 pvAddr, cb, fAllocType, fProt, pvMem));
9599 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9600 SetLastError(NO_ERROR);
9601 break;
9602 }
9603 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9604 }
9605 else
9606 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9607 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9608 }
9609 }
9610 if (!pvMem)
9611#endif
9612 {
9613 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9614 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9615 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9616 if ( pvAddr
9617 && pvAddr != pvMem
9618 && !( fAllocType == MEM_RESERVE /* After mapping the PCH, VS2019 ends up here (happens */
9619 && fProt == PAGE_READWRITE /* in real cl.exe runs too). Just shut it up to avoid confusion. */
9620#if K_ARCH_BITS >= 64
9621 && cb > 0x10000000 /* seen 67c00000, 33e00000, ++ */
9622#else
9623 && cb > 0x04000000 /* no idea */
9624#endif
9625 )
9626 )
9627 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9628 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9629 }
9630
9631 if (pvMem)
9632 {
9633 /*
9634 * Track it.
9635 */
9636 PKWVIRTALLOC pTracker;
9637
9638 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9639 pTracker = g_Sandbox.pVirtualAllocHead;
9640 while ( pTracker
9641 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9642 pTracker = pTracker->pNext;
9643 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9644 if (!pTracker)
9645 {
9646 DWORD dwErr = GetLastError();
9647 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9648 if (pTracker)
9649 {
9650 pTracker->pvAlloc = pvMem;
9651 pTracker->cbAlloc = cb;
9652 pTracker->idxPreAllocated = idxPreAllocated;
9653 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9654 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9655 g_Sandbox.pVirtualAllocHead = pTracker;
9656 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9657 }
9658 SetLastError(dwErr);
9659 }
9660 }
9661 }
9662 else
9663 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9664 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9665 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9666 return pvMem;
9667}
9668
9669
9670/** Kernel32 - VirtualFree. */
9671static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9672{
9673 BOOL fRc;
9674 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9675 {
9676 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9677 if (dwFreeType & MEM_RELEASE)
9678 {
9679 PKWVIRTALLOC pTracker;
9680 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9681 pTracker = g_Sandbox.pVirtualAllocHead;
9682 if (pTracker)
9683 {
9684 if (pTracker->pvAlloc == pvAddr)
9685 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9686 else
9687 {
9688 PKWVIRTALLOC pPrev;
9689 do
9690 {
9691 pPrev = pTracker;
9692 pTracker = pTracker->pNext;
9693 } while (pTracker && pTracker->pvAlloc != pvAddr);
9694 if (pTracker)
9695 pPrev->pNext = pTracker->pNext;
9696 }
9697 if (pTracker)
9698 {
9699#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9700 if (pTracker->idxPreAllocated != KU32_MAX)
9701 {
9702 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9703 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9704 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9705 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9706 kHlpFree(pTracker);
9707 return TRUE;
9708 }
9709#endif
9710
9711 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9712 if (fRc)
9713 kHlpFree(pTracker);
9714 else
9715 {
9716 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9717 g_Sandbox.pVirtualAllocHead = pTracker;
9718 }
9719 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9720 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9721 return fRc;
9722 }
9723
9724 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9725 }
9726 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9727 }
9728 }
9729
9730#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9731 /*
9732 * Protect our fixed allocations (this isn't just paranoia, btw.).
9733 */
9734 if (dwFreeType & MEM_RELEASE)
9735 {
9736 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9737 while (idxFixed-- > 0)
9738 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9739 {
9740 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9741 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9742 return TRUE;
9743 }
9744 }
9745#endif
9746
9747 /*
9748 * Not tracker or not actually free the virtual range.
9749 */
9750 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9751 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9752 return fRc;
9753}
9754
9755
9756/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9757HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9758{
9759 HANDLE hHeap;
9760 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9761
9762 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9763 if (hHeap != NULL)
9764 {
9765 DWORD dwErr = GetLastError();
9766 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9767 if (pTracker)
9768 {
9769 pTracker->hHeap = hHeap;
9770 pTracker->pNext = g_Sandbox.pHeapHead;
9771 g_Sandbox.pHeapHead = pTracker;
9772 }
9773
9774 SetLastError(dwErr);
9775 }
9776 return hHeap;
9777
9778}
9779
9780
9781/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9782BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9783{
9784 BOOL fRc = HeapDestroy(hHeap);
9785 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9786 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9787 if (fRc)
9788 {
9789 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9790 if (pTracker)
9791 {
9792 if (pTracker->hHeap == hHeap)
9793 g_Sandbox.pHeapHead = pTracker->pNext;
9794 else
9795 {
9796 PKWHEAP pPrev;
9797 do
9798 {
9799 pPrev = pTracker;
9800 pTracker = pTracker->pNext;
9801 } while (pTracker && pTracker->hHeap == hHeap);
9802 if (pTracker)
9803 pPrev->pNext = pTracker->pNext;
9804 }
9805 if (pTracker)
9806 kHlpFree(pTracker);
9807 else
9808 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9809 }
9810 }
9811
9812 return fRc;
9813}
9814
9815
9816
9817/*
9818 *
9819 * Thread/Fiber local storage leak prevention.
9820 * Thread/Fiber local storage leak prevention.
9821 * Thread/Fiber local storage leak prevention.
9822 *
9823 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9824 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9825 * we're leaking these indexes, but more importantely we crash during
9826 * worker exit since the callback is triggered multiple times.
9827 */
9828
9829
9830/** Kernel32 - FlsAlloc */
9831DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9832{
9833 DWORD idxFls = FlsAlloc(pfnCallback);
9834 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9835 if (idxFls != FLS_OUT_OF_INDEXES)
9836 {
9837 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9838 if (pTracker)
9839 {
9840 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9841 pTracker->idx = idxFls;
9842 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9843 g_Sandbox.pFlsAllocHead = pTracker;
9844 }
9845 }
9846
9847 return idxFls;
9848}
9849
9850/** Kernel32 - FlsFree */
9851BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9852{
9853 BOOL fRc = FlsFree(idxFls);
9854 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9855 if (fRc)
9856 {
9857 PKWLOCALSTORAGE pTracker;
9858 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9859
9860 pTracker = g_Sandbox.pFlsAllocHead;
9861 if (pTracker)
9862 {
9863 if (pTracker->idx == idxFls)
9864 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9865 else
9866 {
9867 PKWLOCALSTORAGE pPrev;
9868 do
9869 {
9870 pPrev = pTracker;
9871 pTracker = pTracker->pNext;
9872 } while (pTracker && pTracker->idx != idxFls);
9873 if (pTracker)
9874 pPrev->pNext = pTracker->pNext;
9875 }
9876 if (pTracker)
9877 {
9878 pTracker->idx = FLS_OUT_OF_INDEXES;
9879 pTracker->pNext = NULL;
9880 kHlpFree(pTracker);
9881 }
9882 }
9883 }
9884 return fRc;
9885}
9886
9887
9888/** Kernel32 - TlsAlloc */
9889DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9890{
9891 DWORD idxTls = TlsAlloc();
9892 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9893 if (idxTls != TLS_OUT_OF_INDEXES)
9894 {
9895 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9896 if (pTracker)
9897 {
9898 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9899 pTracker->idx = idxTls;
9900 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9901 g_Sandbox.pTlsAllocHead = pTracker;
9902 }
9903 }
9904
9905 return idxTls;
9906}
9907
9908/** Kernel32 - TlsFree */
9909BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9910{
9911 BOOL fRc = TlsFree(idxTls);
9912 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9913 if (fRc)
9914 {
9915 PKWLOCALSTORAGE pTracker;
9916 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9917
9918 pTracker = g_Sandbox.pTlsAllocHead;
9919 if (pTracker)
9920 {
9921 if (pTracker->idx == idxTls)
9922 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9923 else
9924 {
9925 PKWLOCALSTORAGE pPrev;
9926 do
9927 {
9928 pPrev = pTracker;
9929 pTracker = pTracker->pNext;
9930 } while (pTracker && pTracker->idx != idxTls);
9931 if (pTracker)
9932 pPrev->pNext = pTracker->pNext;
9933 }
9934 if (pTracker)
9935 {
9936 pTracker->idx = TLS_OUT_OF_INDEXES;
9937 pTracker->pNext = NULL;
9938 kHlpFree(pTracker);
9939 }
9940 }
9941 }
9942 return fRc;
9943}
9944
9945
9946
9947/*
9948 *
9949 * Header file hashing.
9950 * Header file hashing.
9951 * Header file hashing.
9952 *
9953 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9954 * indicated that ~12% of the time was spent doing MD5 caluclation when
9955 * rebuiling openssl. The hashing it done right after reading the source
9956 * via ReadFile, same buffers and sizes.
9957 */
9958
9959#ifdef WITH_HASH_CACHE
9960
9961/**
9962 * Gets our crypto provider context/instance, creating it if needed.
9963 */
9964static HCRYPTPROV kwSandboxGetCryptoProvider(ALG_ID idAlg)
9965{
9966 DWORD dwProvider;
9967 HCRYPTPROV *phCryptProv;
9968 HCRYPTPROV hCryptProv;
9969 if ( idAlg == CALG_SHA_256
9970 || idAlg == CALG_SHA_512)
9971 {
9972 phCryptProv = &g_Sandbox.hCryptProvAes;
9973 dwProvider = PROV_RSA_AES;
9974 }
9975 else
9976 {
9977 phCryptProv = &g_Sandbox.hCryptProvRsa;
9978 dwProvider = PROV_RSA_FULL;
9979 }
9980 hCryptProv = *phCryptProv;
9981 if (hCryptProv)
9982 return hCryptProv;
9983
9984 /* Create it. */
9985 if (CryptAcquireContextW(&hCryptProv, NULL, NULL, dwProvider, CRYPT_VERIFYCONTEXT))
9986 {
9987 kHlpAssert(hCryptProv != 0);
9988 kHlpAssert(hCryptProv != KUPTR_MAX);
9989 *phCryptProv = hCryptProv;
9990 return hCryptProv;
9991 }
9992
9993 kwErrPrintf("kwSandboxGetCryptoProvider: CryptAcquireContext(,,,%#x, CRYPT_VERIFYCONTEXT) failed! %u\n",
9994 dwProvider, GetLastError());
9995 return (HCRYPTPROV)NULL;
9996}
9997
9998/** AdvApi32 - CryptCreateHash */
9999static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
10000 HCRYPTHASH *phHash)
10001{
10002 BOOL fRc;
10003
10004 /*
10005 * Only do this for cl.exe when it request normal MD5.
10006 */
10007 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
10008 {
10009 KU32 cbDigest;
10010 const char *pszName;
10011 switch (idAlg)
10012 {
10013 case CALG_MD5:
10014 cbDigest = 128/8;
10015 pszName = "MD5";
10016 g_cHashesMd5++;
10017 break;
10018 case CALG_SHA1:
10019 cbDigest = 160/8;
10020 pszName = "SHA1";
10021 g_cHashesSha1++;
10022 break;
10023 case CALG_SHA_256:
10024 cbDigest = 256/8;
10025 pszName = "SHA-256";
10026 g_cHashesSha256++;
10027 break;
10028 case CALG_SHA_512:
10029 cbDigest = 512/8;
10030 pszName = "SHA-512";
10031 g_cHashesSha512++;
10032 break;
10033 default:
10034 cbDigest = 0;
10035 pszName = NULL;
10036 break;
10037 }
10038
10039 if (cbDigest)
10040 {
10041 if (hKey == 0)
10042 {
10043 if (dwFlags == 0)
10044 {
10045 PKWCRYPTHASH pHash = (PKWCRYPTHASH)kHlpAllocZ(sizeof(*pHash));
10046 if (pHash)
10047 {
10048 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10049 pHash->uMagic = KWCRYPTHASH_MAGIC;
10050 pHash->cbHashed = 0;
10051 pHash->fGoneBad = K_FALSE;
10052 pHash->fFinal = K_FALSE;
10053 pHash->hFallback = KUPTR_MAX;
10054 pHash->idAlg = idAlg;
10055 pHash->pszAlgName = pszName;
10056 pHash->cbDigest = cbDigest;
10057
10058 /* link it. */
10059 pHash->pNext = g_Sandbox.pHashHead;
10060 g_Sandbox.pHashHead = pHash;
10061
10062 *phHash = (KUPTR)pHash;
10063 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%s, 0, 0, *phHash=%p) -> %d [cached]\n",
10064 hProv, pszName, *phHash, TRUE));
10065 return TRUE;
10066 }
10067
10068 kwErrPrintf("CryptCreateHash: out of memory!\n");
10069 }
10070 else
10071 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with %s\n", hKey, pszName);
10072 }
10073 else
10074 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with %s\n", hKey, pszName);
10075 }
10076 else
10077 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
10078 }
10079
10080 /*
10081 * Fallback.
10082 */
10083 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
10084 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
10085 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
10086 return fRc;
10087}
10088
10089
10090/** AdvApi32 - CryptHashData */
10091static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
10092{
10093 BOOL fRc;
10094 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10095 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10096 while (pHash && (KUPTR)pHash != hHash)
10097 pHash = pHash->pNext;
10098 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
10099 hHash, pHash, pbData, cbData, dwFlags));
10100 if (pHash)
10101 {
10102 /*
10103 * Validate the state.
10104 */
10105 if ( pHash->uMagic == KWCRYPTHASH_MAGIC
10106 && !pHash->fFinal
10107 && !pHash->fGoneBad)
10108 {
10109 if (pHash->hFallback == KUPTR_MAX)
10110 {
10111 /*
10112 * Does this match the previous ReadFile call to a cached file?
10113 * If it doesn't, try falling back.
10114 */
10115 if ( g_Sandbox.LastHashRead.cbRead == cbData
10116 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
10117 {
10118 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
10119 if ( pCachedFile
10120 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
10121 {
10122
10123 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
10124 {
10125 if ( pHash->pCachedFile == NULL
10126 && pHash->cbHashed == 0)
10127 pHash->pCachedFile = pCachedFile;
10128 if (pHash->pCachedFile == pCachedFile)
10129 {
10130 pHash->cbHashed += cbData;
10131 g_Sandbox.LastHashRead.pCachedFile = NULL;
10132 g_Sandbox.LastHashRead.pvRead = NULL;
10133 g_Sandbox.LastHashRead.cbRead = 0;
10134 g_Sandbox.LastHashRead.offRead = 0;
10135 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
10136 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
10137 return TRUE;
10138 }
10139
10140 /* Note! it's possible to fall back here too, if necessary. */
10141 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
10142 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
10143 }
10144 else
10145 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
10146 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
10147 }
10148 else if (!pCachedFile)
10149 KWCRYPT_LOG(("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n"));
10150 else
10151 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
10152 }
10153 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
10154 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
10155 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
10156 if (pHash->cbHashed == 0)
10157 {
10158 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
10159 HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(pHash->idAlg);
10160 if (hCryptProv)
10161 {
10162 HCRYPTHASH hCryptHash = KUPTR_MAX;
10163 if (CryptCreateHash(hCryptProv, pHash->idAlg, 0, 0, &hCryptHash))
10164 {
10165 kHlpAssert(hCryptHash != KUPTR_MAX);
10166 pHash->hFallback = hCryptHash;
10167 fRc = CryptHashData(hCryptHash, pbData, cbData, dwFlags);
10168 if (fRc)
10169 pHash->cbHashed = cbData;
10170 g_cHashesFallbacks++;
10171 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d (%u) [fallback!]\n",
10172 hHash, pbData, cbData, dwFlags, fRc, GetLastError()));
10173 }
10174 else
10175 {
10176 kwErrPrintf("kwSandbox_Advapi32_CryptHashData: Fallback CryptCreateHash(%u) failed: %u\n",
10177 pHash->idAlg, GetLastError());
10178 fRc = FALSE;
10179 }
10180 return fRc;
10181 }
10182 }
10183 pHash->fGoneBad = K_TRUE;
10184 SetLastError(ERROR_INVALID_PARAMETER);
10185 fRc = FALSE;
10186 }
10187 else
10188 {
10189 /* fallback. */
10190 fRc = CryptHashData(pHash->hFallback, pbData, cbData, dwFlags);
10191 if (fRc)
10192 pHash->cbHashed += cbData;
10193 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d [fallback]\n",
10194 hHash, pbData, cbData, dwFlags, fRc));
10195 }
10196 }
10197 /*
10198 * Bad handle state.
10199 */
10200 else
10201 {
10202 if (pHash->uMagic != KWCRYPTHASH_MAGIC)
10203 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
10204 else
10205 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
10206 SetLastError((DWORD)NTE_BAD_HASH);
10207 fRc = FALSE;
10208 }
10209 }
10210 else
10211 {
10212
10213 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
10214 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
10215 }
10216 return fRc;
10217}
10218
10219
10220/** Helper for simpe data hashing. */
10221static BOOL kwSandboxCalcHash(ALG_ID idAlg, void const *pvData, KSIZE cbData, KU8 *pbDigest, KSIZE cbDigest)
10222{
10223 BOOL fRet = FALSE;
10224 if (idAlg == CALG_MD5)
10225 {
10226 struct MD5Context Ctx;
10227 MD5Init(&Ctx);
10228 MD5Update(&Ctx, (unsigned char const *)pvData, (unsigned)cbData);
10229 MD5Final(pbDigest, &Ctx);
10230 fRet = TRUE;
10231 }
10232 else
10233 {
10234 HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(idAlg);
10235 if (hCryptProv)
10236 {
10237 HCRYPTHASH hCryptHash = KUPTR_MAX;
10238 if (CryptCreateHash(hCryptProv, idAlg, 0, 0, &hCryptHash))
10239 {
10240 if (CryptHashData(hCryptHash, (const BYTE *)pvData, (DWORD)cbData, 0))
10241 {
10242 DWORD cbActual = (DWORD)cbDigest;
10243 if (CryptGetHashParam(hCryptHash, HP_HASHVAL, pbDigest, &cbActual, 0))
10244 {
10245 fRet = TRUE;
10246 kHlpAssert(cbActual == cbDigest);
10247 }
10248 else
10249 kwErrPrintf("CryptGetHashParam([%#x],HP_HASHVAL,%p,%#x,0) failed: %u\n",
10250 idAlg, pbDigest, cbDigest, GetLastError());
10251 }
10252 else
10253 kwErrPrintf("CryptHashData([%#x],%p,%#x,0) failed: %u\n", idAlg, pvData, cbData, GetLastError());
10254 CryptDestroyHash(hCryptHash);
10255 }
10256 else
10257 kwErrPrintf("CryptCreateHash(%#x) failed: %u\n", idAlg, GetLastError());
10258 }
10259 }
10260 return fRet;
10261}
10262
10263
10264/** AdvApi32 - CryptGetHashParam */
10265static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
10266 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
10267{
10268 BOOL fRc;
10269 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10270 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10271 while (pHash && (KUPTR)pHash != hHash)
10272 pHash = pHash->pNext;
10273 if (pHash)
10274 {
10275 if (pHash->uMagic == KWCRYPTHASH_MAGIC)
10276 {
10277 if (dwFlags == 0)
10278 {
10279 DWORD cbRet;
10280 void *pvRet;
10281 union
10282 {
10283 DWORD dw;
10284 } uBuf;
10285
10286 switch (dwParam)
10287 {
10288 case HP_HASHVAL:
10289 {
10290 /* Check the hash progress. */
10291 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
10292 g_cHashes++;
10293 if (pCachedFile)
10294 {
10295 if ( pCachedFile->cbCached == pHash->cbHashed
10296 && !pHash->fGoneBad)
10297 {
10298 KBOOL *pfValid;
10299 switch (pHash->idAlg)
10300 {
10301 case CALG_MD5:
10302 pfValid = &pCachedFile->fValidMd5;
10303 pvRet = pCachedFile->abMd5Digest;
10304 break;
10305 case CALG_SHA1:
10306 pfValid = &pCachedFile->fValidSha1;
10307 pvRet = pCachedFile->abSha1Digest;
10308 break;
10309 case CALG_SHA_256:
10310 pfValid = &pCachedFile->fValidSha256;
10311 pvRet = pCachedFile->abSha256Digest;
10312 break;
10313 case CALG_SHA_512:
10314 pfValid = &pCachedFile->fValidSha512;
10315 pvRet = pCachedFile->abSha512Digest;
10316 break;
10317 default:
10318 kwErrPrintf("Invalid idAlg value: %#x\n", pHash->idAlg);
10319 SetLastError(ERROR_INVALID_SERVER_STATE);
10320 return FALSE;
10321 }
10322
10323 if (*pfValid)
10324 {
10325 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
10326 g_cHashesCached++;
10327 }
10328 else
10329 {
10330 fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pCachedFile->cbCached,
10331 pvRet, pHash->cbDigest);
10332 if (!fRc)
10333 return FALSE;
10334 *pfValid = K_TRUE;
10335 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
10336 }
10337 }
10338 else
10339 {
10340 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
10341 from what I can tell, so just deal with it. */
10342 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
10343 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
10344 pHash, pCachedFile, pCachedFile->szPath));
10345 g_cHashesPartial++;
10346 pHash->pCachedFile = NULL;
10347 pvRet = pHash->abDigest;
10348 fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pHash->cbHashed,
10349 pvRet, pHash->cbDigest);
10350 if (!fRc)
10351 {
10352 pHash->fGoneBad = K_TRUE;
10353 return FALSE;
10354 }
10355 }
10356 pHash->fFinal = K_TRUE;
10357 cbRet = pHash->cbDigest;
10358 break;
10359 }
10360
10361 pvRet = pHash->abDigest;
10362 cbRet = pHash->cbDigest;
10363 if (pHash->fFinal)
10364 break;
10365 if (pHash->hFallback != KUPTR_MAX)
10366 {
10367 DWORD cbActual = (DWORD)pHash->cbDigest;
10368 if (CryptGetHashParam(pHash->hFallback, HP_HASHVAL, pHash->abDigest, &cbActual, 0))
10369 {
10370 kHlpAssert(cbActual == pHash->cbDigest);
10371 pHash->fFinal = K_TRUE;
10372 break;
10373 }
10374 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: Fallback CryptGetHashParam failed: %u!!\n", GetLastError());
10375 }
10376 else
10377 {
10378 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
10379 SetLastError(ERROR_INVALID_SERVER_STATE);
10380 }
10381 return FALSE;
10382 }
10383
10384 case HP_HASHSIZE:
10385 uBuf.dw = pHash->cbDigest;
10386 pvRet = &uBuf;
10387 cbRet = sizeof(DWORD);
10388 break;
10389
10390 case HP_ALGID:
10391 uBuf.dw = pHash->idAlg;
10392 pvRet = &uBuf;
10393 cbRet = sizeof(DWORD);
10394 break;
10395
10396 default:
10397 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
10398 SetLastError((DWORD)NTE_BAD_TYPE);
10399 return FALSE;
10400 }
10401
10402 /*
10403 * Copy out cbRet from pvRet.
10404 */
10405 if (pbData)
10406 {
10407 if (*pcbData >= cbRet)
10408 {
10409 *pcbData = cbRet;
10410 kHlpMemCopy(pbData, pvRet, cbRet);
10411 if (cbRet == 4)
10412 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
10413 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
10414 else if (cbRet == 16)
10415 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",
10416 dwParam, pHash, pHash->pCachedFile, cbRet,
10417 pbData[0], pbData[1], pbData[2], pbData[3],
10418 pbData[4], pbData[5], pbData[6], pbData[7],
10419 pbData[8], pbData[9], pbData[10], pbData[11],
10420 pbData[12], pbData[13], pbData[14], pbData[15]));
10421 else if (cbRet == 20)
10422 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",
10423 dwParam, pHash, pHash->pCachedFile, cbRet,
10424 pbData[0], pbData[1], pbData[2], pbData[3],
10425 pbData[4], pbData[5], pbData[6], pbData[7],
10426 pbData[8], pbData[9], pbData[10], pbData[11],
10427 pbData[12], pbData[13], pbData[14], pbData[15],
10428 pbData[16], pbData[17], pbData[18], pbData[19] ));
10429 else if (cbRet >= 32)
10430 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",
10431 dwParam, pHash, pHash->pCachedFile, cbRet,
10432 pbData[0], pbData[1], pbData[2], pbData[3],
10433 pbData[4], pbData[5], pbData[6], pbData[7],
10434 pbData[8], pbData[9], pbData[10], pbData[11],
10435 pbData[12], pbData[13], pbData[14], pbData[15],
10436 pbData[16], pbData[17], pbData[18], pbData[19],
10437 pbData[20], pbData[21], pbData[22], pbData[23],
10438 pbData[24], pbData[25], pbData[26], pbData[27],
10439 pbData[28], pbData[29], pbData[30], pbData[31], cbRet > 32 ? "..." : ""));
10440 else
10441 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
10442 dwParam, pHash, pHash->pCachedFile, cbRet));
10443 return TRUE;
10444 }
10445
10446 kHlpMemCopy(pbData, pvRet, *pcbData);
10447 }
10448 SetLastError(ERROR_MORE_DATA);
10449 *pcbData = cbRet;
10450 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
10451 }
10452 else
10453 {
10454 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
10455 SetLastError((DWORD)NTE_BAD_FLAGS);
10456 }
10457 }
10458 else
10459 {
10460 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
10461 SetLastError((DWORD)NTE_BAD_HASH);
10462 }
10463 fRc = FALSE;
10464 }
10465 /*
10466 * Regular handle.
10467 */
10468 else
10469 {
10470 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
10471 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
10472 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
10473 }
10474
10475 return fRc;
10476}
10477
10478
10479/** AdvApi32 - CryptDestroyHash */
10480static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
10481{
10482 BOOL fRc;
10483 PKWCRYPTHASH pPrev = NULL;
10484 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10485 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10486 while (pHash && (KUPTR)pHash != hHash)
10487 {
10488 pPrev = pHash;
10489 pHash = pHash->pNext;
10490 }
10491 if (pHash)
10492 {
10493 if (pHash->uMagic == KWCRYPTHASH_MAGIC)
10494 {
10495 pHash->uMagic = 0;
10496 if (!pPrev)
10497 g_Sandbox.pHashHead = pHash->pNext;
10498 else
10499 pPrev->pNext = pHash->pNext;
10500 kHlpFree(pHash);
10501 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
10502 fRc = TRUE;
10503 }
10504 else
10505 {
10506 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10507 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10508 SetLastError(ERROR_INVALID_HANDLE);
10509 fRc = FALSE;
10510 }
10511 }
10512 /*
10513 * Regular handle.
10514 */
10515 else
10516 {
10517 fRc = CryptDestroyHash(hHash);
10518 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10519 }
10520 return fRc;
10521}
10522
10523#endif /* WITH_HASH_CACHE */
10524
10525
10526/*
10527 *
10528 * Reuse crypt context.
10529 * Reuse crypt context.
10530 * Reuse crypt context.
10531 *
10532 *
10533 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10534 *
10535 */
10536
10537#ifdef WITH_CRYPT_CTX_REUSE
10538
10539/** AdvApi32 - CryptAcquireContextW. */
10540static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10541 DWORD dwProvType, DWORD dwFlags)
10542{
10543 BOOL fRet;
10544
10545 /*
10546 * Lookup reusable context based on the input.
10547 */
10548 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10549 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10550 KU32 iCtx = g_Sandbox.cCryptCtxs;
10551 while (iCtx-- > 0)
10552 {
10553 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10554 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10555 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10556 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10557 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10558 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10559 {
10560 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10561 {
10562 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10563 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10564 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10565 return TRUE;
10566 }
10567 }
10568 }
10569
10570 /*
10571 * Create it and enter it into the reused array if possible.
10572 */
10573 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10574 if (fRet)
10575 {
10576 iCtx = g_Sandbox.cCryptCtxs;
10577 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10578 {
10579 /* Try duplicate the input strings. */
10580 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10581 (cwcContainer + 1) * sizeof(wchar_t));
10582 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10583 {
10584 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10585 (cwcProvider + 1) * sizeof(wchar_t));
10586 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10587 {
10588 /* Add a couple of references just to be on the safe side and all that. */
10589 HCRYPTPROV hProv = *phProv;
10590 if (CryptContextAddRef(hProv, NULL, 0))
10591 {
10592 if (CryptContextAddRef(hProv, NULL, 0))
10593 {
10594 /* Okay, finish the entry and return success */
10595 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10596 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10597 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10598 g_Sandbox.cCryptCtxs = iCtx + 1;
10599
10600 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10601 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10602 return TRUE;
10603 }
10604 CryptReleaseContext(hProv, 0);
10605 }
10606 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10607
10608 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10609 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10610 }
10611 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10612 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10613 }
10614 }
10615 else
10616 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10617 }
10618
10619 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10620 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10621 return fRet;
10622}
10623
10624
10625/** AdvApi32 - CryptReleaseContext */
10626static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10627{
10628 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10629 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10630 return fRet;
10631}
10632
10633
10634/** AdvApi32 - CryptContextAddRef */
10635static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10636{
10637 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10638 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10639 return fRet;
10640}
10641
10642#endif /* WITH_CRYPT_CTX_REUSE */
10643
10644/*
10645 *
10646 * Structured exception handling.
10647 * Structured exception handling.
10648 * Structured exception handling.
10649 *
10650 */
10651#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10652
10653# define EH_NONCONTINUABLE KU32_C(0x00000001)
10654# define EH_UNWINDING KU32_C(0x00000002)
10655# define EH_EXIT_UNWIND KU32_C(0x00000004)
10656# define EH_STACK_INVALID KU32_C(0x00000008)
10657# define EH_NESTED_CALL KU32_C(0x00000010)
10658
10659typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10660 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10661typedef struct _EXCEPTION_REGISTRATION_RECORD
10662{
10663 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10664 PFNXCPTHANDLER pfnXcptHandler;
10665};
10666
10667
10668/**
10669 * Calls @a pfnHandler.
10670 */
10671static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10672 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10673 PFNXCPTHANDLER pfnHandler)
10674{
10675# if 1
10676 /* This is a more robust version that isn't subject to calling
10677 convension cleanup disputes and such. */
10678 KU32 uSavedEdi;
10679 KU32 uSavedEsi;
10680 KU32 uSavedEbx;
10681 KU32 rcHandler;
10682
10683 __asm
10684 {
10685 mov [uSavedEdi], edi
10686 mov [uSavedEsi], esi
10687 mov [uSavedEbx], ebx
10688 mov esi, esp
10689 mov edi, esp
10690 mov edi, [pXcptRec]
10691 mov edx, [pRegRec]
10692 mov eax, [pXcptCtx]
10693 mov ebx, [ppRegRec]
10694 mov ecx, [pfnHandler]
10695 sub esp, 16
10696 and esp, 0fffffff0h
10697 mov [esp ], edi
10698 mov [esp + 4], edx
10699 mov [esp + 8], eax
10700 mov [esp + 12], ebx
10701 mov edi, esi
10702 call ecx
10703 mov esp, esi
10704 cmp esp, edi
10705 je stack_ok
10706 int 3
10707 stack_ok:
10708 mov edi, [uSavedEdi]
10709 mov esi, [uSavedEsi]
10710 mov ebx, [uSavedEbx]
10711 mov [rcHandler], eax
10712 }
10713 return rcHandler;
10714# else
10715 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10716# endif
10717}
10718
10719
10720/**
10721 * Vectored exception handler that emulates x86 chained exception handler.
10722 *
10723 * This is necessary because the RtlIsValidHandler check fails for self loaded
10724 * code and prevents cl.exe from working. (On AMD64 we can register function
10725 * tables, but on X86 cooking your own handling seems to be the only viabke
10726 * alternative.)
10727 *
10728 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10729 * @param pXcptPtrs The exception details.
10730 */
10731static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10732{
10733 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10734 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10735 if (g_Sandbox.fRunning)
10736 {
10737 HANDLE const hCurProc = GetCurrentProcess();
10738 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10739 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10740 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10741 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10742 {
10743 /* Read the exception record in a safe manner. */
10744 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10745 DWORD cbActuallyRead = 0;
10746 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10747 && cbActuallyRead == sizeof(RegRec))
10748 {
10749 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10750 KU32 rcHandler;
10751 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10752 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10753 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10754 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10755 if (rcHandler == ExceptionContinueExecution)
10756 {
10757 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10758 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10759 return EXCEPTION_CONTINUE_EXECUTION;
10760 }
10761
10762 if (rcHandler == ExceptionContinueSearch)
10763 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10764 else if (rcHandler == ExceptionNestedException)
10765 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10766 else
10767 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10768 }
10769 else
10770 {
10771 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10772 break;
10773 }
10774
10775 /*
10776 * Next.
10777 */
10778 pRegRec = RegRec.pPrevRegRec;
10779 }
10780 }
10781 return EXCEPTION_CONTINUE_SEARCH;
10782}
10783
10784
10785/** NtDll,Kernel32 - RtlUnwind */
10786static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10787 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10788{
10789 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10790 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10791 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10792 if (g_Sandbox.fRunning)
10793 {
10794 HANDLE const hCurProc = GetCurrentProcess();
10795 PCONTEXT pXcptCtx = NULL;
10796 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10797
10798 /*
10799 * Update / create an exception record.
10800 */
10801 if (pXcptRec)
10802 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10803 else
10804 {
10805 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10806 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10807 pXcptRec->ExceptionCode = (DWORD)STATUS_UNWIND;
10808 pXcptRec->ExceptionFlags = EH_UNWINDING;
10809 }
10810 if (!pStopXcptRec)
10811 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10812
10813 /*
10814 * Walk the chain till we find pStopXctpRec.
10815 */
10816 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10817 && pRegRec != NULL
10818 && pRegRec != pStopXcptRec)
10819 {
10820 /* Read the exception record in a safe manner. */
10821 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10822 DWORD cbActuallyRead = 0;
10823 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10824 && cbActuallyRead == sizeof(RegRec))
10825 {
10826 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10827 KU32 rcHandler;
10828 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10829 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10830 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10831 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10832
10833 if (rcHandler == ExceptionContinueSearch)
10834 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10835 else if (rcHandler == ExceptionCollidedUnwind)
10836 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10837 else
10838 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10839 }
10840 else
10841 {
10842 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10843 break;
10844 }
10845
10846 /*
10847 * Pop next.
10848 */
10849 pTib->ExceptionList = RegRec.pPrevRegRec;
10850 pRegRec = RegRec.pPrevRegRec;
10851 }
10852 return;
10853 }
10854
10855 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10856}
10857
10858#endif /* WINDOWS + X86 */
10859
10860
10861/*
10862 *
10863 * Misc function only intercepted while debugging.
10864 * Misc function only intercepted while debugging.
10865 * Misc function only intercepted while debugging.
10866 *
10867 */
10868
10869#ifndef NDEBUG
10870
10871/** CRT - memcpy */
10872static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10873{
10874 KU8 const *pbSrc = (KU8 const *)pvSrc;
10875 KU8 *pbDst = (KU8 *)pvDst;
10876 KSIZE cbLeft = cb;
10877 while (cbLeft-- > 0)
10878 *pbDst++ = *pbSrc++;
10879 return pvDst;
10880}
10881
10882
10883/** CRT - memset */
10884static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10885{
10886 KU8 *pbDst = (KU8 *)pvDst;
10887 KSIZE cbLeft = cb;
10888 while (cbLeft-- > 0)
10889 *pbDst++ = (KU8)bFiller;
10890 return pvDst;
10891}
10892
10893#endif /* NDEBUG */
10894
10895
10896/** @todo consider hooking NtQueryDirectoryFile as c1xx.dll/c1.dll in 2019
10897 * uses it directly to read the content of include directories, however
10898 * they do it one file at the time. We already have the info in the
10899 * cache (where we do bulk reads). There are a lot of calls for the
10900 * SDK include directories, as one can imagine. */
10901
10902/**
10903 * Functions that needs replacing for sandboxed execution.
10904 */
10905KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10906{
10907 /*
10908 * Kernel32.dll and friends.
10909 */
10910 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10911 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10912
10913 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10914 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10915 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10916 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10917 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10918 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10919 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10920 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10921 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10922 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10923 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10924
10925 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10926 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10927 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10928 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10929
10930 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10931
10932 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10933 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10934 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10935 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10936 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10937 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10938 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10939 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10940 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10941 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10942 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10943
10944 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10945 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10946 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10947 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10948#ifdef WITH_TEMP_MEMORY_FILES
10949 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10950 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10951 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10952 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10953 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10954 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10955 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10956 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10957 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10958 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10959#endif
10960 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10961 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10962 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10963 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10964 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10965 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10966 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10967 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10968 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10969#ifdef WITH_TEMP_MEMORY_FILES
10970 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10971#endif
10972
10973#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10974 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10975 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10976#endif
10977
10978 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10979 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10980
10981 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10982 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10983
10984 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10985 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10986 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10987 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10988
10989 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10990
10991#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10992 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10993#endif
10994
10995#ifdef WITH_HASH_CACHE
10996 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10997 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10998 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10999 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
11000#endif
11001
11002#ifdef WITH_CRYPT_CTX_REUSE
11003 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
11004 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
11005 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
11006#endif
11007
11008 /*
11009 * MS Visual C++ CRTs.
11010 */
11011 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
11012 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
11013 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
11014 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
11015 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
11016 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
11017
11018 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
11019 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
11020 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
11021
11022 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
11023 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
11024 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
11025
11026 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
11027 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
11028 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
11029 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
11030 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
11031 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
11032 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
11033 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
11034 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
11035 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
11036 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
11037 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
11038 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
11039 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
11040 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
11041 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
11042 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
11043 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
11044 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
11045 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
11046
11047 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
11048 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
11049 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
11050 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
11051 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
11052 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
11053 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
11054 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
11055 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
11056 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
11057 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
11058 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
11059 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
11060 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
11061
11062#ifndef NDEBUG
11063 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
11064 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
11065#endif
11066};
11067/** Number of entries in g_aReplacements. */
11068KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
11069
11070
11071/**
11072 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
11073 * execution.
11074 */
11075KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
11076{
11077 /*
11078 * Kernel32.dll and friends.
11079 */
11080 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
11081 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
11082
11083 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
11084 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
11085
11086#if 0
11087 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
11088#endif
11089
11090 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
11091 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
11092 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
11093 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
11094#ifdef WITH_TEMP_MEMORY_FILES
11095 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
11096 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
11097 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
11098 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
11099 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
11100 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
11101 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
11102 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
11103 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
11104 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
11105#endif
11106 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
11107 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
11108 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
11109 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
11110 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
11111 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
11112 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
11113 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
11114 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
11115#ifdef WITH_TEMP_MEMORY_FILES
11116 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
11117#endif
11118 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
11119 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
11120 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExW },
11121#ifndef NDEBUG
11122 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_Native_GetProcAddress },
11123#endif
11124
11125#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11126 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
11127 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
11128#endif
11129
11130#ifdef WITH_HASH_CACHE
11131 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
11132 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
11133 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
11134 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
11135#endif
11136
11137 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
11138
11139#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
11140 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
11141#endif
11142
11143 /*
11144 * MS Visual C++ CRTs.
11145 */
11146 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
11147 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
11148 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
11149 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
11150 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
11151 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
11152 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
11153
11154#if 0 /* used by mspdbXXX.dll */
11155 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
11156 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
11157#endif
11158};
11159/** Number of entries in g_aSandboxNativeReplacements. */
11160KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
11161
11162
11163/**
11164 * Functions that needs replacing when queried by GetProcAddress.
11165 */
11166KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
11167{
11168 /*
11169 * Kernel32.dll and friends.
11170 */
11171 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
11172 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
11173 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
11174 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
11175};
11176/** Number of entries in g_aSandboxGetProcReplacements. */
11177KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
11178
11179
11180/**
11181 * Control handler.
11182 *
11183 * @returns TRUE if handled, FALSE if not.
11184 * @param dwCtrlType The signal.
11185 */
11186static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
11187{
11188 DWORD cbIgn;
11189 int volatile rc; /* volatile for debugging */
11190 int volatile rcPrev;
11191 const char *pszMsg;
11192 switch (dwCtrlType)
11193 {
11194 case CTRL_C_EVENT:
11195 rc = 9;
11196 pszMsg = "kWorker: Ctrl-C\r\n";
11197 break;
11198
11199 case CTRL_BREAK_EVENT:
11200 rc = 10;
11201 pszMsg = "kWorker: Ctrl-Break\r\n";
11202 break;
11203
11204 case CTRL_CLOSE_EVENT:
11205 rc = 11;
11206 pszMsg = "kWorker: console closed\r\n";
11207 break;
11208
11209 case CTRL_LOGOFF_EVENT:
11210 rc = 11;
11211 pszMsg = "kWorker: logoff event\r\n";
11212 break;
11213
11214 case CTRL_SHUTDOWN_EVENT:
11215 rc = 11;
11216 pszMsg = "kWorker: shutdown event\r\n";
11217 break;
11218
11219 default:
11220 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
11221 return TRUE;
11222 }
11223
11224 /*
11225 * Terminate the process after 5 seconds.
11226 * If we get here a second time we just terminate the process ourselves.
11227 *
11228 * Note! We do no try call exit() here as it turned out to deadlock a lot
11229 * flusing file descriptors (stderr back when we first wrote to it).
11230 */
11231 rcPrev = g_rcCtrlC;
11232 g_rcCtrlC = rc;
11233 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
11234 if (rcPrev == 0)
11235 {
11236 int i;
11237 for (i = 0; i < 10; i++)
11238 {
11239 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
11240 Sleep(500);
11241 }
11242 }
11243 TerminateProcess(GetCurrentProcess(), rc);
11244 return TRUE;
11245}
11246
11247
11248#if 0
11249/**
11250 * Resets the KWMODULE::fVisited flag for _all_ known modules.
11251 */
11252static void kwSandboxResetModuleVisited(void)
11253{
11254 PKWMODULE pMod = g_pModuleHead;
11255 while (pMod)
11256 {
11257 pMod->fVisited = K_FALSE;
11258 pMod = pMod->pNextList;
11259 }
11260}
11261
11262
11263/**
11264 * Used by kwSandboxExec to reset the state of the module tree.
11265 *
11266 * This is done recursively.
11267 *
11268 * @param pMod The root of the tree to consider.
11269 */
11270static void kwSandboxResetModuleState(PKWMODULE pMod)
11271{
11272 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
11273 if (!pMod->fNative)
11274 {
11275 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
11276 if (!pMod->fVisited) /* Avoid loops. */
11277 {
11278 KSIZE iImp;
11279 pMod->fVisited = K_TRUE;
11280 iImp = pMod->u.Manual.cImpMods;
11281 while (iImp-- > 0)
11282 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
11283 }
11284 }
11285 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11286 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
11287 {
11288 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11289 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11290 {
11291 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11292 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11293 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11294 pMod->pszPath, pszValue ? pszValue : "<null>"));
11295 }
11296 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11297 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11298 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11299 pMod->pszPath, pszValue ? pszValue : "<null>"));
11300 else
11301 {
11302 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11303 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11304 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11305 if (pszValue != NULL)
11306 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11307 else
11308 pMod->pszMsPdbSrvEndpoint = NULL;
11309 pMod->fNeedReInit = K_TRUE;
11310 }
11311 }
11312}
11313#else
11314/**
11315 * Used by kwSandboxExec to reset the state of the module tree.
11316 */
11317static void kwSandboxResetModuleState(void)
11318{
11319 PKWMODULE pMod = g_pModuleHead;
11320 while (pMod)
11321 {
11322 if (!pMod->fNative)
11323 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
11324 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11325 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
11326 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
11327 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
11328 {
11329 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11330 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11331 {
11332 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11333 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11334 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11335 pMod->pszPath, pszValue ? pszValue : "<null>"));
11336 }
11337 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11338 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11339 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11340 pMod->pszPath, pszValue ? pszValue : "<null>"));
11341 else
11342 {
11343 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11344 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11345 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11346 if (pszValue != NULL)
11347 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11348 else
11349 pMod->pszMsPdbSrvEndpoint = NULL;
11350 pMod->fNeedReInit = K_TRUE;
11351 }
11352 }
11353
11354 pMod = pMod->pNextList;
11355 }
11356}
11357#endif
11358
11359static PPEB kwSandboxGetProcessEnvironmentBlock(void)
11360{
11361#if K_ARCH == K_ARCH_X86_32
11362 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
11363#elif K_ARCH == K_ARCH_AMD64
11364 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
11365#else
11366# error "Port me!"
11367#endif
11368}
11369
11370
11371/**
11372 * Enters the given handle into the handle table.
11373 *
11374 * @returns K_TRUE on success, K_FALSE on failure.
11375 * @param pSandbox The sandbox.
11376 * @param pHandle The handle.
11377 * @param hHandle The handle value to enter it under (for the
11378 * duplicate handle API).
11379 */
11380static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
11381{
11382 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
11383 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
11384
11385 EnterCriticalSection(&g_Sandbox.HandlesLock);
11386
11387 /*
11388 * Grow handle table.
11389 */
11390 if (idxHandle >= pSandbox->cHandles)
11391 {
11392 void *pvNew;
11393 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
11394 while (cHandles <= idxHandle)
11395 cHandles *= 2;
11396 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
11397 if (!pvNew)
11398 {
11399 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11400 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
11401 return K_FALSE;
11402 }
11403 pSandbox->papHandles = (PKWHANDLE *)pvNew;
11404 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
11405 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
11406 pSandbox->cHandles = cHandles;
11407 }
11408
11409 /*
11410 * Check that the entry is unused then insert it.
11411 */
11412 kHlpAssertStmtReturn(pSandbox->papHandles[idxHandle] == NULL, LeaveCriticalSection(&g_Sandbox.HandlesLock), K_FALSE);
11413 pSandbox->papHandles[idxHandle] = pHandle;
11414 pSandbox->cActiveHandles++;
11415 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11416 return K_TRUE;
11417}
11418
11419
11420/**
11421 * Safely looks up a handle, does not get it and it must not be 'put'.
11422 *
11423 * @returns Pointer to the handle structure if found, otherwise NULL.
11424 * @param hFile The handle to resolve.
11425 */
11426static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile)
11427{
11428 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11429 EnterCriticalSection(&g_Sandbox.HandlesLock);
11430 if (idxHandle < g_Sandbox.cHandles)
11431 {
11432 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11433 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11434 return pHandle;
11435 }
11436 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11437 return NULL;
11438}
11439
11440
11441/**
11442 * Safely gets a handle, must be "put" when done with it.
11443 *
11444 * @returns Pointer to the handle structure if found, otherwise NULL.
11445 * @param hFile The handle to resolve.
11446 */
11447static PKWHANDLE kwSandboxHandleGet(HANDLE hFile)
11448{
11449 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11450 EnterCriticalSection(&g_Sandbox.HandlesLock);
11451 if (idxHandle < g_Sandbox.cHandles)
11452 {
11453 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11454 if (pHandle)
11455 {
11456 kHlpAssertMsg(pHandle->tidOwner == KU32_MAX, ("hFile=%p tidOwner=%#x\n", hFile, pHandle->tidOwner));
11457 pHandle->tidOwner = GetCurrentThreadId();
11458 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11459 return pHandle;
11460 }
11461 }
11462 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11463 return NULL;
11464}
11465
11466
11467/**
11468 * Puts a handle returned by kwSandboxHandleGet.
11469 *
11470 * @param pHandle The handle to "put".
11471 */
11472K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle)
11473{
11474 kHlpAssertMsg(pHandle->tidOwner == GetCurrentThreadId(),
11475 ("hFile tidOwner=%#x tidMe=%#x\n", pHandle->hHandle, pHandle->tidOwner, GetCurrentThreadId()));
11476 pHandle->tidOwner = KU32_MAX;
11477}
11478
11479
11480/**
11481 * Creates a correctly quoted ANSI command line string from the given argv.
11482 *
11483 * @returns Pointer to the command line.
11484 * @param cArgs Number of arguments.
11485 * @param papszArgs The argument vector.
11486 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11487 * @param pcbCmdLine Where to return the command line length,
11488 * including one terminator.
11489 */
11490static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
11491{
11492 KU32 i;
11493 KSIZE cbCmdLine;
11494 char *pszCmdLine;
11495
11496 /* Make a copy of the argument vector that we'll be quoting. */
11497 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
11498 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
11499
11500 /* Quote the arguments that need it. */
11501 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
11502
11503 /* figure out cmd line length. */
11504 cbCmdLine = 0;
11505 for (i = 0; i < cArgs; i++)
11506 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
11507 *pcbCmdLine = cbCmdLine;
11508
11509 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
11510 if (pszCmdLine)
11511 {
11512 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
11513 if (papszQuotedArgs[0] != papszArgs[0])
11514 free(papszQuotedArgs[0]);
11515
11516 for (i = 1; i < cArgs; i++)
11517 {
11518 *psz++ = ' ';
11519 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
11520 if (papszQuotedArgs[i] != papszArgs[i])
11521 free(papszQuotedArgs[i]);
11522 }
11523 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
11524
11525 *psz++ = '\0';
11526 *psz++ = '\0';
11527 }
11528
11529 return pszCmdLine;
11530}
11531
11532
11533
11534static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
11535 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11536 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11537{
11538 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11539 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11540 wchar_t *pwcPool;
11541 KSIZE cbStrings;
11542 KSIZE cwc;
11543 KSIZE cbCmdLine;
11544 KU32 i;
11545
11546 /* Simple stuff. */
11547 pSandbox->rcExitCode = 256;
11548 pSandbox->pTool = pTool;
11549 pSandbox->idMainThread = GetCurrentThreadId();
11550 pSandbox->pgmptr = (char *)pTool->pszPath;
11551 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
11552#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11553 if (pSandbox->StdOut.fIsConsole)
11554 pSandbox->StdOut.u.Con.cwcBuf = 0;
11555 else
11556 pSandbox->StdOut.u.Fully.cchBuf = 0;
11557 if (pSandbox->StdErr.fIsConsole)
11558 pSandbox->StdErr.u.Con.cwcBuf = 0;
11559 else
11560 pSandbox->StdErr.u.Fully.cchBuf = 0;
11561 pSandbox->Combined.cwcBuf = 0;
11562 pSandbox->Combined.cFlushes = 0;
11563#endif
11564 pSandbox->fNoPchCaching = fNoPchCaching;
11565 pSandbox->cArgs = cArgs;
11566 pSandbox->papszArgs = (char **)papszArgs;
11567 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
11568 if (!pSandbox->pszCmdLine)
11569 return KERR_NO_MEMORY;
11570
11571 /*
11572 * Convert command line and argv to UTF-16.
11573 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
11574 */
11575 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
11576 if (!pSandbox->papwszArgs)
11577 return KERR_NO_MEMORY;
11578 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
11579 for (i = 0; i < cArgs; i++)
11580 {
11581 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
11582 pSandbox->papwszArgs[i] = pwcPool;
11583 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11584 pwcPool++;
11585 }
11586 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11587 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11588
11589 /*
11590 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11591 */
11592 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11593 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11594 if (!pSandbox->pwszCmdLine)
11595 return KERR_NO_MEMORY;
11596 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11597
11598 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11599 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11600 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11601
11602 /*
11603 * Setup the environment.
11604 */
11605 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11606 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11607 {
11608 KU32 iDst = 0;
11609 for (i = 0; i < cEnvVars; i++)
11610 {
11611 const char *pszVar = papszEnvVars[i];
11612 KSIZE cchVar = kHlpStrLen(pszVar);
11613 const char *pszEqual;
11614 if ( cchVar > 0
11615 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11616 {
11617 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11618 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11619 if (pszCopy && pwszCopy)
11620 {
11621 pSandbox->papszEnvVars[iDst] = pszCopy;
11622 pSandbox->environ[iDst] = pszCopy;
11623 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11624 pSandbox->wenviron[iDst] = pwszCopy;
11625
11626 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11627 if ( (pszEqual - pszVar) == 4
11628 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11629 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11630 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11631 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11632 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11633 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11634
11635 iDst++;
11636 }
11637 else
11638 {
11639 kHlpFree(pszCopy);
11640 kHlpFree(pwszCopy);
11641 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11642 }
11643 }
11644 else
11645 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11646 }
11647 pSandbox->papszEnvVars[iDst] = NULL;
11648 pSandbox->environ[iDst] = NULL;
11649 pSandbox->papwszEnvVars[iDst] = NULL;
11650 pSandbox->wenviron[iDst] = NULL;
11651 }
11652 else
11653 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11654
11655 /*
11656 * Invalidate the volatile parts of cache (kBuild output directory,
11657 * temporary directory, whatever).
11658 */
11659 kFsCacheInvalidateCustomBoth(g_pFsCache);
11660
11661#ifdef WITH_HISTORY
11662 /*
11663 * Record command line in debug history.
11664 */
11665 kHlpFree(g_apszHistory[g_iHistoryNext]);
11666 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11667 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11668#endif
11669
11670 return 0;
11671}
11672
11673
11674/**
11675 * Does sandbox cleanup between jobs.
11676 *
11677 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11678 * influence the result, so that kmk can get on with things ASAP.
11679 *
11680 * @param pSandbox The sandbox.
11681 */
11682static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11683{
11684 PROCESS_MEMORY_COUNTERS MemInfo;
11685 PKWVIRTALLOC pTracker;
11686 PKWHEAP pHeap;
11687 PKWLOCALSTORAGE pLocalStorage;
11688#ifdef WITH_HASH_CACHE
11689 PKWCRYPTHASH pHash;
11690#endif
11691#ifdef WITH_TEMP_MEMORY_FILES
11692 PKWFSTEMPFILE pTempFile;
11693#endif
11694 PKWEXITCALLACK pExitCallback;
11695
11696 /*
11697 * First stuff that may cause code to run.
11698 */
11699
11700 /* Do exit callback first. */
11701 pExitCallback = g_Sandbox.pExitCallbackHead;
11702 g_Sandbox.pExitCallbackHead = NULL;
11703 while (pExitCallback)
11704 {
11705 PKWEXITCALLACK pNext = pExitCallback->pNext;
11706 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11707 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11708 __try
11709 {
11710 pExitCallback->pfnCallback();
11711 }
11712 __except (EXCEPTION_EXECUTE_HANDLER)
11713 {
11714 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11715 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11716 kHlpAssertFailed();
11717 }
11718 kHlpFree(pExitCallback);
11719 pExitCallback = pNext;
11720 }
11721
11722 /* Free left behind FlsAlloc leaks. */
11723 pLocalStorage = g_Sandbox.pFlsAllocHead;
11724 g_Sandbox.pFlsAllocHead = NULL;
11725 while (pLocalStorage)
11726 {
11727 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11728 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11729 FlsFree(pLocalStorage->idx);
11730 kHlpFree(pLocalStorage);
11731 pLocalStorage = pNext;
11732 }
11733
11734 /* Free left behind TlsAlloc leaks. */
11735 pLocalStorage = g_Sandbox.pTlsAllocHead;
11736 g_Sandbox.pTlsAllocHead = NULL;
11737 while (pLocalStorage)
11738 {
11739 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11740 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11741 TlsFree(pLocalStorage->idx);
11742 kHlpFree(pLocalStorage);
11743 pLocalStorage = pNext;
11744 }
11745
11746
11747 /*
11748 * Then free resources associated with the sandbox run.
11749 */
11750
11751 /* Open handles, except fixed handles (stdout and stderr). */
11752 EnterCriticalSection(&pSandbox->HandlesLock);
11753 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11754 {
11755 KU32 idxHandle = pSandbox->cHandles;
11756 while (idxHandle-- > 0)
11757 if (pSandbox->papHandles[idxHandle] == NULL)
11758 { /* likely */ }
11759 else
11760 {
11761 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11762 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11763 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11764 {
11765 pSandbox->papHandles[idxHandle] = NULL;
11766 pSandbox->cLeakedHandles++;
11767
11768 switch (pHandle->enmType)
11769 {
11770 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11771 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11772 idxHandle, pHandle->hHandle, pHandle->cRefs));
11773 break;
11774 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11775 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11776 idxHandle, pHandle->hHandle, pHandle->cRefs));
11777 break;
11778 case KWHANDLETYPE_OUTPUT_BUF:
11779 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11780 idxHandle, pHandle->hHandle, pHandle->cRefs));
11781 break;
11782#ifdef WITH_TEMP_MEMORY_FILES
11783 case KWHANDLETYPE_TEMP_FILE:
11784 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11785 idxHandle, pHandle->hHandle, pHandle->cRefs));
11786 pHandle->u.pTempFile->cActiveHandles--;
11787 break;
11788 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11789 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11790 idxHandle, pHandle->hHandle, pHandle->cRefs));
11791 pHandle->u.pTempFile->cActiveHandles--;
11792 break;
11793#endif
11794 default:
11795 kHlpAssertFailed();
11796 }
11797 if (--pHandle->cRefs == 0)
11798 kHlpFree(pHandle);
11799 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11800 break;
11801 }
11802 }
11803 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11804 }
11805 LeaveCriticalSection(&pSandbox->HandlesLock);
11806
11807 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11808 g_Sandbox.cMemMappings = 0;
11809
11810#ifdef WITH_TEMP_MEMORY_FILES
11811 /* The temporary files aren't externally visible, they're all in memory. */
11812 pTempFile = pSandbox->pTempFileHead;
11813 pSandbox->pTempFileHead = NULL;
11814 while (pTempFile)
11815 {
11816 PKWFSTEMPFILE pNext = pTempFile->pNext;
11817 KU32 iSeg = pTempFile->cSegs;
11818 while (iSeg-- > 0)
11819 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11820 kHlpFree(pTempFile->paSegs);
11821 pTempFile->pNext = NULL;
11822 kHlpFree(pTempFile);
11823
11824 pTempFile = pNext;
11825 }
11826#endif
11827
11828 /* Free left behind HeapCreate leaks. */
11829 pHeap = g_Sandbox.pHeapHead;
11830 g_Sandbox.pHeapHead = NULL;
11831 while (pHeap != NULL)
11832 {
11833 PKWHEAP pNext = pHeap->pNext;
11834 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11835 HeapDestroy(pHeap->hHeap);
11836 pHeap = pNext;
11837 }
11838
11839 /* Free left behind VirtualAlloc leaks. */
11840 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11841 pTracker = g_Sandbox.pVirtualAllocHead;
11842 g_Sandbox.pVirtualAllocHead = NULL;
11843 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11844 while (pTracker)
11845 {
11846 PKWVIRTALLOC pNext = pTracker->pNext;
11847 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11848
11849#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11850 if (pTracker->idxPreAllocated != KU32_MAX)
11851 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11852 else
11853#endif
11854 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11855 kHlpFree(pTracker);
11856 pTracker = pNext;
11857 }
11858
11859 /* Free the environment. */
11860 if (pSandbox->papszEnvVars)
11861 {
11862 KU32 i;
11863 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11864 kHlpFree(pSandbox->papszEnvVars[i]);
11865 pSandbox->environ[0] = NULL;
11866 pSandbox->papszEnvVars[0] = NULL;
11867
11868 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11869 kHlpFree(pSandbox->papwszEnvVars[i]);
11870 pSandbox->wenviron[0] = NULL;
11871 pSandbox->papwszEnvVars[0] = NULL;
11872 }
11873
11874#ifdef WITH_HASH_CACHE
11875 /*
11876 * Hash handles.
11877 */
11878 pHash = pSandbox->pHashHead;
11879 pSandbox->pHashHead = NULL;
11880 while (pHash)
11881 {
11882 PKWCRYPTHASH pNext = pHash->pNext;
11883 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11884 if (pHash->hFallback != KUPTR_MAX)
11885 CryptDestroyHash(pHash->hFallback);
11886 kHlpFree(pHash);
11887 pHash = pNext;
11888 }
11889#endif
11890
11891 /*
11892 * Check the memory usage. If it's getting high, trigger a respawn
11893 * after the next job.
11894 */
11895 MemInfo.WorkingSetSize = 0;
11896 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11897 {
11898 /* The first time thru, we figure out approximately when to restart
11899 based on installed RAM and CPU threads. */
11900 static KU64 s_cbMaxWorkingSet = 0;
11901 if (s_cbMaxWorkingSet != 0)
11902 { /* likely */ }
11903 else
11904 {
11905 SYSTEM_INFO SysInfo;
11906 MEMORYSTATUSEX GlobalMemInfo;
11907 const char *pszValue;
11908
11909 /* Calculate a reasonable estimate. */
11910 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11911 GetNativeSystemInfo(&SysInfo);
11912
11913 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11914 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11915 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11916#if K_ARCH_BITS >= 64
11917 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11918#else
11919 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11920#endif
11921 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11922 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11923
11924 /* User limit. */
11925 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11926 if (pszValue != NULL)
11927 {
11928 char *pszNext;
11929 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11930 if (*pszNext == '\0' || *pszNext == 'M')
11931 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11932 else if (*pszNext == 'K')
11933 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11934 else if (*pszNext == 'G')
11935 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11936 else
11937 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11938 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11939 }
11940
11941 /* Clamp it a little. */
11942 if (s_cbMaxWorkingSet < 168*1024*1024)
11943 s_cbMaxWorkingSet = 168*1024*1024;
11944#if K_ARCH_BITS < 64
11945 else
11946 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11947 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11948 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11949 : 1536*1024*1024 /* got 4GB VA */);
11950#endif
11951 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11952 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11953 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11954 }
11955
11956 /* Finally the check. */
11957 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11958 {
11959 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11960 g_fRestart = K_TRUE;
11961 }
11962 }
11963
11964 /*
11965 * The CRT has a max of 8192 handles, so we better restart after a while if
11966 * someone is leaking handles or we risk running out of descriptors.
11967 *
11968 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11969 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11970 */
11971 if (pSandbox->cLeakedHandles > 6000)
11972 {
11973 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11974 g_fRestart = K_TRUE;
11975 }
11976}
11977
11978
11979/**
11980 * Does essential cleanups and restoring, anything externally visible.
11981 *
11982 * All cleanups that aren't externally visible are postponed till after we've
11983 * informed kmk of the result, so it can be done in the dead time between jobs.
11984 *
11985 * @param pSandbox The sandbox.
11986 */
11987static void kwSandboxCleanup(PKWSANDBOX pSandbox)
11988{
11989 /*
11990 * Restore the parent command line string.
11991 */
11992 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11993 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11994 pProcParams->CommandLine = pSandbox->SavedCommandLine;
11995#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11996 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
11997 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
11998#endif
11999}
12000
12001
12002static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12003 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
12004{
12005 int rcExit = 42;
12006 int rc;
12007
12008 /*
12009 * Initialize the sandbox environment.
12010 */
12011 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
12012 if (rc == 0)
12013 {
12014 if (g_cVerbose > 2)
12015 fprintf(stderr, "kWorker: info: Executing (sandboxed): %s\n", g_Sandbox.pszCmdLine);
12016
12017 /*
12018 * Do module initialization.
12019 */
12020#if 0
12021 //kwSandboxResetModuleVisited();
12022 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
12023#else
12024 kwSandboxResetModuleState();
12025#endif
12026 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
12027 if (rc == 0)
12028 {
12029 /*
12030 * Call the main function.
12031 */
12032#if K_ARCH == K_ARCH_AMD64
12033 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
12034#elif K_ARCH == K_ARCH_X86_32
12035 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
12036#else
12037# error "Port me!"
12038#endif
12039
12040 /* Save the NT TIB first (should do that here, not in some other function). */
12041 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
12042 pSandbox->TibMainThread = *pTib;
12043
12044 /* Make the call in a guarded fashion. */
12045#if K_ARCH == K_ARCH_AMD64
12046 /* AMD64 */
12047 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
12048 __try
12049 {
12050 pSandbox->pOutXcptListHead = pTib->ExceptionList;
12051 if (setjmp(pSandbox->JmpBuf) == 0)
12052 {
12053 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
12054 pSandbox->fRunning = K_TRUE;
12055 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
12056 pSandbox->fRunning = K_FALSE;
12057 }
12058 else
12059 rcExit = pSandbox->rcExitCode;
12060 }
12061#elif K_ARCH == K_ARCH_X86_32
12062 /* x86 (see _tmainCRTStartup) */
12063 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
12064 __try
12065 {
12066 pSandbox->pOutXcptListHead = pTib->ExceptionList;
12067 if (setjmp(pSandbox->JmpBuf) == 0)
12068 {
12069 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
12070 pSandbox->fRunning = K_TRUE;
12071 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
12072 pSandbox->fRunning = K_FALSE;
12073 }
12074 else
12075 rcExit = pSandbox->rcExitCode;
12076 }
12077#endif
12078 __except (EXCEPTION_EXECUTE_HANDLER)
12079 {
12080 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
12081#ifdef WITH_HISTORY
12082 {
12083 KU32 cPrinted = 0;
12084 while (cPrinted++ < 5)
12085 {
12086 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
12087 if (g_apszHistory[idx])
12088 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
12089 }
12090 }
12091#endif
12092 rcExit = 512;
12093 }
12094 pSandbox->fRunning = K_FALSE;
12095
12096 /* Now, restore the NT TIB. */
12097 *pTib = pSandbox->TibMainThread;
12098 }
12099 else
12100 rcExit = 42 + 4;
12101
12102 /*
12103 * Flush and clean up the essential bits only, postpone whatever we
12104 * can till after we've replied to kmk.
12105 */
12106#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12107 kwSandboxConsoleFlushAll(&g_Sandbox);
12108#endif
12109 kwSandboxCleanup(&g_Sandbox);
12110 /** @todo Flush sandboxed native CRTs too. */
12111 }
12112 else
12113 rcExit = 42 + 3;
12114
12115 return rcExit;
12116}
12117
12118
12119/**
12120 * Does the post command part of a job (optional).
12121 *
12122 * @returns The exit code of the job.
12123 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12124 * @param papszPostCmdArgs The post command and its argument.
12125 */
12126static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12127{
12128 const char *pszCmd = papszPostCmdArgs[0];
12129
12130 /* Allow the kmk builtin prefix. */
12131 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
12132 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
12133 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
12134
12135 /* Command switch. */
12136 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
12137 {
12138 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
12139 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
12140 }
12141
12142 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
12143}
12144
12145
12146/**
12147 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
12148 */
12149static unsigned kwGetCurrentProcessorGroup(void)
12150{
12151 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
12152 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
12153 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
12154 if (pfnGetter)
12155 {
12156 GROUP_AFFINITY GroupAffinity;
12157 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
12158 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
12159 return GroupAffinity.Group;
12160 }
12161 return 0;
12162}
12163
12164
12165/**
12166 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
12167 */
12168static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
12169{
12170 KSIZE cchRet = 0;
12171 HANDLE hToken;
12172 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
12173 {
12174 DWORD cbRet;
12175 TOKEN_STATISTICS TokenStats;
12176 memset(&TokenStats, 0, sizeof(TokenStats));
12177 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
12178 cchRet = sprintf(pszValue, "%" KX64_PRI,
12179 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
12180 else
12181 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
12182 CloseHandle(hToken);
12183 }
12184 else
12185 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
12186 return cchRet;
12187}
12188
12189
12190/**
12191 * Look for and expand the special environment variable.
12192 *
12193 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
12194 * couldn't accuratly determine. Currently the following variables are
12195 * implemented:
12196 * - "@@PROCESSOR_GROUP@@" - The processor group number.
12197 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
12198 * - "@@PID@@" - The kWorker process ID.
12199 * - "@@@@" - Escaped "@@".
12200 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
12201 */
12202static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
12203{
12204 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
12205 KU32 i = cEnvVars;
12206 while (i-- > 0)
12207 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
12208 && papszEnvVars[i][cchSpecialEnv] == '=')
12209 {
12210 /* We will expand stuff like @@NAME@@ */
12211 const char *pszValue = papszEnvVars[i];
12212 KSIZE offDst = 0;
12213 char szTmp[1024];
12214 for (;;)
12215 {
12216 const char *pszAt = kHlpStrChr(pszValue, '@');
12217 while (pszAt && pszAt[1] != '@')
12218 pszAt = kHlpStrChr(pszAt + 1, '@');
12219 if (pszAt)
12220 {
12221 KSIZE cchSrc = pszAt - pszValue;
12222 if (offDst + cchSrc < sizeof(szTmp))
12223 {
12224 char szSrc[64];
12225
12226 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12227 offDst += cchSrc;
12228 pszValue = pszAt + 2;
12229
12230 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
12231 {
12232 pszValue += 15;
12233 if (g_iProcessGroup == -1)
12234 g_iProcessGroup = kwGetCurrentProcessorGroup();
12235 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
12236 }
12237 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
12238 {
12239 pszValue += 19;
12240 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
12241 }
12242 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
12243 {
12244 pszValue += 5;
12245 cchSrc = sprintf(szSrc, "%d", getpid());
12246 }
12247 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
12248 {
12249 pszValue += 2;
12250 szSrc[0] = '@';
12251 szSrc[1] = '@';
12252 szSrc[2] = '\0';
12253 cchSrc = 2;
12254 }
12255 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
12256 {
12257 static unsigned int s_iCounter = 0;
12258 pszValue += 15;
12259 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
12260 }
12261 else
12262 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
12263 pszValue - 2);
12264 if (offDst + cchSrc < sizeof(szTmp))
12265 {
12266 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
12267 offDst += cchSrc;
12268 continue;
12269 }
12270 }
12271 }
12272 else
12273 {
12274 KSIZE cchSrc = kHlpStrLen(pszValue);
12275 if (offDst + cchSrc < sizeof(szTmp))
12276 {
12277 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12278 offDst += cchSrc;
12279 break;
12280 }
12281 }
12282 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
12283 }
12284 szTmp[offDst] = '\0';
12285
12286 /* Return a copy of it: */
12287 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
12288 if (papszEnvVars[i])
12289 {
12290 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
12291 return 0;
12292 }
12293 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
12294 }
12295
12296 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
12297}
12298
12299
12300/**
12301 * Part 2 of the "JOB" command handler.
12302 *
12303 * @returns The exit code of the job.
12304 * @param pszExecutable The executable to execute.
12305 * @param pszCwd The current working directory of the job.
12306 * @param cArgs The number of arguments.
12307 * @param papszArgs The argument vector.
12308 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
12309 * @param cEnvVars The number of environment variables.
12310 * @param papszEnvVars The environment vector.
12311 * @param pszSpecialEnv Name of special environment variable that
12312 * requires selective expansion here.
12313 * @param fNoPchCaching Whether to disable precompiled header file
12314 * caching. Avoid trouble when creating them.
12315 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12316 * @param papszPostCmdArgs The post command and its argument.
12317 */
12318static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
12319 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12320 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
12321 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12322{
12323 int rcExit;
12324 PKWTOOL pTool;
12325 char *pszSpecialEnvFree = NULL;
12326
12327 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
12328 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
12329#ifdef KW_LOG_ENABLED
12330 {
12331 KU32 i;
12332 for (i = 0; i < cArgs; i++)
12333 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
12334 for (i = 0; i < cPostCmdArgs; i++)
12335 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
12336 }
12337#endif
12338 g_cJobs++;
12339
12340 /*
12341 * Expand pszSpecialEnv if present.
12342 */
12343 if (pszSpecialEnv && *pszSpecialEnv)
12344 {
12345 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
12346 if (!rcExit)
12347 { /* likely */ }
12348 else
12349 return rcExit;
12350 }
12351
12352 /*
12353 * Lookup the tool.
12354 */
12355 g_Sandbox.pTool = NULL; /* Avoid confusion between the SetDllDirectoryW hacks. */
12356 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
12357 if (pTool)
12358 {
12359 /*
12360 * Change the directory if we're going to execute the job inside
12361 * this process. Then invoke the tool type specific handler.
12362 */
12363 switch (pTool->enmType)
12364 {
12365 case KWTOOLTYPE_SANDBOXED:
12366 case KWTOOLTYPE_WATCOM:
12367 {
12368 /* Change dir. */
12369 KFSLOOKUPERROR enmError;
12370 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
12371 if ( pNewCurDir == g_pCurDirObj
12372 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
12373 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12374 else if (SetCurrentDirectoryA(pszCwd))
12375 {
12376 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
12377 g_pCurDirObj = pNewCurDir;
12378 }
12379 else
12380 {
12381 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
12382 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12383 rcExit = 42 + 1;
12384 break;
12385 }
12386
12387 /* Call specific handler. */
12388 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
12389 {
12390 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
12391 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
12392 cEnvVars, papszEnvVars, fNoPchCaching);
12393 }
12394 else
12395 {
12396 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
12397 rcExit = 42 + 2;
12398 }
12399 break;
12400 }
12401
12402 case KWTOOLTYPE_EXEC:
12403 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
12404 rcExit = 42 + 2;
12405 break;
12406
12407 default:
12408 kHlpAssertFailed();
12409 kwErrPrintf("Internal tool type corruption!!\n");
12410 rcExit = 42 + 2;
12411 g_fRestart = K_TRUE;
12412 break;
12413 }
12414
12415 /*
12416 * Do the post command, if present.
12417 */
12418 if (cPostCmdArgs && rcExit == 0)
12419 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
12420 }
12421 else
12422 rcExit = 42 + 1;
12423 if (pszSpecialEnvFree)
12424 {
12425 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
12426 kHlpFree(pszSpecialEnvFree);
12427 }
12428 return rcExit;
12429}
12430
12431
12432/**
12433 * Handles a "JOB" command.
12434 *
12435 * @returns The exit code of the job.
12436 * @param pszMsg Points to the "JOB" command part of the message.
12437 * @param cbMsg Number of message bytes at @a pszMsg. There are
12438 * 4 more zero bytes after the message body to
12439 * simplify parsing.
12440 */
12441static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
12442{
12443 int rcExit = 42;
12444
12445 /*
12446 * Unpack the message.
12447 */
12448 const char *pszExecutable;
12449 KSIZE cbTmp;
12450
12451 pszMsg += sizeof("JOB");
12452 cbMsg -= sizeof("JOB");
12453
12454 /* Executable name. */
12455 pszExecutable = pszMsg;
12456 cbTmp = kHlpStrLen(pszMsg) + 1;
12457 pszMsg += cbTmp;
12458 if ( cbTmp < cbMsg
12459 && cbTmp > 2)
12460 {
12461 const char *pszCwd;
12462 cbMsg -= cbTmp;
12463
12464 /* Current working directory. */
12465 pszCwd = pszMsg;
12466 cbTmp = kHlpStrLen(pszMsg) + 1;
12467 pszMsg += cbTmp;
12468 if ( cbTmp + sizeof(KU32) < cbMsg
12469 && cbTmp >= 2)
12470 {
12471 KU32 cArgs;
12472 cbMsg -= cbTmp;
12473
12474 /* Argument count. */
12475 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
12476 pszMsg += sizeof(cArgs);
12477 cbMsg -= sizeof(cArgs);
12478
12479 if (cArgs > 0 && cArgs < 4096)
12480 {
12481 /* The argument vector. */
12482 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
12483 if (papszArgs)
12484 {
12485 KU32 i;
12486 for (i = 0; i < cArgs; i++)
12487 {
12488 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
12489 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
12490 pszMsg += cbTmp;
12491 if (cbTmp < cbMsg)
12492 cbMsg -= cbTmp;
12493 else
12494 {
12495 cbMsg = 0;
12496 break;
12497 }
12498
12499 }
12500 papszArgs[cArgs] = 0;
12501
12502 /* Environment variable count. */
12503 if (cbMsg > sizeof(KU32))
12504 {
12505 KU32 cEnvVars;
12506 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
12507 pszMsg += sizeof(cEnvVars);
12508 cbMsg -= sizeof(cEnvVars);
12509
12510 if (cEnvVars >= 0 && cEnvVars < 4096)
12511 {
12512 /* The argument vector. */
12513 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
12514 if (papszEnvVars)
12515 {
12516 for (i = 0; i < cEnvVars; i++)
12517 {
12518 papszEnvVars[i] = pszMsg;
12519 cbTmp = kHlpStrLen(pszMsg) + 1;
12520 pszMsg += cbTmp;
12521 if (cbTmp < cbMsg)
12522 cbMsg -= cbTmp;
12523 else
12524 {
12525 cbMsg = 0;
12526 break;
12527 }
12528 }
12529 papszEnvVars[cEnvVars] = 0;
12530
12531 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
12532 if (cbMsg >= sizeof(KU8) * 2)
12533 {
12534 KBOOL fWatcomBrainDamange = *pszMsg++;
12535 KBOOL fNoPchCaching = *pszMsg++;
12536 cbMsg -= 2;
12537
12538 /* Name of special enviornment variable requiring selective expansion. */
12539 if (cbMsg >= 1)
12540 {
12541 const char *pszSpecialEnv = pszMsg;
12542 cbTmp = kHlpStrLen(pszMsg);
12543 pszMsg += cbTmp + 1;
12544 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
12545
12546 /* Post command argument count (can be zero). */
12547 if (cbMsg >= sizeof(KU32))
12548 {
12549 KU32 cPostCmdArgs;
12550 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
12551 pszMsg += sizeof(cPostCmdArgs);
12552 cbMsg -= sizeof(cPostCmdArgs);
12553
12554 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
12555 {
12556 char const *apszPostCmdArgs[32+1];
12557 for (i = 0; i < cPostCmdArgs; i++)
12558 {
12559 apszPostCmdArgs[i] = pszMsg;
12560 cbTmp = kHlpStrLen(pszMsg) + 1;
12561 pszMsg += cbTmp;
12562 if ( cbTmp < cbMsg
12563 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
12564 cbMsg -= cbTmp;
12565 else
12566 {
12567 cbMsg = KSIZE_MAX;
12568 break;
12569 }
12570 }
12571 if (cbMsg == 0)
12572 {
12573 apszPostCmdArgs[cPostCmdArgs] = NULL;
12574
12575 /*
12576 * The next step.
12577 */
12578 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
12579 cArgs, papszArgs, fWatcomBrainDamange,
12580 cEnvVars, papszEnvVars, pszSpecialEnv,
12581 fNoPchCaching,
12582 cPostCmdArgs, apszPostCmdArgs);
12583 }
12584 else if (cbMsg == KSIZE_MAX)
12585 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
12586 else
12587 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
12588 }
12589 else
12590 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
12591 }
12592 else
12593 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
12594 }
12595 else
12596 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12597 }
12598 else
12599 kwErrPrintf("Detected bogus message unpacking flags!\n");
12600 kHlpFree((void *)papszEnvVars);
12601 }
12602 else
12603 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12604 }
12605 else
12606 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12607 }
12608 else
12609 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12610 kHlpFree((void *)papszArgs);
12611 }
12612 else
12613 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12614 }
12615 else
12616 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12617 }
12618 else
12619 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12620 }
12621 else
12622 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12623 return rcExit;
12624}
12625
12626
12627/**
12628 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12629 *
12630 * @retval 0 on success.
12631 * @retval -1 on error (fully bitched).
12632 *
12633 * @param hPipe The pipe handle.
12634 * @param pvBuf The buffer to write out out.
12635 * @param cbToWrite The number of bytes to write.
12636 */
12637static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12638{
12639 KU8 const *pbBuf = (KU8 const *)pvBuf;
12640 KU32 cbLeft = cbToWrite;
12641 while (g_rcCtrlC == 0)
12642 {
12643 DWORD cbActuallyWritten = 0;
12644 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12645 {
12646 cbLeft -= cbActuallyWritten;
12647 if (!cbLeft)
12648 return 0;
12649 pbBuf += cbActuallyWritten;
12650 }
12651 else
12652 {
12653 DWORD dwErr = GetLastError();
12654 if (cbLeft == cbToWrite)
12655 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12656 else
12657 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12658 return -1;
12659 }
12660 }
12661 return -1;
12662}
12663
12664
12665/**
12666 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12667 *
12668 * @retval 0 on success.
12669 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12670 * @retval -1 on error (fully bitched).
12671 * @param hPipe The pipe handle.
12672 * @param pvBuf The buffer to read into.
12673 * @param cbToRead The number of bytes to read.
12674 * @param fShutdownOkay Whether connection shutdown while reading the
12675 * first byte is okay or not.
12676 */
12677static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12678{
12679 KU8 *pbBuf = (KU8 *)pvBuf;
12680 KU32 cbLeft = cbToRead;
12681 while (g_rcCtrlC == 0)
12682 {
12683 DWORD cbActuallyRead = 0;
12684 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12685 {
12686 cbLeft -= cbActuallyRead;
12687 if (!cbLeft)
12688 return 0;
12689 pbBuf += cbActuallyRead;
12690 }
12691 else
12692 {
12693 DWORD dwErr = GetLastError();
12694 if (cbLeft == cbToRead)
12695 {
12696 if ( fMayShutdown
12697 && dwErr == ERROR_BROKEN_PIPE)
12698 return 1;
12699 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12700 }
12701 else
12702 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12703 return -1;
12704 }
12705 }
12706 return -1;
12707}
12708
12709
12710/**
12711 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12712 *
12713 * @returns pszBuf
12714 * @param pszBuf The buffer (sufficiently large).
12715 * @param uValue The value.
12716 */
12717static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12718{
12719 char szTmp[64];
12720 char *psz = &szTmp[63];
12721 int cch = 4;
12722
12723 *psz-- = '\0';
12724 do
12725 {
12726 if (--cch == 0)
12727 {
12728 *psz-- = ' ';
12729 cch = 3;
12730 }
12731 *psz-- = (uValue % 10) + '0';
12732 uValue /= 10;
12733 } while (uValue != 0);
12734
12735 return strcpy(pszBuf, psz + 1);
12736}
12737
12738
12739/**
12740 * Prints statistics.
12741 */
12742static void kwPrintStats(void)
12743{
12744 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12745 MEMORYSTATUSEX MemStatus;
12746 IO_COUNTERS IoCounters;
12747 DWORD cHandles;
12748 KSIZE cMisses;
12749 char szBuf[16*1024];
12750 int off = 0;
12751 char szPrf[24];
12752 char sz1[64];
12753 char sz2[64];
12754 char sz3[64];
12755 char sz4[64];
12756
12757 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12758
12759 szBuf[off++] = '\n';
12760
12761 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12762 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12763 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12764 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12765 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12766
12767 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12768 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12769
12770 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12771 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12772 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12773
12774 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12775 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12776 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12777
12778 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12779 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12780 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12781 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12782 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12783 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12784 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12785
12786 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12787 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12788 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12789 kwFmtU64(sz1, g_pFsCache->cObjects),
12790 kwFmtU64(sz2, g_pFsCache->cbObjects),
12791 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12792 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12793 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12794 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12795 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12796 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12797#ifdef KFSCACHE_CFG_UTF16
12798 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12799 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12800 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12801 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12802 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12803#endif
12804 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12805 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12806 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12807 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12808 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12809
12810 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12811 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12812 kwFmtU64(sz1, g_pFsCache->cLookups),
12813 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12814 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12815 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12816 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12817 kwFmtU64(sz4, cMisses),
12818 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12819
12820 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12821 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12822 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12823 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12824 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12825 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12826 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12827 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12828
12829#ifdef WITH_HASH_CACHE
12830 off += sprintf(&szBuf[off], "%s %14s hashes calculated, %s cache hits (%u%%), %s fallbacks, %s partial\n", szPrf,
12831 kwFmtU64(sz1, g_cHashes),
12832 kwFmtU64(sz2, g_cHashesCached),
12833 (unsigned)(g_cHashesCached * 100 / K_MAX(g_cHashes, 1)),
12834 kwFmtU64(sz3, g_cHashesFallbacks),
12835 kwFmtU64(sz4, g_cHashesPartial));
12836 off += sprintf(&szBuf[off], "%s %14s MD5: %s SHA-1: %s SHA-256: %s SHA-512: %s\n", szPrf, "", kwFmtU64(sz1, g_cHashesMd5),
12837 kwFmtU64(sz2, g_cHashesSha1), kwFmtU64(sz3, g_cHashesSha256), kwFmtU64(sz4, g_cHashesSha512));
12838#endif
12839
12840 /*
12841 * Process & Memory details.
12842 */
12843 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12844 cHandles = 0;
12845 MemInfo.cb = sizeof(MemInfo);
12846 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12847 memset(&MemInfo, 0, sizeof(MemInfo));
12848 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12849 kwFmtU64(sz1, cHandles),
12850 kwFmtU64(sz2, MemInfo.PageFaultCount),
12851 kwFmtU64(sz3, MemInfo.PagefileUsage),
12852 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12853 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12854 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12855 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12856 kwFmtU64(sz3, MemInfo.PrivateUsage));
12857 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12858 szPrf,
12859 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12860 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12861 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12862 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12863
12864 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12865 memset(&IoCounters, 0, sizeof(IoCounters));
12866 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12867 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12868 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12869 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12870 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12871 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12872 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12873 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12874 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12875
12876 MemStatus.dwLength = sizeof(MemStatus);
12877 if (!GlobalMemoryStatusEx(&MemStatus))
12878 memset(&MemStatus, 0, sizeof(MemStatus));
12879 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12880 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12881 MemStatus.ullAvailVirtual);
12882 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12883
12884 maybe_con_fwrite(szBuf, off, 1, stdout);
12885 fflush(stdout);
12886}
12887
12888
12889/**
12890 * Handles what comes after --test.
12891 *
12892 * @returns Exit code.
12893 * @param argc Number of arguments after --test.
12894 * @param argv Arguments after --test.
12895 */
12896static int kwTestRun(int argc, char **argv)
12897{
12898 int i;
12899 int j;
12900 int rcExit;
12901 int cRepeats;
12902 char szCwd[MAX_PATH];
12903 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12904 KU32 cEnvVars;
12905 char **papszEnvVars;
12906 const char *pszSpecialEnv = "";
12907 const char *pszSpecialEnvFull = NULL;
12908 KBOOL fWatcomBrainDamange = K_FALSE;
12909 KBOOL fNoPchCaching = K_FALSE;
12910
12911 /*
12912 * Parse arguments.
12913 */
12914 /* Repeat count. */
12915 i = 0;
12916 if (i >= argc)
12917 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12918 if (strcmp(argv[i], "--") != 0)
12919 {
12920 cRepeats = atoi(argv[i]);
12921 if (cRepeats <= 0)
12922 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12923 i++;
12924
12925 /* Optional directory change. */
12926 if ( i < argc
12927 && ( strcmp(argv[i], "--chdir") == 0
12928 || strcmp(argv[i], "-C") == 0 ) )
12929 {
12930 i++;
12931 if (i >= argc)
12932 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12933 pszCwd = argv[i++];
12934 }
12935
12936 /* Optional watcom flag directory change. */
12937 if ( i < argc
12938 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12939 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12940 {
12941 fWatcomBrainDamange = K_TRUE;
12942 i++;
12943 }
12944
12945 /* Optional watcom flag directory change. */
12946 if ( i < argc
12947 && strcmp(argv[i], "--no-pch-caching") == 0)
12948 {
12949 fNoPchCaching = K_TRUE;
12950 i++;
12951 }
12952
12953 /* Optional directory change. */
12954 if ( i < argc
12955 && ( strcmp(argv[i], "--set-special") == 0
12956 || strcmp(argv[i], "-s") == 0 ) )
12957 {
12958 i++;
12959 if (i >= argc)
12960 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12961 pszSpecialEnvFull = argv[i++];
12962 putenv(pszSpecialEnvFull);
12963 pszSpecialEnv = strdup(pszSpecialEnvFull);
12964 *strchr(pszSpecialEnv, '=') = '\0';
12965 }
12966
12967 /* Trigger breakpoint */
12968 if ( i < argc
12969 && strcmp(argv[i], "--breakpoint") == 0)
12970 {
12971 __debugbreak();
12972 i++;
12973 }
12974
12975 /* Check for '--'. */
12976 if (i >= argc)
12977 return kwErrPrintfRc(2, "Missing '--'\n");
12978 if (strcmp(argv[i], "--") != 0)
12979 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
12980 i++;
12981 }
12982 else
12983 {
12984 cRepeats = 1;
12985 i++;
12986 }
12987 if (i >= argc)
12988 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
12989
12990 /*
12991 * Duplicate the environment.
12992 */
12993 cEnvVars = 0;
12994 while (environ[cEnvVars] != NULL)
12995 cEnvVars++;
12996 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
12997
12998 /*
12999 * Do the job.
13000 */
13001 rcExit = 0;
13002 for (j = 0; j < cRepeats; j++)
13003 {
13004 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
13005 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
13006 argc - i, &argv[i], fWatcomBrainDamange,
13007 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
13008 0, NULL);
13009 KW_LOG(("rcExit=%d\n", rcExit));
13010 kwSandboxCleanupLate(&g_Sandbox);
13011 }
13012
13013 if (getenv("KWORKER_STATS") != NULL)
13014 kwPrintStats();
13015
13016# ifdef WITH_LOG_FILE
13017 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13018 CloseHandle(g_hLogFile);
13019# endif
13020 return rcExit;
13021}
13022
13023
13024/**
13025 * Reads @a pszFile into memory and chops it up into an argument vector.
13026 *
13027 * @returns Pointer to the argument vector on success, NULL on failure.
13028 * @param pszFile The file to load.
13029 * @param pcArgs Where to return the number of arguments.
13030 * @param ppszFileContent Where to return the allocation.
13031 */
13032static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
13033{
13034 char **papszArgs = NULL;
13035 FILE *pFile = fopen(pszFile, "r");
13036 if (pFile)
13037 {
13038 long cbFile;
13039 if ( fseek(pFile, 0, SEEK_END) == 0
13040 && (cbFile = ftell(pFile)) >= 0
13041 && fseek(pFile, 0, SEEK_SET) == 0)
13042 {
13043 char *pszFile = kHlpAllocZ(cbFile + 3);
13044 if (pszFile)
13045 {
13046 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
13047 if ( feof(pFile)
13048 && !ferror(pFile))
13049 {
13050 size_t off = 0;
13051 int cArgs = 0;
13052 int cAllocated = 0;
13053 char ch;
13054
13055 pszFile[cbRead] = '\0';
13056 pszFile[cbRead + 1] = '\0';
13057 pszFile[cbRead + 2] = '\0';
13058
13059 while ((ch = pszFile[off]) != '\0')
13060 {
13061 char *pszArg;
13062 switch (ch)
13063 {
13064 case ' ':
13065 case '\t':
13066 case '\n':
13067 case '\r':
13068 off++;
13069 continue;
13070
13071 case '\\':
13072 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
13073 {
13074 off += 2;
13075 continue;
13076 }
13077 /* fall thru */
13078 default:
13079 pszArg = &pszFile[off];
13080 do
13081 ch = pszFile[++off];
13082 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
13083 pszFile[off++] = '\0';
13084 break;
13085
13086 case '\'':
13087 pszArg = &pszFile[++off];
13088 while ((ch = pszFile[off]) != '\0' && ch != '\'')
13089 off++;
13090 pszFile[off++] = '\0';
13091 break;
13092
13093 case '\"': /** @todo escape sequences */
13094 pszArg = &pszFile[++off];
13095 while ((ch = pszFile[off]) != '\0' && ch != '"')
13096 off++;
13097 pszFile[off++] = '\0';
13098 break;
13099 }
13100 if (cArgs + 1 >= cAllocated)
13101 {
13102 void *pvNew;
13103 cAllocated = cAllocated ? cAllocated * 2 : 16;
13104 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
13105 if (pvNew)
13106 papszArgs = (char **)pvNew;
13107 else
13108 {
13109 kHlpFree(papszArgs);
13110 papszArgs = NULL;
13111 break;
13112 }
13113 }
13114 papszArgs[cArgs] = pszArg;
13115 papszArgs[++cArgs] = NULL;
13116 }
13117 *pcArgs = cArgs;
13118 }
13119 else
13120 kwErrPrintf("Error reading '%s'!\n", pszFile);
13121 }
13122 else
13123 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
13124 }
13125 else
13126 kwErrPrintf("Error seeking '%s'!\n", pszFile);
13127 fclose(pFile);
13128 }
13129 else
13130 kwErrPrintf("Error opening '%s'!\n", pszFile);
13131 return papszArgs;
13132}
13133
13134/**
13135 * Appends a string to an string vector (arguments or enviornment).
13136 *
13137 * @returns 0 on success, non-zero on failure (exit code).
13138 * @param ppapszVector Pointer to the string pointer array.
13139 * @param pcEntries Pointer to the array size.
13140 * @param pszAppend The string to append.
13141 */
13142static int kwFullTestVectorAppend(const char ***ppapszVector, KU32 *pcEntries, char const *pszAppend)
13143{
13144 KU32 cEntries = *pcEntries;
13145 if (!(cEntries & 15))
13146 {
13147 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
13148 if (pvNew)
13149 *ppapszVector = (const char **)pvNew;
13150 else
13151 return kwErrPrintfRc(2, "Out of memory!\n");
13152 }
13153 (*ppapszVector)[cEntries] = pszAppend;
13154 (*ppapszVector)[++cEntries] = NULL;
13155 *pcEntries = cEntries;
13156 return 0;
13157}
13158
13159
13160/**
13161 * Parses arguments for --full-test.
13162 *
13163 * @returns 0 on success, non-zero on failure (exit code).
13164 */
13165static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
13166 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
13167{
13168 PKWONETEST pCur = *ppHead;
13169 int i;
13170 for (i = 0; i < argc; i++)
13171 {
13172 int rc = 0;
13173 const char *pszArg = argv[i];
13174 if (*pszArg == 'k')
13175 {
13176 if (kHlpStrComp(pszArg, "kSubmit") == 0)
13177 {
13178 if (*piState != 0)
13179 {
13180 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
13181 if (!pCur)
13182 return kwErrPrintfRc(2, "Out of memory!\n");
13183 pCur->fVirgin = K_TRUE;
13184 pCur->pszCwd = pszDefaultCwd;
13185 pCur->cRuns = 1;
13186 pCur->pNext = *ppHead;
13187 *ppHead = pCur;
13188 *piState = 0;
13189 }
13190 else if (!pCur->fVirgin)
13191 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
13192 pCur->pszJobSrc = pszJobSrc;
13193 pCur->iJobSrc = i;
13194 continue; /* (to stay virgin) */
13195 }
13196
13197 /* Ignore "kWorker 378172/62:" sequences that kmk/kSubmit spews out on failure. */
13198 if ( kHlpStrComp(pszArg, "kWorker") == 0
13199 && i + 1 < argc
13200 && (unsigned)(argv[i + 1][0] - '0') <= 9)
13201 {
13202 i++;
13203 continue;
13204 }
13205 }
13206
13207 if ( *pszArg == '-'
13208 && ( *piState == 0
13209 || pszArg[1] == '@'))
13210 {
13211 const char *pszValue = NULL;
13212 char ch = *++pszArg;
13213 pszArg++;
13214 if (ch == '-')
13215 {
13216 ch = '\0';
13217 if (*pszArg == '\0') /* -- */
13218 *piState = 2;
13219 /* Translate or handle long options: */
13220 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
13221 ch = 'E';
13222 else if (kHlpStrComp(pszArg, "special-env") == 0)
13223 ch = 's';
13224 else if (kHlpStrComp(pszArg, "default-env") == 0)
13225 {
13226 unsigned i;
13227 pCur->cEnvVars = 0;
13228 for (i = 0; environ[i] && rc == 0; i++)
13229 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
13230 }
13231 else if (kHlpStrComp(pszArg, "chdir") == 0)
13232 ch = 'C';
13233 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
13234 ch = 'P';
13235 else if (kHlpStrComp(pszArg, "response-file") == 0)
13236 ch = '@';
13237 else if (kHlpStrComp(pszArg, "runs") == 0)
13238 ch = 'R';
13239 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
13240 pCur->fWatcomBrainDamange = K_TRUE;
13241 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
13242 pCur->fNoPchCaching = K_TRUE;
13243 else if (kHlpStrComp(pszArg, "executable") == 0)
13244 ch = 'e';
13245 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
13246 {
13247 __debugbreak();
13248 continue; /* (to stay virgin) */
13249 }
13250 else
13251 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
13252 pszArg = "";
13253 }
13254
13255 while (ch != '\0' && rc == 0)
13256 {
13257 /* Fetch value if needed: */
13258 switch (ch)
13259 {
13260 case '@':
13261 case 'e':
13262 case 'E':
13263 case 's':
13264 case 'C':
13265 case 'R':
13266 if (*pszArg == ':' || *pszArg == '=')
13267 pszValue = &pszArg[1];
13268 else if (*pszArg)
13269 pszValue = pszArg;
13270 else if (i + 1 < argc)
13271 pszValue = argv[++i];
13272 else
13273 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
13274 pszArg = "";
13275 break;
13276 }
13277
13278 /* Handle the option: */
13279 switch (ch)
13280 {
13281 case 'E':
13282 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
13283 break;
13284 case 'C':
13285 pCur->pszCwd = pszValue;
13286 break;
13287 case 's':
13288 pCur->pszSpecialEnv = pszValue;
13289 break;
13290 case 'e':
13291 pCur->pszExecutable = pszValue;
13292 break;
13293 case 'P':
13294 *piState = 1;
13295 if (*pszArg)
13296 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
13297 break;
13298 case 'R':
13299 pCur->cRuns = atoi(pszValue);
13300 if ((int)pCur->cRuns < 0)
13301 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
13302 break;
13303 case '@':
13304 if (cRecursions < 5)
13305 {
13306 char *pszLeaked = NULL;
13307 int cArgs = 0;
13308 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
13309 if (papszArgsLeaked)
13310 {
13311 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
13312 cRecursions + 1, pszValue);
13313 pCur = *ppHead;
13314 }
13315 else
13316 return 2;
13317 }
13318 else
13319 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
13320 break;
13321 }
13322
13323 /* next */
13324 ch = *pszArg++;
13325 }
13326 }
13327 else if (*piState == 2)
13328 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
13329 else if (*piState == 1)
13330 {
13331 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
13332 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
13333 else
13334 *piState = 2;
13335 }
13336 else
13337 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
13338 if (rc)
13339 return rc;
13340 pCur->fVirgin = K_FALSE;
13341 }
13342 return 0;
13343}
13344
13345
13346/**
13347 * Handles what comes after --full-test.
13348 *
13349 * @returns Exit code.
13350 * @param argc Number of arguments after --full-test.
13351 * @param argv Arguments after --full-test.
13352 */
13353static int kwFullTestRun(int argc, char **argv)
13354{
13355 char szDefaultCwd[MAX_PATH];
13356 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
13357 KWONETEST FirstTest;
13358 PKWONETEST pHead = &FirstTest;
13359 PKWONETEST pCur;
13360 int iState = 0;
13361 int rcExit;
13362
13363 /*
13364 * Parse arguments.
13365 */
13366 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
13367 FirstTest.pszJobSrc = "command-line";
13368 FirstTest.iJobSrc = 1;
13369 FirstTest.fVirgin = K_TRUE;
13370 FirstTest.pszCwd = pszDefaultCwd;
13371 FirstTest.cRuns = 1;
13372
13373 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
13374 if (rcExit)
13375 return rcExit;
13376
13377 /*
13378 * Do the job. LIFO ordering (see kSubmit).
13379 */
13380 for (pCur = pHead; pCur; pCur = pCur->pNext)
13381 {
13382 if (!pCur->pszExecutable && pCur->papszArgs)
13383 pCur->pszExecutable = pCur->papszArgs[0];
13384 if ( pCur->pszExecutable
13385 && pCur->cArgs > 0
13386 && pCur->cEnvVars > 0)
13387 {
13388 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
13389 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
13390 unsigned iRun;
13391
13392 for (iRun = 0; iRun < pCur->cRuns; iRun++)
13393 {
13394 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
13395 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
13396 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
13397 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
13398
13399 KW_LOG(("rcExit=%d\n", rcExit));
13400 kwSandboxCleanupLate(&g_Sandbox);
13401
13402 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
13403 }
13404 kHlpFree(papszEnvVarsCopy);
13405 }
13406 else
13407 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
13408 pCur->pszExecutable ? "" : " No executable!",
13409 pCur->cArgs < 1 ? " No arguments!" : "",
13410 pCur->cEnvVars < 1 ? " No environment!" : "",
13411 pCur->iJobSrc, pCur->pszJobSrc);
13412 }
13413
13414 if (getenv("KWORKER_STATS") != NULL)
13415 kwPrintStats();
13416
13417# ifdef WITH_LOG_FILE
13418 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13419 CloseHandle(g_hLogFile);
13420# endif
13421 return rcExit;
13422}
13423
13424
13425/**
13426 * Helper for main() argument handling that sets the processor group if
13427 * possible.
13428 */
13429static void kwSetProcessorGroup(unsigned long uGroup)
13430{
13431 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
13432 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
13433 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
13434
13435 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
13436 if (pfnSetThreadGroupAffinity)
13437 {
13438 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
13439 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
13440 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
13441 if (NewAff.Mask && (WORD)uGroup == uGroup)
13442 {
13443 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
13444 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
13445 }
13446 else if (GetLastError() == NO_ERROR)
13447 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
13448 else
13449 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
13450 uGroup, GetLastError());
13451 }
13452 else
13453 {
13454 OSVERSIONINFOA VerInfo = {0};
13455 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
13456 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
13457 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
13458 }
13459}
13460
13461
13462int main(int argc, char **argv)
13463{
13464 KSIZE cbMsgBuf = 0;
13465 KU8 *pbMsgBuf = NULL;
13466 int i;
13467 HANDLE hPipe = INVALID_HANDLE_VALUE;
13468 const char *pszTmp;
13469 KFSLOOKUPERROR enmIgnored;
13470 DWORD dwType;
13471#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
13472 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
13473 kwSandboxVecXcptEmulateChained);
13474#endif
13475#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13476 HANDLE hCurProc = GetCurrentProcess();
13477 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
13478 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
13479#endif
13480
13481
13482#ifdef WITH_FIXED_VIRTUAL_ALLOCS
13483 /*
13484 * Reserve memory for cl.exe
13485 */
13486 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
13487 {
13488 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
13489 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
13490 g_aFixedVirtualAllocs[i].cbFixed,
13491 MEM_RESERVE, PAGE_READWRITE);
13492 if ( !g_aFixedVirtualAllocs[i].pvReserved
13493 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
13494 {
13495 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
13496 GetLastError());
13497 if (g_aFixedVirtualAllocs[i].pvReserved)
13498 {
13499 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
13500 g_aFixedVirtualAllocs[i].pvReserved = NULL;
13501 }
13502 }
13503 }
13504#endif
13505
13506 /*
13507 * Register our Control-C and Control-Break handlers.
13508 */
13509 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
13510 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
13511
13512 /*
13513 * Create the cache and mark the temporary directory as using the custom revision.
13514 */
13515 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
13516 if (!g_pFsCache)
13517 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
13518
13519 pszTmp = getenv("TEMP");
13520 if (pszTmp && *pszTmp != '\0')
13521 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13522 pszTmp = getenv("TMP");
13523 if (pszTmp && *pszTmp != '\0')
13524 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13525 pszTmp = getenv("TMPDIR");
13526 if (pszTmp && *pszTmp != '\0')
13527 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13528
13529 /*
13530 * Make g_abDefLdBuf executable.
13531 */
13532 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
13533 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
13534 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
13535 InitializeCriticalSection(&g_Sandbox.HandlesLock);
13536 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
13537
13538#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13539 /*
13540 * Get and duplicate the console handles.
13541 */
13542 /* Standard output. */
13543 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
13544 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
13545 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13546 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
13547 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
13548 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
13549 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13550 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13551 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
13552 g_Sandbox.HandleStdOut.cRefs = 0x10001;
13553 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
13554 g_Sandbox.HandleStdOut.tidOwner = KU32_MAX;
13555 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
13556 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
13557 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
13558 {
13559 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
13560 g_Sandbox.cFixedHandles++;
13561 else
13562 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
13563 }
13564 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13565 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
13566
13567 /* Standard error. */
13568 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
13569 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
13570 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13571 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
13572 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
13573 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
13574 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13575 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13576 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
13577 g_Sandbox.HandleStdErr.cRefs = 0x10001;
13578 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
13579 g_Sandbox.HandleStdErr.tidOwner = KU32_MAX;
13580 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
13581 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
13582 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
13583 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
13584 {
13585 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
13586 g_Sandbox.cFixedHandles++;
13587 else
13588 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
13589 }
13590 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13591 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
13592
13593 /* Combined console buffer. */
13594 if (g_Sandbox.StdErr.fIsConsole)
13595 {
13596 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
13597 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13598 }
13599 else if (g_Sandbox.StdOut.fIsConsole)
13600 {
13601 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
13602 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13603 }
13604 else
13605 {
13606 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
13607 g_Sandbox.Combined.uCodepage = CP_ACP;
13608 }
13609 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
13610#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
13611
13612
13613 /*
13614 * Parse arguments.
13615 */
13616 for (i = 1; i < argc; i++)
13617 {
13618 if (strcmp(argv[i], "--pipe") == 0)
13619 {
13620 i++;
13621 if (i < argc)
13622 {
13623 char *pszEnd = NULL;
13624 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13625 if ( *argv[i]
13626 && pszEnd != NULL
13627 && *pszEnd == '\0'
13628 && u64Value != 0
13629 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13630 && (uintptr_t)u64Value == u64Value)
13631 hPipe = (HANDLE)(uintptr_t)u64Value;
13632 else
13633 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13634 }
13635 else
13636 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13637 }
13638 else if (strcmp(argv[i], "--volatile") == 0)
13639 {
13640 i++;
13641 if (i < argc)
13642 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13643 else
13644 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13645 }
13646 else if (strcmp(argv[i], "--test") == 0)
13647 return kwTestRun(argc - i - 1, &argv[i + 1]);
13648 else if (strcmp(argv[i], "--full-test") == 0)
13649 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13650 else if (strcmp(argv[i], "--priority") == 0)
13651 {
13652 i++;
13653 if (i < argc)
13654 {
13655 char *pszEnd = NULL;
13656 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13657 if ( *argv[i]
13658 && pszEnd != NULL
13659 && *pszEnd == '\0'
13660 && uValue >= 1
13661 && uValue <= 5)
13662 {
13663 DWORD dwClass;
13664 int dwPriority;
13665 switch (uValue)
13666 {
13667 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13668 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13669 default:
13670 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13671 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13672 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13673 }
13674 SetPriorityClass(GetCurrentProcess(), dwClass);
13675 if (dwPriority != INT_MAX)
13676 SetThreadPriority(GetCurrentThread(), dwPriority);
13677 }
13678 else
13679 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13680 }
13681 else
13682 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13683 }
13684 else if (strcmp(argv[i], "--group") == 0)
13685 {
13686 i++;
13687 if (i < argc)
13688 {
13689 char *pszEnd = NULL;
13690 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13691 if ( *argv[i]
13692 && pszEnd != NULL
13693 && *pszEnd == '\0'
13694 && uValue == (WORD)uValue)
13695 kwSetProcessorGroup(uValue);
13696 else
13697 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13698 }
13699 else
13700 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13701 }
13702 else if ( strcmp(argv[i], "--verbose") == 0
13703 || strcmp(argv[i], "-v") == 0)
13704 g_cVerbose++;
13705 else if ( strcmp(argv[i], "--help") == 0
13706 || strcmp(argv[i], "-h") == 0
13707 || strcmp(argv[i], "-?") == 0)
13708 {
13709 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13710 "usage: kWorker <--help|-h>\n"
13711 "usage: kWorker <--version|-V>\n"
13712 "usage: kWorker [--volatile dir] --full-test kSubmit ...\n"
13713 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13714 "\n"
13715 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13716 return 0;
13717 }
13718 else if ( strcmp(argv[i], "--version") == 0
13719 || strcmp(argv[i], "-V") == 0)
13720 return kbuild_version(argv[0]);
13721 else
13722 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13723 }
13724
13725 /*
13726 * If no --pipe argument, then assume its standard input.
13727 * We need to carefully replace the CRT stdin with a handle to "nul".
13728 */
13729 if (hPipe == INVALID_HANDLE_VALUE)
13730 {
13731 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13732 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13733 {
13734 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13735 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13736 {
13737 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13738 if (fdNul >= 0)
13739 {
13740 if (_dup2(fdNul, 0) >= 0)
13741 {
13742 close(fdNul);
13743 hPipe = hDuplicate;
13744 }
13745 else
13746 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13747 }
13748 else
13749 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13750 }
13751 else
13752 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13753 }
13754 else
13755 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13756 GetFileType(hPipe), GetLastError());
13757 }
13758 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13759 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13760 GetFileType(hPipe), GetLastError());
13761 g_hPipe = hPipe;
13762
13763 /*
13764 * Serve the pipe.
13765 */
13766 for (;;)
13767 {
13768 KU32 cbMsg = 0;
13769 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13770 if (rc == 0)
13771 {
13772 /* Make sure the message length is within sane bounds. */
13773 if ( cbMsg > 4
13774 && cbMsg <= 256*1024*1024)
13775 {
13776 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13777 if (cbMsg + 4 <= cbMsgBuf)
13778 { /* likely */ }
13779 else
13780 {
13781 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13782 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13783 if (!pbMsgBuf)
13784 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13785 }
13786
13787 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13788 *(KU32 *)pbMsgBuf = cbMsg;
13789 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13790 if (rc == 0)
13791 {
13792 const char *psz;
13793
13794 pbMsgBuf[cbMsg] = '\0';
13795 pbMsgBuf[cbMsg + 1] = '\0';
13796 pbMsgBuf[cbMsg + 2] = '\0';
13797 pbMsgBuf[cbMsg + 3] = '\0';
13798
13799 /* The first string after the header is the command. */
13800 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13801 if ( strcmp(psz, "JOB") == 0
13802 && g_rcCtrlC == 0)
13803 {
13804 struct
13805 {
13806 KI32 rcExitCode;
13807 KU8 bExiting;
13808 KU8 abZero[3];
13809 } Reply;
13810 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13811 Reply.bExiting = g_fRestart;
13812 Reply.abZero[0] = 0;
13813 Reply.abZero[1] = 0;
13814 Reply.abZero[2] = 0;
13815 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13816 if ( rc == 0
13817 && !g_fRestart)
13818 {
13819 kwSandboxCleanupLate(&g_Sandbox);
13820 if (g_rcCtrlC == 0)
13821 continue;
13822 }
13823 }
13824 else
13825 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13826 }
13827 }
13828 else
13829 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13830 }
13831
13832 /*
13833 * If we're exitting because we're restarting, we need to delay till
13834 * kmk/kSubmit has read the result. Windows documentation says it
13835 * immediately discards pipe buffers once the pipe is broken by the
13836 * server (us). So, We flush the buffer and queues a 1 byte read
13837 * waiting for kSubmit to close the pipe when it receives the
13838 * bExiting = K_TRUE result.
13839 */
13840 if (g_fRestart)
13841 {
13842 DWORD cbIgnored = 1;
13843 KU8 b;
13844 FlushFileBuffers(hPipe);
13845 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13846 }
13847
13848 CloseHandle(hPipe);
13849#ifdef WITH_LOG_FILE
13850 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13851 CloseHandle(g_hLogFile);
13852#endif
13853 if (getenv("KWORKER_STATS") != NULL)
13854 kwPrintStats();
13855 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13856 }
13857}
13858
13859
13860/** @page pg_kWorker kSubmit / kWorker
13861 *
13862 * @section sec_kWorker_Motivation Motivation / Inspiration
13863 *
13864 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13865 * builds on machines "infested" by Anti Virus protection and disk encryption
13866 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13867 * got "infected".
13868 *
13869 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13870 * mainly a bunch of tiny assembly and C files being compiler a million times.
13871 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13872 * own toolchain from within the same process, saving a lot of process creation
13873 * and teardown overhead.
13874 *
13875 *
13876 * @section sec_kWorker_kSubmit About kSubmit
13877 *
13878 * When wanting to execute a job in a kWorker instance, it must be submitted
13879 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13880 * built into kmk and does not exist as an external program. The reason for
13881 * this is that it keep track of the kWorker instances.
13882 *
13883 * The kSubmit command has the --32-bit and --64-bit options for selecting
13884 * between 32-bit and 64-bit worker instance. We generally assume the user of
13885 * the command knows which bit count the executable has, so kSubmit is spared
13886 * the extra work of finding out.
13887 *
13888 * The kSubmit command shares a environment and current directory manipulation
13889 * with the kRedirect command, but not the file redirection. So long no file
13890 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13891 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13892 * and/or current directory changes to work.
13893 *
13894 * Unlike the kRedirect command, the kSubmit command can also specify an
13895 * internall post command to be executed after the main command succeeds.
13896 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13897 * information from Microsoft COFF object files and Watcom OMF object files and
13898 * is scheduled to replace kDepIDB.
13899 *
13900 *
13901 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13902 *
13903 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13904 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13905 * it. When the job is completed, kWorker writes a short reply with the exit
13906 * code and an internal status indicating whether it is going to restart.
13907 *
13908 * The kWorker intance will reply to kSubmit before completing all the internal
13909 * cleanup work, so as not to delay the next job execution unnecessarily. This
13910 * includes checking its own memory consumption and checking whether it needs
13911 * restarting. So, a decision to restart unfortunately have to wait till after
13912 * the next job has completed. This is a little bit unfortunate if the next job
13913 * requires a lot of memory and kWorker has already leaked/used a lot.
13914 *
13915 *
13916 * @section sec_kWorker_How_Works How kWorker Works
13917 *
13918 * kWorker will load the executable specified by kSubmit into memory and call
13919 * it's entrypoint in a lightly sandbox'ed environment.
13920 *
13921 *
13922 * @subsection ssec_kWorker_Loaing Image loading
13923 *
13924 * kWorker will manually load all the executable images into memory, fix them
13925 * up, and make a copy of the virgin image so it can be restored using memcpy
13926 * the next time it is used.
13927 *
13928 * Imported functions are monitored and replacements used for a few of them.
13929 * These replacements are serve the following purposes:
13930 * - Provide a different command line.
13931 * - Provide a different environment.
13932 * - Intercept process termination.
13933 * - Intercept thread creation (only linker is allowed to create threads).
13934 * - Intercept file reading for caching (header files, ++) as file system
13935 * access is made even slower by anti-virus software.
13936 * - Intercept crypto hash APIs to cache MD5 digests of header files
13937 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13938 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13939 * in memory as writing files grows expensive with encryption and
13940 * anti-virus software active.
13941 * - Intercept some file system queries to use the kFsCache instead of
13942 * going to the kernel and slowly worm thru the AV filter driver.
13943 * - Intercept standard output/error and console writes to aggressivly
13944 * buffer the output. The MS CRT does not buffer either when it goes to
13945 * the console, resulting in terrible performance and mixing up output
13946 * with other compile jobs.
13947 * This also allows us to filter out the annoying source file announcements
13948 * by cl.exe.
13949 * - Intercept VirtualAlloc and VirtualFree to prevent
13950 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13951 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13952 * the callbacks run after each job.
13953 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13954 * executables and tools using custom heaps (like the microsoft linker).
13955 * [exectuable images only]
13956 * - Intercept atexit and _onexit registration to be able run them after
13957 * each job instead of crashing as kWorker exits. This also helps avoid
13958 * some leaks. [executable image only]
13959 *
13960 * DLLs falls into two categories, system DLLs which we always load using the
13961 * native loader, and tool DLLs which can be handled like the executable or
13962 * optionally using the native loader. We maintain a hardcoded white listing of
13963 * tool DLLs we trust to load using the native loader.
13964 *
13965 * Imports of natively loaded DLLs are processed too, but we only replace a
13966 * subset of the functions compared to natively loaded excutable and DLL images.
13967 *
13968 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13969 * This is to speed up job execution.
13970 *
13971 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13972 * for each job run, but so far this hasn't been necessary.
13973 *
13974 *
13975 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
13976 *
13977 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
13978 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
13979 * intermediate representation between the first (c1/c1xx.dll) and second pass
13980 * (c2.dll).
13981 *
13982 * kWorker helps the compiler as best as it can. Given a little knowledge about
13983 * stable and volatile file system areas, it can do a lot of caching that a
13984 * normal compiler driver cannot easily do when given a single file.
13985 *
13986 *
13987 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
13988 *
13989 * The preprocessor part will open and process header files exactly as they are
13990 * encountered in the source files. If string.h is included by the main source
13991 * and five other header files, it will be searched for (include path), opened,
13992 * read, MD5-summed, and pre-processed six times. The last five times is just a
13993 * waste of time because of the guards or \#pragma once. A smart compiler would
13994 * make a little extra effort and realize this.
13995 *
13996 * kWorker will cache help the preprocessor by remembering places where the
13997 * header was not found with help of kFsCache, and cache the file in memory when
13998 * found. The first part is taken care of by intercepting GetFileAttributesW,
13999 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
14000 * cached, the file is kept open and the CreateFileW call returns a duplicate of
14001 * that handle. An internal handle table is used by ReadFile and CloseFile to
14002 * keep track of intercepted handles (also used for temporary file, temporary
14003 * file mappings, console buffering, and standard out/err buffering).
14004 *
14005 * PS. The header search optimization also comes in handy when cl.exe goes on
14006 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
14007 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
14008 * optionally compile the three pass DLLs as executables during development
14009 * and problem analysis.
14010 *
14011 *
14012 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
14013 *
14014 * The issues of the temporary files is pretty severe on the Dell machine used
14015 * for benchmarking with full AV and encryption. The synthetic benchmark
14016 * improved by 30% when kWorker implemented measures to keep them entirely in
14017 * memory.
14018 *
14019 * kWorker implement these by recognizing the filename pattern in CreateFileW
14020 * and creating/opening the given file as needed. The handle returned is a
14021 * duplicate of the current process, thus giving us a good chance of catching
14022 * API calls we're not intercepting.
14023 *
14024 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
14025 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
14026 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
14027 * UnmapViewOfFile.
14028 *
14029 *
14030 * @section sec_kWorker_Numbers Some measurements.
14031 *
14032 * - r2881 building src/VBox/Runtime:
14033 * - without: 2m01.016388s = 120.016388 s
14034 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
14035 * - r2884 building vbox/debug (r110512):
14036 * - without: 11m14.446609s = 674.446609 s
14037 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
14038 * - r2896 building vbox/debug (r110577):
14039 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
14040 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
14041 * MS Defender as AV):
14042 * - without: 10m24.990389s = 624.990389s
14043 * - with: 08m04.738184s = 484.738184s
14044 * - delta: 624.99s - 484.74s = 140.25s
14045 * - saved: 140.25/624.99 = 22% faster
14046 *
14047 *
14048 * @subsection subsec_kWorker_Early_Numbers Early Experiments
14049 *
14050 * These are some early experiments doing 1024 compilations of
14051 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
14052 * main function:
14053 *
14054 * Skylake (W10/amd64, only stdandard MS defender):
14055 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
14056 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
14057 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
14058 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
14059 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
14060 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
14061 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
14062 *
14063 * Dell (W7/amd64, infected by mcafee):
14064 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
14065 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
14066 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
14067 *
14068 * The command line:
14069 * @code{.cpp}
14070 "\"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"
14071 * @endcode
14072 */
14073
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