VirtualBox

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

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

kWorker: More complete TLS handling. More TLS DLLs. Make handle table management thread safe. Lots more stack for new 201x compilers. ++

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 518.5 KB
Line 
1/* $Id: kWorker.c 3366 2020-06-09 23:53:39Z 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_MD5_CACHE
73 * Enables caching of MD5 sums for cl.exe.
74 * This prevents wasting time on rehashing common headers each time
75 * they are included. */
76#define WITH_HASH_MD5_CACHE
77
78/** @def WITH_CRYPT_CTX_REUSE
79 * Enables reusing crypt contexts. The Visual C++ compiler always creates a
80 * context which is only used for MD5 and maybe some random bytes (VS 2010).
81 * So, only create it once and add a reference to it instead of creating new
82 * ones. Saves registry access among other things. */
83#define WITH_CRYPT_CTX_REUSE
84
85/** @def WITH_CONSOLE_OUTPUT_BUFFERING
86 * Enables buffering of all console output as well as removal of annoying
87 * source file echo by cl.exe. */
88#define WITH_CONSOLE_OUTPUT_BUFFERING
89
90/** @def WITH_STD_OUT_ERR_BUFFERING
91 * Enables buffering of standard output and standard error buffer as well as
92 * removal of annoying source file echo by cl.exe. */
93#define WITH_STD_OUT_ERR_BUFFERING
94
95/** @def WITH_LOG_FILE
96 * Log to file instead of stderr. */
97#define WITH_LOG_FILE
98
99/** @def WITH_HISTORY
100 * Keep history of the last jobs. For debugging. */
101#define WITH_HISTORY
102
103/** @def WITH_FIXED_VIRTUAL_ALLOCS
104 * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
105 * there is only one, but an important one, from cl.exe).
106 */
107#if K_ARCH == K_ARCH_X86_32
108# define WITH_FIXED_VIRTUAL_ALLOCS
109#endif
110
111/** @def WITH_PCH_CACHING
112 * Enables read caching of precompiled header files. */
113#if K_ARCH_BITS >= 64
114# define WITH_PCH_CACHING
115#endif
116
117
118#ifndef NDEBUG
119# define KW_LOG_ENABLED
120#endif
121
122/** @def KW_LOG
123 * Generic logging.
124 * @param a Argument list for kwDbgPrintf */
125#ifdef KW_LOG_ENABLED
126# define KW_LOG(a) kwDbgPrintf a
127#else
128# define KW_LOG(a) do { } while (0)
129#endif
130
131/** @def KWLDR_LOG
132 * Loader related logging.
133 * @param a Argument list for kwDbgPrintf */
134#ifdef KW_LOG_ENABLED
135# define KWLDR_LOG(a) kwDbgPrintf a
136#else
137# define KWLDR_LOG(a) do { } while (0)
138#endif
139
140
141/** @def KWFS_LOG
142 * FS cache logging.
143 * @param a Argument list for kwDbgPrintf */
144#ifdef KW_LOG_ENABLED
145# define KWFS_LOG(a) kwDbgPrintf a
146#else
147# define KWFS_LOG(a) do { } while (0)
148#endif
149
150/** @def KWOUT_LOG
151 * Output related logging.
152 * @param a Argument list for kwDbgPrintf */
153#ifdef KW_LOG_ENABLED
154# define KWOUT_LOG(a) kwDbgPrintf a
155#else
156# define KWOUT_LOG(a) do { } while (0)
157#endif
158
159/** @def KWCRYPT_LOG
160 * FS cache logging.
161 * @param a Argument list for kwDbgPrintf */
162#ifdef KW_LOG_ENABLED
163# define KWCRYPT_LOG(a) kwDbgPrintf a
164#else
165# define KWCRYPT_LOG(a) do { } while (0)
166#endif
167
168/** Converts a windows handle to a handle table index.
169 * @note We currently just mask off the 31th bit, and do no shifting or anything
170 * else to create an index of the handle.
171 * @todo consider shifting by 2 or 3. */
172#define KW_HANDLE_TO_INDEX(a_hHandle) ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
173/** Maximum handle value we can deal with. */
174#define KW_HANDLE_MAX 0x20000
175
176/** Max temporary file size (memory backed). */
177#if K_ARCH_BITS >= 64
178# define KWFS_TEMP_FILE_MAX (256*1024*1024)
179#else
180# define KWFS_TEMP_FILE_MAX (64*1024*1024)
181#endif
182
183/** Marks unfinished code. */
184#if 1
185# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
186#else
187# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
188#endif
189
190/** User data key for tools. */
191#define KW_DATA_KEY_TOOL (~(KUPTR)16381)
192/** User data key for a cached file. */
193#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
194
195/** String constant comma length. */
196#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
197
198
199/**
200 * Generate CRT slot wrapper functions.
201 */
202#define CRT_SLOT_FUNCTION_WRAPPER(a_RetTypeAndCallConv, a_FnName, a_aArgsDecl, a_aArgCall) \
203 static a_RetTypeAndCallConv a_FnName##00 a_aArgsDecl { const unsigned iCrtSlot = 0; return a_FnName##_wrapped a_aArgCall; } \
204 static a_RetTypeAndCallConv a_FnName##01 a_aArgsDecl { const unsigned iCrtSlot = 1; return a_FnName##_wrapped a_aArgCall; } \
205 static a_RetTypeAndCallConv a_FnName##02 a_aArgsDecl { const unsigned iCrtSlot = 2; return a_FnName##_wrapped a_aArgCall; } \
206 static a_RetTypeAndCallConv a_FnName##03 a_aArgsDecl { const unsigned iCrtSlot = 3; return a_FnName##_wrapped a_aArgCall; } \
207 static a_RetTypeAndCallConv a_FnName##04 a_aArgsDecl { const unsigned iCrtSlot = 4; return a_FnName##_wrapped a_aArgCall; } \
208 static a_RetTypeAndCallConv a_FnName##05 a_aArgsDecl { const unsigned iCrtSlot = 5; return a_FnName##_wrapped a_aArgCall; } \
209 static a_RetTypeAndCallConv a_FnName##06 a_aArgsDecl { const unsigned iCrtSlot = 6; return a_FnName##_wrapped a_aArgCall; } \
210 static a_RetTypeAndCallConv a_FnName##07 a_aArgsDecl { const unsigned iCrtSlot = 7; return a_FnName##_wrapped a_aArgCall; } \
211 static a_RetTypeAndCallConv a_FnName##08 a_aArgsDecl { const unsigned iCrtSlot = 8; return a_FnName##_wrapped a_aArgCall; } \
212 static a_RetTypeAndCallConv a_FnName##09 a_aArgsDecl { const unsigned iCrtSlot = 9; return a_FnName##_wrapped a_aArgCall; } \
213 static a_RetTypeAndCallConv a_FnName##10 a_aArgsDecl { const unsigned iCrtSlot = 10; return a_FnName##_wrapped a_aArgCall; } \
214 static a_RetTypeAndCallConv a_FnName##11 a_aArgsDecl { const unsigned iCrtSlot = 11; return a_FnName##_wrapped a_aArgCall; } \
215 static a_RetTypeAndCallConv a_FnName##12 a_aArgsDecl { const unsigned iCrtSlot = 12; return a_FnName##_wrapped a_aArgCall; } \
216 static a_RetTypeAndCallConv a_FnName##13 a_aArgsDecl { const unsigned iCrtSlot = 13; return a_FnName##_wrapped a_aArgCall; } \
217 static a_RetTypeAndCallConv a_FnName##14 a_aArgsDecl { const unsigned iCrtSlot = 14; return a_FnName##_wrapped a_aArgCall; } \
218 static a_RetTypeAndCallConv a_FnName##15 a_aArgsDecl { const unsigned iCrtSlot = 15; return a_FnName##_wrapped a_aArgCall; } \
219 static a_RetTypeAndCallConv a_FnName##16 a_aArgsDecl { const unsigned iCrtSlot = 16; return a_FnName##_wrapped a_aArgCall; } \
220 static a_RetTypeAndCallConv a_FnName##17 a_aArgsDecl { const unsigned iCrtSlot = 17; return a_FnName##_wrapped a_aArgCall; } \
221 static a_RetTypeAndCallConv a_FnName##18 a_aArgsDecl { const unsigned iCrtSlot = 18; return a_FnName##_wrapped a_aArgCall; } \
222 static a_RetTypeAndCallConv a_FnName##19 a_aArgsDecl { const unsigned iCrtSlot = 19; return a_FnName##_wrapped a_aArgCall; } \
223 static a_RetTypeAndCallConv a_FnName##20 a_aArgsDecl { const unsigned iCrtSlot = 20; return a_FnName##_wrapped a_aArgCall; } \
224 static a_RetTypeAndCallConv a_FnName##21 a_aArgsDecl { const unsigned iCrtSlot = 21; return a_FnName##_wrapped a_aArgCall; } \
225 static a_RetTypeAndCallConv a_FnName##22 a_aArgsDecl { const unsigned iCrtSlot = 22; return a_FnName##_wrapped a_aArgCall; } \
226 static a_RetTypeAndCallConv a_FnName##23 a_aArgsDecl { const unsigned iCrtSlot = 23; return a_FnName##_wrapped a_aArgCall; } \
227 static a_RetTypeAndCallConv a_FnName##24 a_aArgsDecl { const unsigned iCrtSlot = 24; return a_FnName##_wrapped a_aArgCall; } \
228 static a_RetTypeAndCallConv a_FnName##25 a_aArgsDecl { const unsigned iCrtSlot = 25; return a_FnName##_wrapped a_aArgCall; } \
229 static a_RetTypeAndCallConv a_FnName##26 a_aArgsDecl { const unsigned iCrtSlot = 26; return a_FnName##_wrapped a_aArgCall; } \
230 static a_RetTypeAndCallConv a_FnName##27 a_aArgsDecl { const unsigned iCrtSlot = 27; return a_FnName##_wrapped a_aArgCall; } \
231 static a_RetTypeAndCallConv a_FnName##28 a_aArgsDecl { const unsigned iCrtSlot = 28; return a_FnName##_wrapped a_aArgCall; } \
232 static a_RetTypeAndCallConv a_FnName##29 a_aArgsDecl { const unsigned iCrtSlot = 29; return a_FnName##_wrapped a_aArgCall; } \
233 static a_RetTypeAndCallConv a_FnName##30 a_aArgsDecl { const unsigned iCrtSlot = 30; return a_FnName##_wrapped a_aArgCall; } \
234 static a_RetTypeAndCallConv a_FnName##31 a_aArgsDecl { const unsigned iCrtSlot = 31; return a_FnName##_wrapped a_aArgCall; } \
235 static const KUPTR a_FnName[] = \
236 { \
237 (KUPTR)a_FnName##00, \
238 (KUPTR)a_FnName##01, \
239 (KUPTR)a_FnName##02, \
240 (KUPTR)a_FnName##03, \
241 (KUPTR)a_FnName##04, \
242 (KUPTR)a_FnName##05, \
243 (KUPTR)a_FnName##06, \
244 (KUPTR)a_FnName##07, \
245 (KUPTR)a_FnName##08, \
246 (KUPTR)a_FnName##09, \
247 (KUPTR)a_FnName##10, \
248 (KUPTR)a_FnName##11, \
249 (KUPTR)a_FnName##12, \
250 (KUPTR)a_FnName##13, \
251 (KUPTR)a_FnName##14, \
252 (KUPTR)a_FnName##15, \
253 (KUPTR)a_FnName##16, \
254 (KUPTR)a_FnName##17, \
255 (KUPTR)a_FnName##18, \
256 (KUPTR)a_FnName##19, \
257 (KUPTR)a_FnName##20, \
258 (KUPTR)a_FnName##21, \
259 (KUPTR)a_FnName##22, \
260 (KUPTR)a_FnName##23, \
261 (KUPTR)a_FnName##24, \
262 (KUPTR)a_FnName##25, \
263 (KUPTR)a_FnName##26, \
264 (KUPTR)a_FnName##27, \
265 (KUPTR)a_FnName##28, \
266 (KUPTR)a_FnName##29, \
267 (KUPTR)a_FnName##30, \
268 (KUPTR)a_FnName##31, \
269 }
270
271
272/*********************************************************************************************************************************
273* Structures and Typedefs *
274*********************************************************************************************************************************/
275typedef enum KWLOCATION
276{
277 KWLOCATION_INVALID = 0,
278 KWLOCATION_EXE_DIR,
279 KWLOCATION_IMPORTER_DIR,
280 KWLOCATION_SYSTEM32,
281 KWLOCATION_UNKNOWN_NATIVE,
282 KWLOCATION_UNKNOWN,
283} KWLOCATION;
284
285typedef enum KWMODSTATE
286{
287 KWMODSTATE_INVALID = 0,
288 KWMODSTATE_NEEDS_BITS,
289 KWMODSTATE_NEEDS_INIT,
290 KWMODSTATE_BEING_INITED,
291 KWMODSTATE_INIT_FAILED,
292 KWMODSTATE_READY,
293} KWMODSTATE;
294
295typedef struct KWMODULE *PKWMODULE;
296typedef struct KWMODULE
297{
298 /** Pointer to the next image withe the same hash. */
299 PKWMODULE pNextHash;
300 /** Pointer to the next image in the global list. */
301 PKWMODULE pNextList;
302 /** The normalized path to the image. */
303 const char *pszPath;
304 /** The hash of the program path. */
305 KU32 uHashPath;
306 /** Number of references. */
307 KU32 cRefs;
308 /** UTF-16 version of pszPath. */
309 const wchar_t *pwszPath;
310 /** The offset of the filename in pszPath. */
311 KU16 offFilename;
312 /** The offset of the filename in pwszPath. */
313 KU16 offFilenameW;
314 /** Set if executable. */
315 KBOOL fExe;
316 /** Set if native module entry. */
317 KBOOL fNative;
318 /** Loader module handle. */
319 PKLDRMOD pLdrMod;
320 /** The windows module handle. */
321 HMODULE hOurMod;
322 /** Parent (real) module if this is a virtual API module (api-ms-*.dll or
323 * ext-ms-*.dll). Referenced. */
324 PKWMODULE pVirtualApiMod;
325 /** The of the loaded image bits. */
326 KSIZE cbImage;
327 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
328 KU8 iCrtSlot;
329 /** Loop prevention when working the tree. */
330 KBOOL fVisited;
331 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
332 KBOOL fNeedReInit;
333 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
334 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
335 KU8 fReInitOnMsPdbSrvEndpointChange;
336 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
337 char *pszMsPdbSrvEndpoint;
338
339 union
340 {
341 /** Data for a manually loaded image. */
342 struct
343 {
344 /** Where we load the image. */
345 KU8 *pbLoad;
346 /** Virgin copy of the image. */
347 KU8 *pbCopy;
348 /** Ldr pvBits argument. This is NULL till we've successfully resolved
349 * the imports. */
350 void *pvBits;
351 /** The state. */
352 KWMODSTATE enmState;
353 /** The re-init state. */
354 KWMODSTATE enmReInitState;
355#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
356 /** The number of entries in the table. */
357 KU32 cFunctions;
358 /** The function table address (in the copy). */
359 PRUNTIME_FUNCTION paFunctions;
360 /** Set if we've already registered a function table already. */
361 KBOOL fRegisteredFunctionTable;
362#endif
363 /** Set if we share memory with other executables. */
364 KBOOL fUseLdBuf;
365 /** Set after the first whole image copy is done. */
366 KBOOL fCanDoQuick;
367 /** Number of quick copy chunks. */
368 KU8 cQuickCopyChunks;
369 /** Number of quick zero chunks. */
370 KU8 cQuickZeroChunks;
371 /** Quicker image copy instructions that skips non-writable parts when
372 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
373 * image. */
374 struct
375 {
376 /** The copy destination. */
377 KU8 *pbDst;
378 /** The copy source. */
379 KU8 const *pbSrc;
380 /** How much to copy. */
381 KSIZE cbToCopy;
382 } aQuickCopyChunks[3];
383 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
384 struct
385 {
386 /** Where to start zeroing. */
387 KU8 *pbDst;
388 /** How much to zero. */
389 KSIZE cbToZero;
390 } aQuickZeroChunks[3];
391
392 /** Pointer to g_abInitData of the kWorkerTlsXxxK.c instance.
393 * This member is set by kwLdrTlsAllocationHook. */
394 KU8 *pabTlsInitData;
395 /** Pointer to the g_pvWorkerModule variable in kWorkerTlsXxxK.c (our instance
396 * of it). This member is set by kwLdrTlsAllocationHook. Used by our
397 * destructor to prevent after-free references. */
398 PKWMODULE *ppTlsWorkerModuleVar;
399 /** TLS index if one was allocated, otherwise KU32_MAX.
400 * This member is set by kwLdrTlsAllocationHook. */
401 KU32 idxTls;
402 /** Offset (RVA) of the TLS initialization data. */
403 KU32 offTlsInitData;
404 /** Number of bytes of TLS initialization data. */
405 KU32 cbTlsInitData;
406 /** Number of allocated bytes for TLS. */
407 KU32 cbTlsAlloc;
408 /** Number of TLS callbacks. */
409 KU32 cTlsCallbacks;
410 /** Offset (RVA) of the TLS callback table. */
411 KU32 offTlsCallbacks;
412
413 /** Number of imported modules. */
414 KSIZE cImpMods;
415 /** Import array (variable size). */
416 PKWMODULE apImpMods[1];
417 } Manual;
418 } u;
419} KWMODULE;
420
421
422typedef struct KWDYNLOAD *PKWDYNLOAD;
423typedef struct KWDYNLOAD
424{
425 /** Pointer to the next in the list. */
426 PKWDYNLOAD pNext;
427
428 /** The module handle we present to the application.
429 * This is the LoadLibraryEx return value for special modules and the
430 * KWMODULE.hOurMod value for the others. */
431 HMODULE hmod;
432
433 /** The module for non-special resource stuff, NULL if special. */
434 PKWMODULE pMod;
435
436 /** The length of the LoadLibary filename. */
437 KSIZE cchRequest;
438 /** The LoadLibrary filename. */
439 char szRequest[1];
440} KWDYNLOAD;
441
442
443/**
444 * GetModuleHandle cache for system modules frequently queried.
445 */
446typedef struct KWGETMODULEHANDLECACHE
447{
448 const char *pszName;
449 const wchar_t *pwszName;
450 KU8 cchName;
451 KU8 cwcName;
452 KBOOL fAlwaysPresent;
453 HANDLE hmod;
454} KWGETMODULEHANDLECACHE;
455typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
456
457
458/** One TLS DLL. */
459typedef struct KWTLSDLL
460{
461 const wchar_t *pwszName; /**< The DLL name. */
462 KBOOL fUsed; /**< Set if used, clear if not. */
463} KWTLSDLL;
464typedef KWTLSDLL *PKWTLSDLL;
465
466/**
467 * TLS DLL tracker.
468 */
469typedef struct KWTLSDLLENTRY
470{
471 KU32 cbTls; /**< Max TLS size. */
472 KU32 cDlls; /**< Number of DLLs we ship (paDlls). */
473 PKWTLSDLL paDlls; /**< Array of DLLs we ship. */
474} KWTLSDLLENTRY;
475typedef KWTLSDLLENTRY *PKWTLSDLLENTRY;
476
477
478/**
479 * A cached file.
480 */
481typedef struct KFSWCACHEDFILE
482{
483 /** The user data core. */
484 KFSUSERDATA Core;
485
486 /** Cached file handle. */
487 HANDLE hCached;
488 /** Cached file section handle. */
489 HANDLE hSection;
490 /** Cached file content. */
491 KU8 *pbCached;
492 /** The file size. */
493 KU32 cbCached;
494#ifdef WITH_HASH_MD5_CACHE
495 /** Set if we've got a valid MD5 hash in abMd5Digest. */
496 KBOOL fValidMd5;
497 /** The MD5 digest if fValidMd5 is set. */
498 KU8 abMd5Digest[16];
499#endif
500
501 /** Circular self reference. Prevents the object from ever going away and
502 * keeps it handy for debugging. */
503 PKFSOBJ pFsObj;
504 /** The file path (for debugging). */
505 char szPath[1];
506} KFSWCACHEDFILE;
507/** Pointer to a cached filed. */
508typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
509
510#ifdef WITH_HASH_MD5_CACHE
511
512/** Pointer to a MD5 hash instance. */
513typedef struct KWHASHMD5 *PKWHASHMD5;
514/**
515 * A MD5 hash instance.
516 */
517typedef struct KWHASHMD5
518{
519 /** The magic value. */
520 KUPTR uMagic;
521 /** Pointer to the next hash handle. */
522 PKWHASHMD5 pNext;
523 /** The cached file we've associated this handle with. */
524 PKFSWCACHEDFILE pCachedFile;
525 /** The number of bytes we've hashed. */
526 KU32 cbHashed;
527 /** Set if this has gone wrong. */
528 KBOOL fGoneBad;
529 /** Set if we're in fallback mode (file not cached). */
530 KBOOL fFallbackMode;
531 /** Set if we've already finalized the digest. */
532 KBOOL fFinal;
533 /** The MD5 fallback context. */
534 struct MD5Context Md5Ctx;
535 /** The finalized digest. */
536 KU8 abDigest[16];
537
538} KWHASHMD5;
539/** Magic value for KWHASHMD5::uMagic (Les McCann). */
540# define KWHASHMD5_MAGIC KUPTR_C(0x19350923)
541
542#endif /* WITH_HASH_MD5_CACHE */
543#ifdef WITH_TEMP_MEMORY_FILES
544
545typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
546typedef struct KWFSTEMPFILESEG
547{
548 /** File offset of data. */
549 KU32 offData;
550 /** The size of the buffer pbData points to. */
551 KU32 cbDataAlloc;
552 /** The segment data. */
553 KU8 *pbData;
554} KWFSTEMPFILESEG;
555
556typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
557typedef struct KWFSTEMPFILE
558{
559 /** Pointer to the next temporary file for this run. */
560 PKWFSTEMPFILE pNext;
561 /** The UTF-16 path. (Allocated after this structure.) */
562 const wchar_t *pwszPath;
563 /** The path length. */
564 KU16 cwcPath;
565 /** Number of active handles using this file/mapping (<= 2). */
566 KU8 cActiveHandles;
567 /** Number of active mappings (mapped views) (0 or 1). */
568 KU8 cMappings;
569 /** The amount of space allocated in the segments. */
570 KU32 cbFileAllocated;
571 /** The current file size. */
572 KU32 cbFile;
573 /** The number of segments. */
574 KU32 cSegs;
575 /** Segments making up the file. */
576 PKWFSTEMPFILESEG paSegs;
577} KWFSTEMPFILE;
578
579#endif /* WITH_TEMP_MEMORY_FILES */
580#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
581
582/**
583 * Console line buffer or output full buffer.
584 */
585typedef struct KWOUTPUTSTREAMBUF
586{
587 /** The main output handle. */
588 HANDLE hOutput;
589 /** Our backup handle. */
590 HANDLE hBackup;
591 /** Set if this is a console handle and we're in line buffered mode.
592 * When clear, we may buffer multiple lines, though try flush on line
593 * boundraries when ever possible. */
594 KBOOL fIsConsole;
595 /** Compressed GetFileType result. */
596 KU8 fFileType;
597 KU8 abPadding[2];
598 union
599 {
600 /** Line buffer mode (fIsConsole == K_TRUE). */
601 struct
602 {
603 /** Amount of pending console output in wchar_t's. */
604 KU32 cwcBuf;
605 /** The allocated buffer size. */
606 KU32 cwcBufAlloc;
607 /** Pending console output. */
608 wchar_t *pwcBuf;
609 } Con;
610 /** Fully buffered mode (fIsConsole == K_FALSE). */
611 struct
612 {
613 /** Amount of pending output (in chars). */
614 KU32 cchBuf;
615#ifdef WITH_STD_OUT_ERR_BUFFERING
616 /** The allocated buffer size (in chars). */
617 KU32 cchBufAlloc;
618 /** Pending output. */
619 char *pchBuf;
620#endif
621 } Fully;
622 } u;
623} KWOUTPUTSTREAMBUF;
624/** Pointer to a console line buffer. */
625typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
626
627/**
628 * Combined console buffer of complete lines.
629 */
630typedef struct KWCONSOLEOUTPUT
631{
632 /** The console output handle.
633 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
634 * combined output buffering. */
635 HANDLE hOutput;
636 /** The current code page for the console. */
637 KU32 uCodepage;
638 /** Amount of pending console output in wchar_t's. */
639 KU32 cwcBuf;
640 /** Number of times we've flushed it in any way (for cl.exe hack). */
641 KU32 cFlushes;
642 /** Pending console output. */
643 wchar_t wszBuf[8192];
644} KWCONSOLEOUTPUT;
645/** Pointer to a combined console buffer. */
646typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
647
648#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
649
650/** Handle type. */
651typedef enum KWHANDLETYPE
652{
653 KWHANDLETYPE_INVALID = 0,
654 KWHANDLETYPE_FSOBJ_READ_CACHE,
655 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
656#ifdef WITH_TEMP_MEMORY_FILES
657 KWHANDLETYPE_TEMP_FILE,
658 KWHANDLETYPE_TEMP_FILE_MAPPING,
659#endif
660 KWHANDLETYPE_OUTPUT_BUF
661} KWHANDLETYPE;
662
663/** Handle data. */
664typedef struct KWHANDLE
665{
666 KWHANDLETYPE enmType;
667 /** Number of references */
668 KU32 cRefs;
669 /** The current file offset. */
670 KU32 offFile;
671 /** Handle access. */
672 KU32 dwDesiredAccess;
673 /** The handle. */
674 HANDLE hHandle;
675 /** The current owner (GetCurrentThreadId). */
676 KU32 tidOwner;
677
678 /** Type specific data. */
679 union
680 {
681 /** The file system object. */
682 PKFSWCACHEDFILE pCachedFile;
683#ifdef WITH_TEMP_MEMORY_FILES
684 /** Temporary file handle or mapping handle. */
685 PKWFSTEMPFILE pTempFile;
686#endif
687#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
688 /** Buffered output stream. */
689 PKWOUTPUTSTREAMBUF pOutBuf;
690#endif
691 } u;
692} KWHANDLE;
693typedef KWHANDLE *PKWHANDLE;
694
695/**
696 * Tracking one of our memory mappings.
697 */
698typedef struct KWMEMMAPPING
699{
700 /** Number of references. */
701 KU32 cRefs;
702 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
703 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
704 KWHANDLETYPE enmType;
705 /** The mapping address. */
706 PVOID pvMapping;
707 /** Type specific data. */
708 union
709 {
710 /** The file system object. */
711 PKFSWCACHEDFILE pCachedFile;
712#ifdef WITH_TEMP_MEMORY_FILES
713 /** Temporary file handle or mapping handle. */
714 PKWFSTEMPFILE pTempFile;
715#endif
716 } u;
717} KWMEMMAPPING;
718/** Pointer to a memory mapping tracker. */
719typedef KWMEMMAPPING *PKWMEMMAPPING;
720
721
722/** Pointer to a VirtualAlloc tracker entry. */
723typedef struct KWVIRTALLOC *PKWVIRTALLOC;
724/**
725 * Tracking an VirtualAlloc allocation.
726 */
727typedef struct KWVIRTALLOC
728{
729 PKWVIRTALLOC pNext;
730 void *pvAlloc;
731 KSIZE cbAlloc;
732 /** This is KU32_MAX if not a preallocated chunk. */
733 KU32 idxPreAllocated;
734} KWVIRTALLOC;
735
736
737/** Pointer to a heap (HeapCreate) tracker entry. */
738typedef struct KWHEAP *PKWHEAP;
739/**
740 * Tracking an heap (HeapCreate)
741 */
742typedef struct KWHEAP
743{
744 PKWHEAP pNext;
745 HANDLE hHeap;
746} KWHEAP;
747
748
749/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
750typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
751/**
752 * Tracking an FlsAlloc/TlsAlloc index.
753 */
754typedef struct KWLOCALSTORAGE
755{
756 PKWLOCALSTORAGE pNext;
757 KU32 idx;
758} KWLOCALSTORAGE;
759
760
761/** Pointer to an at exit callback record */
762typedef struct KWEXITCALLACK *PKWEXITCALLACK;
763/**
764 * At exit callback record.
765 */
766typedef struct KWEXITCALLACK
767{
768 PKWEXITCALLACK pNext;
769 _onexit_t pfnCallback;
770 /** At exit doesn't have an exit code. */
771 KBOOL fAtExit;
772} KWEXITCALLACK;
773
774
775typedef enum KWTOOLTYPE
776{
777 KWTOOLTYPE_INVALID = 0,
778 KWTOOLTYPE_SANDBOXED,
779 KWTOOLTYPE_WATCOM,
780 KWTOOLTYPE_EXEC,
781 KWTOOLTYPE_END
782} KWTOOLTYPE;
783
784typedef enum KWTOOLHINT
785{
786 KWTOOLHINT_INVALID = 0,
787 KWTOOLHINT_NONE,
788 KWTOOLHINT_VISUAL_CPP_CL,
789 KWTOOLHINT_VISUAL_CPP_LINK,
790 KWTOOLHINT_END
791} KWTOOLHINT;
792
793
794/**
795 * A kWorker tool.
796 */
797typedef struct KWTOOL
798{
799 /** The user data core structure. */
800 KFSUSERDATA Core;
801
802 /** The normalized path to the program. */
803 const char *pszPath;
804 /** UTF-16 version of pszPath. */
805 wchar_t const *pwszPath;
806 /** The kind of tool. */
807 KWTOOLTYPE enmType;
808
809 union
810 {
811 struct
812 {
813 /** The main entry point. */
814 KUPTR uMainAddr;
815 /** The executable. */
816 PKWMODULE pExe;
817 /** List of dynamically loaded modules.
818 * These will be kept loaded till the tool is destroyed (if we ever do that). */
819 PKWDYNLOAD pDynLoadHead;
820 /** Module array sorted by hOurMod. */
821 PKWMODULE *papModules;
822 /** Number of entries in papModules. */
823 KU32 cModules;
824
825 /** Tool hint (for hacks and such). */
826 KWTOOLHINT enmHint;
827 } Sandboxed;
828 } u;
829} KWTOOL;
830/** Pointer to a tool. */
831typedef struct KWTOOL *PKWTOOL;
832
833
834typedef struct KWSANDBOX *PKWSANDBOX;
835typedef struct KWSANDBOX
836{
837 /** Jump buffer (first for alignment reasons). */
838 jmp_buf JmpBuf;
839 /** The tool currently running in the sandbox. */
840 PKWTOOL pTool;
841 /** The thread ID of the main thread (owner of JmpBuf). */
842 DWORD idMainThread;
843 /** Copy of the NT TIB of the main thread. */
844 NT_TIB TibMainThread;
845 /** The NT_TIB::ExceptionList value inside the try case.
846 * We restore this prior to the longjmp. */
847 void *pOutXcptListHead;
848 /** The exit code in case of longjmp. */
849 int rcExitCode;
850 /** Set if we're running. */
851 KBOOL fRunning;
852 /** Whether to disable caching of ".pch" files. */
853 KBOOL fNoPchCaching;
854
855 /** The command line. */
856 char *pszCmdLine;
857 /** The UTF-16 command line. */
858 wchar_t *pwszCmdLine;
859 /** Number of arguments in papszArgs. */
860 int cArgs;
861 /** The argument vector. */
862 char **papszArgs;
863 /** The argument vector. */
864 wchar_t **papwszArgs;
865
866 /** The _pgmptr msvcrt variable. */
867 char *pgmptr;
868 /** The _wpgmptr msvcrt variable. */
869 wchar_t *wpgmptr;
870
871 /** The _initenv msvcrt variable. */
872 char **initenv;
873 /** The _winitenv msvcrt variable. */
874 wchar_t **winitenv;
875
876 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
877 KSIZE cEnvVarsAllocated;
878 /** The _environ msvcrt variable. */
879 char **environ;
880 /** The _wenviron msvcrt variable. */
881 wchar_t **wenviron;
882 /** The shadow _environ msvcrt variable. */
883 char **papszEnvVars;
884 /** The shadow _wenviron msvcrt variable. */
885 wchar_t **papwszEnvVars;
886
887
888 /** Critical section protecting the below handle members below.
889 * @note Does not protect the individual handles. */
890 CRITICAL_SECTION HandlesLock;
891 /** Handle table. */
892 PKWHANDLE *papHandles;
893 /** Size of the handle table. */
894 KU32 cHandles;
895 /** Number of active handles in the table. */
896 KU32 cActiveHandles;
897 /** Number of handles in the handle table that will not be freed. */
898 KU32 cFixedHandles;
899 /** Total number of leaked handles. */
900 KU32 cLeakedHandles;
901
902 /** Number of active memory mappings in paMemMappings. */
903 KU32 cMemMappings;
904 /** The allocated size of paMemMappings. */
905 KU32 cMemMappingsAlloc;
906 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
907 PKWMEMMAPPING paMemMappings;
908
909#ifdef WITH_TEMP_MEMORY_FILES
910 /** Head of the list of temporary file. */
911 PKWFSTEMPFILE pTempFileHead;
912#endif
913
914 /** Critical section protecting pVirtualAllocHead. */
915 CRITICAL_SECTION VirtualAllocLock;
916 /** Head of the virtual alloc allocations. */
917 PKWVIRTALLOC pVirtualAllocHead;
918 /** Head of the heap list (HeapCreate).
919 * This is only done from images we forcibly restore. */
920 PKWHEAP pHeapHead;
921 /** Head of the FlsAlloc indexes. */
922 PKWLOCALSTORAGE pFlsAllocHead;
923 /** Head of the TlsAlloc indexes. */
924 PKWLOCALSTORAGE pTlsAllocHead;
925
926 /** The at exit callback head.
927 * This is only done from images we forcibly restore. */
928 PKWEXITCALLACK pExitCallbackHead;
929
930 MY_UNICODE_STRING SavedCommandLine;
931
932#ifdef WITH_HASH_MD5_CACHE
933 /** The special MD5 hash instance. */
934 PKWHASHMD5 pHashHead;
935 /** ReadFile sets these while CryptHashData claims and clears them.
936 *
937 * This is part of the heuristics we use for MD5 caching for header files. The
938 * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
939 * header, then passes the same buffer and read byte count to CryptHashData.
940 */
941 struct
942 {
943 /** The cached file last read from. */
944 PKFSWCACHEDFILE pCachedFile;
945 /** The file offset of the last cached read. */
946 KU32 offRead;
947 /** The number of bytes read last. */
948 KU32 cbRead;
949 /** The buffer pointer of the last read. */
950 void *pvRead;
951 } LastHashRead;
952#endif
953
954#ifdef WITH_CRYPT_CTX_REUSE
955 /** Reusable crypt contexts. */
956 struct
957 {
958 /** The creation provider type. */
959 KU32 dwProvType;
960 /** The creation flags. */
961 KU32 dwFlags;
962 /** The length of the container name. */
963 KU32 cwcContainer;
964 /** The length of the provider name. */
965 KU32 cwcProvider;
966 /** The container name string. */
967 wchar_t *pwszContainer;
968 /** The provider name string. */
969 wchar_t *pwszProvider;
970 /** The context handle. */
971 HCRYPTPROV hProv;
972 } aCryptCtxs[4];
973 /** Number of reusable crypt conexts in aCryptCtxs. */
974 KU32 cCryptCtxs;
975#endif
976
977
978#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
979 /** The internal standard output handle. */
980 KWHANDLE HandleStdOut;
981 /** The internal standard error handle. */
982 KWHANDLE HandleStdErr;
983 /** Standard output (and whatever else) buffer. */
984 KWOUTPUTSTREAMBUF StdOut;
985 /** Standard error buffer. */
986 KWOUTPUTSTREAMBUF StdErr;
987 /** Combined buffer of completed lines. */
988 KWCONSOLEOUTPUT Combined;
989#endif
990} KWSANDBOX;
991
992
993/** A CRT slot. */
994typedef struct KWCRTSLOT
995{
996 KU32 iSlot;
997
998 /** The CRT module data. */
999 PKWMODULE pModule;
1000 /** Pointer to the malloc function. */
1001 void * (__cdecl *pfnMalloc)(size_t);
1002 /** Pointer to the beginthreadex function. */
1003 uintptr_t (__cdecl *pfnBeginThreadEx)(void *, unsigned, unsigned (__stdcall *)(void *), void *, unsigned, unsigned *);
1004
1005} KWCRTSLOT;
1006typedef KWCRTSLOT *PKWCRTSLOT;
1007
1008
1009/** Replacement function entry. */
1010typedef struct KWREPLACEMENTFUNCTION
1011{
1012 /** The function name. */
1013 const char *pszFunction;
1014 /** The length of the function name. */
1015 KSIZE cchFunction;
1016 /** The module name (optional). */
1017 const char *pszModule;
1018 /** The replacement function, data address or CRT slot function array. */
1019 KUPTR pfnReplacement;
1020 /** Only replace in the executable.
1021 * @todo fix the reinitialization of non-native DLLs! */
1022 KBOOL fOnlyExe;
1023 /** Set if pfnReplacement points to a CRT slot function array. */
1024 KBOOL fCrtSlotArray;
1025} KWREPLACEMENTFUNCTION;
1026typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
1027
1028#if 0
1029/** Replacement function entry. */
1030typedef struct KWREPLACEMENTDATA
1031{
1032 /** The function name. */
1033 const char *pszFunction;
1034 /** The length of the function name. */
1035 KSIZE cchFunction;
1036 /** The module name (optional). */
1037 const char *pszModule;
1038 /** Function providing the replacement. */
1039 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
1040} KWREPLACEMENTDATA;
1041typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
1042#endif
1043
1044/**
1045 * One test job (--full-test).
1046 */
1047typedef struct KWONETEST
1048{
1049 /** Where this job originated. */
1050 const char *pszJobSrc;
1051 /** The argument number it started with. */
1052 unsigned iJobSrc;
1053 /** Set if virgin, clear if modified. */
1054 KBOOL fVirgin;
1055
1056 /** Number of runs to give it. */
1057 unsigned cRuns;
1058
1059 /** @name kSubmitHandleJobUnpacked arguments
1060 * @{ */
1061 const char *pszExecutable;
1062 const char *pszCwd;
1063 KU32 cArgs;
1064 const char **papszArgs;
1065 KU32 cEnvVars;
1066 const char **papszEnvVars;
1067 const char *pszSpecialEnv;
1068 KBOOL fWatcomBrainDamange;
1069 KBOOL fNoPchCaching;
1070 KU32 cPostCmdArgs;
1071 const char **papszPostCmdArgs;
1072 /** @} */
1073
1074 /** Pointer to the next one. */
1075 struct KWONETEST *pNext;
1076} KWONETEST;
1077/** Pointer to one test job. */
1078typedef KWONETEST *PKWONETEST;
1079
1080
1081/*********************************************************************************************************************************
1082* Global Variables *
1083*********************************************************************************************************************************/
1084/** The sandbox data. */
1085static KWSANDBOX g_Sandbox;
1086
1087/** The module currently occupying g_abDefLdBuf. */
1088static PKWMODULE g_pModInLdBuf = NULL;
1089
1090/** The module that previuosly occupied g_abDefLdBuf. */
1091static PKWMODULE g_pModPrevInLdBuf = NULL;
1092
1093/** Module list head. */
1094static PKWMODULE g_pModuleHead = NULL;
1095/** Where to insert the next module. */
1096static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1097
1098/** Module hash table. */
1099static PKWMODULE g_apModules[127];
1100
1101/** GetModuleHandle cache. */
1102static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1103{
1104#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1105 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1106#if 1
1107 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1108 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1109#endif
1110 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1111};
1112
1113/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1114static PKWMODULE g_pModPendingTlsAlloc = NULL;
1115
1116/** The 1KB TLS DLLs. */
1117static KWTLSDLL g_aTls1KDlls[] =
1118{
1119 { L"kWorkerTls1K.dll", K_FALSE },
1120 { L"kWorkerTls1K01.dll", K_FALSE },
1121 { L"kWorkerTls1K02.dll", K_FALSE },
1122 { L"kWorkerTls1K03.dll", K_FALSE },
1123 { L"kWorkerTls1K04.dll", K_FALSE },
1124 { L"kWorkerTls1K05.dll", K_FALSE },
1125 { L"kWorkerTls1K06.dll", K_FALSE },
1126 { L"kWorkerTls1K07.dll", K_FALSE },
1127};
1128
1129/** The 64KB TLS DLLs. */
1130static KWTLSDLL g_aTls64KDlls[] =
1131{
1132 { L"kWorkerTls64K.dll", K_FALSE },
1133 { L"kWorkerTls64K01.dll", K_FALSE },
1134 { L"kWorkerTls64K02.dll", K_FALSE },
1135 { L"kWorkerTls64K03.dll", K_FALSE },
1136 { L"kWorkerTls64K04.dll", K_FALSE },
1137 { L"kWorkerTls64K05.dll", K_FALSE },
1138 { L"kWorkerTls64K06.dll", K_FALSE },
1139 { L"kWorkerTls64K07.dll", K_FALSE },
1140};
1141
1142/** The 128KB TLS DLLs. */
1143static KWTLSDLL g_aTls128KDlls[] =
1144{
1145 { L"kWorkerTls128K.dll", K_FALSE },
1146 { L"kWorkerTls128K01.dll", K_FALSE },
1147 { L"kWorkerTls128K02.dll", K_FALSE },
1148 { L"kWorkerTls128K03.dll", K_FALSE },
1149 { L"kWorkerTls128K04.dll", K_FALSE },
1150 { L"kWorkerTls128K05.dll", K_FALSE },
1151 { L"kWorkerTls128K06.dll", K_FALSE },
1152 { L"kWorkerTls128K07.dll", K_FALSE },
1153};
1154
1155/** The 512KB TLS DLLs. */
1156static KWTLSDLL g_aTls512KDlls[] =
1157{
1158 { L"kWorkerTls512K.dll", K_FALSE },
1159 { L"kWorkerTls512K01.dll", K_FALSE },
1160 { L"kWorkerTls512K02.dll", K_FALSE },
1161 { L"kWorkerTls512K03.dll", K_FALSE },
1162 { L"kWorkerTls512K04.dll", K_FALSE },
1163 { L"kWorkerTls512K05.dll", K_FALSE },
1164 { L"kWorkerTls512K06.dll", K_FALSE },
1165 { L"kWorkerTls512K07.dll", K_FALSE },
1166};
1167
1168/** The TLS DLLs grouped by size. */
1169static KWTLSDLLENTRY const g_aTlsDlls[] =
1170{
1171 { 1024, K_ELEMENTS(g_aTls1KDlls), g_aTls1KDlls },
1172 { 64*1024, K_ELEMENTS(g_aTls64KDlls), g_aTls64KDlls },
1173 { 128*1024, K_ELEMENTS(g_aTls128KDlls), g_aTls128KDlls },
1174 { 512*1024, K_ELEMENTS(g_aTls512KDlls), g_aTls512KDlls },
1175};
1176
1177/** CRT slots.
1178 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1179static KWCRTSLOT g_aCrtSlots[32];
1180
1181/** windbg .reload statements. vs */
1182char g_szReloads[4096];
1183/** Current offset into g_szReloads. */
1184KU32 volatile g_cchReloads;
1185
1186/** The file system cache. */
1187static PKFSCACHE g_pFsCache;
1188/** The current directory (referenced). */
1189static PKFSOBJ g_pCurDirObj = NULL;
1190#ifdef KBUILD_OS_WINDOWS
1191/** The windows system32 directory (referenced). */
1192static PKFSDIR g_pWinSys32 = NULL;
1193#endif
1194
1195/** Verbosity level. */
1196static int g_cVerbose = 2;
1197
1198/** Whether we should restart the worker. */
1199static KBOOL g_fRestart = K_FALSE;
1200
1201/** The process group this worker is tied to (--group option), -1 if none. */
1202static KI32 g_iProcessGroup = -1;
1203
1204/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1205static int volatile g_rcCtrlC = 0;
1206
1207/** The communication pipe handle. We break this when we see Ctrl-C such. */
1208#ifdef KBUILD_OS_WINDOWS
1209static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1210#else
1211static int g_hPipe = -1;
1212#endif
1213
1214
1215/* Further down. */
1216extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1217extern KU32 const g_cSandboxReplacements;
1218
1219extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1220extern KU32 const g_cSandboxNativeReplacements;
1221
1222extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1223extern KU32 const g_cSandboxGetProcReplacements;
1224
1225
1226/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1227 * cover the default executable link address of 0x400000.
1228 * @remarks Early main() makes it read+write+executable. Attempts as having
1229 * it as a separate section failed because the linker insists on
1230 * writing out every zero in the uninitialized section, resulting in
1231 * really big binaries. */
1232__declspec(align(0x1000))
1233static KU8 g_abDefLdBuf[16*1024*1024];
1234
1235#ifdef WITH_LOG_FILE
1236/** Log file handle. */
1237static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1238#endif
1239
1240
1241#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1242/** Virtual address space reserved for CL.EXE heap manager.
1243 *
1244 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1245 * address. It's among other things used for precompiled headers, which
1246 * seemingly have addresses hardcoded into them and won't work if mapped
1247 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1248 * for it. (The /Zm option may affect this allocation.)
1249 */
1250static struct
1251{
1252 /** The memory address we need. */
1253 KUPTR const uFixed;
1254 /** How much we need to fix. */
1255 KSIZE const cbFixed;
1256 /** What we actually got, NULL if given back. */
1257 void *pvReserved;
1258 /** Whether it is in use or not. */
1259 KBOOL fInUse;
1260} g_aFixedVirtualAllocs[] =
1261{
1262# if K_ARCH == K_ARCH_X86_32
1263 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1264 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1265 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1266# else
1267 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1268# endif
1269};
1270#endif
1271
1272
1273#ifdef WITH_HISTORY
1274/** The job history. */
1275static char *g_apszHistory[32];
1276/** Index of the next history entry. */
1277static unsigned g_iHistoryNext = 0;
1278#endif
1279
1280
1281/** Number of jobs executed. */
1282static KU32 g_cJobs;
1283/** Number of tools. */
1284static KU32 g_cTools;
1285/** Number of modules. */
1286static KU32 g_cModules;
1287/** Number of non-native modules. */
1288static KU32 g_cNonNativeModules;
1289/** Number of read-cached files. */
1290static KU32 g_cReadCachedFiles;
1291/** Total size of read-cached files. */
1292static KSIZE g_cbReadCachedFiles;
1293
1294/** Total number of ReadFile calls. */
1295static KSIZE g_cReadFileCalls;
1296/** Total bytes read via ReadFile. */
1297static KSIZE g_cbReadFileTotal;
1298/** Total number of read from read-cached files. */
1299static KSIZE g_cReadFileFromReadCached;
1300/** Total bytes read from read-cached files. */
1301static KSIZE g_cbReadFileFromReadCached;
1302/** Total number of read from in-memory temporary files. */
1303static KSIZE g_cReadFileFromInMemTemp;
1304/** Total bytes read from in-memory temporary files. */
1305static KSIZE g_cbReadFileFromInMemTemp;
1306
1307/** Total number of WriteFile calls. */
1308static KSIZE g_cWriteFileCalls;
1309/** Total bytes written via WriteFile. */
1310static KSIZE g_cbWriteFileTotal;
1311/** Total number of written to from in-memory temporary files. */
1312static KSIZE g_cWriteFileToInMemTemp;
1313/** Total bytes written to in-memory temporary files. */
1314static KSIZE g_cbWriteFileToInMemTemp;
1315
1316
1317/*********************************************************************************************************************************
1318* Internal Functions *
1319*********************************************************************************************************************************/
1320static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1321static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1322 const char *pszSearchPath, PKWMODULE *ppMod);
1323static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1324static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName);
1325static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule);
1326static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod);
1327static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1328static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1329static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile);
1330static PKWHANDLE kwSandboxHandleGet(HANDLE hFile);
1331K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle);
1332#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1333static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1334#endif
1335static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1336
1337
1338
1339
1340/**
1341 * Debug printing.
1342 * @param pszFormat Debug format string.
1343 * @param ... Format argument.
1344 */
1345static void kwDbgPrintfV(const char *pszFormat, va_list va)
1346{
1347 if (g_cVerbose >= 2)
1348 {
1349 DWORD const dwSavedErr = GetLastError();
1350#ifdef WITH_LOG_FILE
1351 DWORD dwIgnored;
1352 char szTmp[2048];
1353 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1354 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1355 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1356 cch += cchPrefix;
1357 else
1358 {
1359 cch = sizeof(szTmp) - 1;
1360 szTmp[cch] = '\0';
1361 }
1362
1363 if (g_hLogFile == INVALID_HANDLE_VALUE)
1364 {
1365 wchar_t wszFilename[128];
1366 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1367 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1368 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1369 }
1370
1371 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1372#else
1373 fprintf(stderr, "debug: ");
1374 vfprintf(stderr, pszFormat, va);
1375#endif
1376
1377 SetLastError(dwSavedErr);
1378 }
1379}
1380
1381
1382/**
1383 * Debug printing.
1384 * @param pszFormat Debug format string.
1385 * @param ... Format argument.
1386 */
1387static void kwDbgPrintf(const char *pszFormat, ...)
1388{
1389 if (g_cVerbose >= 2)
1390 {
1391 va_list va;
1392 va_start(va, pszFormat);
1393 kwDbgPrintfV(pszFormat, va);
1394 va_end(va);
1395 }
1396}
1397
1398
1399/**
1400 * Debugger printing.
1401 * @param pszFormat Debug format string.
1402 * @param ... Format argument.
1403 */
1404static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1405{
1406 if (IsDebuggerPresent())
1407 {
1408 DWORD const dwSavedErr = GetLastError();
1409 char szTmp[2048];
1410
1411 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1412 OutputDebugStringA(szTmp);
1413
1414 SetLastError(dwSavedErr);
1415 }
1416}
1417
1418
1419/**
1420 * Debugger printing.
1421 * @param pszFormat Debug format string.
1422 * @param ... Format argument.
1423 */
1424static void kwDebuggerPrintf(const char *pszFormat, ...)
1425{
1426 va_list va;
1427 va_start(va, pszFormat);
1428 kwDebuggerPrintfV(pszFormat, va);
1429 va_end(va);
1430}
1431
1432
1433
1434/**
1435 * Error printing.
1436 * @param pszFormat Message format string.
1437 * @param ... Format argument.
1438 */
1439static void kwErrPrintfV(const char *pszFormat, va_list va)
1440{
1441 DWORD const dwSavedErr = GetLastError();
1442
1443#if defined(KW_LOG_ENABLED) && defined(WITH_LOG_FILE)
1444 va_list vaCopy;
1445# if defined(va_copy) || !defined(_MSC_VER) || _MSC_VER >= 1700 /*??*/
1446 va_copy(vaCopy, va);
1447# else
1448 vaCopy = va;
1449# endif
1450 kwDebuggerPrintf("kWorker: error: ");
1451 kwDebuggerPrintfV(pszFormat, vaCopy);
1452#endif
1453
1454 fprintf(stderr, "kWorker: error: ");
1455 vfprintf(stderr, pszFormat, va);
1456 fflush(stderr); /* In case it's a pipe. */
1457
1458 SetLastError(dwSavedErr);
1459}
1460
1461
1462/**
1463 * Error printing.
1464 * @param pszFormat Message format string.
1465 * @param ... Format argument.
1466 */
1467static void kwErrPrintf(const char *pszFormat, ...)
1468{
1469 va_list va;
1470 va_start(va, pszFormat);
1471 kwErrPrintfV(pszFormat, va);
1472 va_end(va);
1473}
1474
1475
1476/**
1477 * Error printing.
1478 * @return rc;
1479 * @param rc Return value
1480 * @param pszFormat Message format string.
1481 * @param ... Format argument.
1482 */
1483static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1484{
1485 va_list va;
1486 va_start(va, pszFormat);
1487 kwErrPrintfV(pszFormat, va);
1488 va_end(va);
1489 return rc;
1490}
1491
1492
1493#ifdef K_STRICT
1494
1495KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1496{
1497 DWORD const dwSavedErr = GetLastError();
1498
1499 fprintf(stderr,
1500 "\n"
1501 "!!Assertion failed!!\n"
1502 "Expression: %s\n"
1503 "Function : %s\n"
1504 "File: %s\n"
1505 "Line: %d\n"
1506 , pszExpr, pszFunction, pszFile, iLine);
1507
1508 SetLastError(dwSavedErr);
1509}
1510
1511
1512KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1513{
1514 DWORD const dwSavedErr = GetLastError();
1515 va_list va;
1516
1517 va_start(va, pszFormat);
1518 fprintf(stderr, pszFormat, va);
1519 va_end(va);
1520
1521 SetLastError(dwSavedErr);
1522}
1523
1524#endif /* K_STRICT */
1525
1526
1527/**
1528 * Hashes a string.
1529 *
1530 * @returns 32-bit string hash.
1531 * @param pszString String to hash.
1532 */
1533static KU32 kwStrHash(const char *pszString)
1534{
1535 /* This algorithm was created for sdbm (a public-domain reimplementation of
1536 ndbm) database library. it was found to do well in scrambling bits,
1537 causing better distribution of the keys and fewer splits. it also happens
1538 to be a good general hashing function with good distribution. the actual
1539 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1540 is the faster version used in gawk. [there is even a faster, duff-device
1541 version] the magic constant 65599 was picked out of thin air while
1542 experimenting with different constants, and turns out to be a prime.
1543 this is one of the algorithms used in berkeley db (see sleepycat) and
1544 elsewhere. */
1545 KU32 uHash = 0;
1546 KU32 uChar;
1547 while ((uChar = (unsigned char)*pszString++) != 0)
1548 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1549 return uHash;
1550}
1551
1552
1553/**
1554 * Hashes a string.
1555 *
1556 * @returns The string length.
1557 * @param pszString String to hash.
1558 * @param puHash Where to return the 32-bit string hash.
1559 */
1560static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1561{
1562 const char * const pszStart = pszString;
1563 KU32 uHash = 0;
1564 KU32 uChar;
1565 while ((uChar = (unsigned char)*pszString) != 0)
1566 {
1567 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1568 pszString++;
1569 }
1570 *puHash = uHash;
1571 return pszString - pszStart;
1572}
1573
1574
1575/**
1576 * Hashes a string.
1577 *
1578 * @returns The string length in wchar_t units.
1579 * @param pwszString String to hash.
1580 * @param puHash Where to return the 32-bit string hash.
1581 */
1582static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1583{
1584 const wchar_t * const pwszStart = pwszString;
1585 KU32 uHash = 0;
1586 KU32 uChar;
1587 while ((uChar = *pwszString) != 0)
1588 {
1589 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1590 pwszString++;
1591 }
1592 *puHash = uHash;
1593 return pwszString - pwszStart;
1594}
1595
1596
1597/**
1598 * Converts the given string to unicode.
1599 *
1600 * @returns Length of the resulting string in wchar_t's.
1601 * @param pszSrc The source string.
1602 * @param pwszDst The destination buffer.
1603 * @param cwcDst The size of the destination buffer in wchar_t's.
1604 */
1605static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1606{
1607 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1608 KSIZE offDst = 0;
1609 while (offDst < cwcDst)
1610 {
1611 char ch = *pszSrc++;
1612 pwszDst[offDst++] = ch;
1613 if (!ch)
1614 return offDst - 1;
1615 kHlpAssert((unsigned)ch < 127);
1616 }
1617
1618 pwszDst[offDst - 1] = '\0';
1619 return offDst;
1620}
1621
1622
1623/**
1624 * Converts the given string to UTF-16, allocating the buffer.
1625 *
1626 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1627 * the source string.
1628 * @param pchSrc The source string.
1629 * @param cchSrc The length of the source string.
1630 */
1631static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1632{
1633 DWORD const dwErrSaved = GetLastError();
1634 KSIZE cwcBuf = cchSrc + 1;
1635 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1636 if (pwszBuf)
1637 {
1638 if (cchSrc > 0)
1639 {
1640 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1641 if (cwcRet > 0)
1642 {
1643 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1644 pwszBuf[cwcRet] = '\0';
1645 }
1646 else
1647 {
1648 kHlpFree(pwszBuf);
1649
1650 /* Figure the length and allocate the right buffer size. */
1651 SetLastError(NO_ERROR);
1652 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1653 if (cwcRet)
1654 {
1655 cwcBuf = cwcRet + 2;
1656 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1657 if (pwszBuf)
1658 {
1659 SetLastError(NO_ERROR);
1660 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1661 if (cwcRet)
1662 {
1663 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1664 pwszBuf[cwcRet] = '\0';
1665 }
1666 else
1667 {
1668 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1669 kHlpFree(pwszBuf);
1670 pwszBuf = NULL;
1671 }
1672 }
1673 }
1674 else
1675 {
1676 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1677 pwszBuf = NULL;
1678 }
1679 }
1680 }
1681 else
1682 pwszBuf[0] = '\0';
1683 }
1684 SetLastError(dwErrSaved);
1685 return pwszBuf;
1686}
1687
1688
1689/**
1690 * Converts the given UTF-16 to a normal string.
1691 *
1692 * @returns Length of the resulting string.
1693 * @param pwszSrc The source UTF-16 string.
1694 * @param pszDst The destination buffer.
1695 * @param cbDst The size of the destination buffer in bytes.
1696 */
1697static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1698{
1699 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1700 KSIZE offDst = 0;
1701 while (offDst < cbDst)
1702 {
1703 wchar_t wc = *pwszSrc++;
1704 pszDst[offDst++] = (char)wc;
1705 if (!wc)
1706 return offDst - 1;
1707 kHlpAssert((unsigned)wc < 127);
1708 }
1709
1710 pszDst[offDst - 1] = '\0';
1711 return offDst;
1712}
1713
1714
1715/**
1716 * Converts the given UTF-16 to ASSI, allocating the buffer.
1717 *
1718 * @returns Pointer to the new heap allocation containing the ANSI version of
1719 * the source string.
1720 * @param pwcSrc The source string.
1721 * @param cwcSrc The length of the source string.
1722 */
1723static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1724{
1725 DWORD const dwErrSaved = GetLastError();
1726 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1727 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1728 if (pszBuf)
1729 {
1730 if (cwcSrc > 0)
1731 {
1732 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1733 if (cchRet > 0)
1734 {
1735 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1736 pszBuf[cchRet] = '\0';
1737 }
1738 else
1739 {
1740 kHlpFree(pszBuf);
1741
1742 /* Figure the length and allocate the right buffer size. */
1743 SetLastError(NO_ERROR);
1744 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1745 if (cchRet)
1746 {
1747 cbBuf = cchRet + 2;
1748 pszBuf = (char *)kHlpAlloc(cbBuf);
1749 if (pszBuf)
1750 {
1751 SetLastError(NO_ERROR);
1752 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1753 if (cchRet)
1754 {
1755 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1756 pszBuf[cchRet] = '\0';
1757 }
1758 else
1759 {
1760 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1761 kHlpFree(pszBuf);
1762 pszBuf = NULL;
1763 }
1764 }
1765 }
1766 else
1767 {
1768 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1769 pszBuf = NULL;
1770 }
1771 }
1772 }
1773 else
1774 pszBuf[0] = '\0';
1775 }
1776 SetLastError(dwErrSaved);
1777 return pszBuf;
1778}
1779
1780
1781
1782/** UTF-16 string length. */
1783static KSIZE kwUtf16Len(wchar_t const *pwsz)
1784{
1785 KSIZE cwc = 0;
1786 while (*pwsz != '\0')
1787 cwc++, pwsz++;
1788 return cwc;
1789}
1790
1791/**
1792 * Copy out the UTF-16 string following the convension of GetModuleFileName
1793 */
1794static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1795{
1796 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1797 if (cwcSrc + 1 <= cwcDst)
1798 {
1799 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1800 return (DWORD)cwcSrc;
1801 }
1802 if (cwcDst > 0)
1803 {
1804 KSIZE cwcDstTmp = cwcDst - 1;
1805 pwszDst[cwcDstTmp] = '\0';
1806 if (cwcDstTmp > 0)
1807 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1808 }
1809 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1810 return (DWORD)cwcDst;
1811}
1812
1813
1814/**
1815 * Copy out the ANSI string following the convension of GetModuleFileName
1816 */
1817static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1818{
1819 KSIZE cchSrc = kHlpStrLen(pszSrc);
1820 if (cchSrc + 1 <= cbDst)
1821 {
1822 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1823 return (DWORD)cchSrc;
1824 }
1825 if (cbDst > 0)
1826 {
1827 KSIZE cbDstTmp = cbDst - 1;
1828 pszDst[cbDstTmp] = '\0';
1829 if (cbDstTmp > 0)
1830 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1831 }
1832 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1833 return (DWORD)cbDst;
1834}
1835
1836
1837/**
1838 * Normalizes the path so we get a consistent hash.
1839 *
1840 * @returns status code.
1841 * @param pszPath The path.
1842 * @param pszNormPath The output buffer.
1843 * @param cbNormPath The size of the output buffer.
1844 */
1845static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1846{
1847 KFSLOOKUPERROR enmError;
1848 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1849 if (pFsObj)
1850 {
1851 KBOOL fRc;
1852 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1853 kFsCacheObjRelease(g_pFsCache, pFsObj);
1854 if (fRc)
1855 return 0;
1856 return KERR_BUFFER_OVERFLOW;
1857 }
1858 return KERR_FILE_NOT_FOUND;
1859}
1860
1861
1862/**
1863 * Get the pointer to the filename part of the path.
1864 *
1865 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1866 * @returns Pointer to the terminator char if no filename.
1867 * @param pszPath The path to parse.
1868 */
1869static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1870{
1871 const wchar_t *pwszLast = NULL;
1872 for (;;)
1873 {
1874 wchar_t wc = *pwszPath;
1875#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1876 if (wc == '/' || wc == '\\' || wc == ':')
1877 {
1878 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1879 /* nothing */;
1880 pwszLast = pwszPath;
1881 }
1882#else
1883 if (wc == '/')
1884 {
1885 while ((wc = *++pszFilename) == '/')
1886 /* betsuni */;
1887 pwszLast = pwszPath;
1888 }
1889#endif
1890 if (!wc)
1891 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1892 pwszPath++;
1893 }
1894}
1895
1896
1897
1898/**
1899 * Retains a new reference to the given module
1900 * @returns pMod
1901 * @param pMod The module to retain.
1902 */
1903static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1904{
1905 kHlpAssert(pMod->cRefs > 0);
1906 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1907 pMod->cRefs++;
1908 return pMod;
1909}
1910
1911
1912/**
1913 * Releases a module reference.
1914 *
1915 * @param pMod The module to release.
1916 */
1917static void kwLdrModuleRelease(PKWMODULE pMod)
1918{
1919 if (--pMod->cRefs == 0)
1920 {
1921 /* Make sure it doesn't receive any more native TLS callbacks.if non-native. */
1922 if (!pMod->fNative && pMod->u.Manual.ppTlsWorkerModuleVar)
1923 {
1924 *pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1925 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1926 }
1927
1928 /* Unlink it from the hash table. */
1929 if (!pMod->fExe)
1930 {
1931 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1932 if (g_apModules[idx] == pMod)
1933 g_apModules[idx] = pMod->pNextHash;
1934 else
1935 {
1936 PKWMODULE pPrev = g_apModules[idx];
1937 kHlpAssert(pPrev != NULL);
1938 while (pPrev->pNextHash != pMod)
1939 {
1940 pPrev = pPrev->pNextHash;
1941 kHlpAssert(pPrev != NULL);
1942 }
1943 pPrev->pNextHash = pMod->pNextHash;
1944 }
1945 }
1946
1947 /* Unlink it from the list. */
1948 if (pMod != g_pModuleHead)
1949 {
1950 PKWMODULE pPrev = g_pModuleHead;
1951 while (pPrev)
1952 {
1953 if (pPrev->pNextList == pMod)
1954 {
1955 pPrev->pNextList = pMod->pNextList;
1956 if (!pMod->pNextList)
1957 g_ppModuleNext = &pPrev->pNextList;
1958 break;
1959 }
1960 pPrev = pPrev->pNextList;
1961 }
1962 kHlpAssert(pPrev != NULL);
1963 }
1964 else
1965 {
1966 g_pModuleHead = pMod->pNextList;
1967 if (!pMod->pNextList)
1968 g_ppModuleNext = &g_pModuleHead;
1969 }
1970
1971 /* Release import modules. */
1972 if (!pMod->fNative)
1973 {
1974 KSIZE idx = pMod->u.Manual.cImpMods;
1975 while (idx-- > 0)
1976 if (pMod->u.Manual.apImpMods[idx])
1977 {
1978 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1979 pMod->u.Manual.apImpMods[idx] = NULL;
1980 }
1981 }
1982
1983 /* Free our resources. */
1984 kLdrModClose(pMod->pLdrMod);
1985 pMod->pLdrMod = NULL;
1986
1987 if (!pMod->fNative)
1988 {
1989 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1990 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1991 }
1992
1993 if (pMod->iCrtSlot != KU8_MAX)
1994 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
1995
1996 if (pMod->pszMsPdbSrvEndpoint)
1997 {
1998 kHlpFree(pMod->pszMsPdbSrvEndpoint);
1999 pMod->pszMsPdbSrvEndpoint = NULL;
2000 }
2001
2002 kHlpFree(pMod);
2003 }
2004 else
2005 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
2006}
2007
2008
2009/**
2010 * Links the module into the module hash table.
2011 *
2012 * @returns pMod
2013 * @param pMod The module to link.
2014 */
2015static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
2016{
2017 if (!pMod->fExe)
2018 {
2019 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
2020 pMod->pNextHash = g_apModules[idx];
2021 g_apModules[idx] = pMod;
2022 }
2023
2024 pMod->pNextList = NULL;
2025 *g_ppModuleNext = pMod;
2026 g_ppModuleNext = &pMod->pNextList;
2027
2028 return pMod;
2029}
2030
2031
2032/**
2033 * Replaces imports for this module according to g_aSandboxNativeReplacements.
2034 *
2035 * @param pMod The natively loaded module to process.
2036 */
2037static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
2038{
2039 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
2040 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
2041 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
2042 IMAGE_NT_HEADERS const *pNtHdrs;
2043 IMAGE_DATA_DIRECTORY const *pDirEnt;
2044
2045 kHlpAssert(pMod->fNative);
2046
2047 /*
2048 * Locate the export descriptors.
2049 */
2050 /* MZ header. */
2051 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
2052 {
2053 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
2054 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
2055 }
2056 else
2057 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
2058
2059 /* Check PE header. */
2060 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2061 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
2062
2063 /* Locate the import descriptor array. */
2064 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2065 if ( pDirEnt->Size > 0
2066 && pDirEnt->VirtualAddress != 0)
2067 {
2068 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
2069 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
2070 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
2071 KU8 *pbProtRange = NULL;
2072 SIZE_T cbProtRange = 0;
2073 DWORD fOldProt = 0;
2074 KU32 const cbPage = 0x1000;
2075 BOOL fRc;
2076
2077
2078 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
2079 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
2080 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
2081
2082 /*
2083 * Walk the import descriptor array.
2084 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
2085 */
2086 while ( cLeft-- > 0
2087 && pImpDesc->Name > 0
2088 && pImpDesc->FirstThunk > 0)
2089 {
2090 KU32 iThunk;
2091 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
2092 PKWMODULE pImportMod = NULL;
2093 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
2094 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
2095 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
2096 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
2097 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
2098 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
2099 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
2100
2101 /* Iterate the thunks. */
2102 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
2103 {
2104 KUPTR const off = paOrgThunks[iThunk].u1.Function;
2105 kHlpAssertReturnVoid(off < cbImage);
2106 if (!IMAGE_SNAP_BY_ORDINAL(off))
2107 {
2108 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
2109 KSIZE const cchSymbol = kHlpStrLen((const char *)&pName->Name[0]);
2110 KU32 i = g_cSandboxNativeReplacements;
2111 while (i-- > 0)
2112 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
2113 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
2114 {
2115 if ( !g_aSandboxNativeReplacements[i].pszModule
2116 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
2117 {
2118 KWLDR_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
2119
2120 /* The .rdata section is normally read-only, so we need to make it writable first. */
2121 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
2122 {
2123 /* Restore previous .rdata page. */
2124 if (fOldProt)
2125 {
2126 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
2127 kHlpAssert(fRc);
2128 fOldProt = 0;
2129 }
2130
2131 /* Query attributes for the current .rdata page. */
2132 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
2133 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
2134 kHlpAssert(cbProtRange);
2135 if (cbProtRange)
2136 {
2137 switch (ProtInfo.Protect)
2138 {
2139 case PAGE_READWRITE:
2140 case PAGE_WRITECOPY:
2141 case PAGE_EXECUTE_READWRITE:
2142 case PAGE_EXECUTE_WRITECOPY:
2143 /* Already writable, nothing to do. */
2144 fRc = TRUE;
2145 break;
2146
2147 default:
2148 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2149 case PAGE_READONLY:
2150 cbProtRange = cbPage;
2151 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2152 break;
2153
2154 case PAGE_EXECUTE:
2155 case PAGE_EXECUTE_READ:
2156 cbProtRange = cbPage;
2157 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2158 break;
2159 }
2160 kHlpAssertStmt(fRc, fOldProt = 0);
2161 }
2162 }
2163
2164 /*
2165 * Unslotted replacements are simple.
2166 */
2167 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2168 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2169 else
2170 {
2171 /*
2172 * Must find our module entry for this module, possibly creating one.
2173 */
2174 if (!pImportMod)
2175 {
2176 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2177 K_TRUE /*fAlwaysPresent*/);
2178 if (!pImportMod)
2179 {
2180 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2181 pszImport, pMod->pszPath);
2182 break;
2183 }
2184 }
2185 paThunks[iThunk].u1.AddressOfData
2186 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2187 }
2188 break;
2189 }
2190 }
2191 }
2192 }
2193
2194
2195 /* Next import descriptor. */
2196 pImpDesc++;
2197 }
2198
2199
2200 if (fOldProt)
2201 {
2202 DWORD fIgnore = 0;
2203 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2204 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2205 }
2206 }
2207
2208}
2209
2210
2211/**
2212 * Creates a module from a native kLdr module handle.
2213 *
2214 * @returns Module w/ 1 reference on success, NULL on failure.
2215 * @param pLdrMod The native kLdr module.
2216 * @param pszPath The normalized path to the module.
2217 * @param cbPath The module path length with terminator.
2218 * @param uHashPath The module path hash.
2219 * @param fDoReplacements Whether to do import replacements on this
2220 * module.
2221 */
2222static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2223 KBOOL fDoReplacements, PKWMODULE pVirtualApiMod)
2224{
2225 /*
2226 * Create the entry.
2227 */
2228 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2229 if (pMod)
2230 {
2231 pMod->pwszPath = (wchar_t *)(pMod + 1);
2232 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2233 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2234 pMod->uHashPath = uHashPath;
2235 pMod->cRefs = 1;
2236 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2237 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2238 pMod->fExe = K_FALSE;
2239 pMod->fNative = K_TRUE;
2240 pMod->pLdrMod = pLdrMod;
2241 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2242 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2243 pMod->iCrtSlot = KU8_MAX;
2244 pMod->fNeedReInit = K_FALSE;
2245 pMod->pszMsPdbSrvEndpoint = NULL;
2246 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2247 pMod->pVirtualApiMod = pVirtualApiMod;
2248 if (pVirtualApiMod)
2249 kwLdrModuleRetain(pVirtualApiMod);
2250
2251 if (fDoReplacements)
2252 {
2253 DWORD const dwSavedErr = GetLastError();
2254 kwLdrModuleDoNativeImportReplacements(pMod);
2255 SetLastError(dwSavedErr);
2256 }
2257
2258 KWLDR_LOG(("New module: %p LB %#010x %s (native%s%s)\n",
2259 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath,
2260 pVirtualApiMod ? ", virtual api => " : "", pVirtualApiMod ? pVirtualApiMod->pszPath : ""));
2261 g_cModules++;
2262 return kwLdrModuleLink(pMod);
2263 }
2264 return NULL;
2265}
2266
2267
2268
2269/**
2270 * Creates a module using the native loader.
2271 *
2272 * @returns Module w/ 1 reference on success, NULL on failure.
2273 * @param pszPath The normalized path to the module.
2274 * @param uHashPath The module path hash.
2275 * @param fDoReplacements Whether to do import replacements on this
2276 * module.
2277 */
2278static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2279{
2280 PKLDRMOD pLdrMod;
2281 int rc;
2282
2283 /*
2284 * HACK ALERT! Make sure the application path is searched when looking for
2285 * imports in the module we're loading.
2286 */
2287 /** @todo improve on this hack! */
2288 PKWMODULE pExe = g_Sandbox.pTool ? g_Sandbox.pTool->u.Sandboxed.pExe : NULL;
2289 if (pExe)
2290 {
2291 /* HACK ALERT! */
2292 wchar_t *pwzFilename = (wchar_t *)&pExe->pwszPath[pExe->offFilenameW];
2293 wchar_t wcSaved = pExe->pwszPath[pExe->offFilenameW];
2294 *pwzFilename = '\0';
2295 if (!SetDllDirectoryW(pExe->pwszPath))
2296 kwErrPrintf("SetDllDirectoryW failed: %u\n", GetLastError());
2297 KW_LOG(("kwLdrModuleCreateNative: Applied SetDllDirectoryW hack (%ls)\n", pExe->pwszPath));
2298 *pwzFilename = wcSaved;
2299 }
2300 else
2301 KW_LOG(("kwLdrModuleCreateNative: Warning! Too early for SetDllDirectoryW hack\n"));
2302
2303
2304 /*
2305 * Load the library and create a module structure for it.
2306 */
2307 rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2308 if (rc == 0)
2309 {
2310 KSIZE cchPath = kHlpStrLen(pszPath);
2311 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, cchPath + 1, uHashPath,
2312 fDoReplacements, NULL /*pVirtualApiMod*/);
2313 if (pMod)
2314 return pMod;
2315 kLdrModClose(pLdrMod);
2316 }
2317 return NULL;
2318}
2319
2320
2321/**
2322 * Checks if the given name could be a virtual API module or not.
2323 */
2324static KBOOL kwLdrIsVirtualApiModule(const char *pszName, KSIZE cchName)
2325{
2326 if (cchName <= 7)
2327 return K_FALSE;
2328 switch (*pszName)
2329 {
2330 default:
2331 return K_FALSE;
2332 case 'a':
2333 case 'A':
2334 if (pszName[1] != 'p' && pszName[1] != 'P')
2335 return K_FALSE;
2336 if (pszName[2] != 'i' && pszName[2] != 'I')
2337 return K_FALSE;
2338 break;
2339 case 'e':
2340 case 'E':
2341 if (pszName[1] != 'x' && pszName[1] != 'X')
2342 return K_FALSE;
2343 if (pszName[2] != 't' && pszName[2] != 'T')
2344 return K_FALSE;
2345 break;
2346 }
2347 if (pszName[3] != '-')
2348 return K_FALSE;
2349 if (pszName[4] != 'm' && pszName[4] != 'M')
2350 return K_FALSE;
2351 if (pszName[5] != 's' && pszName[5] != 'S')
2352 return K_FALSE;
2353 if (pszName[6] != '-')
2354 return K_FALSE;
2355 return K_TRUE;
2356}
2357
2358
2359/**
2360 * Try load what seems to be a virtual API DLL.
2361 *
2362 * This is a worker for kwLdrModuleResolveAndLookup and
2363 * kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule.
2364 *
2365 * @returns Pointer to module on success, NULL on failure.
2366 * @param pszName The name of the module. This must be
2367 * normalized already!
2368 * @param cchName The length of the name.
2369 */
2370static PKWMODULE kwLdrModuleTryLoadVirtualDll(const char *pszName, KSIZE cchName)
2371{
2372 HMODULE hModule;
2373
2374 /*
2375 * Look it up in the hash table.
2376 */
2377 KU32 const uHashPath = kwStrHash(pszName);
2378 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2379 PKWMODULE pMod = g_apModules[idxHash];
2380 if (pMod)
2381 {
2382 do
2383 {
2384 if ( pMod->uHashPath == uHashPath
2385 && kHlpStrComp(pMod->pszPath, pszName) == 0)
2386 return kwLdrModuleRetain(pMod);
2387 pMod = pMod->pNextHash;
2388 } while (pMod);
2389 }
2390
2391 /*
2392 * Not found. Try load it.
2393 */
2394 hModule = LoadLibraryA(pszName);
2395 if (!hModule)
2396 {
2397 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s failed (%u)\n", pszName, GetLastError()));
2398 return NULL;
2399 }
2400
2401 /*
2402 * Loaded successfully. Create a module for the real module.
2403 */
2404 pMod = kwLdrModuleForLoadedNativeByHandle(hModule, K_FALSE /*fEnsureCrtSlot*/, pszName);
2405 if (pMod)
2406 {
2407 /* Create a module for the virtual API name too, unless it is actually a real DLL. */
2408 if (stricmp(&pMod->pszPath[pMod->offFilename], pszName) != 0)
2409 {
2410 PKLDRMOD pLdrMod;
2411 int rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2412 if (rc == 0)
2413 {
2414 PKWMODULE pVirtMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszName, cchName + 1, kwStrHash(pszName),
2415 K_FALSE /*fDoReplacements*/, pMod /*pVirtualApiMod*/);
2416 if (pVirtMod)
2417 {
2418 kwLdrModuleRelease(pMod);
2419 pMod = pVirtMod;
2420 }
2421 else
2422 {
2423 kLdrModClose(pLdrMod);
2424 kwErrPrintf("out of memory\n");
2425 }
2426 }
2427 else
2428 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
2429 }
2430 else
2431 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s -> %s - A real DLL!\n", pszName, pMod->pszPath));
2432 }
2433
2434 return pMod;
2435}
2436
2437
2438/**
2439 * Sets up the quick zero & copy tables for the non-native module.
2440 *
2441 * This is a worker for kwLdrModuleCreateNonNative.
2442 *
2443 * @param pMod The module.
2444 */
2445static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2446{
2447 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2448 KU32 cSegs = pMod->pLdrMod->cSegments;
2449 KU32 iSeg;
2450
2451 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2452 pMod->u.Manual.cQuickCopyChunks = 0;
2453 pMod->u.Manual.cQuickZeroChunks = 0;
2454
2455 for (iSeg = 0; iSeg < cSegs; iSeg++)
2456 switch (paSegs[iSeg].enmProt)
2457 {
2458 case KPROT_READWRITE:
2459 case KPROT_WRITECOPY:
2460 case KPROT_EXECUTE_READWRITE:
2461 case KPROT_EXECUTE_WRITECOPY:
2462 if (paSegs[iSeg].cbMapped)
2463 {
2464 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2465 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2466 {
2467 /*
2468 * Check for trailing zero words.
2469 */
2470 KSIZE cbTrailingZeros;
2471 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2472 && (paSegs[iSeg].cbMapped & 7) == 0
2473 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2474 {
2475 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2476 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2477 KSIZE idxFirstZero = cNatural;
2478 while (idxFirstZero > 0)
2479 if (pauNatural[--idxFirstZero] == 0)
2480 { /* likely */ }
2481 else
2482 {
2483 idxFirstZero++;
2484 break;
2485 }
2486 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2487 if (cbTrailingZeros < 128)
2488 cbTrailingZeros = 0;
2489 }
2490 else
2491 cbTrailingZeros = 0;
2492
2493 /*
2494 * Add quick copy entry.
2495 */
2496 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2497 {
2498 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2499 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2500 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2501 pMod->u.Manual.cQuickCopyChunks = (KU8)(iChunk + 1);
2502 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2503 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2504 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2505 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2506 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2507 }
2508
2509 /*
2510 * Add quick zero entry.
2511 */
2512 if (cbTrailingZeros)
2513 {
2514 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2515 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2516 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2517 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2518 pMod->u.Manual.cQuickZeroChunks = (KU8)(iZero + 1);
2519 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2520 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2521 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2522 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2523 }
2524 }
2525 else
2526 {
2527 /*
2528 * We're out of quick copy table entries, so just copy the whole darn thing.
2529 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2530 */
2531 kHlpAssertFailed();
2532 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2533 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2534 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2535 pMod->u.Manual.cQuickCopyChunks = 1;
2536 KWLDR_LOG(("Quick copy not possible!\n"));
2537 return;
2538 }
2539 }
2540 break;
2541
2542 default:
2543 break;
2544 }
2545}
2546
2547
2548/**
2549 * Called from the TLS allocation DLL when ever the native loader wants to issue
2550 * a TLS callback after the initial kwLdrTlsAllocationHook callout.
2551 *
2552 * @param hDll The DLL handle.
2553 * @param dwReason The callback reason.
2554 * @param pvContext Some context value that seems to always be NULL.
2555 * @param pMod Out internal module.
2556 */
2557static void kwLdrTlsNativeLoaderCallback(void *hDll, DWORD dwReason, void *pvContext, PKWMODULE pMod)
2558{
2559 if ( pMod
2560 && pMod->u.Manual.enmState == KWMODSTATE_READY)
2561 {
2562 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p\n",
2563 hDll, dwReason, pvContext, pMod));
2564 if (pMod->u.Manual.cTlsCallbacks)
2565 {
2566 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
2567 do
2568 {
2569 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: Calling TLS callback %p(%p, %#x, %p) - %s\n",
2570 *ppfnCallback, pMod->hOurMod, dwReason, pvContext, pMod->pszPath));
2571 (*ppfnCallback)(pMod->hOurMod, dwReason, pvContext);
2572 ppfnCallback++;
2573 } while (*ppfnCallback);
2574 }
2575 }
2576 else
2577 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p - skipped\n",
2578 hDll, dwReason, pvContext, pMod));
2579}
2580
2581
2582/**
2583 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2584 *
2585 * @returns Address of the callback function (kwLdrTlsNativeLoaderCallback).
2586 * @param hDll The DLL handle.
2587 * @param idxTls The allocated TLS index.
2588 * @param pabInitData The init data in the TLS allocation DLL
2589 * (g_abInitData).
2590 * @param ppWorkerModuleVar Pointer to the variable holding the pMod
2591 * callback parameter value (g_pvWorkerModule).
2592 *
2593 * @see KWLDRTLSALLOCATIONHOOK in kWorkerTlsXxxxK.c
2594 */
2595__declspec(dllexport) KUPTR kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, KU8 *pabInitData, PKWMODULE *ppWorkerModuleVar)
2596{
2597 /*
2598 * Do the module initialization thing first.
2599 */
2600 PKWMODULE pMod = g_pModPendingTlsAlloc;
2601 if (pMod)
2602 {
2603 if ( pMod->u.Manual.idxTls == KU32_MAX
2604 && pMod->u.Manual.pabTlsInitData == NULL)
2605 {
2606 pMod->u.Manual.idxTls = idxTls;
2607 pMod->u.Manual.pabTlsInitData = pabInitData;
2608 pMod->u.Manual.ppTlsWorkerModuleVar = ppWorkerModuleVar;
2609 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2610
2611#if 0 /** @todo this doesn't work W10 18363 */
2612 {
2613 /*
2614 * Try sabotage the DLL name so we can load this module again.
2615 */
2616 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2617 LIST_ENTRY *pHead;
2618 LIST_ENTRY *pCur;
2619
2620 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2621 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2622 {
2623 LDR_DATA_TABLE_ENTRY *pMte;
2624 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2625 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2626 {
2627 PUNICODE_STRING pStr = &pMte->FullDllName;
2628 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2629 pStr->Buffer[--off]++;
2630 pStr->Buffer[--off]++;
2631 pStr->Buffer[--off]++;
2632 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2633 break;
2634 }
2635 }
2636 }
2637#endif
2638
2639 /*
2640 * Don't return a callback function unless the module has callbacks to service.
2641 */
2642 if (pMod->u.Manual.cTlsCallbacks > 0)
2643 {
2644 *ppWorkerModuleVar = pMod;
2645 return (KUPTR)kwLdrTlsNativeLoaderCallback;
2646 }
2647 return 0;
2648 }
2649 KWLDR_LOG(("kwLdrTlsAllocationHook: WTF? pMod=%p: idxTls=%#x pabTlsInitData=%p\n",
2650 pMod, pMod->u.Manual.idxTls, pMod->u.Manual.pabTlsInitData));
2651 }
2652 return 0;
2653}
2654
2655
2656/**
2657 * Allocates and initializes TLS variables.
2658 *
2659 * @returns 0 on success, non-zero failure.
2660 * @param pMod The module.
2661 */
2662static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2663{
2664 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2665 IMAGE_NT_HEADERS const *pNtHdrs;
2666 IMAGE_DATA_DIRECTORY const *pTlsDir;
2667
2668 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2669 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2670 else
2671 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2672 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2673
2674 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2675 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2676 {
2677 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2678 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2679 KU32 iEntry;
2680 KU32 iTlsDll;
2681 KU32 iTlsDllSub;
2682 KUPTR offIndex;
2683 KUPTR offCallbacks;
2684 KUPTR const *puCallbacks;
2685 KSIZE cbData;
2686 const wchar_t *pwszTlsDll;
2687 HMODULE hmodTlsDll;
2688
2689 /*
2690 * Check and log.
2691 */
2692 for (iEntry = 0; iEntry < cEntries; iEntry++)
2693 {
2694 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2695 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2696 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2697 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2698 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2699 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2700 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2701
2702 if (offIndex >= pMod->cbImage)
2703 {
2704 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2705 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2706 return -1;
2707 }
2708 if (offCallbacks >= pMod->cbImage)
2709 {
2710 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2711 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2712 return -1;
2713 }
2714 while (*puCallbacks != 0)
2715 {
2716 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2717 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2718 puCallbacks++;
2719 }
2720 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2721 {
2722 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2723 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2724 return -1;
2725 }
2726 }
2727
2728 if (cEntries > 1)
2729 {
2730 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2731 return -1;
2732 }
2733
2734 /*
2735 * Make the allocation by loading a new instance of one of the TLS dlls.
2736 * The DLL will make a call to kwLdrTlsAllocationHook.
2737 */
2738 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2739 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2740 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2741 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2742
2743 /** @todo find better strategy here. Like temporary copy or whatever when
2744 * there is more than a single user. */
2745 for (iTlsDll = 0; cbData > g_aTlsDlls[iTlsDll].cbTls;)
2746 if (++iTlsDll >= K_ELEMENTS(g_aTlsDlls))
2747 {
2748 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2749 return -1;
2750 }
2751 for (iTlsDllSub = 0; g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed;)
2752 if (++iTlsDllSub >= g_aTlsDlls[iTlsDll].cDlls)
2753 {
2754 kwErrPrintf("No unused TLS DLLs for %s of size %u!\n", pMod->pszPath, (unsigned)cbData);
2755 return -1;
2756 }
2757
2758 g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed = K_TRUE;
2759 pwszTlsDll = g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].pwszName;
2760
2761 pMod->u.Manual.pabTlsInitData = NULL;
2762 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2763 pMod->u.Manual.idxTls = KU32_MAX;
2764
2765 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2766 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2767 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2768 pMod->u.Manual.cTlsCallbacks = 0;
2769 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2770 pMod->u.Manual.cTlsCallbacks++;
2771 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2772
2773 g_pModPendingTlsAlloc = pMod;
2774 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2775 g_pModPendingTlsAlloc = NULL;
2776 if (hmodTlsDll == NULL)
2777 {
2778 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2779 return -1;
2780 }
2781 if (pMod->u.Manual.idxTls == KU32_MAX)
2782 {
2783 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2784 return -1;
2785 }
2786
2787 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2788 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x pabTlsInitData=%p\n",
2789 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData, pMod->u.Manual.pabTlsInitData));
2790
2791 kHlpAssert(pMod->u.Manual.pabTlsInitData);
2792 if (pMod->u.Manual.pabTlsInitData && pMod->u.Manual.cbTlsInitData)
2793 kHlpMemCopy(pMod->u.Manual.pabTlsInitData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData],
2794 pMod->u.Manual.cbTlsInitData);
2795 }
2796 return 0;
2797}
2798
2799
2800/**
2801 * Creates a module using the our own loader.
2802 *
2803 * @returns Module w/ 1 reference on success, NULL on failure.
2804 * @param pszPath The normalized path to the module.
2805 * @param uHashPath The module path hash.
2806 * @param fExe K_TRUE if this is an executable image, K_FALSE
2807 * if not. Executable images does not get entered
2808 * into the global module table.
2809 * @param pExeMod The executable module of the process (for
2810 * resolving imports). NULL if fExe is set.
2811 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2812 */
2813static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2814 PKWMODULE pExeMod, const char *pszSearchPath)
2815{
2816 /*
2817 * Open the module and check the type.
2818 */
2819 PKLDRMOD pLdrMod;
2820 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2821 if (rc == 0)
2822 {
2823 switch (pLdrMod->enmType)
2824 {
2825 case KLDRTYPE_EXECUTABLE_FIXED:
2826 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2827 case KLDRTYPE_EXECUTABLE_PIC:
2828 if (!fExe)
2829 rc = KERR_GENERAL_FAILURE;
2830 break;
2831
2832 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2833 case KLDRTYPE_SHARED_LIBRARY_PIC:
2834 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2835 if (fExe)
2836 rc = KERR_GENERAL_FAILURE;
2837 break;
2838
2839 default:
2840 rc = KERR_GENERAL_FAILURE;
2841 break;
2842 }
2843 if (rc == 0)
2844 {
2845 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2846 if (cImports >= 0)
2847 {
2848 /*
2849 * Create the entry.
2850 */
2851 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2852 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2853 + sizeof(pMod) * cImports
2854 + cbPath
2855 + cbPath * 2 * sizeof(wchar_t));
2856 if (pMod)
2857 {
2858 KBOOL fFixed;
2859
2860 pMod->cRefs = 1;
2861 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2862 pMod->uHashPath = uHashPath;
2863 pMod->fExe = fExe;
2864 pMod->fNative = K_FALSE;
2865 pMod->pLdrMod = pLdrMod;
2866 pMod->iCrtSlot = KU8_MAX;
2867 pMod->fNeedReInit = K_FALSE;
2868 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2869 pMod->pszMsPdbSrvEndpoint = NULL;
2870 pMod->u.Manual.cImpMods = (KU32)cImports;
2871#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2872 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2873#endif
2874 pMod->u.Manual.fUseLdBuf = K_FALSE;
2875 pMod->u.Manual.fCanDoQuick = K_FALSE;
2876 pMod->u.Manual.cQuickZeroChunks = 0;
2877 pMod->u.Manual.cQuickCopyChunks = 0;
2878 pMod->u.Manual.pabTlsInitData = NULL;
2879 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2880 pMod->u.Manual.idxTls = KU32_MAX;
2881 pMod->u.Manual.offTlsInitData = KU32_MAX;
2882 pMod->u.Manual.cbTlsInitData = 0;
2883 pMod->u.Manual.cbTlsAlloc = 0;
2884 pMod->u.Manual.cTlsCallbacks = 0;
2885 pMod->u.Manual.offTlsCallbacks = 0;
2886 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2887 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2888 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2889 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2890
2891 /*
2892 * Figure out where to load it and get memory there.
2893 */
2894 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2895 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2896 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2897 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2898 if ( !fFixed
2899 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2900 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2901 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2902 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2903 else
2904 pMod->u.Manual.fUseLdBuf = K_TRUE;
2905 if (rc == 0)
2906 {
2907 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2908 if (rc == 0)
2909 {
2910 KI32 iImp;
2911 KU32 cchReloads;
2912
2913 /*
2914 * Link the module (unless it's an executable image) and process the imports.
2915 */
2916 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2917 kwLdrModuleLink(pMod);
2918 KWLDR_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2919 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2920 KWLDR_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2921 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2922 cchReloads = g_cchReloads;
2923 if (cchReloads + 80 < sizeof(g_szReloads))
2924 {
2925 cchReloads += _snprintf(&g_szReloads[cchReloads], sizeof(g_szReloads) - cchReloads,
2926 "%s.reload /f %s=%p\n", cchReloads ? "; " : "",
2927 pMod->pszPath, pMod->u.Manual.pbLoad);
2928 g_cchReloads = cchReloads;
2929 }
2930
2931 for (iImp = 0; iImp < cImports; iImp++)
2932 {
2933 char szName[1024];
2934 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2935 if (rc == 0)
2936 {
2937 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2938 &pMod->u.Manual.apImpMods[iImp]);
2939 if (rc == 0)
2940 continue;
2941 }
2942 break;
2943 }
2944
2945 if (rc == 0)
2946 {
2947 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2948 kwLdrModuleGetImportCallback, pMod);
2949 if (rc == 0)
2950 {
2951#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2952 /*
2953 * Find the function table. No validation here because the
2954 * loader did that already, right...
2955 */
2956 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2957 IMAGE_NT_HEADERS const *pNtHdrs;
2958 IMAGE_DATA_DIRECTORY const *pXcptDir;
2959 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2960 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2961 else
2962 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2963 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2964 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2965 if (pXcptDir->Size > 0)
2966 {
2967 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2968 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2969 == pXcptDir->Size);
2970 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2971 }
2972 else
2973 {
2974 pMod->u.Manual.cFunctions = 0;
2975 pMod->u.Manual.paFunctions = NULL;
2976 }
2977#endif
2978
2979 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2980
2981 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2982 if (rc == 0)
2983 {
2984 /*
2985 * Final finish.
2986 */
2987 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2988 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2989 pMod->u.Manual.enmReInitState = KWMODSTATE_NEEDS_BITS;
2990 if ( g_Sandbox.pTool
2991 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
2992 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
2993 && !pMod->fExe)
2994 pMod->u.Manual.enmReInitState = KWMODSTATE_READY;
2995 g_cModules++;
2996 g_cNonNativeModules++;
2997 return pMod;
2998 }
2999 }
3000 else
3001 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
3002 }
3003
3004 kwLdrModuleRelease(pMod);
3005 return NULL;
3006 }
3007
3008 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
3009 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3010 }
3011 else if (fFixed)
3012 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
3013 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
3014 else
3015 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3016 }
3017 }
3018 }
3019 kLdrModClose(pLdrMod);
3020 }
3021 else
3022 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
3023 return NULL;
3024}
3025
3026
3027/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
3028static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
3029 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
3030{
3031 PKWMODULE pCurMod = (PKWMODULE)pvUser;
3032 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
3033 int rc;
3034 K_NOREF(pMod);
3035
3036 if (pImpMod->fNative)
3037 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
3038 iSymbol, pchSymbol, cchSymbol, pszVersion,
3039 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3040 puValue, pfKind);
3041 else
3042 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
3043 iSymbol, pchSymbol, cchSymbol, pszVersion,
3044 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3045 puValue, pfKind);
3046 if (rc == 0)
3047 {
3048 KU32 i = g_cSandboxReplacements;
3049 while (i-- > 0)
3050 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
3051 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
3052 {
3053 if ( !g_aSandboxReplacements[i].pszModule
3054 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
3055 {
3056 if ( pCurMod->fExe
3057 || !g_aSandboxReplacements[i].fOnlyExe)
3058 {
3059 KWLDR_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
3060 if (!g_aSandboxReplacements[i].fCrtSlotArray)
3061 *puValue = g_aSandboxReplacements[i].pfnReplacement;
3062 else
3063 {
3064 if (pImpMod->iCrtSlot == KU8_MAX)
3065 {
3066 rc = kwLdrModuleCreateCrtSlot(pImpMod);
3067 if (rc)
3068 KWLDR_LOG(("kwLdrModuleGetImportCallback: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
3069 }
3070 *puValue = ((KUPTR *)g_aSandboxReplacements[i].pfnReplacement)[pImpMod->iCrtSlot];
3071 }
3072 }
3073 break;
3074 }
3075 }
3076 }
3077
3078 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
3079 KWLDR_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
3080 return rc;
3081
3082}
3083
3084
3085/**
3086 * Gets the main entrypoint for a module.
3087 *
3088 * @returns 0 on success, KERR on failure
3089 * @param pMod The module.
3090 * @param puAddrMain Where to return the address.
3091 */
3092static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
3093{
3094 KLDRADDR uLdrAddrMain;
3095 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
3096 if (rc == 0)
3097 {
3098 *puAddrMain = (KUPTR)uLdrAddrMain;
3099 return 0;
3100 }
3101 return rc;
3102}
3103
3104
3105/**
3106 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
3107 *
3108 * @returns K_TRUE/K_FALSE.
3109 * @param pszFilename The filename (no path).
3110 * @param enmLocation The location.
3111 */
3112static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
3113{
3114 if (enmLocation != KWLOCATION_SYSTEM32)
3115 return K_TRUE;
3116 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3117 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3118 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3119 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3120 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3121 || kHlpStrNICompAscii(pszFilename, TUPLE("api-ms-win-crt-")) == 0
3122#if 1 /* for debugging, only for debugging. */
3123 || kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3124 || kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3125 || kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3126#endif
3127 ;
3128}
3129
3130
3131/**
3132 * Lazily initializes the g_pWinSys32 variable.
3133 */
3134static PKFSDIR kwLdrResolveWinSys32(void)
3135{
3136 KFSLOOKUPERROR enmError;
3137 PKFSDIR pWinSys32;
3138
3139 /* Get the path first. */
3140 char szSystem32[MAX_PATH];
3141 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
3142 {
3143 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
3144 strcpy(szSystem32, "C:\\Windows\\System32");
3145 }
3146
3147 /* Look it up and verify it. */
3148 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
3149 if (pWinSys32)
3150 {
3151 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
3152 {
3153 g_pWinSys32 = pWinSys32;
3154 return pWinSys32;
3155 }
3156
3157 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
3158 }
3159 else
3160 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
3161 return NULL;
3162}
3163
3164
3165/**
3166 * Whether we can load this DLL natively or not.
3167 *
3168 * @returns K_TRUE/K_FALSE.
3169 * @param pszFilename The filename (no path).
3170 * @param enmLocation The location.
3171 * @param pszFullPath The full filename and path.
3172 */
3173static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
3174{
3175 if (enmLocation == KWLOCATION_SYSTEM32)
3176 return K_TRUE;
3177 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
3178 return K_TRUE;
3179 if ( enmLocation == KWLOCATION_UNKNOWN
3180 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3181 return K_TRUE;
3182
3183 /* If the location is unknown, we must check if it's some dynamic loading
3184 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
3185 if (enmLocation == KWLOCATION_UNKNOWN)
3186 {
3187 PKFSDIR pWinSys32 = g_pWinSys32;
3188 if (!pWinSys32)
3189 pWinSys32 = kwLdrResolveWinSys32();
3190 if (pWinSys32)
3191 {
3192 KFSLOOKUPERROR enmError;
3193 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
3194 if (pFsObj)
3195 {
3196 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
3197 kFsCacheObjRelease(g_pFsCache, pFsObj);
3198 if (fInWinSys32)
3199 return K_TRUE;
3200 }
3201 }
3202 }
3203
3204 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3205 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3206 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3207 || kHlpStrNICompAscii(pszFilename, TUPLE("tbbmalloc")) == 0
3208 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3209 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3210 || ( enmLocation != KWLOCATION_UNKNOWN
3211 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3212#if 1 /* for debugging, only for debugging. */
3213 //|| kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3214 //|| kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3215// || kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3216#endif
3217 ;
3218}
3219
3220
3221/**
3222 * Check if the path leads to a regular file (that exists).
3223 *
3224 * @returns K_TRUE / K_FALSE
3225 * @param pszPath Path to the file to check out.
3226 */
3227static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
3228{
3229 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
3230 KSIZE cchPath = kHlpStrLen(pszPath);
3231 if ( cchPath > 3
3232 && pszPath[cchPath - 4] == '.'
3233 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
3234 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
3235 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
3236 {
3237 KFSLOOKUPERROR enmError;
3238 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3239 if (pFsObj)
3240 {
3241 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
3242 kFsCacheObjRelease(g_pFsCache, pFsObj);
3243 return fRc;
3244 }
3245 }
3246 else
3247 {
3248 BirdStat_T Stat;
3249 int rc = birdStatFollowLink(pszPath, &Stat);
3250 if (rc == 0)
3251 {
3252 if (S_ISREG(Stat.st_mode))
3253 return K_TRUE;
3254 }
3255 }
3256 return K_FALSE;
3257}
3258
3259
3260/**
3261 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
3262 *
3263 * If the file exists, we consult the module hash table before trying to load it
3264 * off the disk.
3265 *
3266 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
3267 * failure.
3268 * @param pszPath The name of the import module.
3269 * @param enmLocation The location we're searching. This is used in
3270 * the heuristics for determining if we can use the
3271 * native loader or need to sandbox the DLL.
3272 * @param pExe The executable (optional).
3273 * @param pszSearchPath The PATH to search (optional).
3274 */
3275static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
3276{
3277 /*
3278 * Does the file exists and is it a regular file?
3279 */
3280 if (kwLdrModuleIsRegularFile(pszPath))
3281 {
3282 /*
3283 * Yes! Normalize it and look it up in the hash table.
3284 */
3285 char szNormPath[1024];
3286 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
3287 if (rc == 0)
3288 {
3289 const char *pszName;
3290 KU32 const uHashPath = kwStrHash(szNormPath);
3291 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3292 PKWMODULE pMod = g_apModules[idxHash];
3293 if (pMod)
3294 {
3295 do
3296 {
3297 if ( pMod->uHashPath == uHashPath
3298 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3299 return kwLdrModuleRetain(pMod);
3300 pMod = pMod->pNextHash;
3301 } while (pMod);
3302 }
3303
3304 /*
3305 * Not in the hash table, so we have to load it from scratch.
3306 */
3307 pszName = kHlpGetFilename(szNormPath);
3308 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
3309 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
3310 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
3311 else
3312 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
3313 if (pMod)
3314 return pMod;
3315 return (PKWMODULE)~(KUPTR)0;
3316 }
3317 }
3318 return NULL;
3319}
3320
3321
3322/**
3323 * Gets a reference to the module by the given name.
3324 *
3325 * We must do the search path thing, as our hash table may multiple DLLs with
3326 * the same base name due to different tools version and similar. We'll use a
3327 * modified search sequence, though. No point in searching the current
3328 * directory for instance.
3329 *
3330 * @returns 0 on success, KERR on failure.
3331 * @param pszName The name of the import module.
3332 * @param pExe The executable (optional).
3333 * @param pImporter The module doing the importing (optional).
3334 * @param pszSearchPath The PATH to search (optional).
3335 * @param ppMod Where to return the module pointer w/ reference.
3336 */
3337static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
3338 const char *pszSearchPath, PKWMODULE *ppMod)
3339{
3340 KSIZE const cchName = kHlpStrLen(pszName);
3341 char szPath[1024];
3342 char *psz;
3343 PKWMODULE pMod = NULL;
3344 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
3345 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
3346
3347 /* Virtual API module. Normalize and try load it. */
3348 if (pMod == NULL && cchName > 7 && kwLdrIsVirtualApiModule(pszName, cchName))
3349 {
3350 if (cchName + cchSuffix >= sizeof(szPath))
3351 return KERR_BUFFER_OVERFLOW;
3352 kHlpMemCopy(szPath, pszName, cchName);
3353 if (fNeedSuffix)
3354 kHlpMemCopy(&szPath[cchName], ".dll", sizeof(".dll"));
3355 szPath[cchName + cchSuffix] = '\0';
3356 _strlwr(szPath);
3357 pMod = kwLdrModuleTryLoadVirtualDll(szPath, cchName + cchSuffix);
3358 }
3359
3360 /* The import path. */
3361 if (pMod == NULL && pImporter != NULL)
3362 {
3363 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
3364 return KERR_BUFFER_OVERFLOW;
3365
3366 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
3367 if (fNeedSuffix)
3368 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3369 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
3370 }
3371
3372 /* Application directory first. */
3373 if (pMod == NULL && pExe != NULL && pExe != pImporter)
3374 {
3375 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
3376 return KERR_BUFFER_OVERFLOW;
3377 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
3378 if (fNeedSuffix)
3379 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3380 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
3381 }
3382
3383 /* The windows directory. */
3384 if (pMod == NULL)
3385 {
3386 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
3387 if ( cchDir <= 2
3388 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
3389 return KERR_BUFFER_OVERFLOW;
3390 szPath[cchDir++] = '\\';
3391 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
3392 if (fNeedSuffix)
3393 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3394 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3395 }
3396
3397 /* The path. */
3398 if ( pMod == NULL
3399 && pszSearchPath)
3400 {
3401 const char *pszCur = pszSearchPath;
3402 while (*pszCur != '\0')
3403 {
3404 /* Find the end of the component */
3405 KSIZE cch = 0;
3406 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
3407 cch++;
3408
3409 if ( cch > 0 /* wrong, but whatever */
3410 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
3411 {
3412 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
3413 if ( szPath[cch - 1] != ':'
3414 && szPath[cch - 1] != '/'
3415 && szPath[cch - 1] != '\\')
3416 *pszDst++ = '\\';
3417 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
3418 if (fNeedSuffix)
3419 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
3420 *pszDst = '\0';
3421
3422 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3423 if (pMod)
3424 break;
3425 }
3426
3427 /* Advance */
3428 pszCur += cch;
3429 while (*pszCur == ';')
3430 pszCur++;
3431 }
3432 }
3433
3434 /* Return. */
3435 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3436 {
3437 *ppMod = pMod;
3438 return 0;
3439 }
3440 *ppMod = NULL;
3441 return KERR_GENERAL_FAILURE;
3442}
3443
3444
3445/**
3446 * Creates a CRT slot for the given module.
3447 *
3448 * @returns 0 on success, non-zero on failure.
3449 * @param pModule The module.
3450 */
3451static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3452{
3453 KSIZE iSlot;
3454 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3455 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3456 if (g_aCrtSlots[iSlot].pModule == NULL)
3457 {
3458 KLDRADDR uAddr;
3459 int rc;
3460
3461 /* Do the linking: */
3462 g_aCrtSlots[iSlot].pModule = pModule;
3463 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3464 pModule->iCrtSlot = (KU8)iSlot;
3465
3466 /* resolve symbols: */
3467 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3468 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3469 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3470 if (rc != 0)
3471 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3472
3473 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "_beginthreadex", 14,
3474 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3475 *(KUPTR *)&g_aCrtSlots[iSlot].pfnBeginThreadEx = rc == 0 ? (KUPTR)uAddr : 0;
3476 //if (rc != 0)
3477 // kwErrPrintf("Failed to resolved '_beginthreadex' in '%s': %d\n", pModule->pszPath, rc);
3478
3479 return 0;
3480 }
3481 kwErrPrintf("Out of CRT slots!\n");
3482 return KERR_NO_MEMORY;
3483}
3484
3485
3486/**
3487 * Locates the module structure for an already loaded native module.
3488 *
3489 * This will create a module structure if needed.
3490 *
3491 * @returns Pointer to the module structure on success, NULL on failure.
3492 * @param hModule The native module handle.
3493 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3494 * @param pszLogName The name to use for logging/errors.
3495 */
3496static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName)
3497{
3498 /*
3499 * Get a normalized path for it.
3500 */
3501 char szModPath[1024];
3502 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3503 {
3504 char szNormPath[1024];
3505 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3506 if (rc == 0)
3507 {
3508 /*
3509 * Hash the path and look it up.
3510 */
3511 KU32 uHashPath;
3512 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3513 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3514 PKWMODULE pMod = g_apModules[idxHash];
3515 if (pMod)
3516 {
3517 do
3518 {
3519 if ( pMod->uHashPath == uHashPath
3520 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3521 {
3522 kwLdrModuleRetain(pMod);
3523 break;
3524 }
3525 pMod = pMod->pNextHash;
3526 } while (pMod);
3527 }
3528
3529 /*
3530 * If not in the hash table, so create a module entry.
3531 */
3532 if (!pMod)
3533 {
3534 PKLDRMOD pLdrMod;
3535 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3536 if (rc == 0)
3537 {
3538 /** @todo more accurately determine location */
3539 const char *pszFilename = kHlpGetFilename(szNormPath);
3540 KBOOL fDoReplacements = kwLdrModuleShouldDoNativeReplacements(pszFilename, KWLOCATION_SYSTEM32);
3541 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3542 fDoReplacements, NULL /*pVirtualApiMod*/);
3543 if (!pMod)
3544 {
3545 kLdrModClose(pLdrMod);
3546 kwErrPrintf("out of memory\n");
3547 }
3548 }
3549 else
3550 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszLogName, rc);
3551 }
3552 if (pMod)
3553 {
3554 /*
3555 * Create a CRT slot for the module if necessary.
3556 */
3557 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3558 return pMod;
3559 rc = kwLdrModuleCreateCrtSlot(pMod);
3560 if (rc == 0)
3561 return pMod;
3562 kwLdrModuleRelease(pMod);
3563 }
3564 }
3565 else
3566 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszLogName, GetLastError());
3567 }
3568 else
3569 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszLogName, GetLastError());
3570 return NULL;
3571}
3572
3573
3574/**
3575 * Locates the module structure for an already loaded native module.
3576 *
3577 * This will create a module structure if needed.
3578 *
3579 * @returns Pointer to the module structure on success, NULL on failure.
3580 * @param pszName The name of the module.
3581 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3582 * @param fAlwaysPresent Whether the module is expected to always be present,
3583 * or not. If not, complain less.
3584 */
3585static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3586{
3587 /*
3588 * Locate the module handle and pass it to kwLdrModuleForLoadedNativeByHandle.
3589 */
3590 HANDLE hModule = GetModuleHandleA(pszName);
3591 if (hModule)
3592 return kwLdrModuleForLoadedNativeByHandle(hModule, fEnsureCrtSlot, pszName);
3593 if (fAlwaysPresent)
3594 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3595 return NULL;
3596}
3597
3598
3599/**
3600 * Does the TLS memory initialization for a module on the current thread.
3601 *
3602 * @returns 0 on success, error on failure.
3603 * @param pMod The module.
3604 */
3605static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3606{
3607 if (pMod->u.Manual.idxTls != KU32_MAX)
3608 {
3609 PTEB pTeb = NtCurrentTeb();
3610 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3611 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3612 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3613 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3614 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3615 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3616 if (pMod->u.Manual.cbTlsInitData)
3617 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3618 }
3619 return 0;
3620}
3621
3622
3623/**
3624 * Does the TLS callbacks for a module.
3625 *
3626 * @param pMod The module.
3627 * @param dwReason The callback reason.
3628 */
3629static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3630{
3631 if (pMod->u.Manual.cTlsCallbacks)
3632 {
3633 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3634 do
3635 {
3636 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *ppfnCallback, pMod->hOurMod, dwReason));
3637 (*ppfnCallback)(pMod->hOurMod, dwReason, 0);
3638 } while (*++ppfnCallback);
3639 }
3640}
3641
3642
3643/**
3644 * Does module initialization starting at @a pMod.
3645 *
3646 * This is initially used on the executable. Later it is used by the
3647 * LoadLibrary interceptor.
3648 *
3649 * @returns 0 on success, error on failure.
3650 * @param pMod The module to initialize.
3651 */
3652static int kwLdrModuleInitTree(PKWMODULE pMod)
3653{
3654 int rc = 0;
3655 if (!pMod->fNative)
3656 {
3657 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3658 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3659
3660 /*
3661 * Need to copy bits?
3662 */
3663 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3664 {
3665 if (pMod->u.Manual.fUseLdBuf)
3666 {
3667#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3668 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3669 {
3670 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3671 kHlpAssert(fRc); K_NOREF(fRc);
3672 }
3673#endif
3674 g_pModPrevInLdBuf = g_pModInLdBuf;
3675 g_pModInLdBuf = pMod;
3676 }
3677
3678 /* Do quick zeroing and copying when we can. */
3679 pMod->u.Manual.fCanDoQuick = K_FALSE;
3680 if ( pMod->u.Manual.fCanDoQuick
3681 && ( !pMod->u.Manual.fUseLdBuf
3682 || g_pModPrevInLdBuf == pMod))
3683 {
3684 /* Zero first. */
3685 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3686 switch (pMod->u.Manual.cQuickZeroChunks)
3687 {
3688 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3689 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3690 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3691 case 0: break;
3692 }
3693
3694 /* Then copy. */
3695 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3696 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3697 switch (pMod->u.Manual.cQuickCopyChunks)
3698 {
3699 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3700 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3701 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3702 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3703 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3704 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3705 case 0: break;
3706 }
3707 }
3708 /* Must copy the whole image. */
3709 else
3710 {
3711 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3712 pMod->u.Manual.fCanDoQuick = K_TRUE;
3713 }
3714 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3715 }
3716
3717#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3718 /*
3719 * Need to register function table?
3720 */
3721 if ( !pMod->u.Manual.fRegisteredFunctionTable
3722 && pMod->u.Manual.cFunctions > 0)
3723 {
3724 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3725 pMod->u.Manual.cFunctions,
3726 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3727 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3728 }
3729#endif
3730
3731
3732 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3733 {
3734 /*
3735 * Must do imports first, but mark our module as being initialized to avoid
3736 * endless recursion should there be a dependency loop.
3737 */
3738 KSIZE iImp;
3739 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3740
3741 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3742 {
3743 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3744 if (rc != 0)
3745 return rc;
3746 }
3747
3748 /* Do TLS allocations for module init? */
3749 rc = kwLdrCallTlsAllocateAndInit(pMod);
3750 if (rc != 0)
3751 return rc;
3752 if (pMod->u.Manual.cTlsCallbacks > 0)
3753 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3754
3755 /* Finally call the entry point. */
3756 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3757 if (rc == 0)
3758 pMod->u.Manual.enmState = KWMODSTATE_READY;
3759 else
3760 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3761 }
3762 }
3763 /*
3764 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3765 * _MSPDBSRV_ENDPOINT_ changes value.
3766 */
3767 else if (pMod->fNeedReInit)
3768 {
3769 int rc2;
3770 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3771 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3772 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3773 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3774 if (!rc && !rc2)
3775 { /* likely */ }
3776 else
3777 {
3778 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3779 if (rc2 && !rc)
3780 rc = rc2;
3781 }
3782 pMod->fNeedReInit = K_FALSE;
3783 }
3784 return rc;
3785}
3786
3787
3788/**
3789 * Looks up a module handle for a tool.
3790 *
3791 * @returns Referenced loader module on success, NULL on if not found.
3792 * @param pTool The tool.
3793 * @param hmod The module handle.
3794 */
3795static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3796{
3797 KUPTR const uHMod = (KUPTR)hmod;
3798 PKWMODULE *papMods;
3799 KU32 iEnd;
3800 KU32 i;
3801 PKWDYNLOAD pDynLoad;
3802
3803 if (pTool)
3804 { /* likely */ }
3805 else
3806 return NULL;
3807
3808 /* The executable. */
3809 if ( hmod == NULL
3810 || (pTool->u.Sandboxed.pExe && pTool->u.Sandboxed.pExe->hOurMod == hmod))
3811 {
3812 if (pTool->u.Sandboxed.pExe)
3813 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3814 return NULL;
3815 }
3816
3817 /*
3818 * Binary lookup using the module table.
3819 */
3820 papMods = pTool->u.Sandboxed.papModules;
3821 iEnd = pTool->u.Sandboxed.cModules;
3822 if (iEnd)
3823 {
3824 KU32 iStart = 0;
3825 i = iEnd / 2;
3826 for (;;)
3827 {
3828 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
3829 if (uHMod < uHModCur)
3830 {
3831 iEnd = i--;
3832 if (iStart <= i)
3833 { }
3834 else
3835 break;
3836 }
3837 else if (uHMod != uHModCur)
3838 {
3839 iStart = ++i;
3840 if (i < iEnd)
3841 { }
3842 else
3843 break;
3844 }
3845 /* We've got a match. Always return the non-virtual module (first) when there is one. */
3846 else if (!papMods[i]->pVirtualApiMod)
3847 return kwLdrModuleRetain(papMods[i]);
3848 else
3849 {
3850 while (i > 0 && papMods[i - 1]->pVirtualApiMod && papMods[i - 1]->hOurMod == hmod)
3851 i--;
3852 return kwLdrModuleRetain(papMods[i]);
3853 }
3854
3855 i = iStart + (iEnd - iStart) / 2;
3856 }
3857
3858#ifndef NDEBUG
3859 iStart = pTool->u.Sandboxed.cModules;
3860 while (--iStart > 0)
3861 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3862 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3863#endif
3864 }
3865
3866 /*
3867 * Dynamically loaded images.
3868 */
3869 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3870 if (pDynLoad->hmod == hmod)
3871 {
3872 if (pDynLoad->pMod)
3873 return kwLdrModuleRetain(pDynLoad->pMod);
3874 KWFS_TODO();
3875 return NULL;
3876 }
3877
3878 return NULL;
3879}
3880
3881/**
3882 * Adds the given module to the tool import table.
3883 *
3884 * @returns 0 on success, non-zero on failure.
3885 * @param pTool The tool.
3886 * @param pMod The module.
3887 */
3888static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3889{
3890 /*
3891 * Binary lookup. Locating the right slot for it, return if already there.
3892 */
3893 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3894 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3895 KU32 iEnd = pTool->u.Sandboxed.cModules;
3896 KU32 i;
3897 if (iEnd)
3898 {
3899 KU32 iStart = 0;
3900 i = iEnd / 2;
3901 for (;;)
3902 {
3903 PKWMODULE pCurMod = papMods[i];
3904 KUPTR const uHModCur = (KUPTR)pCurMod->hOurMod;
3905 if (uHMod < uHModCur)
3906 {
3907 iEnd = i;
3908 if (iStart < i)
3909 { }
3910 else
3911 break;
3912 }
3913 else if (uHMod != uHModCur)
3914 {
3915 iStart = ++i;
3916 if (i < iEnd)
3917 { }
3918 else
3919 break;
3920 }
3921 else
3922 {
3923 /* Already there in the table. The non-virtual module must be the first
3924 entry if we've got duplicate hmod values because of virtual modules. */
3925 if (pMod != pCurMod)
3926 {
3927 /* Skip to the last module with the same hmod. */
3928 while (i + 1 < iEnd && (KUPTR)(pCurMod = papMods[i + 1])->hOurMod == uHMod)
3929 {
3930 if (pMod == pCurMod)
3931 return 0;
3932 i++;
3933 }
3934
3935 /* Then scan backwards till the first one. */
3936 while (i > 0 && (KUPTR)(pCurMod = papMods[i - 1])->hOurMod == uHMod)
3937 {
3938 if (pMod == pCurMod)
3939 return 0;
3940 i--;
3941 }
3942 pCurMod = papMods[i];
3943 if (pMod != pCurMod)
3944 {
3945 if (pMod->pVirtualApiMod && !pCurMod->pVirtualApiMod)
3946 i++;
3947 break;
3948 }
3949 }
3950 return 0;
3951 }
3952
3953 i = iStart + (iEnd - iStart) / 2;
3954 }
3955#ifndef NDEBUG
3956 iStart = pTool->u.Sandboxed.cModules;
3957 while (--iStart > 0)
3958 {
3959 kHlpAssert(papMods[iStart] != pMod);
3960 kHlpAssert( (KUPTR)papMods[iStart]->hOurMod != uHMod
3961 || pMod->pVirtualApiMod
3962 || papMods[iStart]->pVirtualApiMod);
3963 }
3964 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod <= uHMod);
3965 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod >= uHMod);
3966#endif
3967 }
3968 else
3969 i = 0;
3970
3971 /*
3972 * Grow the table?
3973 */
3974 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3975 {
3976 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3977 if (!pvNew)
3978 return KERR_NO_MEMORY;
3979 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3980 }
3981
3982 /* Insert it. */
3983 if (i != pTool->u.Sandboxed.cModules)
3984 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3985 papMods[i] = kwLdrModuleRetain(pMod);
3986 pTool->u.Sandboxed.cModules++;
3987 KWLDR_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3988 return 0;
3989}
3990
3991
3992/**
3993 * Adds the given module and all its imports to the
3994 *
3995 * @returns 0 on success, non-zero on failure.
3996 * @param pTool The tool.
3997 * @param pMod The module.
3998 */
3999static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
4000{
4001 int rc = kwToolAddModule(pTool, pMod);
4002 if (pMod->pVirtualApiMod && rc == 0)
4003 rc = kwToolAddModule(pTool, pMod->pVirtualApiMod);
4004 if (!pMod->fNative && rc == 0)
4005 {
4006 KSIZE iImp = pMod->u.Manual.cImpMods;
4007 while (iImp-- > 0)
4008 {
4009 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
4010 if (rc == 0)
4011 { }
4012 else
4013 break;
4014 }
4015 }
4016
4017 return 0;
4018}
4019
4020
4021/**
4022 * Creates a tool entry and inserts it.
4023 *
4024 * @returns Pointer to the tool entry. NULL on failure.
4025 * @param pToolFsObj The file object of the tool. The created tool
4026 * will be associated with it.
4027 *
4028 * A reference is donated by the caller and must be
4029 * released.
4030 * @param pszSearchPath The PATH environment variable value, or NULL.
4031 */
4032static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
4033{
4034 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
4035 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
4036 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
4037 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
4038 if (pTool)
4039 {
4040 KBOOL fRc;
4041 pTool->pwszPath = (wchar_t const *)(pTool + 1);
4042 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
4043 kHlpAssert(fRc); K_NOREF(fRc);
4044
4045 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
4046 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
4047 kHlpAssert(fRc);
4048
4049 pTool->enmType = KWTOOLTYPE_SANDBOXED;
4050 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
4051 NULL /*pEexeMod*/, pszSearchPath);
4052 if (pTool->u.Sandboxed.pExe)
4053 {
4054 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
4055 if (rc == 0)
4056 {
4057 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
4058 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
4059 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
4060 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
4061 else
4062 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
4063 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
4064 }
4065 else
4066 {
4067 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
4068 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
4069 pTool->u.Sandboxed.pExe = NULL;
4070 pTool->enmType = KWTOOLTYPE_EXEC;
4071 }
4072 }
4073 else
4074 pTool->enmType = KWTOOLTYPE_EXEC;
4075
4076 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4077 g_cTools++;
4078 return pTool;
4079 }
4080 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4081 return NULL;
4082}
4083
4084
4085/**
4086 * Looks up the given tool, creating a new tool table entry if necessary.
4087 *
4088 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
4089 * @param pszExe The executable for the tool (not normalized).
4090 * @param cEnvVars Number of environment varibles.
4091 * @param papszEnvVars Environment variables. For getting the PATH.
4092 */
4093static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
4094{
4095 /*
4096 * We associate the tools instances with the file system objects.
4097 *
4098 * We'd like to do the lookup without invaliding the volatile parts of the
4099 * cache, thus the double lookup here. The cache gets invalidate later on.
4100 */
4101 KFSLOOKUPERROR enmError;
4102 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4103 if ( !pToolFsObj
4104 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
4105 {
4106 kFsCacheInvalidateCustomBoth(g_pFsCache);
4107 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4108 }
4109 if (pToolFsObj)
4110 {
4111 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
4112 {
4113 const char *pszSearchPath;
4114 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
4115 if (pTool)
4116 {
4117 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4118 return pTool;
4119 }
4120
4121 /*
4122 * Need to create a new tool.
4123 */
4124 pszSearchPath = NULL;
4125 while (cEnvVars-- > 0)
4126 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
4127 {
4128 pszSearchPath = &papszEnvVars[cEnvVars][5];
4129 break;
4130 }
4131
4132 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
4133 if (pTool)
4134 return pTool;
4135
4136 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
4137 }
4138 else
4139 {
4140 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4141 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
4142 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
4143 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
4144 }
4145 }
4146 else
4147 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
4148 return NULL;
4149}
4150
4151
4152
4153/*
4154 *
4155 * File system cache.
4156 * File system cache.
4157 * File system cache.
4158 *
4159 */
4160
4161
4162/**
4163 * This is for kDep.
4164 */
4165int kwFsPathExists(const char *pszPath)
4166{
4167 BirdTimeSpec_T TsIgnored;
4168 KFSLOOKUPERROR enmError;
4169 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
4170 if (pFsObj)
4171 {
4172 kFsCacheObjRelease(g_pFsCache, pFsObj);
4173 return 1;
4174 }
4175 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
4176}
4177
4178
4179/* duplicated in dir-nt-bird.c */
4180void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
4181{
4182 KFSLOOKUPERROR enmError;
4183 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
4184 if (pPathObj)
4185 {
4186 KSIZE off = pPathObj->cchParent;
4187 if (off > 0)
4188 {
4189 KSIZE offEnd = off + pPathObj->cchName;
4190 if (offEnd < cbFull)
4191 {
4192 PKFSDIR pAncestor;
4193
4194 pszFull[off + pPathObj->cchName] = '\0';
4195 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
4196
4197 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4198 {
4199 kHlpAssert(off > 1);
4200 kHlpAssert(pAncestor != NULL);
4201 kHlpAssert(pAncestor->Obj.cchName > 0);
4202 pszFull[--off] = '/';
4203 off -= pAncestor->Obj.cchName;
4204 kHlpAssert(pAncestor->Obj.cchParent == off);
4205 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4206 }
4207 kFsCacheObjRelease(g_pFsCache, pPathObj);
4208 return;
4209 }
4210 }
4211 else
4212 {
4213 if ((size_t)pPathObj->cchName + 1 < cbFull)
4214 {
4215 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
4216 pszFull[pPathObj->cchName] = '/';
4217 pszFull[pPathObj->cchName + 1] = '\0';
4218
4219 kFsCacheObjRelease(g_pFsCache, pPathObj);
4220 return;
4221 }
4222 }
4223
4224 /* do fallback. */
4225 kHlpAssertFailed();
4226 kFsCacheObjRelease(g_pFsCache, pPathObj);
4227 }
4228
4229 nt_fullpath(pszPath, pszFull, cbFull);
4230}
4231
4232
4233/**
4234 * Helper for getting the extension of a UTF-16 path.
4235 *
4236 * @returns Pointer to the extension or the terminator.
4237 * @param pwszPath The path.
4238 * @param pcwcExt Where to return the length of the extension.
4239 */
4240static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
4241{
4242 wchar_t const *pwszName = pwszPath;
4243 wchar_t const *pwszExt = NULL;
4244 for (;;)
4245 {
4246 wchar_t const wc = *pwszPath++;
4247 if (wc == '.')
4248 pwszExt = pwszPath;
4249 else if (wc == '/' || wc == '\\' || wc == ':')
4250 {
4251 pwszName = pwszPath;
4252 pwszExt = NULL;
4253 }
4254 else if (wc == '\0')
4255 {
4256 if (pwszExt)
4257 {
4258 *pcwcExt = pwszPath - pwszExt - 1;
4259 return pwszExt;
4260 }
4261 *pcwcExt = 0;
4262 return pwszPath - 1;
4263 }
4264 }
4265}
4266
4267
4268
4269/**
4270 * Parses the argument string passed in as pszSrc.
4271 *
4272 * @returns size of the processed arguments.
4273 * @param pszSrc Pointer to the commandline that's to be parsed.
4274 * @param pcArgs Where to return the number of arguments.
4275 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
4276 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
4277 *
4278 * @remarks Lifted from startuphacks-win.c
4279 */
4280static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
4281{
4282 int bs;
4283 char chQuote;
4284 char *pfFlags;
4285 int cbArgs;
4286 int cArgs;
4287
4288#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
4289#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
4290#define WHITE(c) ((c) == ' ' || (c) == '\t')
4291
4292#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
4293#define _ARG_RESPONSE 0x02 /* Argument read from response file */
4294#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
4295#define _ARG_ENV 0x08 /* Argument from environment */
4296#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
4297
4298 cArgs = 0;
4299 cbArgs = 0;
4300
4301#if 0
4302 /* argv[0] */
4303 PUTC((char)_ARG_NONZERO);
4304 PUTV;
4305 for (;;)
4306 {
4307 PUTC(*pszSrc);
4308 if (*pszSrc == 0)
4309 break;
4310 ++pszSrc;
4311 }
4312 ++pszSrc;
4313#endif
4314
4315 for (;;)
4316 {
4317 while (WHITE(*pszSrc))
4318 ++pszSrc;
4319 if (*pszSrc == 0)
4320 break;
4321 pfFlags = pchPool;
4322 PUTC((unsigned char)_ARG_NONZERO);
4323 PUTV;
4324 bs = 0; chQuote = 0;
4325 for (;;)
4326 {
4327 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
4328 {
4329 while (bs >= 2)
4330 {
4331 PUTC('\\');
4332 bs -= 2;
4333 }
4334 if (bs & 1)
4335 PUTC(*pszSrc);
4336 else
4337 {
4338 chQuote = chQuote ? 0 : *pszSrc;
4339 if (pfFlags != NULL)
4340 *pfFlags |= _ARG_DQUOTE;
4341 }
4342 bs = 0;
4343 }
4344 else if (*pszSrc == '\\')
4345 ++bs;
4346 else
4347 {
4348 while (bs != 0)
4349 {
4350 PUTC('\\');
4351 --bs;
4352 }
4353 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
4354 break;
4355 PUTC(*pszSrc);
4356 }
4357 ++pszSrc;
4358 }
4359 PUTC(0);
4360 }
4361
4362 *pcArgs = cArgs;
4363 return cbArgs;
4364}
4365
4366
4367
4368
4369/*
4370 *
4371 * Process and thread related APIs.
4372 * Process and thread related APIs.
4373 * Process and thread related APIs.
4374 *
4375 */
4376
4377/** Common worker for ExitProcess(), exit() and friends. */
4378static void WINAPI kwSandboxDoExit(int uExitCode)
4379{
4380 if (g_Sandbox.idMainThread == GetCurrentThreadId())
4381 {
4382 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
4383
4384 g_Sandbox.rcExitCode = (int)uExitCode;
4385
4386 /* Before we jump, restore the TIB as we're not interested in any
4387 exception chain stuff installed by the sandboxed executable. */
4388 *pTib = g_Sandbox.TibMainThread;
4389 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
4390
4391 longjmp(g_Sandbox.JmpBuf, 1);
4392 }
4393 KWFS_TODO();
4394}
4395
4396
4397/** ExitProcess replacement. */
4398static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
4399{
4400 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
4401 kwSandboxDoExit((int)uExitCode);
4402}
4403
4404
4405/** ExitProcess replacement. */
4406static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
4407{
4408 if (hProcess == GetCurrentProcess())
4409 kwSandboxDoExit(uExitCode);
4410 KWFS_TODO();
4411 return TerminateProcess(hProcess, uExitCode);
4412}
4413
4414
4415/** Normal CRT exit(). */
4416static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
4417{
4418 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
4419 kwSandboxDoExit(rcExitCode);
4420}
4421
4422
4423/** Quick CRT _exit(). */
4424static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
4425{
4426 /* Quick. */
4427 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
4428 kwSandboxDoExit(rcExitCode);
4429}
4430
4431
4432/** Return to caller CRT _cexit(). */
4433static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
4434{
4435 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
4436 kwSandboxDoExit(rcExitCode);
4437}
4438
4439
4440/** Quick return to caller CRT _c_exit(). */
4441static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
4442{
4443 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
4444 kwSandboxDoExit(rcExitCode);
4445}
4446
4447
4448/** Runtime error and exit _amsg_exit(). */
4449static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
4450{
4451 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
4452 kwSandboxDoExit(255);
4453}
4454
4455
4456/** CRT - terminate(). */
4457static void __cdecl kwSandbox_msvcrt_terminate(void)
4458{
4459 KW_LOG(("\nRuntime - terminate!\n"));
4460 kwSandboxDoExit(254);
4461}
4462
4463
4464/** CRT - _onexit */
4465static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
4466{
4467 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4468 {
4469 PKWEXITCALLACK pCallback;
4470 KW_LOG(("_onexit(%p)\n", pfnFunc));
4471 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4472
4473 pCallback = kHlpAlloc(sizeof(*pCallback));
4474 if (pCallback)
4475 {
4476 pCallback->pfnCallback = pfnFunc;
4477 pCallback->fAtExit = K_FALSE;
4478 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4479 g_Sandbox.pExitCallbackHead = pCallback;
4480 return pfnFunc;
4481 }
4482 return NULL;
4483 }
4484 //KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
4485 //return pfnFunc;
4486}
4487
4488
4489/** CRT - atexit */
4490static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
4491{
4492 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4493 {
4494 PKWEXITCALLACK pCallback;
4495 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4496 KW_LOG(("atexit(%p)\n", pfnFunc));
4497
4498 pCallback = kHlpAlloc(sizeof(*pCallback));
4499 if (pCallback)
4500 {
4501 pCallback->pfnCallback = (_onexit_t)pfnFunc;
4502 pCallback->fAtExit = K_TRUE;
4503 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4504 g_Sandbox.pExitCallbackHead = pCallback;
4505 return 0;
4506 }
4507 return -1;
4508 }
4509 //KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4510 //return 0;
4511}
4512
4513
4514/** Kernel32 - SetConsoleCtrlHandler(). */
4515static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4516{
4517 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4518 return TRUE;
4519}
4520
4521
4522/** The CRT internal __getmainargs() API. */
4523static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4524 int dowildcard, int const *piNewMode)
4525{
4526 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4527 *pargc = g_Sandbox.cArgs;
4528 *pargv = g_Sandbox.papszArgs;
4529 *penvp = g_Sandbox.environ;
4530
4531 /** @todo startinfo points at a newmode (setmode) value. */
4532 return 0;
4533}
4534
4535
4536/** The CRT internal __wgetmainargs() API. */
4537static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4538 int dowildcard, int const *piNewMode)
4539{
4540 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4541 *pargc = g_Sandbox.cArgs;
4542 *pargv = g_Sandbox.papwszArgs;
4543 *penvp = g_Sandbox.wenviron;
4544
4545 /** @todo startinfo points at a newmode (setmode) value. */
4546 return 0;
4547}
4548
4549
4550
4551/** Kernel32 - GetCommandLineA() */
4552static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4553{
4554 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4555 return g_Sandbox.pszCmdLine;
4556}
4557
4558
4559/** Kernel32 - GetCommandLineW() */
4560static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4561{
4562 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4563 return g_Sandbox.pwszCmdLine;
4564}
4565
4566
4567/** Kernel32 - GetStartupInfoA() */
4568static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4569{
4570 KW_LOG(("GetStartupInfoA\n"));
4571 GetStartupInfoA(pStartupInfo);
4572 pStartupInfo->lpReserved = NULL;
4573 pStartupInfo->lpTitle = NULL;
4574 pStartupInfo->lpReserved2 = NULL;
4575 pStartupInfo->cbReserved2 = 0;
4576}
4577
4578
4579/** Kernel32 - GetStartupInfoW() */
4580static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4581{
4582 KW_LOG(("GetStartupInfoW\n"));
4583 GetStartupInfoW(pStartupInfo);
4584 pStartupInfo->lpReserved = NULL;
4585 pStartupInfo->lpTitle = NULL;
4586 pStartupInfo->lpReserved2 = NULL;
4587 pStartupInfo->cbReserved2 = 0;
4588}
4589
4590
4591/** CRT - __p___argc(). */
4592static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4593{
4594 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4595 return &g_Sandbox.cArgs;
4596}
4597
4598
4599/** CRT - __p___argv(). */
4600static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4601{
4602 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4603 return &g_Sandbox.papszArgs;
4604}
4605
4606
4607/** CRT - __p___sargv(). */
4608static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4609{
4610 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4611 return &g_Sandbox.papwszArgs;
4612}
4613
4614
4615/** CRT - __p__acmdln(). */
4616static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4617{
4618 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4619 return (char **)&g_Sandbox.pszCmdLine;
4620}
4621
4622
4623/** CRT - __p__acmdln(). */
4624static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4625{
4626 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4627 return &g_Sandbox.pwszCmdLine;
4628}
4629
4630
4631/** CRT - __p__pgmptr(). */
4632static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4633{
4634 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4635 return &g_Sandbox.pgmptr;
4636}
4637
4638
4639/** CRT - __p__wpgmptr(). */
4640static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4641{
4642 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4643 return &g_Sandbox.wpgmptr;
4644}
4645
4646
4647/** CRT - _get_pgmptr(). */
4648static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4649{
4650 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4651 *ppszValue = g_Sandbox.pgmptr;
4652 return 0;
4653}
4654
4655
4656/** CRT - _get_wpgmptr(). */
4657static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4658{
4659 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4660 *ppwszValue = g_Sandbox.wpgmptr;
4661 return 0;
4662}
4663
4664/** Just in case. */
4665static void kwSandbox_msvcrt__wincmdln(void)
4666{
4667 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4668 KWFS_TODO();
4669}
4670
4671
4672/** Just in case. */
4673static void kwSandbox_msvcrt__wwincmdln(void)
4674{
4675 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4676 KWFS_TODO();
4677}
4678
4679/** CreateThread interceptor. */
4680static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4681 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4682 DWORD fFlags, PDWORD pidThread)
4683{
4684 HANDLE hThread = NULL;
4685 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4686 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4687 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4688 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4689 {
4690 /* Allow link::DbgThread. */
4691 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4692 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4693 }
4694 else
4695 KWFS_TODO();
4696 return hThread;
4697}
4698
4699
4700/** _beginthread - create a new thread. */
4701static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4702{
4703 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4704 KWFS_TODO();
4705 return 0;
4706}
4707
4708
4709/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4710static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4711 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4712 unsigned fCreate, unsigned *pidThread)
4713{
4714 /*
4715 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4716 * whatever it needs to.
4717 */
4718 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4719 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4720 pfnThreadProc, pvUser, fCreate, pidThread));
4721 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4722 {
4723 uintptr_t rcRet;
4724 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4725 if (!s_pfnReal)
4726 {
4727 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4728 if (!s_pfnReal)
4729 {
4730 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4731 __debugbreak();
4732 }
4733 }
4734 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4735 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4736 return rcRet;
4737 }
4738
4739 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4740 KWFS_TODO();
4741 return 0;
4742}
4743
4744
4745/** _beginthreadex - create a new thread. */
4746static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex_wrapped(void *pvSecAttr, unsigned cbStack,
4747 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4748 unsigned fCreate, unsigned *pidThread, PKWCRTSLOT pSlot)
4749{
4750 /*
4751 * Since the VC++ 12 (VS 2013) compiler, the 2nd pass is now threaded.
4752 * Let it do whatever it needs to.
4753 */
4754 KW_LOG(("kwSandbox_msvcrt__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4755 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4756 pfnThreadProc, pvUser, fCreate, pidThread));
4757 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4758 && pSlot->pfnBeginThreadEx)
4759 {
4760 uintptr_t rcRet = pSlot->pfnBeginThreadEx(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4761 KW_LOG(("kwSandbox_msvcrt__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4762 return rcRet;
4763 }
4764
4765 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4766 KWFS_TODO();
4767 return 0;
4768}
4769
4770CRT_SLOT_FUNCTION_WRAPPER(uintptr_t __cdecl, kwSandbox_msvcrt__beginthreadex,
4771 (void *pvSecAttr, unsigned cbStack, unsigned (__stdcall *pfnThreadProc)(void *),
4772 void *pvUser, unsigned fCreate, unsigned *pidThread),
4773 (pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread, &g_aCrtSlots[iCrtSlot]));
4774
4775
4776
4777/*
4778 *
4779 * Environment related APIs.
4780 * Environment related APIs.
4781 * Environment related APIs.
4782 *
4783 */
4784
4785/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4786static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4787{
4788 char *pszzEnv;
4789 char *pszCur;
4790 KSIZE cbNeeded = 1;
4791 KSIZE iVar = 0;
4792
4793 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4794
4795 /* Figure how space much we need first. */
4796 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4797 cbNeeded += kHlpStrLen(pszCur) + 1;
4798
4799 /* Allocate it. */
4800 pszzEnv = kHlpAlloc(cbNeeded);
4801 if (pszzEnv)
4802 {
4803 char *psz = pszzEnv;
4804 iVar = 0;
4805 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4806 {
4807 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4808 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4809 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4810 }
4811 *psz++ = '\0';
4812 kHlpAssert((KUPTR)(psz - pszzEnv) == cbNeeded);
4813 }
4814
4815 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4816#if 0
4817 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4818 pszCur = pszzEnv;
4819 iVar = 0;
4820 while (*pszCur)
4821 {
4822 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4823 iVar++;
4824 pszCur += kHlpStrLen(pszCur) + 1;
4825 }
4826 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4827 pszCur++;
4828 fprintf(stderr, "ended at %p, after %u bytes (expected %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4829#endif
4830 return pszzEnv;
4831}
4832
4833
4834/** Kernel32 - GetEnvironmentStrings */
4835static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4836{
4837 KW_LOG(("GetEnvironmentStrings!\n"));
4838 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4839}
4840
4841
4842/** Kernel32 - GetEnvironmentStringsW */
4843static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4844{
4845 wchar_t *pwszzEnv;
4846 wchar_t *pwszCur;
4847 KSIZE cwcNeeded = 1;
4848 KSIZE iVar = 0;
4849
4850 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4851
4852 /* Figure how space much we need first. */
4853 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4854 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4855
4856 /* Allocate it. */
4857 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4858 if (pwszzEnv)
4859 {
4860 wchar_t *pwsz = pwszzEnv;
4861 iVar = 0;
4862 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4863 {
4864 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4865 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4866 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4867 }
4868 *pwsz++ = '\0';
4869 kHlpAssert((KUPTR)(pwsz - pwszzEnv) == cwcNeeded);
4870 }
4871
4872 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4873 return pwszzEnv;
4874}
4875
4876
4877/** Kernel32 - FreeEnvironmentStringsA */
4878static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4879{
4880 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4881 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4882 kHlpFree(pszzEnv);
4883 return TRUE;
4884}
4885
4886
4887/** Kernel32 - FreeEnvironmentStringsW */
4888static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4889{
4890 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4891 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4892 kHlpFree(pwszzEnv);
4893 return TRUE;
4894}
4895
4896
4897/**
4898 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4899 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4900 *
4901 * @returns 0 on success, non-zero on failure.
4902 * @param pSandbox The sandbox.
4903 * @param cMin Minimum size, including terminator.
4904 */
4905static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4906{
4907 void *pvNew;
4908 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4909 KSIZE cNew = cOld + 256;
4910 while (cNew < cMin)
4911 cNew += 256;
4912
4913 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4914 if (pvNew)
4915 {
4916 pSandbox->environ = (char **)pvNew;
4917 pSandbox->environ[cOld] = NULL;
4918
4919 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4920 if (pvNew)
4921 {
4922 pSandbox->papszEnvVars = (char **)pvNew;
4923 pSandbox->papszEnvVars[cOld] = NULL;
4924
4925 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4926 if (pvNew)
4927 {
4928 pSandbox->wenviron = (wchar_t **)pvNew;
4929 pSandbox->wenviron[cOld] = NULL;
4930
4931 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4932 if (pvNew)
4933 {
4934 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4935 pSandbox->papwszEnvVars[cOld] = NULL;
4936
4937 pSandbox->cEnvVarsAllocated = cNew;
4938 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4939 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4940 return 0;
4941 }
4942 }
4943 }
4944 }
4945 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4946 return KERR_NO_MEMORY;
4947}
4948
4949
4950/**
4951 * Sets an environment variable, ANSI style.
4952 *
4953 * @returns 0 on success, non-zero on failure.
4954 * @param pSandbox The sandbox.
4955 * @param pchVar The variable name.
4956 * @param cchVar The length of the name.
4957 * @param pszValue The value.
4958 */
4959static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4960{
4961 /* Allocate and construct the new strings. */
4962 KSIZE cchTmp = kHlpStrLen(pszValue);
4963 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4964 if (pszNew)
4965 {
4966 wchar_t *pwszNew;
4967 kHlpMemCopy(pszNew, pchVar, cchVar);
4968 pszNew[cchVar] = '=';
4969 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4970 cchTmp += cchVar + 1;
4971 pszNew[cchTmp] = '\0';
4972
4973 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4974 if (pwszNew)
4975 {
4976 /* Look it up. */
4977 KSIZE iVar = 0;
4978 char *pszEnv;
4979 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4980 {
4981 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4982 && pszEnv[cchVar] == '=')
4983 {
4984 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4985 " iVar=%d: %p='%s' and %p='%ls'\n",
4986 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4987 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4988 iVar, pszNew, pszNew, pwszNew, pwszNew));
4989
4990 kHlpFree(pSandbox->papszEnvVars[iVar]);
4991 pSandbox->papszEnvVars[iVar] = pszNew;
4992 pSandbox->environ[iVar] = pszNew;
4993
4994 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4995 pSandbox->papwszEnvVars[iVar] = pwszNew;
4996 pSandbox->wenviron[iVar] = pwszNew;
4997 return 0;
4998 }
4999 iVar++;
5000 }
5001
5002 /* Not found, do we need to grow the table first? */
5003 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5004 kwSandboxGrowEnv(pSandbox, iVar + 2);
5005 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5006 {
5007 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5008
5009 pSandbox->papszEnvVars[iVar + 1] = NULL;
5010 pSandbox->papszEnvVars[iVar] = pszNew;
5011 pSandbox->environ[iVar + 1] = NULL;
5012 pSandbox->environ[iVar] = pszNew;
5013
5014 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5015 pSandbox->papwszEnvVars[iVar] = pwszNew;
5016 pSandbox->wenviron[iVar + 1] = NULL;
5017 pSandbox->wenviron[iVar] = pwszNew;
5018 return 0;
5019 }
5020
5021 kHlpFree(pwszNew);
5022 }
5023 kHlpFree(pszNew);
5024 }
5025 KW_LOG(("Out of memory!\n"));
5026 return 0;
5027}
5028
5029
5030/**
5031 * Sets an environment variable, UTF-16 style.
5032 *
5033 * @returns 0 on success, non-zero on failure.
5034 * @param pSandbox The sandbox.
5035 * @param pwcVar The variable name.
5036 * @param cwcVar The length of the name.
5037 * @param pwszValue The value.
5038 */
5039static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
5040{
5041 /* Allocate and construct the new strings. */
5042 KSIZE cwcTmp = kwUtf16Len(pwszValue);
5043 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
5044 if (pwszNew)
5045 {
5046 char *pszNew;
5047 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
5048 pwszNew[cwcVar] = '=';
5049 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
5050 cwcTmp += cwcVar + 1;
5051 pwszNew[cwcVar] = '\0';
5052
5053 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
5054 if (pszNew)
5055 {
5056 /* Look it up. */
5057 KSIZE iVar = 0;
5058 wchar_t *pwszEnv;
5059 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5060 {
5061 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
5062 && pwszEnv[cwcVar] == '=')
5063 {
5064 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5065 " iVar=%d: %p='%s' and %p='%ls'\n",
5066 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5067 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5068 iVar, pszNew, pszNew, pwszNew, pwszNew));
5069
5070 kHlpFree(pSandbox->papszEnvVars[iVar]);
5071 pSandbox->papszEnvVars[iVar] = pszNew;
5072 pSandbox->environ[iVar] = pszNew;
5073
5074 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5075 pSandbox->papwszEnvVars[iVar] = pwszNew;
5076 pSandbox->wenviron[iVar] = pwszNew;
5077 return 0;
5078 }
5079 iVar++;
5080 }
5081
5082 /* Not found, do we need to grow the table first? */
5083 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5084 kwSandboxGrowEnv(pSandbox, iVar + 2);
5085 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5086 {
5087 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5088
5089 pSandbox->papszEnvVars[iVar + 1] = NULL;
5090 pSandbox->papszEnvVars[iVar] = pszNew;
5091 pSandbox->environ[iVar + 1] = NULL;
5092 pSandbox->environ[iVar] = pszNew;
5093
5094 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5095 pSandbox->papwszEnvVars[iVar] = pwszNew;
5096 pSandbox->wenviron[iVar + 1] = NULL;
5097 pSandbox->wenviron[iVar] = pwszNew;
5098 return 0;
5099 }
5100
5101 kHlpFree(pwszNew);
5102 }
5103 kHlpFree(pszNew);
5104 }
5105 KW_LOG(("Out of memory!\n"));
5106 return 0;
5107}
5108
5109
5110/** ANSI unsetenv worker. */
5111static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5112{
5113 KSIZE iVar = 0;
5114 char *pszEnv;
5115 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5116 {
5117 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5118 && pszEnv[cchVar] == '=')
5119 {
5120 KSIZE cVars = iVar;
5121 while (pSandbox->papszEnvVars[cVars])
5122 cVars++;
5123 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
5124 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
5125
5126 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5127 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5128 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5129
5130 kHlpFree(pSandbox->papszEnvVars[iVar]);
5131 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5132 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5133 pSandbox->papszEnvVars[cVars] = NULL;
5134 pSandbox->environ[cVars] = NULL;
5135
5136 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5137 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5138 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5139 pSandbox->papwszEnvVars[cVars] = NULL;
5140 pSandbox->wenviron[cVars] = NULL;
5141 return 0;
5142 }
5143 iVar++;
5144 }
5145 return KERR_ENVVAR_NOT_FOUND;
5146}
5147
5148
5149/** UTF-16 unsetenv worker. */
5150static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5151{
5152 KSIZE iVar = 0;
5153 wchar_t *pwszEnv;
5154 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5155 {
5156 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5157 && pwszEnv[cwcVar] == '=')
5158 {
5159 KSIZE cVars = iVar;
5160 while (pSandbox->papwszEnvVars[cVars])
5161 cVars++;
5162 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
5163 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
5164
5165 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5166 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5167 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5168
5169 kHlpFree(pSandbox->papszEnvVars[iVar]);
5170 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5171 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5172 pSandbox->papszEnvVars[cVars] = NULL;
5173 pSandbox->environ[cVars] = NULL;
5174
5175 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5176 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5177 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5178 pSandbox->papwszEnvVars[cVars] = NULL;
5179 pSandbox->wenviron[cVars] = NULL;
5180 return 0;
5181 }
5182 iVar++;
5183 }
5184 return KERR_ENVVAR_NOT_FOUND;
5185}
5186
5187
5188
5189/** ANSI getenv worker. */
5190static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5191{
5192 KSIZE iVar = 0;
5193 char *pszEnv;
5194 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
5195 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5196 && pszEnv[cchVar] == '=')
5197 return &pszEnv[cchVar + 1];
5198 return NULL;
5199}
5200
5201
5202/** UTF-16 getenv worker. */
5203static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5204{
5205 KSIZE iVar = 0;
5206 wchar_t *pwszEnv;
5207 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
5208 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5209 && pwszEnv[cwcVar] == '=')
5210 return &pwszEnv[cwcVar + 1];
5211 return NULL;
5212}
5213
5214
5215/** Kernel32 - GetEnvironmentVariableA() */
5216static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
5217{
5218 char *pszFoundValue;
5219 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5220
5221 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5222 if (pszFoundValue)
5223 {
5224 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
5225 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
5226 return cchRet;
5227 }
5228 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
5229 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5230 return 0;
5231}
5232
5233
5234/** Kernel32 - GetEnvironmentVariableW() */
5235static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
5236{
5237 wchar_t *pwszFoundValue;
5238 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5239
5240 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5241 if (pwszFoundValue)
5242 {
5243 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
5244 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
5245 return cchRet;
5246 }
5247 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
5248 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5249 return 0;
5250}
5251
5252
5253/** Kernel32 - SetEnvironmentVariableA() */
5254static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
5255{
5256 int rc;
5257 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5258
5259 if (pszValue)
5260 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5261 else
5262 {
5263 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5264 rc = 0; //??
5265 }
5266 if (rc == 0)
5267 {
5268 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
5269 return TRUE;
5270 }
5271 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5272 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
5273 return FALSE;
5274}
5275
5276
5277/** Kernel32 - SetEnvironmentVariableW() */
5278static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
5279{
5280 int rc;
5281 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5282
5283 if (pwszValue)
5284 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5285 else
5286 {
5287 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5288 rc = 0; //??
5289 }
5290 if (rc == 0)
5291 {
5292 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
5293 return TRUE;
5294 }
5295 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5296 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
5297 return FALSE;
5298}
5299
5300
5301/** Kernel32 - ExpandEnvironmentStringsA() */
5302static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
5303{
5304 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5305 KWFS_TODO();
5306 return 0;
5307}
5308
5309
5310/** Kernel32 - ExpandEnvironmentStringsW() */
5311static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
5312{
5313 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5314 KWFS_TODO();
5315 return 0;
5316}
5317
5318
5319/** CRT - _putenv(). */
5320static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
5321{
5322 int rc;
5323 char const *pszEqual;
5324 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5325
5326 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
5327 if (pszEqual)
5328 {
5329 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
5330 if (rc == 0)
5331 { }
5332 else
5333 rc = -1;
5334 }
5335 else
5336 {
5337 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
5338 rc = 0;
5339 }
5340 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
5341 return rc;
5342}
5343
5344
5345/** CRT - _wputenv(). */
5346static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
5347{
5348 int rc;
5349 wchar_t const *pwszEqual;
5350 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5351
5352 pwszEqual = wcschr(pwszVarEqualValue, '=');
5353 if (pwszEqual)
5354 {
5355 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
5356 if (rc == 0)
5357 { }
5358 else
5359 rc = -1;
5360 }
5361 else
5362 {
5363 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
5364 rc = 0;
5365 }
5366 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
5367 return rc;
5368}
5369
5370
5371/** CRT - _putenv_s(). */
5372static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
5373{
5374 char const *pszEqual;
5375 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5376
5377 pszEqual = kHlpStrChr(pszVar, '=');
5378 if (pszEqual == NULL)
5379 {
5380 if (pszValue)
5381 {
5382 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5383 if (rc == 0)
5384 {
5385 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
5386 return 0;
5387 }
5388 }
5389 else
5390 {
5391 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5392 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
5393 return 0;
5394 }
5395 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
5396 return ENOMEM;
5397 }
5398 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
5399 return EINVAL;
5400}
5401
5402
5403/** CRT - _wputenv_s(). */
5404static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
5405{
5406 wchar_t const *pwszEqual;
5407 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5408
5409 pwszEqual = wcschr(pwszVar, '=');
5410 if (pwszEqual == NULL)
5411 {
5412 if (pwszValue)
5413 {
5414 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5415 if (rc == 0)
5416 {
5417 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
5418 return 0;
5419 }
5420 }
5421 else
5422 {
5423 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5424 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
5425 return 0;
5426 }
5427 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
5428 return ENOMEM;
5429 }
5430 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
5431 return EINVAL;
5432}
5433
5434
5435/** CRT - get pointer to the __initenv variable (initial environment). */
5436static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
5437{
5438 KW_LOG(("__p___initenv\n"));
5439 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5440 KWFS_TODO();
5441 return &g_Sandbox.initenv;
5442}
5443
5444
5445/** CRT - get pointer to the __winitenv variable (initial environment). */
5446static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
5447{
5448 KW_LOG(("__p___winitenv\n"));
5449 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5450 KWFS_TODO();
5451 return &g_Sandbox.winitenv;
5452}
5453
5454
5455/** CRT - get pointer to the _environ variable (current environment). */
5456static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
5457{
5458 KW_LOG(("__p__environ\n"));
5459 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5460 return &g_Sandbox.environ;
5461}
5462
5463
5464/** CRT - get pointer to the _wenviron variable (current environment). */
5465static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
5466{
5467 KW_LOG(("__p__wenviron\n"));
5468 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5469 return &g_Sandbox.wenviron;
5470}
5471
5472
5473/** CRT - get the _environ variable (current environment).
5474 * @remarks Not documented or prototyped? */
5475static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
5476{
5477 KWFS_TODO(); /** @todo check the callers expectations! */
5478 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5479 *ppapszEnviron = g_Sandbox.environ;
5480 return 0;
5481}
5482
5483
5484/** CRT - get the _wenviron variable (current environment).
5485 * @remarks Not documented or prototyped? */
5486static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
5487{
5488 KWFS_TODO(); /** @todo check the callers expectations! */
5489 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5490 *ppapwszEnviron = g_Sandbox.wenviron;
5491 return 0;
5492}
5493
5494
5495/** CRT - _wdupenv_s() (see _tdupenv_s(). */
5496static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
5497 PKWCRTSLOT pSlot)
5498{
5499 errno_t rc;
5500 wchar_t *pwszValue;
5501 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5502
5503 if (ppwszValue)
5504 {
5505 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
5506 if (pwszValue)
5507 {
5508 size_t cwcValue = wcslen(pwszValue);
5509 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
5510 if (pwszDst)
5511 {
5512 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
5513 pwszDst[cwcValue] = '\0';
5514 *ppwszValue = pwszDst;
5515 if (pcwcValue)
5516 *pcwcValue = cwcValue;
5517 rc = 0;
5518 }
5519 else
5520 {
5521 *ppwszValue = NULL;
5522 if (pcwcValue)
5523 *pcwcValue = 0;
5524 rc = ENOMEM;
5525 }
5526 }
5527 else
5528 {
5529 *ppwszValue = NULL;
5530 if (pcwcValue)
5531 *pcwcValue = 0;
5532 rc = 0;
5533 }
5534 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5535 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5536 }
5537 else
5538 {
5539 /*
5540 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5541 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5542 * was set to a value.
5543 */
5544 if (pcwcValue)
5545 *pcwcValue = 0;
5546 rc = EINVAL;
5547 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5548 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5549 }
5550 return rc;
5551}
5552CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5553 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5554 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5555
5556
5557
5558/*
5559 *
5560 * Loader related APIs
5561 * Loader related APIs
5562 * Loader related APIs
5563 *
5564 */
5565
5566/**
5567 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5568 */
5569static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5570{
5571 /* Load it first. */
5572 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5573 if (hmod)
5574 {
5575 pDynLoad->hmod = hmod;
5576 pDynLoad->pMod = NULL; /* indicates special */
5577
5578 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5579 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5580 KWLDR_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5581 }
5582 else
5583 kHlpFree(pDynLoad);
5584 return hmod;
5585}
5586
5587
5588/**
5589 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5590 */
5591static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5592{
5593 static const char s_szDll[] = ".dll";
5594 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5595 PKWMODULE pMod;
5596 char szNormPath[256];
5597
5598 /*
5599 * Lower case it and make sure it ends with .dll.
5600 */
5601 if (cbFilename > sizeof(szNormPath))
5602 {
5603 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5604 return NULL;
5605 }
5606 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5607 _strlwr(szNormPath);
5608 kHlpAssert(cbFilename > 7 /* api-ms- */ );
5609 if (strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5610 {
5611 if (cbFilename + sizeof(s_szDll) - 1 > sizeof(szNormPath))
5612 {
5613 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5614 return NULL;
5615 }
5616
5617 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5618 cbFilename += sizeof(s_szDll) - 1;
5619 }
5620
5621 /*
5622 * Try load it.
5623 */
5624 pMod = kwLdrModuleTryLoadVirtualDll(szNormPath, cbFilename - 1);
5625 if (pMod)
5626 {
5627 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5628
5629 pDynLoad->pMod = pMod;
5630 pDynLoad->hmod = pMod->hOurMod;
5631
5632 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5633 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5634 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5635 return pDynLoad->hmod;
5636 }
5637 kHlpFree(pDynLoad);
5638 return NULL;
5639}
5640
5641
5642/** Kernel32 - LoadLibraryExA() */
5643static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5644{
5645 KSIZE cchFilename = kHlpStrLen(pszFilename);
5646 const char *pszSearchPath;
5647 PKWDYNLOAD pDynLoad;
5648 PKWMODULE pMod;
5649 int rc;
5650 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5651 //fprintf(stderr, "LoadLibraryExA: %s, %#x\n", pszFilename, fFlags);
5652
5653 /*
5654 * Deal with a couple of extremely unlikely special cases right away.
5655 */
5656 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5657 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5658 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5659 { /* likely */ }
5660 else
5661 {
5662 KWFS_TODO();
5663 return LoadLibraryExA(pszFilename, hFile, fFlags);
5664 }
5665
5666 /*
5667 * Check if we've already got a dynload entry for this one.
5668 */
5669 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5670 if ( pDynLoad->cchRequest == cchFilename
5671 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5672 {
5673 if (pDynLoad->pMod)
5674 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5675 else
5676 rc = 0;
5677 if (rc == 0)
5678 {
5679 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5680 return pDynLoad->hmod;
5681 }
5682 SetLastError(ERROR_DLL_INIT_FAILED);
5683 return NULL;
5684 }
5685
5686 /*
5687 * Allocate a dynload entry for the request.
5688 */
5689 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5690 if (pDynLoad)
5691 {
5692 pDynLoad->cchRequest = cchFilename;
5693 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5694 }
5695 else
5696 {
5697 KWLDR_LOG(("LoadLibraryExA: Out of memory!\n"));
5698 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5699 return NULL;
5700 }
5701
5702 /*
5703 * Deal with resource / data DLLs.
5704 */
5705 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5706 | LOAD_LIBRARY_AS_DATAFILE
5707 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5708 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5709 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5710
5711 /*
5712 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5713 */
5714 if ( kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5715 && kHlpIsFilenameOnly(pszFilename))
5716 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5717
5718 /*
5719 * Normal library loading.
5720 * We start by being very lazy and reusing the code for resolving imports.
5721 */
5722 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5723 if (!kHlpIsFilenameOnly(pszFilename))
5724 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5725 else
5726 {
5727 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5728 if (rc != 0)
5729 pMod = NULL;
5730 }
5731 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5732 {
5733 /* Enter it into the tool module table and dynamic link request cache. */
5734 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5735
5736 pDynLoad->pMod = pMod;
5737 pDynLoad->hmod = pMod->hOurMod;
5738
5739 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5740 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5741
5742 /*
5743 * Make sure it's initialized (need to link it first since DllMain may
5744 * use loader APIs).
5745 */
5746 rc = kwLdrModuleInitTree(pMod);
5747 if (rc == 0)
5748 {
5749 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5750 return pDynLoad->hmod;
5751 }
5752
5753 SetLastError(ERROR_DLL_INIT_FAILED);
5754 }
5755 else
5756 {
5757 KWFS_TODO();
5758 kHlpFree(pDynLoad);
5759 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5760 }
5761 return NULL;
5762}
5763
5764
5765/** Kernel32 - LoadLibraryExA() for native overloads */
5766static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5767{
5768 char szPath[1024];
5769 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5770
5771 /*
5772 * We may have to help resolved unqualified DLLs living in the executable directory.
5773 */
5774 if (kHlpIsFilenameOnly(pszFilename))
5775 {
5776 KSIZE cchFilename = kHlpStrLen(pszFilename);
5777 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5778 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5779 {
5780 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5781 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5782 if (kwFsPathExists(szPath))
5783 {
5784 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5785 pszFilename = szPath;
5786 }
5787 }
5788
5789 if (pszFilename != szPath)
5790 {
5791 KSIZE cchSuffix = 0;
5792 KBOOL fNeedSuffix = K_FALSE;
5793 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5794 kHlpAssert(pszCur);
5795 if (pszCur)
5796 {
5797 while (*pszCur != '\0')
5798 {
5799 /* Find the end of the component */
5800 KSIZE cch = 0;
5801 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5802 cch++;
5803
5804 if ( cch > 0 /* wrong, but whatever */
5805 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5806 {
5807 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5808 if ( szPath[cch - 1] != ':'
5809 && szPath[cch - 1] != '/'
5810 && szPath[cch - 1] != '\\')
5811 *pszDst++ = '\\';
5812 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5813 if (fNeedSuffix)
5814 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5815 *pszDst = '\0';
5816
5817 if (kwFsPathExists(szPath))
5818 {
5819 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5820 pszFilename = szPath;
5821 break;
5822 }
5823 }
5824
5825 /* Advance */
5826 pszCur += cch;
5827 while (*pszCur == ';')
5828 pszCur++;
5829 }
5830 }
5831 }
5832 }
5833
5834 return LoadLibraryExA(pszFilename, hFile, fFlags);
5835}
5836
5837
5838/** Kernel32 - LoadLibraryExW() */
5839static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5840{
5841 char szTmp[4096];
5842 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5843 if (cchTmp < sizeof(szTmp))
5844 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5845
5846 KWFS_TODO();
5847 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5848 return NULL;
5849}
5850
5851/** Kernel32 - LoadLibraryA() */
5852static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5853{
5854 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5855}
5856
5857
5858/** Kernel32 - LoadLibraryW() */
5859static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5860{
5861 char szTmp[4096];
5862 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5863 if (cchTmp < sizeof(szTmp))
5864 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5865 KWFS_TODO();
5866 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5867 return NULL;
5868}
5869
5870
5871/** Kernel32 - FreeLibrary() */
5872static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5873{
5874 /* Ignored, we like to keep everything loaded. */
5875 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5876 return TRUE;
5877}
5878
5879
5880/** Worker for GetModuleHandleA/W for handling cached modules. */
5881static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5882{
5883 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5884 if (hmod)
5885 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5886 hmod, g_aGetModuleHandleCache[i].pszName));
5887 else
5888 {
5889 /*
5890 * The first time around we have to make sure we have a module table
5891 * entry for it, if not we add one. We need to add it to the tools
5892 * module list to for it to work.
5893 */
5894 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
5895 g_aGetModuleHandleCache[i].fAlwaysPresent);
5896 if (pMod)
5897 {
5898 hmod = pMod->hOurMod;
5899 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5900 {
5901 kwToolAddModule(g_Sandbox.pTool, pMod);
5902 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5903 hmod, g_aGetModuleHandleCache[i].pszName));
5904 }
5905 else
5906 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5907 hmod, g_aGetModuleHandleCache[i].pszName));
5908
5909 }
5910 }
5911 return hmod;
5912}
5913
5914
5915/** Kernel32 - GetModuleHandleA() */
5916static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5917{
5918 KSIZE i;
5919 KSIZE cchModule;
5920 PKWDYNLOAD pDynLoad;
5921 KSIZE cchSuffix;
5922 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5923 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5924
5925 /*
5926 * The executable.
5927 */
5928 if (pszModule == NULL)
5929 {
5930 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5931 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5932 }
5933
5934 /*
5935 * If no path of suffix, pretend it ends with .DLL.
5936 */
5937 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5938
5939 /*
5940 * Cache of system modules we've seen queried.
5941 */
5942 cchModule = kHlpStrLen(pszModule);
5943 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5944 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5945 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5946 || ( cchSuffix > 0
5947 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5948 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5949 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5950 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5951
5952 /*
5953 * Modules we've dynamically loaded.
5954 */
5955 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5956 if (pDynLoad->pMod)
5957 {
5958 const char *pszPath = pDynLoad->pMod->pszPath;
5959 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
5960 if ( stricmp(pszPath, pszModule) == 0
5961 || stricmp(pszName, pszModule) == 0
5962 || ( cchSuffix > 0
5963 && strnicmp(pszName, pszModule, cchModule) == 0
5964 && stricmp(&pszName[cchModule], ".dll") == 0))
5965 {
5966 if ( pDynLoad->pMod->fNative
5967 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5968 {
5969 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
5970 return pDynLoad->hmod;
5971 }
5972 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
5973 SetLastError(ERROR_MOD_NOT_FOUND);
5974 return NULL;
5975 }
5976 }
5977
5978 /*
5979 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5980 * to and go via the g_aGetModuleHandleCache cache.
5981 */
5982/** @todo virtual api DLLs */
5983 if (kHlpStrNICompAscii(pszModule, "api-ms-win-", 11) == 0)
5984 {
5985 HMODULE hmod = GetModuleHandleA(pszModule);
5986 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
5987 if (hmod)
5988 {
5989 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5990 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
5991 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5992 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
5993 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5994 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
5995 if (hmod == GetModuleHandleW(L"UCRTBASE.DLL"))
5996 return kwSandbox_Kernel32_GetModuleHandleA("UCRTBASE.DLL");
5997 }
5998 else
5999 dwErr = GetLastError();
6000 }
6001
6002 kwErrPrintf("pszModule=%s\n", pszModule);
6003 KWFS_TODO();
6004 SetLastError(ERROR_MOD_NOT_FOUND);
6005 return NULL;
6006}
6007
6008
6009/** Kernel32 - GetModuleHandleW() */
6010static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
6011{
6012 KSIZE i;
6013 KSIZE cwcModule;
6014 PKWDYNLOAD pDynLoad;
6015 KSIZE cwcSuffix;
6016 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6017 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6018
6019 /*
6020 * The executable.
6021 */
6022 if (pwszModule == NULL)
6023 {
6024 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
6025 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
6026 }
6027
6028 /*
6029 * If no path of suffix, pretend it ends with .DLL.
6030 */
6031 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
6032
6033 /*
6034 * Cache of system modules we've seen queried.
6035 */
6036 cwcModule = kwUtf16Len(pwszModule);
6037 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6038 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
6039 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
6040 || ( cwcSuffix > 0
6041 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
6042 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
6043 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
6044 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
6045
6046 /*
6047 * Modules we've dynamically loaded.
6048 */
6049 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6050 if (pDynLoad->pMod)
6051 {
6052 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
6053 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
6054 if ( _wcsicmp(pwszPath, pwszModule) == 0
6055 || _wcsicmp(pwszName, pwszModule) == 0
6056 || ( cwcSuffix
6057 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
6058 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
6059 {
6060 if ( pDynLoad->pMod->fNative
6061 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6062 {
6063 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
6064 return pDynLoad->hmod;
6065 }
6066 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
6067 SetLastError(ERROR_MOD_NOT_FOUND);
6068 return NULL;
6069 }
6070 }
6071
6072 /*
6073 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6074 * to and go via the g_aGetModuleHandleCache cache.
6075 */
6076 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
6077 {
6078 HMODULE hmod = GetModuleHandleW(pwszModule);
6079 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
6080 if (hmod)
6081 {
6082 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6083 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
6084 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6085 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
6086 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6087 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
6088 }
6089 else
6090 dwErr = GetLastError();
6091 }
6092
6093 kwErrPrintf("pwszModule=%ls\n", pwszModule);
6094 KWFS_TODO();
6095 SetLastError(dwErr);
6096 return NULL;
6097}
6098
6099
6100/** Used to debug dynamically resolved procedures. */
6101static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
6102{
6103#ifdef _MSC_VER
6104 __debugbreak();
6105#else
6106 KWFS_TODO();
6107#endif
6108 return ~(UINT)0;
6109}
6110
6111
6112#ifndef NDEBUG
6113/*
6114 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
6115 */
6116# if K_ARCH == K_ARCH_X86_32
6117static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
6118# else
6119static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
6120# endif
6121typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
6122typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
6123typedef struct KWCXINTERCEPTORENTRY
6124{
6125 PFNINVOKECOMPILERPASSW pfnOrg;
6126 PKWMODULE pModule;
6127 PFNINVOKECOMPILERPASSW pfnWrap;
6128} KWCXINTERCEPTORENTRY;
6129
6130static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
6131 KWCXINTERCEPTORENTRY *pEntry)
6132{
6133 int i;
6134 KIPTR rcExit;
6135 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
6136 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
6137 for (i = 0; i < cArgs; i++)
6138 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
6139
6140 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
6141
6142 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
6143 return rcExit;
6144}
6145
6146static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
6147static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
6148static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
6149
6150static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
6151{
6152 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
6153 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
6154 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
6155};
6156
6157static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6158{
6159 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
6160}
6161
6162static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6163{
6164 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
6165}
6166
6167static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6168{
6169 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
6170}
6171
6172#endif /* !NDEBUG */
6173
6174
6175/** Kernel32 - GetProcAddress() */
6176static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6177{
6178 KSIZE i;
6179 PKWMODULE pMod;
6180 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6181
6182 /*
6183 * Try locate the module.
6184 */
6185 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6186 if (pMod)
6187 {
6188 KLDRADDR uValue;
6189 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
6190 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
6191 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
6192 KU32_MAX /*iSymbol*/,
6193 pszProc,
6194 kHlpStrLen(pszProc),
6195 NULL /*pszVersion*/,
6196 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
6197 &uValue,
6198 NULL /*pfKind*/);
6199 if (rc == 0)
6200 {
6201 //static int s_cDbgGets = 0;
6202 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
6203 KU32 i = g_cSandboxGetProcReplacements;
6204 while (i-- > 0)
6205 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
6206 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
6207 {
6208 if ( !g_aSandboxGetProcReplacements[i].pszModule
6209 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
6210 {
6211 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
6212 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
6213 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
6214 {
6215 if (!g_aSandboxReplacements[i].fCrtSlotArray)
6216 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
6217 else
6218 {
6219 if (pMod->iCrtSlot == KU8_MAX)
6220 {
6221 int rc = kwLdrModuleCreateCrtSlot(pMod);
6222 if (rc)
6223 {
6224 KW_LOG(("GetProcAddress: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
6225 SetLastError(ERROR_INTERNAL_ERROR);
6226 return NULL;
6227 }
6228 }
6229 uValue = ((KUPTR *)g_aSandboxGetProcReplacements[i].pfnReplacement)[pMod->iCrtSlot];
6230 }
6231
6232 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6233 }
6234 kwLdrModuleRelease(pMod);
6235 return (FARPROC)(KUPTR)uValue;
6236 }
6237 }
6238
6239#ifndef NDEBUG
6240 /* Intercept the compiler pass method, dumping arguments. */
6241 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
6242 {
6243 KU32 i;
6244 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6245 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6246 {
6247 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6248 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6249 break;
6250 }
6251 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6252 while (i-- > 0)
6253 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6254 {
6255 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6256 g_aCxInterceptorEntries[i].pModule = pMod;
6257 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6258 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6259 break;
6260 }
6261 }
6262#endif
6263 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6264 kwLdrModuleRelease(pMod);
6265 //s_cDbgGets++;
6266 //if (s_cGets >= 3)
6267 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6268 return (FARPROC)(KUPTR)uValue;
6269 }
6270
6271 KWFS_TODO();
6272 SetLastError(ERROR_PROC_NOT_FOUND);
6273 kwLdrModuleRelease(pMod);
6274 return NULL;
6275 }
6276
6277 /*
6278 * Hmm... could be a cached module-by-name.
6279 */
6280 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6281 if (g_aGetModuleHandleCache[i].hmod == hmod)
6282 return GetProcAddress(hmod, pszProc);
6283
6284 KWFS_TODO();
6285 return GetProcAddress(hmod, pszProc);
6286}
6287
6288
6289/** Kernel32 - GetModuleFileNameA() */
6290static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6291{
6292 PKWMODULE pMod;
6293 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6294
6295 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6296 if (pMod != NULL)
6297 {
6298 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6299 kwLdrModuleRelease(pMod);
6300 return cbRet;
6301 }
6302 KWFS_TODO();
6303 return 0;
6304}
6305
6306
6307/** Kernel32 - GetModuleFileNameW() */
6308static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6309{
6310 PKWMODULE pMod;
6311 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6312
6313 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6314 if (pMod)
6315 {
6316 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6317 kwLdrModuleRelease(pMod);
6318 return cwcRet;
6319 }
6320
6321 KWFS_TODO();
6322 return 0;
6323}
6324
6325
6326/** NtDll - RtlPcToFileHeader
6327 * This is necessary for msvcr100.dll!CxxThrowException. */
6328static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6329{
6330 PVOID pvRet;
6331
6332 /*
6333 * Do a binary lookup of the module table for the current tool.
6334 * This will give us a
6335 */
6336 if (g_Sandbox.fRunning)
6337 {
6338 KUPTR const uPC = (KUPTR)pvPC;
6339 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6340 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6341 KU32 i;
6342 if (iEnd)
6343 {
6344 KU32 iStart = 0;
6345 i = iEnd / 2;
6346 for (;;)
6347 {
6348 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6349 if (uPC < uHModCur)
6350 {
6351 iEnd = i;
6352 if (iStart < i)
6353 { }
6354 else
6355 break;
6356 }
6357 else if (uPC != uHModCur)
6358 {
6359 iStart = ++i;
6360 if (i < iEnd)
6361 { }
6362 else
6363 break;
6364 }
6365 else
6366 {
6367 /* This isn't supposed to happen. */
6368 break;
6369 }
6370
6371 i = iStart + (iEnd - iStart) / 2;
6372 }
6373
6374 /* For reasons of simplicity (= copy & paste), we end up with the
6375 module after the one we're interested in here. */
6376 i--;
6377 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6378 && papMods[i]->pLdrMod)
6379 {
6380 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6381 if (uRvaPC < papMods[i]->cbImage)
6382 {
6383 *ppvImageBase = papMods[i]->hOurMod;
6384 pvRet = papMods[i]->hOurMod;
6385 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6386 return pvRet;
6387 }
6388 }
6389 }
6390 else
6391 i = 0;
6392 }
6393
6394 /*
6395 * Call the regular API.
6396 */
6397 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6398 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6399 return pvRet;
6400}
6401
6402
6403/*
6404 *
6405 * File access APIs (for speeding them up).
6406 * File access APIs (for speeding them up).
6407 * File access APIs (for speeding them up).
6408 *
6409 */
6410
6411
6412/**
6413 * Converts a lookup error to a windows error code.
6414 *
6415 * @returns The windows error code.
6416 * @param enmError The lookup error.
6417 */
6418static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6419{
6420 switch (enmError)
6421 {
6422 case KFSLOOKUPERROR_NOT_FOUND:
6423 case KFSLOOKUPERROR_NOT_DIR:
6424 return ERROR_FILE_NOT_FOUND;
6425
6426 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6427 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6428 case KFSLOOKUPERROR_PATH_TOO_SHORT:
6429 return ERROR_PATH_NOT_FOUND;
6430
6431 case KFSLOOKUPERROR_PATH_TOO_LONG:
6432 return ERROR_FILENAME_EXCED_RANGE;
6433
6434 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6435 return ERROR_NOT_ENOUGH_MEMORY;
6436
6437 default:
6438 return ERROR_PATH_NOT_FOUND;
6439 }
6440}
6441
6442#ifdef WITH_TEMP_MEMORY_FILES
6443
6444/**
6445 * Checks for a cl.exe temporary file.
6446 *
6447 * There are quite a bunch of these. They seems to be passing data between the
6448 * first and second compiler pass. Since they're on disk, they get subjected to
6449 * AV software screening and normal file consistency rules. So, not necessarily
6450 * a very efficient way of handling reasonably small amounts of data.
6451 *
6452 * We make the files live in virtual memory by intercepting their opening,
6453 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6454 *
6455 * @returns K_TRUE / K_FALSE
6456 * @param pwszFilename The file name being accessed.
6457 */
6458static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6459{
6460 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6461 if (pwszName)
6462 {
6463 /* The name starts with _CL_... */
6464 if ( pwszName[0] == '_'
6465 && pwszName[1] == 'C'
6466 && pwszName[2] == 'L'
6467 && pwszName[3] == '_' )
6468 {
6469 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6470 this check by just checking that it's alpha numerical ascii from here on. */
6471 wchar_t wc;
6472 pwszName += 4;
6473 while ((wc = *pwszName++) != '\0')
6474 {
6475 if (wc < 127 && iswalnum(wc))
6476 { /* likely */ }
6477 else
6478 return K_FALSE;
6479 }
6480 return K_TRUE;
6481 }
6482
6483 /* In VC2019 there is also one {UUID} file in temp: */
6484 if (pwszName[0] == '{')
6485 {
6486 KSIZE cwcName = kwUtf16Len(pwszName);
6487 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6488 && pwszName[37] == '}'
6489 && iswalnum(pwszName[1]) // 4
6490 && iswalnum(pwszName[2]) // 4
6491 && iswalnum(pwszName[3]) // 6
6492 && iswalnum(pwszName[4]) // 5
6493 && iswalnum(pwszName[5]) // d
6494 && iswalnum(pwszName[6]) // d
6495 && iswalnum(pwszName[7]) // d
6496 && iswalnum(pwszName[8]) // 9
6497 && pwszName[9] == '-' // -
6498 && iswalnum(pwszName[10]) // e
6499 && iswalnum(pwszName[11]) // 4
6500 && iswalnum(pwszName[12]) // 9
6501 && iswalnum(pwszName[13]) // 4
6502 && pwszName[14] == '-' // -
6503 && iswalnum(pwszName[15]) // 4
6504 && iswalnum(pwszName[16]) // 7
6505 && iswalnum(pwszName[17]) // 1
6506 && iswalnum(pwszName[18]) // b
6507 && pwszName[19] == '-' // -
6508 && iswalnum(pwszName[20]) // 9
6509 && iswalnum(pwszName[21]) // 9
6510 && iswalnum(pwszName[22]) // 6
6511 && iswalnum(pwszName[23]) // b
6512 && pwszName[24] == '-' // -
6513 && iswalnum(pwszName[25]) // 9
6514 && iswalnum(pwszName[26]) // b
6515 && iswalnum(pwszName[27]) // 5
6516 && iswalnum(pwszName[28]) // 5
6517 && iswalnum(pwszName[29]) // 6
6518 && iswalnum(pwszName[30]) // e
6519 && iswalnum(pwszName[31]) // 2
6520 && iswalnum(pwszName[32]) // 5
6521 && iswalnum(pwszName[33]) // a
6522 && iswalnum(pwszName[34]) // 3
6523 && iswalnum(pwszName[35]) // f
6524 && iswalnum(pwszName[36])) // 8
6525 return K_TRUE;
6526 }
6527 }
6528 return K_FALSE;
6529}
6530
6531
6532/**
6533 * Creates a handle to a temporary file.
6534 *
6535 * @returns The handle on success.
6536 * INVALID_HANDLE_VALUE and SetLastError on failure.
6537 * @param pTempFile The temporary file.
6538 * @param dwDesiredAccess The desired access to the handle.
6539 * @param fMapping Whether this is a mapping (K_TRUE) or file
6540 * (K_FALSE) handle type.
6541 */
6542static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6543{
6544 /*
6545 * Create a handle to the temporary file.
6546 */
6547 HANDLE hFile = INVALID_HANDLE_VALUE;
6548 HANDLE hProcSelf = GetCurrentProcess();
6549 if (DuplicateHandle(hProcSelf, hProcSelf,
6550 hProcSelf, &hFile,
6551 SYNCHRONIZE, FALSE,
6552 0 /*dwOptions*/))
6553 {
6554 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6555 if (pHandle)
6556 {
6557 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6558 pHandle->cRefs = 1;
6559 pHandle->offFile = 0;
6560 pHandle->hHandle = hFile;
6561 pHandle->dwDesiredAccess = dwDesiredAccess;
6562 pHandle->tidOwner = KU32_MAX;
6563 pHandle->u.pTempFile = pTempFile;
6564 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6565 {
6566 pTempFile->cActiveHandles++;
6567 kHlpAssert(pTempFile->cActiveHandles >= 1);
6568 kHlpAssert(pTempFile->cActiveHandles <= 2);
6569 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6570 return hFile;
6571 }
6572
6573 kHlpFree(pHandle);
6574 }
6575 else
6576 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6577 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6578 }
6579 else
6580 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6581 return INVALID_HANDLE_VALUE;
6582}
6583
6584
6585static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6586 KBOOL *pfFallback)
6587{
6588 HANDLE hFile;
6589 DWORD dwErr;
6590
6591 /*
6592 * Check if we've got an existing temp file.
6593 * ASSUME exact same path for now.
6594 */
6595 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6596 PKWFSTEMPFILE pTempFile;
6597 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6598 {
6599 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6600 if ( pTempFile->cwcPath == cwcFilename
6601 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6602 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6603 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6604 break;
6605 }
6606
6607 /*
6608 * Create a new temporary file instance if not found.
6609 */
6610 *pfFallback = K_FALSE;
6611 if (pTempFile == NULL)
6612 {
6613 KSIZE cbFilename;
6614
6615 switch (dwCreationDisposition)
6616 {
6617 case CREATE_ALWAYS:
6618 case OPEN_ALWAYS:
6619 case CREATE_NEW:
6620 dwErr = NO_ERROR;
6621 break;
6622
6623 case OPEN_EXISTING:
6624 case TRUNCATE_EXISTING:
6625 *pfFallback = K_TRUE;
6626 kHlpAssertFailed();
6627 SetLastError(ERROR_FILE_NOT_FOUND);
6628 return INVALID_HANDLE_VALUE;
6629
6630 default:
6631 kHlpAssertFailed();
6632 SetLastError(ERROR_INVALID_PARAMETER);
6633 return INVALID_HANDLE_VALUE;
6634 }
6635
6636 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6637 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6638 if (pTempFile)
6639 {
6640 pTempFile->cwcPath = (KU16)cwcFilename;
6641 pTempFile->cbFile = 0;
6642 pTempFile->cbFileAllocated = 0;
6643 pTempFile->cActiveHandles = 0;
6644 pTempFile->cMappings = 0;
6645 pTempFile->cSegs = 0;
6646 pTempFile->paSegs = NULL;
6647 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6648
6649 pTempFile->pNext = g_Sandbox.pTempFileHead;
6650 g_Sandbox.pTempFileHead = pTempFile;
6651 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6652 }
6653 else
6654 {
6655 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6656 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6657 return INVALID_HANDLE_VALUE;
6658 }
6659 }
6660 else
6661 {
6662 switch (dwCreationDisposition)
6663 {
6664 case OPEN_EXISTING:
6665 dwErr = NO_ERROR;
6666 break;
6667 case OPEN_ALWAYS:
6668 dwErr = ERROR_ALREADY_EXISTS;
6669 break;
6670
6671 case TRUNCATE_EXISTING:
6672 case CREATE_ALWAYS:
6673 kHlpAssertFailed();
6674 pTempFile->cbFile = 0;
6675 dwErr = ERROR_ALREADY_EXISTS;
6676 break;
6677
6678 case CREATE_NEW:
6679 kHlpAssertFailed();
6680 SetLastError(ERROR_FILE_EXISTS);
6681 return INVALID_HANDLE_VALUE;
6682
6683 default:
6684 kHlpAssertFailed();
6685 SetLastError(ERROR_INVALID_PARAMETER);
6686 return INVALID_HANDLE_VALUE;
6687 }
6688 }
6689
6690 /*
6691 * Create a handle to the temporary file.
6692 */
6693 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6694 if (hFile != INVALID_HANDLE_VALUE)
6695 SetLastError(dwErr);
6696 return hFile;
6697}
6698
6699#endif /* WITH_TEMP_MEMORY_FILES */
6700
6701/**
6702 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6703 *
6704 * @returns K_TRUE if cacheable, K_FALSE if not.
6705 * @param wcFirst The first extension character.
6706 * @param wcSecond The second extension character.
6707 * @param wcThird The third extension character.
6708 * @param fAttrQuery Set if it's for an attribute query, clear if for
6709 * file creation.
6710 */
6711static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6712{
6713 /* C++ header without an extension or a directory. */
6714 if (wcFirst == '\0')
6715 {
6716 /** @todo exclude temporary files... */
6717 return K_TRUE;
6718 }
6719
6720 /* C Header: .h */
6721 if (wcFirst == 'h' || wcFirst == 'H')
6722 {
6723 if (wcSecond == '\0')
6724 return K_TRUE;
6725
6726 /* C++ Header: .hpp, .hxx */
6727 if ( (wcSecond == 'p' || wcSecond == 'P')
6728 && (wcThird == 'p' || wcThird == 'P'))
6729 return K_TRUE;
6730 if ( (wcSecond == 'x' || wcSecond == 'X')
6731 && (wcThird == 'x' || wcThird == 'X'))
6732 return K_TRUE;
6733 }
6734 /* Misc starting with i. */
6735 else if (wcFirst == 'i' || wcFirst == 'I')
6736 {
6737 if (wcSecond != '\0')
6738 {
6739 if (wcSecond == 'n' || wcSecond == 'N')
6740 {
6741 /* C++ inline header: .inl */
6742 if (wcThird == 'l' || wcThird == 'L')
6743 return K_TRUE;
6744
6745 /* Assembly include file: .inc */
6746 if (wcThird == 'c' || wcThird == 'C')
6747 return K_TRUE;
6748 }
6749 }
6750 }
6751 /* Assembly header: .mac */
6752 else if (wcFirst == 'm' || wcFirst == 'M')
6753 {
6754 if (wcSecond == 'a' || wcSecond == 'A')
6755 {
6756 if (wcThird == 'c' || wcThird == 'C')
6757 return K_TRUE;
6758 }
6759 }
6760#ifdef WITH_PCH_CACHING
6761 /* Precompiled header: .pch */
6762 else if (wcFirst == 'p' || wcFirst == 'P')
6763 {
6764 if (wcSecond == 'c' || wcSecond == 'C')
6765 {
6766 if (wcThird == 'h' || wcThird == 'H')
6767 return !g_Sandbox.fNoPchCaching;
6768 }
6769 }
6770#endif
6771#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6772 /* Linker - Object file: .obj */
6773 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6774 {
6775 if (wcSecond == 'b' || wcSecond == 'B')
6776 {
6777 if (wcThird == 'j' || wcThird == 'J')
6778 return K_TRUE;
6779 }
6780 }
6781#endif
6782 else if (fAttrQuery)
6783 {
6784 /* Dynamic link library: .dll */
6785 if (wcFirst == 'd' || wcFirst == 'D')
6786 {
6787 if (wcSecond == 'l' || wcSecond == 'L')
6788 {
6789 if (wcThird == 'l' || wcThird == 'L')
6790 return K_TRUE;
6791 }
6792 }
6793 /* Executable file: .exe */
6794 else if (wcFirst == 'e' || wcFirst == 'E')
6795 {
6796 if (wcSecond == 'x' || wcSecond == 'X')
6797 {
6798 if (wcThird == 'e' || wcThird == 'E')
6799 return K_TRUE;
6800 }
6801 }
6802 /* Response file: .rsp */
6803 else if (wcFirst == 'r' || wcFirst == 'R')
6804 {
6805 if (wcSecond == 's' || wcSecond == 'S')
6806 {
6807 if (wcThird == 'p' || wcThird == 'P')
6808 return !g_Sandbox.fNoPchCaching;
6809 }
6810 }
6811 /* Linker: */
6812 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6813 {
6814 /* Object file: .obj */
6815 if (wcFirst == 'o' || wcFirst == 'O')
6816 {
6817 if (wcSecond == 'b' || wcSecond == 'B')
6818 {
6819 if (wcThird == 'j' || wcThird == 'J')
6820 return K_TRUE;
6821 }
6822 }
6823 /* Library file: .lib */
6824 else if (wcFirst == 'l' || wcFirst == 'L')
6825 {
6826 if (wcSecond == 'i' || wcSecond == 'I')
6827 {
6828 if (wcThird == 'b' || wcThird == 'B')
6829 return K_TRUE;
6830 }
6831 }
6832 /* Linker definition file: .def */
6833 else if (wcFirst == 'd' || wcFirst == 'D')
6834 {
6835 if (wcSecond == 'e' || wcSecond == 'E')
6836 {
6837 if (wcThird == 'f' || wcThird == 'F')
6838 return K_TRUE;
6839 }
6840 }
6841 }
6842 }
6843
6844 return K_FALSE;
6845}
6846
6847
6848/**
6849 * Checks if the file extension indicates that the file/dir is something we
6850 * ought to cache.
6851 *
6852 * @returns K_TRUE if cachable, K_FALSE if not.
6853 * @param pszExt The kHlpGetExt result.
6854 * @param fAttrQuery Set if it's for an attribute query, clear if for
6855 * file creation.
6856 */
6857static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6858{
6859 wchar_t const wcFirst = *pszExt;
6860 if (wcFirst)
6861 {
6862 wchar_t const wcSecond = pszExt[1];
6863 if (wcSecond)
6864 {
6865 wchar_t const wcThird = pszExt[2];
6866 if (pszExt[3] == '\0')
6867 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6868 return K_FALSE;
6869 }
6870 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6871 }
6872 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6873}
6874
6875
6876/**
6877 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6878 * should be cached.
6879 *
6880 * @returns K_TRUE if cachable, K_FALSE if not.
6881 * @param pwszPath The UTF-16 path to examine.
6882 * @param fAttrQuery Set if it's for an attribute query, clear if for
6883 * file creation.
6884 */
6885static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6886{
6887 KSIZE cwcExt;
6888 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6889 switch (cwcExt)
6890 {
6891 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6892 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6893 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6894 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6895 }
6896 return K_FALSE;
6897}
6898
6899
6900
6901/**
6902 * Creates a new
6903 *
6904 * @returns
6905 * @param pFsObj .
6906 * @param pwszFilename .
6907 */
6908static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6909{
6910 HANDLE hFile;
6911 MY_IO_STATUS_BLOCK Ios;
6912 MY_OBJECT_ATTRIBUTES ObjAttr;
6913 MY_UNICODE_STRING UniStr;
6914 MY_NTSTATUS rcNt;
6915 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6916
6917 /*
6918 * Open the file relative to the parent directory.
6919 */
6920 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6921 kHlpAssert(pFsObj->pParent);
6922 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6923
6924 Ios.Information = ~(ULONG_PTR)0;
6925 Ios.u.Status = -1;
6926
6927 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6928 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6929 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6930
6931 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6932
6933 rcNt = g_pfnNtCreateFile(&hFile,
6934 GENERIC_READ | SYNCHRONIZE,
6935 &ObjAttr,
6936 &Ios,
6937 NULL, /*cbFileInitialAlloc */
6938 FILE_ATTRIBUTE_NORMAL,
6939 FILE_SHARE_READ,
6940 FILE_OPEN,
6941 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6942 NULL, /*pEaBuffer*/
6943 0); /*cbEaBuffer*/
6944 if (MY_NT_SUCCESS(rcNt))
6945 {
6946 /*
6947 * Read the whole file into memory.
6948 */
6949 LARGE_INTEGER cbFile;
6950 if (GetFileSizeEx(hFile, &cbFile))
6951 {
6952 if ( cbFile.QuadPart >= 0
6953#ifdef WITH_PCH_CACHING
6954 && ( cbFile.QuadPart < 16*1024*1024
6955 || ( cbFile.QuadPart < 96*1024*1024
6956 && pFsObj->cchName > 4
6957 && !g_Sandbox.fNoPchCaching
6958 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
6959#endif
6960 )
6961 {
6962 KU32 cbCache = (KU32)cbFile.QuadPart;
6963 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
6964 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
6965 if (hMapping != NULL)
6966 {
6967 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
6968 if (pbCache)
6969 {
6970 /*
6971 * Create the cached file object.
6972 */
6973 PKFSWCACHEDFILE pCachedFile;
6974 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
6975 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
6976 sizeof(*pCachedFile) + cbPath);
6977 if (pCachedFile)
6978 {
6979 pCachedFile->hCached = hFile;
6980 pCachedFile->hSection = hMapping;
6981 pCachedFile->cbCached = cbCache;
6982 pCachedFile->pbCached = pbCache;
6983 pCachedFile->pFsObj = pFsObj;
6984 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
6985 kFsCacheObjRetain(pFsObj);
6986
6987 g_cReadCachedFiles++;
6988 g_cbReadCachedFiles += cbCache;
6989
6990 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
6991 return pCachedFile;
6992 }
6993
6994 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
6995 }
6996 else
6997 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
6998 CloseHandle(hMapping);
6999 }
7000 else
7001 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
7002 }
7003 else
7004 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
7005 }
7006 else
7007 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
7008 g_pfnNtClose(hFile);
7009 }
7010 else
7011 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
7012 return NULL;
7013}
7014
7015
7016/**
7017 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
7018 */
7019static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
7020 KBOOL fIsFileHandle, HANDLE *phFile)
7021{
7022 HANDLE hProcSelf = GetCurrentProcess();
7023 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
7024 hProcSelf, phFile,
7025 dwDesiredAccess, fInheritHandle,
7026 0 /*dwOptions*/))
7027 {
7028 /*
7029 * Create handle table entry for the duplicate handle.
7030 */
7031 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
7032 if (pHandle)
7033 {
7034 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
7035 pHandle->cRefs = 1;
7036 pHandle->offFile = 0;
7037 pHandle->hHandle = *phFile;
7038 pHandle->dwDesiredAccess = dwDesiredAccess;
7039 pHandle->tidOwner = KU32_MAX;
7040 pHandle->u.pCachedFile = pCachedFile;
7041 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
7042 return K_TRUE;
7043
7044 kHlpFree(pHandle);
7045 }
7046 else
7047 KWFS_LOG(("Out of memory for handle!\n"));
7048
7049 CloseHandle(*phFile);
7050 *phFile = INVALID_HANDLE_VALUE;
7051 }
7052 else
7053 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
7054 return K_FALSE;
7055}
7056
7057
7058/**
7059 * Kernel32 - Common code for CreateFileW and CreateFileA.
7060 */
7061static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
7062{
7063 *phFile = INVALID_HANDLE_VALUE;
7064
7065 /*
7066 * At the moment we only handle existing files.
7067 */
7068 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
7069 {
7070 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
7071 kHlpAssert(pFsObj->fHaveStats);
7072 if ( pCachedFile != NULL
7073 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
7074 {
7075 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
7076 return K_TRUE;
7077 }
7078 }
7079 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
7080
7081 /* Do fallback, please. */
7082 return K_FALSE;
7083}
7084
7085
7086/** Kernel32 - CreateFileA */
7087static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7088 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7089 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7090{
7091 HANDLE hFile;
7092
7093 /*
7094 * Check for include files and similar that we do read-only caching of.
7095 */
7096 if (dwCreationDisposition == OPEN_EXISTING)
7097 {
7098 if ( dwDesiredAccess == GENERIC_READ
7099 || dwDesiredAccess == FILE_GENERIC_READ)
7100 {
7101 if (dwShareMode & FILE_SHARE_READ)
7102 {
7103 if ( !pSecAttrs
7104 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7105 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7106 {
7107 const char *pszExt = kHlpGetExt(pszFilename);
7108 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
7109 {
7110 KFSLOOKUPERROR enmError;
7111 PKFSOBJ pFsObj;
7112 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7113
7114 pFsObj = kFsCacheLookupA(g_pFsCache, pszFilename, &enmError);
7115 if (pFsObj)
7116 {
7117 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7118 {
7119 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7120 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7121 kFsCacheObjRelease(g_pFsCache, pFsObj);
7122 if (fRc)
7123 {
7124 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
7125 return hFile;
7126 }
7127 }
7128 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7129 {
7130 KWFS_LOG(("CreateFileA(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7131 SetLastError(ERROR_FILE_NOT_FOUND);
7132 return INVALID_HANDLE_VALUE;
7133 }
7134 /* Always fall back on missing files in volatile areas. */
7135 }
7136 /* These are for nasm and yasm header searching. Cache will already
7137 have checked the directories for the file, no need to call
7138 CreateFile to do it again. */
7139 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7140 {
7141 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7142 SetLastError(ERROR_FILE_NOT_FOUND);
7143 return INVALID_HANDLE_VALUE;
7144 }
7145 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7146 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7147 {
7148 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
7149 SetLastError(ERROR_PATH_NOT_FOUND);
7150 return INVALID_HANDLE_VALUE;
7151 }
7152
7153 /* fallback */
7154 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7155 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7156 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
7157 return hFile;
7158 }
7159 }
7160 }
7161 }
7162 }
7163
7164 /*
7165 * Okay, normal.
7166 */
7167 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7168 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7169 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7170
7171 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7172 return hFile;
7173}
7174
7175
7176/** Kernel32 - CreateFileW */
7177static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7178 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7179 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7180{
7181 HANDLE hFile;
7182
7183#ifdef WITH_TEMP_MEMORY_FILES
7184 /*
7185 * Check for temporary files (cl.exe only).
7186 */
7187 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7188 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7189 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7190 && kwFsIsClTempFileW(pwszFilename))
7191 {
7192 KBOOL fFallback = K_FALSE;
7193 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7194 if (!fFallback)
7195 {
7196 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7197 return hFile;
7198 }
7199 }
7200#endif
7201
7202 /*
7203 * Check for include files and similar that we do read-only caching of.
7204 */
7205 if (dwCreationDisposition == OPEN_EXISTING)
7206 {
7207 if ( dwDesiredAccess == GENERIC_READ
7208 || dwDesiredAccess == FILE_GENERIC_READ)
7209 {
7210 if (dwShareMode & FILE_SHARE_READ)
7211 {
7212 if ( !pSecAttrs
7213 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7214 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7215 {
7216 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7217 {
7218 KFSLOOKUPERROR enmError;
7219 PKFSOBJ pFsObj;
7220 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7221
7222 pFsObj = kFsCacheLookupW(g_pFsCache, pwszFilename, &enmError);
7223 if (pFsObj)
7224 {
7225 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7226 {
7227 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7228 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7229 kFsCacheObjRelease(g_pFsCache, pFsObj);
7230 if (fRc)
7231 {
7232 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7233 return hFile;
7234 }
7235 }
7236 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7237 {
7238 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7239 SetLastError(ERROR_FILE_NOT_FOUND);
7240#if 0
7241 if ( pFsObj->cchName > sizeof("generated.h")
7242 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - sizeof("generated.h") + 1], "generated.h") == 0)
7243 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; pFsObj->fFlags=%#x\n", pwszFilename, pFsObj->fFlags);
7244#endif
7245 return INVALID_HANDLE_VALUE;
7246 }
7247 /* Always fall back on missing files in volatile areas. */
7248 }
7249 /* These are for nasm and yasm style header searching. Cache will
7250 already have checked the directories for the file, no need to call
7251 CreateFile to do it again. */
7252 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7253 {
7254#if 0
7255 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7256 if ( cwcFilename > sizeof("generated.h")
7257 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7258 L"generated.h", sizeof(L"generated.h")) == 0)
7259 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; (KFSLOOKUPERROR_NOT_FOUND)\n", pwszFilename, pFsObj->fFlags);
7260#endif
7261 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7262 SetLastError(ERROR_FILE_NOT_FOUND);
7263 return INVALID_HANDLE_VALUE;
7264 }
7265 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7266 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7267 {
7268#if 0
7269 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7270 if ( cwcFilename > sizeof("generated.h")
7271 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7272 L"generated.h", sizeof(L"generated.h")) == 0)
7273 kwErrPrintf("CreateFileW(%ls) -> ERROR_PATH_NOT_FOUND; (%d)\n", pwszFilename, enmError);
7274#endif
7275 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7276 SetLastError(ERROR_PATH_NOT_FOUND);
7277 return INVALID_HANDLE_VALUE;
7278 }
7279
7280 /* fallback */
7281 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7282 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7283 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7284 return hFile;
7285 }
7286 }
7287 else
7288 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7289 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7290 }
7291 else
7292 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7293 }
7294 else
7295 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7296 }
7297 else
7298 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7299
7300 /*
7301 * Okay, normal.
7302 */
7303 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7304 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7305 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7306
7307 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7308 return hFile;
7309}
7310
7311
7312
7313/** Kernel32 - SetFilePointer */
7314static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7315{
7316 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7317 if (pHandle != NULL)
7318 {
7319 KU32 cbFile;
7320 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7321 switch (pHandle->enmType)
7322 {
7323 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7324 cbFile = pHandle->u.pCachedFile->cbCached;
7325 break;
7326#ifdef WITH_TEMP_MEMORY_FILES
7327 case KWHANDLETYPE_TEMP_FILE:
7328 cbFile = pHandle->u.pTempFile->cbFile;
7329 break;
7330 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7331#endif
7332 case KWHANDLETYPE_OUTPUT_BUF:
7333 default:
7334 kHlpAssertFailed();
7335 kwSandboxHandlePut(pHandle);
7336 SetLastError(ERROR_INVALID_FUNCTION);
7337 return INVALID_SET_FILE_POINTER;
7338 }
7339
7340 switch (dwMoveMethod)
7341 {
7342 case FILE_BEGIN:
7343 break;
7344 case FILE_CURRENT:
7345 offMove += pHandle->offFile;
7346 break;
7347 case FILE_END:
7348 offMove += cbFile;
7349 break;
7350 default:
7351 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7352 kwSandboxHandlePut(pHandle);
7353 SetLastError(ERROR_INVALID_PARAMETER);
7354 return INVALID_SET_FILE_POINTER;
7355 }
7356 if (offMove >= 0)
7357 {
7358 if (offMove >= (KSSIZE)cbFile)
7359 {
7360#ifdef WITH_TEMP_MEMORY_FILES
7361 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7362 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7363#endif
7364 offMove = (KSSIZE)cbFile;
7365#ifdef WITH_TEMP_MEMORY_FILES
7366 /* For writable files, seeking beyond the end is fine, but check that we've got
7367 the type range for the request. */
7368 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7369 {
7370 kHlpAssertMsgFailed(("%#llx\n", offMove));
7371 kwSandboxHandlePut(pHandle);
7372 SetLastError(ERROR_SEEK);
7373 return INVALID_SET_FILE_POINTER;
7374 }
7375#endif
7376 }
7377 pHandle->offFile = (KU32)offMove;
7378 }
7379 else
7380 {
7381 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7382 kwSandboxHandlePut(pHandle);
7383 SetLastError(ERROR_NEGATIVE_SEEK);
7384 return INVALID_SET_FILE_POINTER;
7385 }
7386 if (pcbMoveHi)
7387 *pcbMoveHi = (KU64)offMove >> 32;
7388 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7389 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7390 kwSandboxHandlePut(pHandle);
7391 SetLastError(NO_ERROR);
7392 return (KU32)offMove;
7393 }
7394
7395 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7396 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7397}
7398
7399
7400/** Kernel32 - SetFilePointerEx */
7401static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7402 DWORD dwMoveMethod)
7403{
7404 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7405 if (pHandle != NULL)
7406 {
7407 KI64 offMyMove = offMove.QuadPart;
7408 KU32 cbFile;
7409 switch (pHandle->enmType)
7410 {
7411 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7412 cbFile = pHandle->u.pCachedFile->cbCached;
7413 break;
7414#ifdef WITH_TEMP_MEMORY_FILES
7415 case KWHANDLETYPE_TEMP_FILE:
7416 cbFile = pHandle->u.pTempFile->cbFile;
7417 break;
7418 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7419#endif
7420 case KWHANDLETYPE_OUTPUT_BUF:
7421 default:
7422 kHlpAssertFailed();
7423 kwSandboxHandlePut(pHandle);
7424 SetLastError(ERROR_INVALID_FUNCTION);
7425 return FALSE;
7426 }
7427
7428 switch (dwMoveMethod)
7429 {
7430 case FILE_BEGIN:
7431 break;
7432 case FILE_CURRENT:
7433 offMyMove += pHandle->offFile;
7434 break;
7435 case FILE_END:
7436 offMyMove += cbFile;
7437 break;
7438 default:
7439 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7440 kwSandboxHandlePut(pHandle);
7441 SetLastError(ERROR_INVALID_PARAMETER);
7442 return FALSE;
7443 }
7444 if (offMyMove >= 0)
7445 {
7446 if (offMyMove >= (KSSIZE)cbFile)
7447 {
7448#ifdef WITH_TEMP_MEMORY_FILES
7449 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7450 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7451#endif
7452 offMyMove = (KSSIZE)cbFile;
7453#ifdef WITH_TEMP_MEMORY_FILES
7454 /* For writable files, seeking beyond the end is fine, but check that we've got
7455 the type range for the request. */
7456 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7457 {
7458 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7459 kwSandboxHandlePut(pHandle);
7460 SetLastError(ERROR_SEEK);
7461 return FALSE;
7462 }
7463#endif
7464 }
7465 pHandle->offFile = (KU32)offMyMove;
7466 }
7467 else
7468 {
7469 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7470 kwSandboxHandlePut(pHandle);
7471 SetLastError(ERROR_NEGATIVE_SEEK);
7472 return FALSE;
7473 }
7474 if (poffNew)
7475 poffNew->QuadPart = offMyMove;
7476 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7477 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7478 kwSandboxHandlePut(pHandle);
7479 return TRUE;
7480 }
7481 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7482 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7483}
7484
7485
7486/** Kernel32 - ReadFile */
7487static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7488 LPOVERLAPPED pOverlapped)
7489{
7490 BOOL fRet;
7491 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7492 g_cReadFileCalls++;
7493 if (pHandle != NULL)
7494 {
7495 switch (pHandle->enmType)
7496 {
7497 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7498 {
7499 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7500 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7501 if (cbActually > cbToRead)
7502 cbActually = cbToRead;
7503
7504#ifdef WITH_HASH_MD5_CACHE
7505 if (g_Sandbox.pHashHead)
7506 {
7507 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7508 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7509 g_Sandbox.LastHashRead.cbRead = cbActually;
7510 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7511 }
7512#endif
7513
7514 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7515 pHandle->offFile += cbActually;
7516
7517 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7518 *pcbActuallyRead = cbActually;
7519
7520 g_cbReadFileFromReadCached += cbActually;
7521 g_cbReadFileTotal += cbActually;
7522 g_cReadFileFromReadCached++;
7523
7524 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7525 kwSandboxHandlePut(pHandle);
7526 return TRUE;
7527 }
7528
7529#ifdef WITH_TEMP_MEMORY_FILES
7530 case KWHANDLETYPE_TEMP_FILE:
7531 {
7532 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7533 KU32 cbActually;
7534 if (pHandle->offFile < pTempFile->cbFile)
7535 {
7536 cbActually = pTempFile->cbFile - pHandle->offFile;
7537 if (cbActually > cbToRead)
7538 cbActually = cbToRead;
7539
7540 /* Copy the data. */
7541 if (cbActually > 0)
7542 {
7543 KU32 cbLeft;
7544 KU32 offSeg;
7545 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7546
7547 /* Locate the segment containing the byte at offFile. */
7548 KU32 iSeg = pTempFile->cSegs - 1;
7549 kHlpAssert(pTempFile->cSegs > 0);
7550 while (paSegs[iSeg].offData > pHandle->offFile)
7551 iSeg--;
7552
7553 /* Copy out the data. */
7554 cbLeft = cbActually;
7555 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7556 for (;;)
7557 {
7558 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7559 if (cbAvail >= cbLeft)
7560 {
7561 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7562 break;
7563 }
7564
7565 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7566 cbLeft -= cbAvail;
7567 offSeg = 0;
7568 iSeg++;
7569 kHlpAssert(iSeg < pTempFile->cSegs);
7570 }
7571
7572 /* Update the file offset. */
7573 pHandle->offFile += cbActually;
7574 }
7575 }
7576 /* Read does not commit file space, so return zero bytes. */
7577 else
7578 cbActually = 0;
7579
7580 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7581 *pcbActuallyRead = cbActually;
7582
7583 g_cbReadFileTotal += cbActually;
7584 g_cbReadFileFromInMemTemp += cbActually;
7585 g_cReadFileFromInMemTemp++;
7586
7587 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7588 kwSandboxHandlePut(pHandle);
7589 return TRUE;
7590 }
7591
7592 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7593#endif /* WITH_TEMP_MEMORY_FILES */
7594 case KWHANDLETYPE_OUTPUT_BUF:
7595 default:
7596 kHlpAssertFailed();
7597 kwSandboxHandlePut(pHandle);
7598 SetLastError(ERROR_INVALID_FUNCTION);
7599 *pcbActuallyRead = 0;
7600 return FALSE;
7601 }
7602 }
7603
7604 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7605 if (fRet && pcbActuallyRead)
7606 g_cbReadFileTotal += *pcbActuallyRead;
7607 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7608 return fRet;
7609}
7610
7611
7612/** Kernel32 - ReadFileEx */
7613static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7614 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7615{
7616 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7617
7618 KWFS_LOG(("ReadFile(%p)\n", hFile));
7619 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7620}
7621
7622#ifdef WITH_STD_OUT_ERR_BUFFERING
7623
7624/**
7625 * Write something to a handle, making sure everything is actually written.
7626 *
7627 * @param hHandle Where to write it to.
7628 * @param pchBuf What to write
7629 * @param cchToWrite How much to write.
7630 */
7631static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7632{
7633 if (cchToWrite > 0)
7634 {
7635 DWORD cchWritten = 0;
7636 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7637 {
7638 if (cchWritten == cchToWrite)
7639 { /* likely */ }
7640 else
7641 {
7642 do
7643 {
7644 pchBuf += cchWritten;
7645 cchToWrite -= cchWritten;
7646 cchWritten = 0;
7647 } while ( cchToWrite > 0
7648 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7649 }
7650 }
7651 else
7652 kHlpAssertFailed();
7653 }
7654}
7655
7656
7657/**
7658 * Worker for WriteFile when the output isn't going to the console.
7659 *
7660 * @param pSandbox The sandbox.
7661 * @param pOutBuf The output buffer.
7662 * @param pchBuffer What to write.
7663 * @param cchToWrite How much to write.
7664 */
7665static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7666{
7667 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7668 { /* likely */ }
7669 else
7670 {
7671 /* No realloc, max size is 64KB. */
7672 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7673 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7674 if (!pOutBuf->u.Fully.pchBuf)
7675 {
7676 while ( !pOutBuf->u.Fully.pchBuf
7677 && pOutBuf->u.Fully.cchBufAlloc > 64)
7678 {
7679 pOutBuf->u.Fully.cchBufAlloc /= 2;
7680 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7681 }
7682 if (!pOutBuf->u.Fully.pchBuf)
7683 {
7684 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7685 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7686 }
7687 }
7688 }
7689
7690 /*
7691 * Special case: Output ends with newline and fits in the buffer.
7692 */
7693 if ( cchToWrite > 1
7694 && pchBuffer[cchToWrite - 1] == '\n'
7695 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7696 {
7697 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7698 pOutBuf->u.Fully.cchBuf += cchToWrite;
7699 }
7700 else
7701 {
7702 /*
7703 * Work thru the text line by line, flushing the buffer when
7704 * appropriate. The buffer is not a line buffer here, it's a
7705 * full buffer.
7706 */
7707 while (cchToWrite > 0)
7708 {
7709 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7710 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7711 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7712 {
7713 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7714 pOutBuf->u.Fully.cchBuf += cchLine;
7715 }
7716 /*
7717 * Option one: Flush the buffer and the current line.
7718 *
7719 * We choose this one when the line won't ever fit, or when we have
7720 * an incomplete line in the buffer.
7721 */
7722 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7723 || pOutBuf->u.Fully.cchBuf == 0
7724 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7725 {
7726 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7727 if (pOutBuf->u.Fully.cchBuf > 0)
7728 {
7729 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7730 pOutBuf->u.Fully.cchBuf = 0;
7731 }
7732 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7733 }
7734 /*
7735 * Option two: Only flush the lines in the buffer.
7736 */
7737 else
7738 {
7739 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7740 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7741 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7742 pOutBuf->u.Fully.cchBuf = cchLine;
7743 }
7744
7745 /* advance */
7746 pchBuffer += cchLine;
7747 cchToWrite -= cchLine;
7748 }
7749 }
7750}
7751
7752#endif /* WITH_STD_OUT_ERR_BUFFERING */
7753
7754#ifdef WITH_TEMP_MEMORY_FILES
7755static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7756{
7757 KU32 cbMinFile = offFile + cbNeeded;
7758 if (cbMinFile >= offFile)
7759 {
7760 /* Calc how much space we've already allocated and */
7761 if (cbMinFile <= pTempFile->cbFileAllocated)
7762 return K_TRUE;
7763
7764 /* Grow the file. */
7765 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7766 {
7767 int rc;
7768 KU32 cSegs = pTempFile->cSegs;
7769 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7770 do
7771 {
7772 /* grow the segment array? */
7773 if ((cSegs % 16) == 0)
7774 {
7775 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7776 if (!pvNew)
7777 return K_FALSE;
7778 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7779 }
7780
7781 /* Use page alloc here to simplify mapping later. */
7782 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7783 if (rc == 0)
7784 { /* likely */ }
7785 else
7786 {
7787 cbNewSeg = 64*1024;
7788 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7789 if (rc != 0)
7790 {
7791 kHlpAssertFailed();
7792 return K_FALSE;
7793 }
7794 }
7795 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7796 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7797 pTempFile->cbFileAllocated += cbNewSeg;
7798 pTempFile->cSegs = ++cSegs;
7799
7800 } while (pTempFile->cbFileAllocated < cbMinFile);
7801
7802 return K_TRUE;
7803 }
7804 }
7805
7806 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7807 return K_FALSE;
7808}
7809#endif /* WITH_TEMP_MEMORY_FILES */
7810
7811
7812#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7813/** Kernel32 - WriteFile */
7814static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7815 LPOVERLAPPED pOverlapped)
7816{
7817 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7818 BOOL fRet;
7819 g_cWriteFileCalls++;
7820 if (pHandle != NULL)
7821 {
7822 switch (pHandle->enmType)
7823 {
7824# ifdef WITH_TEMP_MEMORY_FILES
7825 case KWHANDLETYPE_TEMP_FILE:
7826 {
7827 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7828
7829 kHlpAssert(!pOverlapped);
7830 kHlpAssert(pcbActuallyWritten);
7831
7832 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7833 {
7834 KU32 cbLeft;
7835 KU32 offSeg;
7836
7837 /* Locate the segment containing the byte at offFile. */
7838 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7839 KU32 iSeg = pTempFile->cSegs - 1;
7840 kHlpAssert(pTempFile->cSegs > 0);
7841 while (paSegs[iSeg].offData > pHandle->offFile)
7842 iSeg--;
7843
7844 /* Copy in the data. */
7845 cbLeft = cbToWrite;
7846 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7847 for (;;)
7848 {
7849 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7850 if (cbAvail >= cbLeft)
7851 {
7852 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7853 break;
7854 }
7855
7856 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7857 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7858 cbLeft -= cbAvail;
7859 offSeg = 0;
7860 iSeg++;
7861 kHlpAssert(iSeg < pTempFile->cSegs);
7862 }
7863
7864 /* Update the file offset. */
7865 pHandle->offFile += cbToWrite;
7866 if (pHandle->offFile > pTempFile->cbFile)
7867 pTempFile->cbFile = pHandle->offFile;
7868
7869 *pcbActuallyWritten = cbToWrite;
7870
7871 g_cbWriteFileTotal += cbToWrite;
7872 g_cbWriteFileToInMemTemp += cbToWrite;
7873 g_cWriteFileToInMemTemp++;
7874
7875 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7876 kwSandboxHandlePut(pHandle);
7877 return TRUE;
7878 }
7879
7880 kHlpAssertFailed();
7881 kwSandboxHandlePut(pHandle);
7882 *pcbActuallyWritten = 0;
7883 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7884 return FALSE;
7885 }
7886# endif
7887
7888 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7889 kHlpAssertFailed();
7890 kwSandboxHandlePut(pHandle);
7891 SetLastError(ERROR_ACCESS_DENIED);
7892 *pcbActuallyWritten = 0;
7893 return FALSE;
7894
7895# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7896 /*
7897 * Standard output & error.
7898 */
7899 case KWHANDLETYPE_OUTPUT_BUF:
7900 {
7901 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7902 if (pOutBuf->fIsConsole)
7903 {
7904 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7905 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7906 }
7907 else
7908 {
7909# ifdef WITH_STD_OUT_ERR_BUFFERING
7910 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7911 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7912 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7913# else
7914 kHlpAssertFailed();
7915# endif
7916 }
7917 if (pcbActuallyWritten)
7918 *pcbActuallyWritten = cbToWrite;
7919 g_cbWriteFileTotal += cbToWrite;
7920 kwSandboxHandlePut(pHandle);
7921 return TRUE;
7922 }
7923# endif
7924
7925 default:
7926#ifdef WITH_TEMP_MEMORY_FILES
7927 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7928#endif
7929 kHlpAssertFailed();
7930 kwSandboxHandlePut(pHandle);
7931 SetLastError(ERROR_INVALID_FUNCTION);
7932 *pcbActuallyWritten = 0;
7933 return FALSE;
7934 }
7935 }
7936
7937 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7938 if (fRet && pcbActuallyWritten)
7939 g_cbWriteFileTotal += *pcbActuallyWritten;
7940 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7941 return fRet;
7942}
7943
7944
7945/** Kernel32 - WriteFileEx */
7946static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7947 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7948{
7949 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7950
7951 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
7952 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
7953}
7954
7955#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
7956
7957#ifdef WITH_TEMP_MEMORY_FILES
7958
7959/** Kernel32 - SetEndOfFile; */
7960static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
7961{
7962 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7963 if (pHandle != NULL)
7964 {
7965 switch (pHandle->enmType)
7966 {
7967 case KWHANDLETYPE_TEMP_FILE:
7968 {
7969 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7970 if ( pHandle->offFile > pTempFile->cbFile
7971 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
7972 {
7973 kHlpAssertFailed();
7974 kwSandboxHandlePut(pHandle);
7975 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7976 return FALSE;
7977 }
7978
7979 pTempFile->cbFile = pHandle->offFile;
7980 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
7981 kwSandboxHandlePut(pHandle);
7982 return TRUE;
7983 }
7984
7985 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7986 kHlpAssertFailed();
7987 kwSandboxHandlePut(pHandle);
7988 SetLastError(ERROR_ACCESS_DENIED);
7989 return FALSE;
7990
7991# ifdef WITH_CONSOLE_OUTPUT_BUFFERING
7992 case KWHANDLETYPE_OUTPUT_BUF:
7993 kHlpAssertFailed();
7994 kwSandboxHandlePut(pHandle);
7995 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
7996 return FALSE;
7997# endif
7998
7999 default:
8000 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8001 kHlpAssertFailed();
8002 kwSandboxHandlePut(pHandle);
8003 SetLastError(ERROR_INVALID_FUNCTION);
8004 return FALSE;
8005 }
8006 }
8007
8008 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
8009 return SetEndOfFile(hFile);
8010}
8011
8012
8013/** Kernel32 - GetFileType */
8014static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
8015{
8016 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8017 if (pHandle != NULL)
8018 {
8019 switch (pHandle->enmType)
8020 {
8021 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8022 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
8023 kwSandboxHandlePut(pHandle);
8024 return FILE_TYPE_DISK;
8025
8026 case KWHANDLETYPE_TEMP_FILE:
8027 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
8028 kwSandboxHandlePut(pHandle);
8029 return FILE_TYPE_DISK;
8030
8031#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8032 case KWHANDLETYPE_OUTPUT_BUF:
8033 {
8034 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8035 DWORD fRet;
8036 if (pOutBuf->fFileType != KU8_MAX)
8037 {
8038 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
8039 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
8040 }
8041 else
8042 {
8043 fRet = GetFileType(hFile);
8044 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
8045 }
8046 kwSandboxHandlePut(pHandle);
8047 return fRet;
8048 }
8049#endif
8050 }
8051 kwSandboxHandlePut(pHandle);
8052 }
8053
8054 KWFS_LOG(("GetFileType(%p)\n", hFile));
8055 return GetFileType(hFile);
8056}
8057
8058
8059/** Kernel32 - GetFileSize */
8060static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
8061{
8062 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8063 if (pHandle != NULL)
8064 {
8065 if (pcbHighDword)
8066 *pcbHighDword = 0;
8067 SetLastError(NO_ERROR);
8068 switch (pHandle->enmType)
8069 {
8070 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8071 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8072 kwSandboxHandlePut(pHandle);
8073 return pHandle->u.pCachedFile->cbCached;
8074
8075 case KWHANDLETYPE_TEMP_FILE:
8076 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8077 kwSandboxHandlePut(pHandle);
8078 return pHandle->u.pTempFile->cbFile;
8079
8080 case KWHANDLETYPE_OUTPUT_BUF:
8081 /* do default */
8082 break;
8083
8084 default:
8085 kHlpAssertFailed();
8086 kwSandboxHandlePut(pHandle);
8087 SetLastError(ERROR_INVALID_FUNCTION);
8088 return INVALID_FILE_SIZE;
8089 }
8090 kwSandboxHandlePut(pHandle);
8091 }
8092
8093 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
8094 return GetFileSize(hFile, pcbHighDword);
8095}
8096
8097
8098/** Kernel32 - GetFileSizeEx */
8099static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
8100{
8101 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8102 if (pHandle != NULL)
8103 {
8104 switch (pHandle->enmType)
8105 {
8106 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8107 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8108 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
8109 kwSandboxHandlePut(pHandle);
8110 return TRUE;
8111
8112 case KWHANDLETYPE_TEMP_FILE:
8113 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8114 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
8115 kwSandboxHandlePut(pHandle);
8116 return TRUE;
8117
8118 case KWHANDLETYPE_OUTPUT_BUF:
8119 /* do default */
8120 break;
8121
8122 default:
8123 kHlpAssertFailed();
8124 kwSandboxHandlePut(pHandle);
8125 SetLastError(ERROR_INVALID_FUNCTION);
8126 return INVALID_FILE_SIZE;
8127 }
8128 kwSandboxHandlePut(pHandle);
8129 }
8130
8131 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
8132 return GetFileSizeEx(hFile, pcbFile);
8133}
8134
8135
8136/** Kernel32 - CreateFileMappingW */
8137static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
8138 DWORD fProtect, DWORD dwMaximumSizeHigh,
8139 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
8140{
8141 HANDLE hMapping;
8142 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8143 if (pHandle != NULL)
8144 {
8145 switch (pHandle->enmType)
8146 {
8147 case KWHANDLETYPE_TEMP_FILE:
8148 {
8149 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8150 if ( ( fProtect == PAGE_READONLY
8151 || fProtect == PAGE_EXECUTE_READ)
8152 && dwMaximumSizeHigh == 0
8153 && ( dwMaximumSizeLow == 0
8154 || dwMaximumSizeLow == pTempFile->cbFile)
8155 && pwszName == NULL)
8156 {
8157 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
8158 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
8159 kwSandboxHandlePut(pHandle);
8160 return hMapping;
8161 }
8162 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
8163 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8164 kwSandboxHandlePut(pHandle);
8165 SetLastError(ERROR_ACCESS_DENIED);
8166 return INVALID_HANDLE_VALUE;
8167 }
8168
8169 /* moc.exe benefits from this. */
8170 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8171 {
8172 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8173 if ( ( fProtect == PAGE_READONLY
8174 || fProtect == PAGE_EXECUTE_READ)
8175 && dwMaximumSizeHigh == 0
8176 && ( dwMaximumSizeLow == 0
8177 || dwMaximumSizeLow == pCachedFile->cbCached)
8178 && pwszName == NULL)
8179 {
8180 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
8181 K_FALSE /*fIsFileHandle*/, &hMapping))
8182 { /* likely */ }
8183 else
8184 hMapping = NULL;
8185 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8186 kwSandboxHandlePut(pHandle);
8187 return hMapping;
8188 }
8189
8190 /* Do fallback (for .pch). */
8191 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8192 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8193 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8194
8195 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8196 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8197 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8198 kwSandboxHandlePut(pHandle);
8199 return hMapping;
8200 }
8201
8202 /** @todo read cached memory mapped files too for moc. */
8203 }
8204 kwSandboxHandlePut(pHandle);
8205 }
8206
8207 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8208 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8209 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8210 return hMapping;
8211}
8212
8213
8214/** Kernel32 - MapViewOfFile */
8215static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8216 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8217{
8218 PVOID pvRet;
8219 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8220 if (pHandle != NULL)
8221 {
8222 KU32 idxMapping;
8223
8224 /*
8225 * Ensure one free entry in the mapping tracking table first,
8226 * since this is common to both temporary and cached files.
8227 */
8228 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8229 { /* likely */ }
8230 else
8231 {
8232 void *pvNew;
8233 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8234 if (cNew)
8235 cNew *= 2;
8236 else
8237 cNew = 32;
8238 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8239 if (pvNew)
8240 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8241 else
8242 {
8243 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8244 kwSandboxHandlePut(pHandle);
8245 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8246 return NULL;
8247 }
8248 g_Sandbox.cMemMappingsAlloc = cNew;
8249 }
8250
8251 /*
8252 * Type specific work.
8253 */
8254 switch (pHandle->enmType)
8255 {
8256 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8257 case KWHANDLETYPE_TEMP_FILE:
8258 case KWHANDLETYPE_OUTPUT_BUF:
8259 default:
8260 kHlpAssertFailed();
8261 kwSandboxHandlePut(pHandle);
8262 SetLastError(ERROR_INVALID_OPERATION);
8263 return NULL;
8264
8265 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8266 {
8267 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8268 if ( dwDesiredAccess == FILE_MAP_READ
8269 && offFileHigh == 0
8270 && offFileLow == 0
8271 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8272 {
8273 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8274 if (pTempFile->cSegs != 1)
8275 {
8276 KU32 iSeg;
8277 KU32 cbLeft;
8278 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8279 KU8 *pbAll = NULL;
8280 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8281 if (rc != 0)
8282 {
8283 kHlpAssertFailed();
8284 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8285 return NULL;
8286 }
8287
8288 cbLeft = pTempFile->cbFile;
8289 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8290 {
8291 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8292 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8293 cbLeft -= cbToCopy;
8294 }
8295
8296 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8297 {
8298 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8299 pTempFile->paSegs[iSeg].pbData = NULL;
8300 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8301 }
8302
8303 pTempFile->cSegs = 1;
8304 pTempFile->cbFileAllocated = cbAll;
8305 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8306 pTempFile->paSegs[0].pbData = pbAll;
8307 pTempFile->paSegs[0].offData = 0;
8308 }
8309
8310 pTempFile->cMappings++;
8311 kHlpAssert(pTempFile->cMappings == 1);
8312
8313 pvRet = pTempFile->paSegs[0].pbData;
8314 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8315 break;
8316 }
8317
8318 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8319 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8320 kwSandboxHandlePut(pHandle);
8321 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8322 return NULL;
8323 }
8324
8325 /*
8326 * This is simple in comparison to the above temporary file code.
8327 */
8328 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8329 {
8330 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8331 if ( dwDesiredAccess == FILE_MAP_READ
8332 && offFileHigh == 0
8333 && offFileLow == 0
8334 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8335 {
8336 pvRet = pCachedFile->pbCached;
8337 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8338 break;
8339 }
8340 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8341 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8342 kwSandboxHandlePut(pHandle);
8343 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8344 return NULL;
8345 }
8346 }
8347
8348 /*
8349 * Insert into the mapping tracking table. This is common
8350 * and we should only get here with a non-NULL pvRet.
8351 *
8352 * Note! We could look for duplicates and do ref counting, but it's
8353 * easier to just append for now.
8354 */
8355 kHlpAssert(pvRet != NULL);
8356 idxMapping = g_Sandbox.cMemMappings;
8357 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8358
8359 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8360 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8361 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8362 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8363 g_Sandbox.cMemMappings++;
8364
8365 kwSandboxHandlePut(pHandle);
8366 return pvRet;
8367 }
8368
8369 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8370 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8371 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8372 return pvRet;
8373}
8374
8375
8376/** Kernel32 - MapViewOfFileEx */
8377static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8378 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8379{
8380 PVOID pvRet;
8381 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8382 if (pHandle != NULL)
8383 {
8384 switch (pHandle->enmType)
8385 {
8386 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8387 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8388 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8389 if (!pvMapAddr)
8390 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8391 else
8392 {
8393 kHlpAssertFailed();
8394 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8395 }
8396 kwSandboxHandlePut(pHandle);
8397 return NULL;
8398
8399 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8400 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8401 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8402 if (!pvMapAddr)
8403 {
8404 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8405 kwSandboxHandlePut(pHandle);
8406 return pvRet;
8407 }
8408 /* We can use fallback here as the handle is an actual section handle. */
8409 break;
8410
8411 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8412 case KWHANDLETYPE_TEMP_FILE:
8413 case KWHANDLETYPE_OUTPUT_BUF:
8414 default:
8415 kHlpAssertFailed();
8416 kwSandboxHandlePut(pHandle);
8417 SetLastError(ERROR_INVALID_OPERATION);
8418 return NULL;
8419 }
8420 kwSandboxHandlePut(pHandle);
8421 }
8422
8423 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8424 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8425 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8426 return pvRet;
8427
8428}
8429
8430/** Kernel32 - UnmapViewOfFile */
8431static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8432{
8433 /*
8434 * Consult the memory mapping tracker.
8435 */
8436 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8437 KU32 idxMapping = g_Sandbox.cMemMappings;
8438 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8439 while (idxMapping-- > 0)
8440 if (paMemMappings[idxMapping].pvMapping == pvBase)
8441 {
8442 /* Type specific stuff. */
8443 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8444 {
8445 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8446 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8447 }
8448 else
8449 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8450
8451 /* Deref and probably free it. */
8452 if (--paMemMappings[idxMapping].cRefs == 0)
8453 {
8454 g_Sandbox.cMemMappings--;
8455 if (idxMapping != g_Sandbox.cMemMappings)
8456 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8457 }
8458 return TRUE;
8459 }
8460
8461 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8462 return UnmapViewOfFile(pvBase);
8463}
8464
8465/** @todo UnmapViewOfFileEx */
8466
8467#endif /* WITH_TEMP_MEMORY_FILES */
8468
8469
8470/** Kernel32 - DuplicateHandle */
8471static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8472 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8473{
8474 BOOL fRet;
8475
8476 /*
8477 * We must catch our handles being duplicated.
8478 */
8479 if (hSrcProc == GetCurrentProcess())
8480 {
8481 PKWHANDLE pHandle = kwSandboxHandleGet(hSrc);
8482 if (pHandle)
8483 {
8484 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8485 if (fRet)
8486 {
8487 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8488 {
8489 pHandle->cRefs++;
8490 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8491 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8492 pHandle->enmType, pHandle->cRefs));
8493 }
8494 else
8495 {
8496 fRet = FALSE;
8497 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8498 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8499 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8500 }
8501 }
8502 else
8503 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8504 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8505 kwSandboxHandlePut(pHandle);
8506 return fRet;
8507 }
8508 }
8509
8510 /*
8511 * Not one of ours, just do what the caller asks and log it.
8512 */
8513 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8514 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8515 fInheritHandle, dwOptions, fRet, *phNew));
8516 return fRet;
8517}
8518
8519
8520/** Kernel32 - CloseHandle */
8521static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8522{
8523 BOOL fRet;
8524 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8525 PKWHANDLE pHandle = kwSandboxHandleGet(hObject);
8526 if (pHandle)
8527 {
8528 /* Prevent the closing of the standard output and error handles. */
8529 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8530 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) /* why this?!? */)
8531 {
8532 fRet = CloseHandle(hObject);
8533 if (fRet)
8534 {
8535 EnterCriticalSection(&g_Sandbox.HandlesLock);
8536 pHandle = g_Sandbox.papHandles[idxHandle];
8537 g_Sandbox.papHandles[idxHandle] = NULL;
8538 g_Sandbox.cActiveHandles--;
8539 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8540 if (--pHandle->cRefs == 0)
8541 {
8542#ifdef WITH_TEMP_MEMORY_FILES
8543 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8544 {
8545 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8546 pHandle->u.pTempFile->cActiveHandles--;
8547 }
8548#endif
8549 kHlpFree(pHandle);
8550 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8551 }
8552 else
8553 {
8554 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8555 kwSandboxHandlePut(pHandle);
8556 }
8557 LeaveCriticalSection(&g_Sandbox.HandlesLock);
8558 return fRet;
8559 }
8560 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8561 }
8562 else
8563 {
8564#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8565 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8566 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8567#else
8568 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of stdXXX!\n", hObject));
8569#endif
8570 fRet = TRUE;
8571 }
8572 kwSandboxHandlePut(pHandle);
8573 return fRet;
8574 }
8575
8576 fRet = CloseHandle(hObject);
8577 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8578 return fRet;
8579}
8580
8581
8582/** Kernel32 - GetFileAttributesA. */
8583static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8584{
8585 DWORD fRet;
8586 const char *pszExt = kHlpGetExt(pszFilename);
8587 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8588 {
8589 KFSLOOKUPERROR enmError;
8590 PKFSOBJ pFsObj;
8591 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8592
8593 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8594 if (pFsObj)
8595 {
8596 kHlpAssert(pFsObj->fHaveStats);
8597 fRet = pFsObj->Stats.st_attribs;
8598 kFsCacheObjRelease(g_pFsCache, pFsObj);
8599 }
8600 else
8601 {
8602 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8603 fRet = INVALID_FILE_ATTRIBUTES;
8604 }
8605
8606 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8607 return fRet;
8608 }
8609
8610 fRet = GetFileAttributesA(pszFilename);
8611 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8612 return fRet;
8613}
8614
8615
8616/** Kernel32 - GetFileAttributesW. */
8617static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8618{
8619 DWORD fRet;
8620 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8621 {
8622 KFSLOOKUPERROR enmError;
8623 PKFSOBJ pFsObj;
8624 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8625
8626 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8627 if (pFsObj)
8628 {
8629 kHlpAssert(pFsObj->fHaveStats);
8630 fRet = pFsObj->Stats.st_attribs;
8631 kFsCacheObjRelease(g_pFsCache, pFsObj);
8632 }
8633 else
8634 {
8635 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8636 fRet = INVALID_FILE_ATTRIBUTES;
8637 }
8638#ifndef NDEBUG
8639 {
8640 DWORD fCheck = GetFileAttributesW(pwszFilename);
8641 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8642 }
8643#endif
8644 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8645 return fRet;
8646 }
8647
8648 fRet = GetFileAttributesW(pwszFilename);
8649 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8650 return fRet;
8651}
8652
8653
8654/** Kernel32 - GetFileAttributesExA. */
8655static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8656 WIN32_FILE_ATTRIBUTE_DATA *pData)
8657{
8658 BOOL fRet;
8659 const char *pszExt = kHlpGetExt(pszFilename);
8660 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8661 {
8662 KFSLOOKUPERROR enmError;
8663 PKFSOBJ pFsObj;
8664 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8665
8666 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8667 if (pFsObj)
8668 {
8669 kHlpAssert(pFsObj->fHaveStats);
8670 if (enmLevel == GetFileExInfoStandard)
8671 {
8672 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8673 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8674 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8675 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8676 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8677 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8678 kFsCacheObjRelease(g_pFsCache, pFsObj);
8679 fRet = TRUE;
8680 }
8681 else
8682 {
8683 kFsCacheObjRelease(g_pFsCache, pFsObj);
8684 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8685 }
8686 }
8687 else
8688 {
8689 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8690 fRet = FALSE;
8691 }
8692
8693#ifdef K_STRICT
8694 {
8695 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8696 DWORD const dwErrSaved = GetLastError();
8697 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8698 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8699 if (fRetCheck && fRet)
8700 {
8701# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8702 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8703 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8704 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8705 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8706 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8707 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8708 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8709 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8710 }
8711 else
8712 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8713 SetLastError(dwErrSaved);
8714 }
8715#endif
8716 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8717 return fRet;
8718 }
8719
8720 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8721 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8722 return fRet;
8723}
8724
8725
8726/** Kernel32 - GetFileAttributesExW. */
8727static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8728 WIN32_FILE_ATTRIBUTE_DATA *pData)
8729{
8730 BOOL fRet;
8731 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8732 {
8733 KFSLOOKUPERROR enmError;
8734 PKFSOBJ pFsObj;
8735 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8736
8737 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8738 if (pFsObj)
8739 {
8740 kHlpAssert(pFsObj->fHaveStats);
8741 if (enmLevel == GetFileExInfoStandard)
8742 {
8743 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8744 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8745 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8746 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8747 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8748 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8749 kFsCacheObjRelease(g_pFsCache, pFsObj);
8750 fRet = TRUE;
8751 }
8752 else
8753 {
8754 kFsCacheObjRelease(g_pFsCache, pFsObj);
8755 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8756 }
8757 }
8758 else
8759 {
8760 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8761 fRet = FALSE;
8762 }
8763
8764#ifdef K_STRICT
8765 {
8766 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8767 DWORD const dwErrSaved = GetLastError();
8768 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8769 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8770 if (fRetCheck && fRet)
8771 {
8772# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8773 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8774 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8775 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8776 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8777 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8778 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8779 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8780 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8781 }
8782 else
8783 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8784 SetLastError(dwErrSaved);
8785 }
8786#endif
8787 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8788 return fRet;
8789 }
8790
8791 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8792 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8793 return fRet;
8794}
8795
8796
8797/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8798 * directory containing each include file. We cache the result to speed
8799 * things up a little. */
8800static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8801{
8802 DWORD cwcRet;
8803 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8804 {
8805 KFSLOOKUPERROR enmError;
8806 PKFSOBJ pObj;
8807 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8808
8809 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8810 if (pObj)
8811 {
8812 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8813 {
8814 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8815 {
8816 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8817
8818 /* Should preserve trailing slash on directory paths. */
8819 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8820 {
8821 if ( cwcRet + 1 < cwcShortPath
8822 && pwszShortPath[cwcRet - 1] != '\\')
8823 {
8824 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8825 if ( cwcIn > 0
8826 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8827 {
8828 pwszShortPath[cwcRet++] = '\\';
8829 pwszShortPath[cwcRet] = '\0';
8830 }
8831 }
8832 }
8833
8834 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8835 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8836 kFsCacheObjRelease(g_pFsCache, pObj);
8837 return cwcRet;
8838 }
8839
8840 /* fall back for complicated cases. */
8841 }
8842 kFsCacheObjRelease(g_pFsCache, pObj);
8843 }
8844 }
8845 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8846 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8847 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8848 return cwcRet;
8849}
8850
8851
8852#ifdef WITH_TEMP_MEMORY_FILES
8853/** Kernel32 - DeleteFileW
8854 * Skip deleting the in-memory files. */
8855static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8856{
8857 BOOL fRc;
8858 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8859 && kwFsIsClTempFileW(pwszFilename))
8860 {
8861 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8862 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8863 fRc = TRUE;
8864 }
8865 else
8866 {
8867 fRc = DeleteFileW(pwszFilename);
8868 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8869 }
8870 return fRc;
8871}
8872#endif /* WITH_TEMP_MEMORY_FILES */
8873
8874
8875
8876#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8877
8878/*
8879 *
8880 * Console output buffering.
8881 * Console output buffering.
8882 * Console output buffering.
8883 *
8884 */
8885
8886
8887/**
8888 * Write a wide char string to the console.
8889 *
8890 * @param pSandbox The sandbox which output buffer to flush.
8891 */
8892static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8893{
8894 if (cwcToWrite > 0)
8895 {
8896 DWORD cwcWritten = 0;
8897 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8898 {
8899 if (cwcWritten == cwcToWrite)
8900 { /* likely */ }
8901 else
8902 {
8903 DWORD off = 0;
8904 do
8905 {
8906 off += cwcWritten;
8907 cwcWritten = 0;
8908 } while ( off < cwcToWrite
8909 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8910 kHlpAssert(off == cwcWritten);
8911 }
8912 }
8913 else
8914 kHlpAssertFailed();
8915 pSandbox->Combined.cFlushes++;
8916 }
8917}
8918
8919
8920/**
8921 * Flushes the combined console output buffer.
8922 *
8923 * @param pSandbox The sandbox which output buffer to flush.
8924 */
8925static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8926{
8927 if (pSandbox->Combined.cwcBuf > 0)
8928 {
8929 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8930 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8931 pSandbox->Combined.cwcBuf = 0;
8932 }
8933}
8934
8935
8936/**
8937 * For handling combined buffer overflow cases line by line.
8938 *
8939 * @param pSandbox The sandbox.
8940 * @param pwcBuf What to add to the combined buffer. Usually a
8941 * line, unless we're really low on buffer space.
8942 * @param cwcBuf The length of what to add.
8943 * @param fBrokenLine Whether this is a broken line.
8944 */
8945static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8946{
8947 if (fBrokenLine)
8948 kwSandboxConsoleFlushCombined(pSandbox);
8949 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8950 {
8951 kwSandboxConsoleFlushCombined(pSandbox);
8952 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
8953 }
8954 else
8955 {
8956 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
8957 pSandbox->Combined.cwcBuf += cwcBuf;
8958 }
8959}
8960
8961
8962/**
8963 * Called to final flush a line buffer via the combined buffer (if applicable).
8964 *
8965 * @param pSandbox The sandbox.
8966 * @param pLineBuf The line buffer.
8967 * @param pszName The line buffer name (for logging)
8968 */
8969static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
8970{
8971 if (pLineBuf->fIsConsole)
8972 {
8973 if (pLineBuf->u.Con.cwcBuf > 0)
8974 {
8975 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
8976
8977 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
8978 {
8979 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
8980 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
8981 }
8982 else
8983 {
8984 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
8985 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
8986 }
8987 pLineBuf->u.Con.cwcBuf = 0;
8988 }
8989 }
8990#ifdef WITH_STD_OUT_ERR_BUFFERING
8991 else if (pLineBuf->u.Fully.cchBuf > 0)
8992 {
8993 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
8994
8995 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
8996 pLineBuf->u.Fully.cchBuf = 0;
8997 }
8998#endif
8999}
9000
9001
9002/**
9003 * Called at the end of sandboxed execution to flush both stream buffers.
9004 *
9005 * @param pSandbox The sandbox.
9006 */
9007static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
9008{
9009 /*
9010 * First do the cl.exe source file supression trick, if applicable.
9011 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
9012 * handle.
9013 */
9014 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9015 && pSandbox->Combined.cFlushes == 0)
9016 {
9017 if ( pSandbox->StdOut.fIsConsole
9018 || pSandbox->StdErr.fIsConsole)
9019 {
9020 if ( pSandbox->Combined.cwcBuf >= 3
9021 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
9022 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
9023 {
9024 KI32 off = pSandbox->Combined.cwcBuf - 1;
9025 if (pSandbox->Combined.wszBuf[off] == '\n')
9026 {
9027 KBOOL fOk = K_TRUE;
9028 while (off-- > 0)
9029 {
9030 wchar_t const wc = pSandbox->Combined.wszBuf[off];
9031 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
9032 { /* likely */ }
9033 else
9034 {
9035 fOk = K_FALSE;
9036 break;
9037 }
9038 }
9039 if (fOk)
9040 {
9041 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
9042 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9043 pSandbox->Combined.cwcBuf = 0;
9044 return;
9045 }
9046 }
9047 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
9048 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9049 }
9050 }
9051#ifdef WITH_STD_OUT_ERR_BUFFERING
9052 /*
9053 * Otherwise, it goes to standard output (redirected).
9054 */
9055 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
9056 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
9057 {
9058 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
9059 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
9060 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
9061
9062 if (pchBuf[off] == '\n')
9063 {
9064 KBOOL fOk = K_TRUE;
9065 if (pchBuf[off - 1] == '\r')
9066 off--;
9067 while (off-- > 0)
9068 {
9069 char const ch = pchBuf[off];
9070 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
9071 { /* likely */ }
9072 else
9073 {
9074 fOk = K_FALSE;
9075 break;
9076 }
9077 }
9078 if (fOk)
9079 {
9080 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
9081 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9082 pSandbox->StdOut.u.Fully.cchBuf = 0;
9083 return;
9084 }
9085 }
9086 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
9087 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9088 }
9089#endif
9090 }
9091
9092 /*
9093 * Flush the two line buffer, then the combined buffer.
9094 */
9095 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
9096 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
9097 kwSandboxConsoleFlushCombined(pSandbox);
9098}
9099
9100
9101/**
9102 * Writes a string to the given output stream.
9103 *
9104 * @param pSandbox The sandbox.
9105 * @param pLineBuf The line buffer for the output stream.
9106 * @param pwcBuffer The buffer to write.
9107 * @param cwcToWrite The number of wchar_t's in the buffer.
9108 */
9109static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
9110{
9111 kHlpAssert(pLineBuf->fIsConsole);
9112 if (cwcToWrite > 0)
9113 {
9114 /*
9115 * First, find the start of the last incomplete line so we can figure
9116 * out how much line buffering we need to do.
9117 */
9118 KU32 cchLastIncompleteLine;
9119 KU32 offLastIncompleteLine = cwcToWrite;
9120 while ( offLastIncompleteLine > 0
9121 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
9122 offLastIncompleteLine--;
9123 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
9124
9125 /* Was there anything to line buffer? */
9126 if (offLastIncompleteLine < cwcToWrite)
9127 {
9128 /* Need to grow the line buffer? */
9129 KU32 cwcNeeded = offLastIncompleteLine == 0
9130 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
9131 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
9132 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
9133 {
9134 void *pvNew;
9135 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
9136 while (cwcNew < cwcNeeded)
9137 cwcNew *= 2;
9138 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
9139 if (pvNew)
9140 {
9141 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9142 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
9143 }
9144 else
9145 {
9146 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
9147 if (pvNew)
9148 {
9149 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9150 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
9151 }
9152 else
9153 {
9154 /* This isn't perfect, but it will have to do for now. */
9155 if (pLineBuf->u.Con.cwcBuf > 0)
9156 {
9157 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9158 K_TRUE /*fBrokenLine*/);
9159 pLineBuf->u.Con.cwcBuf = 0;
9160 }
9161 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
9162 return;
9163 }
9164 }
9165 }
9166
9167 /*
9168 * Handle the case where we only add to the line buffer.
9169 */
9170 if (offLastIncompleteLine == 0)
9171 {
9172 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
9173 pLineBuf->u.Con.cwcBuf += cwcToWrite;
9174 return;
9175 }
9176 }
9177
9178 /*
9179 * If there is sufficient combined buffer to handle this request, this is rather simple.
9180 */
9181 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
9182 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9183 {
9184 if (pLineBuf->u.Con.cwcBuf > 0)
9185 {
9186 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9187 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9188 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9189 pLineBuf->u.Con.cwcBuf = 0;
9190 }
9191
9192 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9193 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9194 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9195 }
9196 else
9197 {
9198 /*
9199 * Do line-by-line processing of the input, flusing the combined buffer
9200 * when it becomes necessary. We may have to write lines
9201 */
9202 KU32 off = 0;
9203 KU32 offNextLine = 0;
9204
9205 /* If there are buffered chars, we handle the first line outside the
9206 main loop. We must try our best outputting it as a complete line. */
9207 if (pLineBuf->u.Con.cwcBuf > 0)
9208 {
9209 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9210 offNextLine++;
9211 offNextLine++;
9212 kHlpAssert(offNextLine <= offLastIncompleteLine);
9213
9214 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9215 {
9216 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9217 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9218 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9219 pLineBuf->u.Con.cwcBuf = 0;
9220
9221 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9222 pSandbox->Combined.cwcBuf += offNextLine;
9223 }
9224 else
9225 {
9226 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9227 if (cwcLeft > 0)
9228 {
9229 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9230 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9231 pLineBuf->u.Con.cwcBuf += cwcCopy;
9232 off += cwcCopy;
9233 }
9234 if (pLineBuf->u.Con.cwcBuf > 0)
9235 {
9236 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9237 K_TRUE /*fBrokenLine*/);
9238 pLineBuf->u.Con.cwcBuf = 0;
9239 }
9240 if (off < offNextLine)
9241 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9242 }
9243 off = offNextLine;
9244 }
9245
9246 /* Deal with the remaining lines */
9247 while (off < offLastIncompleteLine)
9248 {
9249 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9250 offNextLine++;
9251 offNextLine++;
9252 kHlpAssert(offNextLine <= offLastIncompleteLine);
9253 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9254 off = offNextLine;
9255 }
9256 }
9257
9258 /*
9259 * Buffer any remaining incomplete line chars.
9260 */
9261 if (cchLastIncompleteLine)
9262 {
9263 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9264 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9265 }
9266 }
9267}
9268
9269
9270/**
9271 * Worker for WriteConsoleA and WriteFile.
9272 *
9273 * @param pSandbox The sandbox.
9274 * @param pLineBuf The line buffer.
9275 * @param pchBuffer What to write.
9276 * @param cchToWrite How much to write.
9277 */
9278static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9279{
9280 /*
9281 * Convert it to wide char and use the 'W' to do the work.
9282 */
9283 int cwcRet;
9284 KU32 cwcBuf = cchToWrite * 2 + 1;
9285 wchar_t *pwcBufFree = NULL;
9286 wchar_t *pwcBuf;
9287 kHlpAssert(pLineBuf->fIsConsole);
9288
9289 if (cwcBuf <= 4096)
9290 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9291 else
9292 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9293
9294 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9295 if (cwcRet > 0)
9296 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9297 else
9298 {
9299 DWORD cchWritten;
9300 kHlpAssertFailed();
9301
9302 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9303 if (pLineBuf->u.Con.cwcBuf > 0)
9304 {
9305 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9306 pLineBuf->u.Con.cwcBuf = 0;
9307 }
9308 kwSandboxConsoleFlushCombined(pSandbox);
9309
9310 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9311 {
9312 if (cchWritten >= cchToWrite)
9313 { /* likely */ }
9314 else
9315 {
9316 KU32 off = 0;
9317 do
9318 {
9319 off += cchWritten;
9320 cchWritten = 0;
9321 } while ( off < cchToWrite
9322 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9323 }
9324 }
9325 }
9326
9327 if (pwcBufFree)
9328 kHlpFree(pwcBufFree);
9329}
9330
9331
9332/** Kernel32 - WriteConsoleA */
9333BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9334 PVOID pvReserved)
9335{
9336 BOOL fRc;
9337 PKWOUTPUTSTREAMBUF pLineBuf;
9338 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9339
9340 if (hConOutput == g_Sandbox.StdErr.hOutput)
9341 pLineBuf = &g_Sandbox.StdErr;
9342 else
9343 pLineBuf = &g_Sandbox.StdOut;
9344 if (pLineBuf->fIsConsole)
9345 {
9346 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9347
9348 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9349 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9350 if (pcbWritten)
9351 *pcbWritten = cbToWrite;
9352 fRc = TRUE;
9353 }
9354 else
9355 {
9356 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9357 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9358 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9359 }
9360 return fRc;
9361}
9362
9363
9364/** Kernel32 - WriteConsoleW */
9365BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9366 PVOID pvReserved)
9367{
9368 BOOL fRc;
9369 PKWOUTPUTSTREAMBUF pLineBuf;
9370 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9371
9372 if (hConOutput == g_Sandbox.StdErr.hOutput)
9373 pLineBuf = &g_Sandbox.StdErr;
9374 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9375 pLineBuf = &g_Sandbox.StdOut;
9376 else
9377 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9378 if (pLineBuf->fIsConsole)
9379 {
9380 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9381
9382 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9383 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9384 if (pcwcWritten)
9385 *pcwcWritten = cwcToWrite;
9386 fRc = TRUE;
9387 }
9388 else
9389 {
9390 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9391 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9392 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9393 }
9394 return fRc;
9395}
9396
9397#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9398
9399
9400
9401/*
9402 *
9403 * Virtual memory leak prevension.
9404 * Virtual memory leak prevension.
9405 * Virtual memory leak prevension.
9406 *
9407 */
9408
9409#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9410
9411/** For debug logging. */
9412# ifndef NDEBUG
9413static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9414{
9415 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9416 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9417 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9418 if (cbMemInfo != 0)
9419 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9420 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9421 MemInfo.BaseAddress,
9422 MemInfo.AllocationBase,
9423 MemInfo.RegionSize,
9424 MemInfo.State,
9425 MemInfo.Protect,
9426 MemInfo.Type));
9427}
9428# else
9429# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9430# endif
9431
9432/**
9433 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9434 *
9435 * @param idxFixed The fixed allocation index to "free".
9436 */
9437static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9438{
9439 BOOL fRc;
9440 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9441 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9442 kHlpAssert(fRc); K_NOREF(fRc);
9443 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9444 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9445}
9446
9447#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9448
9449
9450/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9451 * location (~78MB in 32-bit 2010 compiler). */
9452static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9453{
9454 PVOID pvMem;
9455 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9456 {
9457 KU32 idxPreAllocated = KU32_MAX;
9458
9459#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9460 /*
9461 * Look for a pre-reserved CL.exe heap allocation.
9462 */
9463 pvMem = NULL;
9464 if ( pvAddr != 0
9465 && (fAllocType & MEM_RESERVE))
9466 {
9467 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9468 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9469 while (idxFixed-- > 0)
9470 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9471 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9472 {
9473 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9474 {
9475 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9476 {
9477 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9478 pvMem = pvAddr;
9479 idxPreAllocated = idxFixed;
9480 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9481 pvAddr, cb, fAllocType, fProt, pvMem));
9482 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9483 SetLastError(NO_ERROR);
9484 break;
9485 }
9486 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9487 }
9488 else
9489 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9490 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9491 }
9492 }
9493 if (!pvMem)
9494#endif
9495 {
9496 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9497 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9498 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9499 if ( pvAddr
9500 && pvAddr != pvMem
9501 && !( fAllocType == MEM_RESERVE /* After mapping the PCH, VS2019 ends up here (happens */
9502 && fProt == PAGE_READWRITE /* in real cl.exe runs too). Just shut it up to avoid confusion. */
9503#if K_ARCH_BITS >= 64
9504 && cb > 0x10000000 /* seen 67c00000, 33e00000, ++ */
9505#else
9506 && cb > 0x04000000 /* no idea */
9507#endif
9508 )
9509 )
9510 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9511 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9512 }
9513
9514 if (pvMem)
9515 {
9516 /*
9517 * Track it.
9518 */
9519 PKWVIRTALLOC pTracker;
9520
9521 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9522 pTracker = g_Sandbox.pVirtualAllocHead;
9523 while ( pTracker
9524 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9525 pTracker = pTracker->pNext;
9526 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9527 if (!pTracker)
9528 {
9529 DWORD dwErr = GetLastError();
9530 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9531 if (pTracker)
9532 {
9533 pTracker->pvAlloc = pvMem;
9534 pTracker->cbAlloc = cb;
9535 pTracker->idxPreAllocated = idxPreAllocated;
9536 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9537 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9538 g_Sandbox.pVirtualAllocHead = pTracker;
9539 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9540 }
9541 SetLastError(dwErr);
9542 }
9543 }
9544 }
9545 else
9546 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9547 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9548 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9549 return pvMem;
9550}
9551
9552
9553/** Kernel32 - VirtualFree. */
9554static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9555{
9556 BOOL fRc;
9557 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9558 {
9559 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9560 if (dwFreeType & MEM_RELEASE)
9561 {
9562 PKWVIRTALLOC pTracker;
9563 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9564 pTracker = g_Sandbox.pVirtualAllocHead;
9565 if (pTracker)
9566 {
9567 if (pTracker->pvAlloc == pvAddr)
9568 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9569 else
9570 {
9571 PKWVIRTALLOC pPrev;
9572 do
9573 {
9574 pPrev = pTracker;
9575 pTracker = pTracker->pNext;
9576 } while (pTracker && pTracker->pvAlloc != pvAddr);
9577 if (pTracker)
9578 pPrev->pNext = pTracker->pNext;
9579 }
9580 if (pTracker)
9581 {
9582#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9583 if (pTracker->idxPreAllocated != KU32_MAX)
9584 {
9585 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9586 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9587 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9588 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9589 kHlpFree(pTracker);
9590 return TRUE;
9591 }
9592#endif
9593
9594 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9595 if (fRc)
9596 kHlpFree(pTracker);
9597 else
9598 {
9599 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9600 g_Sandbox.pVirtualAllocHead = pTracker;
9601 }
9602 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9603 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9604 return fRc;
9605 }
9606
9607 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9608 }
9609 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9610 }
9611 }
9612
9613#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9614 /*
9615 * Protect our fixed allocations (this isn't just paranoia, btw.).
9616 */
9617 if (dwFreeType & MEM_RELEASE)
9618 {
9619 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9620 while (idxFixed-- > 0)
9621 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9622 {
9623 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9624 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9625 return TRUE;
9626 }
9627 }
9628#endif
9629
9630 /*
9631 * Not tracker or not actually free the virtual range.
9632 */
9633 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9634 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9635 return fRc;
9636}
9637
9638
9639/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9640HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9641{
9642 HANDLE hHeap;
9643 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9644
9645 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9646 if (hHeap != NULL)
9647 {
9648 DWORD dwErr = GetLastError();
9649 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9650 if (pTracker)
9651 {
9652 pTracker->hHeap = hHeap;
9653 pTracker->pNext = g_Sandbox.pHeapHead;
9654 g_Sandbox.pHeapHead = pTracker;
9655 }
9656
9657 SetLastError(dwErr);
9658 }
9659 return hHeap;
9660
9661}
9662
9663
9664/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9665BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9666{
9667 BOOL fRc = HeapDestroy(hHeap);
9668 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9669 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9670 if (fRc)
9671 {
9672 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9673 if (pTracker)
9674 {
9675 if (pTracker->hHeap == hHeap)
9676 g_Sandbox.pHeapHead = pTracker->pNext;
9677 else
9678 {
9679 PKWHEAP pPrev;
9680 do
9681 {
9682 pPrev = pTracker;
9683 pTracker = pTracker->pNext;
9684 } while (pTracker && pTracker->hHeap == hHeap);
9685 if (pTracker)
9686 pPrev->pNext = pTracker->pNext;
9687 }
9688 if (pTracker)
9689 kHlpFree(pTracker);
9690 else
9691 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9692 }
9693 }
9694
9695 return fRc;
9696}
9697
9698
9699
9700/*
9701 *
9702 * Thread/Fiber local storage leak prevention.
9703 * Thread/Fiber local storage leak prevention.
9704 * Thread/Fiber local storage leak prevention.
9705 *
9706 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9707 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9708 * we're leaking these indexes, but more importantely we crash during
9709 * worker exit since the callback is triggered multiple times.
9710 */
9711
9712
9713/** Kernel32 - FlsAlloc */
9714DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9715{
9716 DWORD idxFls = FlsAlloc(pfnCallback);
9717 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9718 if (idxFls != FLS_OUT_OF_INDEXES)
9719 {
9720 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9721 if (pTracker)
9722 {
9723 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9724 pTracker->idx = idxFls;
9725 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9726 g_Sandbox.pFlsAllocHead = pTracker;
9727 }
9728 }
9729
9730 return idxFls;
9731}
9732
9733/** Kernel32 - FlsFree */
9734BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9735{
9736 BOOL fRc = FlsFree(idxFls);
9737 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9738 if (fRc)
9739 {
9740 PKWLOCALSTORAGE pTracker;
9741 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9742
9743 pTracker = g_Sandbox.pFlsAllocHead;
9744 if (pTracker)
9745 {
9746 if (pTracker->idx == idxFls)
9747 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9748 else
9749 {
9750 PKWLOCALSTORAGE pPrev;
9751 do
9752 {
9753 pPrev = pTracker;
9754 pTracker = pTracker->pNext;
9755 } while (pTracker && pTracker->idx != idxFls);
9756 if (pTracker)
9757 pPrev->pNext = pTracker->pNext;
9758 }
9759 if (pTracker)
9760 {
9761 pTracker->idx = FLS_OUT_OF_INDEXES;
9762 pTracker->pNext = NULL;
9763 kHlpFree(pTracker);
9764 }
9765 }
9766 }
9767 return fRc;
9768}
9769
9770
9771/** Kernel32 - TlsAlloc */
9772DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9773{
9774 DWORD idxTls = TlsAlloc();
9775 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9776 if (idxTls != TLS_OUT_OF_INDEXES)
9777 {
9778 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9779 if (pTracker)
9780 {
9781 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9782 pTracker->idx = idxTls;
9783 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9784 g_Sandbox.pTlsAllocHead = pTracker;
9785 }
9786 }
9787
9788 return idxTls;
9789}
9790
9791/** Kernel32 - TlsFree */
9792BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9793{
9794 BOOL fRc = TlsFree(idxTls);
9795 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9796 if (fRc)
9797 {
9798 PKWLOCALSTORAGE pTracker;
9799 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9800
9801 pTracker = g_Sandbox.pTlsAllocHead;
9802 if (pTracker)
9803 {
9804 if (pTracker->idx == idxTls)
9805 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9806 else
9807 {
9808 PKWLOCALSTORAGE pPrev;
9809 do
9810 {
9811 pPrev = pTracker;
9812 pTracker = pTracker->pNext;
9813 } while (pTracker && pTracker->idx != idxTls);
9814 if (pTracker)
9815 pPrev->pNext = pTracker->pNext;
9816 }
9817 if (pTracker)
9818 {
9819 pTracker->idx = TLS_OUT_OF_INDEXES;
9820 pTracker->pNext = NULL;
9821 kHlpFree(pTracker);
9822 }
9823 }
9824 }
9825 return fRc;
9826}
9827
9828
9829
9830/*
9831 *
9832 * Header file hashing.
9833 * Header file hashing.
9834 * Header file hashing.
9835 *
9836 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9837 * indicated that ~12% of the time was spent doing MD5 caluclation when
9838 * rebuiling openssl. The hashing it done right after reading the source
9839 * via ReadFile, same buffers and sizes.
9840 */
9841
9842#ifdef WITH_HASH_MD5_CACHE
9843
9844/** AdvApi32 - CryptCreateHash */
9845static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9846 HCRYPTHASH *phHash)
9847{
9848 BOOL fRc;
9849
9850 /*
9851 * Only do this for cl.exe when it request normal MD5.
9852 */
9853 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9854 {
9855 /** @todo do generic caching of hash results. Need SHA-1 and SHA-256 for
9856 * more recent compilers. */
9857 if (idAlg == CALG_MD5)
9858 {
9859 if (hKey == 0)
9860 {
9861 if (dwFlags == 0)
9862 {
9863 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9864 if (pHash)
9865 {
9866 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9867 pHash->uMagic = KWHASHMD5_MAGIC;
9868 pHash->cbHashed = 0;
9869 pHash->fGoneBad = K_FALSE;
9870 pHash->fFallbackMode = K_FALSE;
9871 pHash->fFinal = K_FALSE;
9872
9873 /* link it. */
9874 pHash->pNext = g_Sandbox.pHashHead;
9875 g_Sandbox.pHashHead = pHash;
9876
9877 *phHash = (KUPTR)pHash;
9878 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9879 hProv, *phHash, TRUE));
9880 return TRUE;
9881 }
9882
9883 kwErrPrintf("CryptCreateHash: out of memory!\n");
9884 }
9885 else
9886 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9887 }
9888 else
9889 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9890 }
9891 //else
9892 // kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9893 }
9894
9895 /*
9896 * Fallback.
9897 */
9898 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9899 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9900 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9901 return fRc;
9902}
9903
9904
9905/** AdvApi32 - CryptHashData */
9906static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9907{
9908 BOOL fRc;
9909 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9910 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9911 while (pHash && (KUPTR)pHash != hHash)
9912 pHash = pHash->pNext;
9913 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9914 hHash, pHash, pbData, cbData, dwFlags));
9915 if (pHash)
9916 {
9917 /*
9918 * Validate the state.
9919 */
9920 if ( pHash->uMagic == KWHASHMD5_MAGIC
9921 && !pHash->fFinal)
9922 {
9923 if (!pHash->fFallbackMode)
9924 {
9925 /*
9926 * Does this match the previous ReadFile call to a cached file?
9927 * If it doesn't, try falling back.
9928 */
9929 if ( g_Sandbox.LastHashRead.cbRead == cbData
9930 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9931 {
9932 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9933 if ( pCachedFile
9934 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9935 {
9936
9937 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9938 {
9939 if ( pHash->pCachedFile == NULL
9940 && pHash->cbHashed == 0)
9941 pHash->pCachedFile = pCachedFile;
9942 if (pHash->pCachedFile == pCachedFile)
9943 {
9944 pHash->cbHashed += cbData;
9945 g_Sandbox.LastHashRead.pCachedFile = NULL;
9946 g_Sandbox.LastHashRead.pvRead = NULL;
9947 g_Sandbox.LastHashRead.cbRead = 0;
9948 g_Sandbox.LastHashRead.offRead = 0;
9949 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9950 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9951 return TRUE;
9952 }
9953
9954 /* Note! it's possible to fall back here too, if necessary. */
9955 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
9956 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
9957 }
9958 else
9959 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
9960 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
9961 }
9962 else if (!pCachedFile)
9963 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
9964 else
9965 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
9966 }
9967 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
9968 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
9969 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
9970 if (pHash->cbHashed == 0)
9971 pHash->fFallbackMode = K_TRUE;
9972 if (pHash->fFallbackMode)
9973 {
9974 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
9975 pHash->fFallbackMode = K_TRUE;
9976 MD5Init(&pHash->Md5Ctx);
9977 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9978 pHash->cbHashed = cbData;
9979 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
9980 hHash, pbData, cbData, dwFlags));
9981 return TRUE;
9982 }
9983 pHash->fGoneBad = K_TRUE;
9984 SetLastError(ERROR_INVALID_PARAMETER);
9985 fRc = FALSE;
9986 }
9987 else
9988 {
9989 /* fallback. */
9990 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9991 pHash->cbHashed += cbData;
9992 fRc = TRUE;
9993 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
9994 hHash, pbData, cbData, dwFlags));
9995 }
9996 }
9997 /*
9998 * Bad handle state.
9999 */
10000 else
10001 {
10002 if (pHash->uMagic != KWHASHMD5_MAGIC)
10003 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
10004 else
10005 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
10006 SetLastError((DWORD)NTE_BAD_HASH);
10007 fRc = FALSE;
10008 }
10009 }
10010 else
10011 {
10012
10013 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
10014 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
10015 }
10016 return fRc;
10017}
10018
10019
10020/** AdvApi32 - CryptGetHashParam */
10021static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
10022 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
10023{
10024 BOOL fRc;
10025 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
10026 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10027 while (pHash && (KUPTR)pHash != hHash)
10028 pHash = pHash->pNext;
10029 if (pHash)
10030 {
10031 if (pHash->uMagic == KWHASHMD5_MAGIC)
10032 {
10033 if (dwFlags == 0)
10034 {
10035 DWORD cbRet;
10036 void *pvRet;
10037 union
10038 {
10039 DWORD dw;
10040 } uBuf;
10041
10042 switch (dwParam)
10043 {
10044 case HP_HASHVAL:
10045 {
10046 /* Check the hash progress. */
10047 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
10048 if (pCachedFile)
10049 {
10050 if ( pCachedFile->cbCached == pHash->cbHashed
10051 && !pHash->fGoneBad)
10052 {
10053 if (pCachedFile->fValidMd5)
10054 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
10055 else
10056 {
10057 MD5Init(&pHash->Md5Ctx);
10058 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
10059 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
10060 pCachedFile->fValidMd5 = K_TRUE;
10061 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
10062 }
10063 pvRet = pCachedFile->abMd5Digest;
10064 }
10065 else
10066 {
10067 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
10068 from what I can tell, so just deal with it. */
10069 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
10070 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
10071 pHash, pCachedFile, pCachedFile->szPath));
10072 pHash->fFallbackMode = K_TRUE;
10073 pHash->pCachedFile = NULL;
10074 MD5Init(&pHash->Md5Ctx);
10075 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
10076 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
10077 pvRet = pHash->abDigest;
10078 }
10079 pHash->fFinal = K_TRUE;
10080 cbRet = 16;
10081 break;
10082 }
10083 else if (pHash->fFallbackMode)
10084 {
10085 if (!pHash->fFinal)
10086 {
10087 pHash->fFinal = K_TRUE;
10088 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
10089 }
10090 pvRet = pHash->abDigest;
10091 cbRet = 16;
10092 break;
10093 }
10094 else
10095 {
10096 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
10097 SetLastError(ERROR_INVALID_SERVER_STATE);
10098 }
10099 return FALSE;
10100 }
10101
10102 case HP_HASHSIZE:
10103 uBuf.dw = 16;
10104 pvRet = &uBuf;
10105 cbRet = sizeof(DWORD);
10106 break;
10107
10108 case HP_ALGID:
10109 uBuf.dw = CALG_MD5;
10110 pvRet = &uBuf;
10111 cbRet = sizeof(DWORD);
10112 break;
10113
10114 default:
10115 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
10116 SetLastError((DWORD)NTE_BAD_TYPE);
10117 return FALSE;
10118 }
10119
10120 /*
10121 * Copy out cbRet from pvRet.
10122 */
10123 if (pbData)
10124 {
10125 if (*pcbData >= cbRet)
10126 {
10127 *pcbData = cbRet;
10128 kHlpMemCopy(pbData, pvRet, cbRet);
10129 if (cbRet == 4)
10130 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
10131 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
10132 else if (cbRet == 16)
10133 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",
10134 dwParam, pHash, pHash->pCachedFile, cbRet,
10135 pbData[0], pbData[1], pbData[2], pbData[3],
10136 pbData[4], pbData[5], pbData[6], pbData[7],
10137 pbData[8], pbData[9], pbData[10], pbData[11],
10138 pbData[12], pbData[13], pbData[14], pbData[15]));
10139 else
10140 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
10141 dwParam, pHash, pHash->pCachedFile, cbRet));
10142 return TRUE;
10143 }
10144
10145 kHlpMemCopy(pbData, pvRet, *pcbData);
10146 }
10147 SetLastError(ERROR_MORE_DATA);
10148 *pcbData = cbRet;
10149 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
10150 }
10151 else
10152 {
10153 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
10154 SetLastError((DWORD)NTE_BAD_FLAGS);
10155 }
10156 }
10157 else
10158 {
10159 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
10160 SetLastError((DWORD)NTE_BAD_HASH);
10161 }
10162 fRc = FALSE;
10163 }
10164 /*
10165 * Regular handle.
10166 */
10167 else
10168 {
10169 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
10170 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
10171 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
10172 }
10173
10174 return fRc;
10175}
10176
10177
10178/** AdvApi32 - CryptDestroyHash */
10179static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
10180{
10181 BOOL fRc;
10182 PKWHASHMD5 pPrev = NULL;
10183 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
10184 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10185 while (pHash && (KUPTR)pHash != hHash)
10186 {
10187 pPrev = pHash;
10188 pHash = pHash->pNext;
10189 }
10190 if (pHash)
10191 {
10192 if (pHash->uMagic == KWHASHMD5_MAGIC)
10193 {
10194 pHash->uMagic = 0;
10195 if (!pPrev)
10196 g_Sandbox.pHashHead = pHash->pNext;
10197 else
10198 pPrev->pNext = pHash->pNext;
10199 kHlpFree(pHash);
10200 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
10201 fRc = TRUE;
10202 }
10203 else
10204 {
10205 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10206 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10207 SetLastError(ERROR_INVALID_HANDLE);
10208 fRc = FALSE;
10209 }
10210 }
10211 /*
10212 * Regular handle.
10213 */
10214 else
10215 {
10216 fRc = CryptDestroyHash(hHash);
10217 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10218 }
10219 return fRc;
10220}
10221
10222#endif /* WITH_HASH_MD5_CACHE */
10223
10224
10225/*
10226 *
10227 * Reuse crypt context.
10228 * Reuse crypt context.
10229 * Reuse crypt context.
10230 *
10231 *
10232 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10233 *
10234 */
10235
10236#ifdef WITH_CRYPT_CTX_REUSE
10237
10238/** AdvApi32 - CryptAcquireContextW. */
10239static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10240 DWORD dwProvType, DWORD dwFlags)
10241{
10242 BOOL fRet;
10243
10244 /*
10245 * Lookup reusable context based on the input.
10246 */
10247 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10248 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10249 KU32 iCtx = g_Sandbox.cCryptCtxs;
10250 while (iCtx-- > 0)
10251 {
10252 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10253 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10254 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10255 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10256 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10257 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10258 {
10259 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10260 {
10261 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10262 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10263 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10264 return TRUE;
10265 }
10266 }
10267 }
10268
10269 /*
10270 * Create it and enter it into the reused array if possible.
10271 */
10272 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10273 if (fRet)
10274 {
10275 iCtx = g_Sandbox.cCryptCtxs;
10276 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10277 {
10278 /* Try duplicate the input strings. */
10279 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10280 (cwcContainer + 1) * sizeof(wchar_t));
10281 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10282 {
10283 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10284 (cwcProvider + 1) * sizeof(wchar_t));
10285 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10286 {
10287 /* Add a couple of references just to be on the safe side and all that. */
10288 HCRYPTPROV hProv = *phProv;
10289 if (CryptContextAddRef(hProv, NULL, 0))
10290 {
10291 if (CryptContextAddRef(hProv, NULL, 0))
10292 {
10293 /* Okay, finish the entry and return success */
10294 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10295 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10296 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10297 g_Sandbox.cCryptCtxs = iCtx + 1;
10298
10299 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10300 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10301 return TRUE;
10302 }
10303 CryptReleaseContext(hProv, 0);
10304 }
10305 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10306
10307 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10308 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10309 }
10310 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10311 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10312 }
10313 }
10314 else
10315 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10316 }
10317
10318 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10319 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10320 return fRet;
10321}
10322
10323
10324/** AdvApi32 - CryptReleaseContext */
10325static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10326{
10327 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10328 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10329 return fRet;
10330}
10331
10332
10333/** AdvApi32 - CryptContextAddRef */
10334static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10335{
10336 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10337 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10338 return fRet;
10339}
10340
10341#endif /* WITH_CRYPT_CTX_REUSE */
10342
10343/*
10344 *
10345 * Structured exception handling.
10346 * Structured exception handling.
10347 * Structured exception handling.
10348 *
10349 */
10350#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10351
10352# define EH_NONCONTINUABLE KU32_C(0x00000001)
10353# define EH_UNWINDING KU32_C(0x00000002)
10354# define EH_EXIT_UNWIND KU32_C(0x00000004)
10355# define EH_STACK_INVALID KU32_C(0x00000008)
10356# define EH_NESTED_CALL KU32_C(0x00000010)
10357
10358typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10359 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10360typedef struct _EXCEPTION_REGISTRATION_RECORD
10361{
10362 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10363 PFNXCPTHANDLER pfnXcptHandler;
10364};
10365
10366
10367/**
10368 * Calls @a pfnHandler.
10369 */
10370static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10371 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10372 PFNXCPTHANDLER pfnHandler)
10373{
10374# if 1
10375 /* This is a more robust version that isn't subject to calling
10376 convension cleanup disputes and such. */
10377 KU32 uSavedEdi;
10378 KU32 uSavedEsi;
10379 KU32 uSavedEbx;
10380 KU32 rcHandler;
10381
10382 __asm
10383 {
10384 mov [uSavedEdi], edi
10385 mov [uSavedEsi], esi
10386 mov [uSavedEbx], ebx
10387 mov esi, esp
10388 mov edi, esp
10389 mov edi, [pXcptRec]
10390 mov edx, [pRegRec]
10391 mov eax, [pXcptCtx]
10392 mov ebx, [ppRegRec]
10393 mov ecx, [pfnHandler]
10394 sub esp, 16
10395 and esp, 0fffffff0h
10396 mov [esp ], edi
10397 mov [esp + 4], edx
10398 mov [esp + 8], eax
10399 mov [esp + 12], ebx
10400 mov edi, esi
10401 call ecx
10402 mov esp, esi
10403 cmp esp, edi
10404 je stack_ok
10405 int 3
10406 stack_ok:
10407 mov edi, [uSavedEdi]
10408 mov esi, [uSavedEsi]
10409 mov ebx, [uSavedEbx]
10410 mov [rcHandler], eax
10411 }
10412 return rcHandler;
10413# else
10414 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10415# endif
10416}
10417
10418
10419/**
10420 * Vectored exception handler that emulates x86 chained exception handler.
10421 *
10422 * This is necessary because the RtlIsValidHandler check fails for self loaded
10423 * code and prevents cl.exe from working. (On AMD64 we can register function
10424 * tables, but on X86 cooking your own handling seems to be the only viabke
10425 * alternative.)
10426 *
10427 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10428 * @param pXcptPtrs The exception details.
10429 */
10430static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10431{
10432 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10433 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10434 if (g_Sandbox.fRunning)
10435 {
10436 HANDLE const hCurProc = GetCurrentProcess();
10437 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10438 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10439 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10440 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10441 {
10442 /* Read the exception record in a safe manner. */
10443 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10444 DWORD cbActuallyRead = 0;
10445 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10446 && cbActuallyRead == sizeof(RegRec))
10447 {
10448 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10449 KU32 rcHandler;
10450 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10451 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10452 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10453 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10454 if (rcHandler == ExceptionContinueExecution)
10455 {
10456 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10457 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10458 return EXCEPTION_CONTINUE_EXECUTION;
10459 }
10460
10461 if (rcHandler == ExceptionContinueSearch)
10462 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10463 else if (rcHandler == ExceptionNestedException)
10464 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10465 else
10466 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10467 }
10468 else
10469 {
10470 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10471 break;
10472 }
10473
10474 /*
10475 * Next.
10476 */
10477 pRegRec = RegRec.pPrevRegRec;
10478 }
10479 }
10480 return EXCEPTION_CONTINUE_SEARCH;
10481}
10482
10483
10484/** NtDll,Kernel32 - RtlUnwind */
10485static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10486 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10487{
10488 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10489 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10490 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10491 if (g_Sandbox.fRunning)
10492 {
10493 HANDLE const hCurProc = GetCurrentProcess();
10494 PCONTEXT pXcptCtx = NULL;
10495 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10496
10497 /*
10498 * Update / create an exception record.
10499 */
10500 if (pXcptRec)
10501 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10502 else
10503 {
10504 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10505 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10506 pXcptRec->ExceptionCode = (DWORD)STATUS_UNWIND;
10507 pXcptRec->ExceptionFlags = EH_UNWINDING;
10508 }
10509 if (!pStopXcptRec)
10510 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10511
10512 /*
10513 * Walk the chain till we find pStopXctpRec.
10514 */
10515 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10516 && pRegRec != NULL
10517 && pRegRec != pStopXcptRec)
10518 {
10519 /* Read the exception record in a safe manner. */
10520 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10521 DWORD cbActuallyRead = 0;
10522 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10523 && cbActuallyRead == sizeof(RegRec))
10524 {
10525 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10526 KU32 rcHandler;
10527 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10528 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10529 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10530 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10531
10532 if (rcHandler == ExceptionContinueSearch)
10533 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10534 else if (rcHandler == ExceptionCollidedUnwind)
10535 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10536 else
10537 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10538 }
10539 else
10540 {
10541 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10542 break;
10543 }
10544
10545 /*
10546 * Pop next.
10547 */
10548 pTib->ExceptionList = RegRec.pPrevRegRec;
10549 pRegRec = RegRec.pPrevRegRec;
10550 }
10551 return;
10552 }
10553
10554 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10555}
10556
10557#endif /* WINDOWS + X86 */
10558
10559
10560/*
10561 *
10562 * Misc function only intercepted while debugging.
10563 * Misc function only intercepted while debugging.
10564 * Misc function only intercepted while debugging.
10565 *
10566 */
10567
10568#ifndef NDEBUG
10569
10570/** CRT - memcpy */
10571static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10572{
10573 KU8 const *pbSrc = (KU8 const *)pvSrc;
10574 KU8 *pbDst = (KU8 *)pvDst;
10575 KSIZE cbLeft = cb;
10576 while (cbLeft-- > 0)
10577 *pbDst++ = *pbSrc++;
10578 return pvDst;
10579}
10580
10581
10582/** CRT - memset */
10583static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10584{
10585 KU8 *pbDst = (KU8 *)pvDst;
10586 KSIZE cbLeft = cb;
10587 while (cbLeft-- > 0)
10588 *pbDst++ = (KU8)bFiller;
10589 return pvDst;
10590}
10591
10592#endif /* NDEBUG */
10593
10594
10595/** @todo consider hooking NtQueryDirectoryFile as c1xx.dll/c1.dll in 2019
10596 * uses it directly to read the content of include directories, however
10597 * they do it one file at the time. We already have the info in the
10598 * cache (where we do bulk reads). There are a lot of calls for the
10599 * SDK include directories, as one can imagine. */
10600
10601/**
10602 * Functions that needs replacing for sandboxed execution.
10603 */
10604KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10605{
10606 /*
10607 * Kernel32.dll and friends.
10608 */
10609 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10610 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10611
10612 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10613 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10614 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10615 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10616 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10617 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10618 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10619 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10620 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10621 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10622 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10623
10624 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10625 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10626 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10627 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10628
10629 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10630
10631 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10632 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10633 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10634 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10635 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10636 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10637 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10638 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10639 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10640 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10641 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10642
10643 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10644 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10645 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10646 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10647#ifdef WITH_TEMP_MEMORY_FILES
10648 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10649 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10650 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10651 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10652 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10653 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10654 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10655 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10656 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10657 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10658#endif
10659 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10660 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10661 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10662 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10663 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10664 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10665 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10666 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10667 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10668#ifdef WITH_TEMP_MEMORY_FILES
10669 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10670#endif
10671
10672#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10673 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10674 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10675#endif
10676
10677 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10678 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10679
10680 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10681 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10682
10683 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10684 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10685 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10686 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10687
10688 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10689
10690#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10691 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10692#endif
10693
10694#ifdef WITH_HASH_MD5_CACHE
10695 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10696 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10697 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10698 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10699#endif
10700
10701#ifdef WITH_CRYPT_CTX_REUSE
10702 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10703 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10704 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10705#endif
10706
10707 /*
10708 * MS Visual C++ CRTs.
10709 */
10710 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10711 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10712 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10713 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10714 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10715 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10716
10717 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10718 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10719 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10720
10721 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10722 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10723 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
10724
10725 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
10726 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
10727 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
10728 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
10729 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
10730 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
10731 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
10732 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
10733 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
10734 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
10735 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
10736 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
10737 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
10738 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
10739 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
10740 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
10741 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
10742 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
10743 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
10744 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
10745
10746 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
10747 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
10748 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
10749 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
10750 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
10751 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
10752 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
10753 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
10754 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
10755 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
10756 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
10757 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
10758 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
10759 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
10760
10761#ifndef NDEBUG
10762 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
10763 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
10764#endif
10765};
10766/** Number of entries in g_aReplacements. */
10767KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
10768
10769
10770/**
10771 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
10772 * execution.
10773 */
10774KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
10775{
10776 /*
10777 * Kernel32.dll and friends.
10778 */
10779 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10780 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10781
10782#if 0
10783 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10784#endif
10785
10786 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10787 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10788 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10789 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10790#ifdef WITH_TEMP_MEMORY_FILES
10791 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10792 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10793 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10794 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10795 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10796 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10797 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10798 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10799 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10800 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10801#endif
10802 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10803 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10804 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10805 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10806 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10807 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10808 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10809 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10810 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10811#ifdef WITH_TEMP_MEMORY_FILES
10812 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10813#endif
10814 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10815 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10816
10817#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10818 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10819 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10820#endif
10821
10822#ifdef WITH_HASH_MD5_CACHE
10823 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10824 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10825 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10826 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10827#endif
10828
10829 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10830
10831#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10832 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10833#endif
10834
10835 /*
10836 * MS Visual C++ CRTs.
10837 */
10838 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10839 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10840 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10841 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10842 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10843 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10844 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10845
10846#if 0 /* used by mspdbXXX.dll */
10847 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10848 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10849#endif
10850};
10851/** Number of entries in g_aSandboxNativeReplacements. */
10852KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10853
10854
10855/**
10856 * Functions that needs replacing when queried by GetProcAddress.
10857 */
10858KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10859{
10860 /*
10861 * Kernel32.dll and friends.
10862 */
10863 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10864 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10865 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10866 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10867};
10868/** Number of entries in g_aSandboxGetProcReplacements. */
10869KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10870
10871
10872/**
10873 * Control handler.
10874 *
10875 * @returns TRUE if handled, FALSE if not.
10876 * @param dwCtrlType The signal.
10877 */
10878static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10879{
10880 DWORD cbIgn;
10881 int volatile rc; /* volatile for debugging */
10882 int volatile rcPrev;
10883 const char *pszMsg;
10884 switch (dwCtrlType)
10885 {
10886 case CTRL_C_EVENT:
10887 rc = 9;
10888 pszMsg = "kWorker: Ctrl-C\r\n";
10889 break;
10890
10891 case CTRL_BREAK_EVENT:
10892 rc = 10;
10893 pszMsg = "kWorker: Ctrl-Break\r\n";
10894 break;
10895
10896 case CTRL_CLOSE_EVENT:
10897 rc = 11;
10898 pszMsg = "kWorker: console closed\r\n";
10899 break;
10900
10901 case CTRL_LOGOFF_EVENT:
10902 rc = 11;
10903 pszMsg = "kWorker: logoff event\r\n";
10904 break;
10905
10906 case CTRL_SHUTDOWN_EVENT:
10907 rc = 11;
10908 pszMsg = "kWorker: shutdown event\r\n";
10909 break;
10910
10911 default:
10912 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10913 return TRUE;
10914 }
10915
10916 /*
10917 * Terminate the process after 5 seconds.
10918 * If we get here a second time we just terminate the process ourselves.
10919 *
10920 * Note! We do no try call exit() here as it turned out to deadlock a lot
10921 * flusing file descriptors (stderr back when we first wrote to it).
10922 */
10923 rcPrev = g_rcCtrlC;
10924 g_rcCtrlC = rc;
10925 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10926 if (rcPrev == 0)
10927 {
10928 int i;
10929 for (i = 0; i < 10; i++)
10930 {
10931 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10932 Sleep(500);
10933 }
10934 }
10935 TerminateProcess(GetCurrentProcess(), rc);
10936 return TRUE;
10937}
10938
10939
10940#if 0
10941/**
10942 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10943 */
10944static void kwSandboxResetModuleVisited(void)
10945{
10946 PKWMODULE pMod = g_pModuleHead;
10947 while (pMod)
10948 {
10949 pMod->fVisited = K_FALSE;
10950 pMod = pMod->pNextList;
10951 }
10952}
10953
10954
10955/**
10956 * Used by kwSandboxExec to reset the state of the module tree.
10957 *
10958 * This is done recursively.
10959 *
10960 * @param pMod The root of the tree to consider.
10961 */
10962static void kwSandboxResetModuleState(PKWMODULE pMod)
10963{
10964 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
10965 if (!pMod->fNative)
10966 {
10967 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
10968 if (!pMod->fVisited) /* Avoid loops. */
10969 {
10970 KSIZE iImp;
10971 pMod->fVisited = K_TRUE;
10972 iImp = pMod->u.Manual.cImpMods;
10973 while (iImp-- > 0)
10974 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
10975 }
10976 }
10977 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10978 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
10979 {
10980 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10981 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10982 {
10983 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10984 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10985 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10986 pMod->pszPath, pszValue ? pszValue : "<null>"));
10987 }
10988 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10989 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10990 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10991 pMod->pszPath, pszValue ? pszValue : "<null>"));
10992 else
10993 {
10994 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10995 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10996 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10997 if (pszValue != NULL)
10998 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10999 else
11000 pMod->pszMsPdbSrvEndpoint = NULL;
11001 pMod->fNeedReInit = K_TRUE;
11002 }
11003 }
11004}
11005#else
11006/**
11007 * Used by kwSandboxExec to reset the state of the module tree.
11008 */
11009static void kwSandboxResetModuleState(void)
11010{
11011 PKWMODULE pMod = g_pModuleHead;
11012 while (pMod)
11013 {
11014 if (!pMod->fNative)
11015 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
11016 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11017 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
11018 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
11019 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
11020 {
11021 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11022 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11023 {
11024 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11025 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11026 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11027 pMod->pszPath, pszValue ? pszValue : "<null>"));
11028 }
11029 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11030 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11031 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11032 pMod->pszPath, pszValue ? pszValue : "<null>"));
11033 else
11034 {
11035 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11036 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11037 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11038 if (pszValue != NULL)
11039 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11040 else
11041 pMod->pszMsPdbSrvEndpoint = NULL;
11042 pMod->fNeedReInit = K_TRUE;
11043 }
11044 }
11045
11046 pMod = pMod->pNextList;
11047 }
11048}
11049#endif
11050
11051static PPEB kwSandboxGetProcessEnvironmentBlock(void)
11052{
11053#if K_ARCH == K_ARCH_X86_32
11054 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
11055#elif K_ARCH == K_ARCH_AMD64
11056 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
11057#else
11058# error "Port me!"
11059#endif
11060}
11061
11062
11063/**
11064 * Enters the given handle into the handle table.
11065 *
11066 * @returns K_TRUE on success, K_FALSE on failure.
11067 * @param pSandbox The sandbox.
11068 * @param pHandle The handle.
11069 * @param hHandle The handle value to enter it under (for the
11070 * duplicate handle API).
11071 */
11072static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
11073{
11074 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
11075 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
11076
11077 EnterCriticalSection(&g_Sandbox.HandlesLock);
11078
11079 /*
11080 * Grow handle table.
11081 */
11082 if (idxHandle >= pSandbox->cHandles)
11083 {
11084 void *pvNew;
11085 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
11086 while (cHandles <= idxHandle)
11087 cHandles *= 2;
11088 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
11089 if (!pvNew)
11090 {
11091 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11092 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
11093 return K_FALSE;
11094 }
11095 pSandbox->papHandles = (PKWHANDLE *)pvNew;
11096 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
11097 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
11098 pSandbox->cHandles = cHandles;
11099 }
11100
11101 /*
11102 * Check that the entry is unused then insert it.
11103 */
11104 kHlpAssertStmtReturn(pSandbox->papHandles[idxHandle] == NULL, LeaveCriticalSection(&g_Sandbox.HandlesLock), K_FALSE);
11105 pSandbox->papHandles[idxHandle] = pHandle;
11106 pSandbox->cActiveHandles++;
11107 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11108 return K_TRUE;
11109}
11110
11111
11112/**
11113 * Safely looks up a handle, does not get it and it must not be 'put'.
11114 *
11115 * @returns Pointer to the handle structure if found, otherwise NULL.
11116 * @param hFile The handle to resolve.
11117 */
11118static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile)
11119{
11120 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11121 EnterCriticalSection(&g_Sandbox.HandlesLock);
11122 if (idxHandle < g_Sandbox.cHandles)
11123 {
11124 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11125 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11126 return pHandle;
11127 }
11128 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11129 return NULL;
11130}
11131
11132
11133/**
11134 * Safely gets a handle, must be "put" when done with it.
11135 *
11136 * @returns Pointer to the handle structure if found, otherwise NULL.
11137 * @param hFile The handle to resolve.
11138 */
11139static PKWHANDLE kwSandboxHandleGet(HANDLE hFile)
11140{
11141 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11142 EnterCriticalSection(&g_Sandbox.HandlesLock);
11143 if (idxHandle < g_Sandbox.cHandles)
11144 {
11145 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11146 if (pHandle)
11147 {
11148 kHlpAssertMsg(pHandle->tidOwner == KU32_MAX, ("hFile=%p tidOwner=%#x\n", hFile, pHandle->tidOwner));
11149 pHandle->tidOwner = GetCurrentThreadId();
11150 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11151 return pHandle;
11152 }
11153 }
11154 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11155 return NULL;
11156}
11157
11158
11159/**
11160 * Puts a handle returned by kwSandboxHandleGet.
11161 *
11162 * @param pHandle The handle to "put".
11163 */
11164K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle)
11165{
11166 kHlpAssertMsg(pHandle->tidOwner == GetCurrentThreadId(),
11167 ("hFile tidOwner=%#x tidMe=%#x\n", pHandle->hHandle, pHandle->tidOwner, GetCurrentThreadId()));
11168 pHandle->tidOwner = KU32_MAX;
11169}
11170
11171
11172/**
11173 * Creates a correctly quoted ANSI command line string from the given argv.
11174 *
11175 * @returns Pointer to the command line.
11176 * @param cArgs Number of arguments.
11177 * @param papszArgs The argument vector.
11178 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11179 * @param pcbCmdLine Where to return the command line length,
11180 * including one terminator.
11181 */
11182static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
11183{
11184 KU32 i;
11185 KSIZE cbCmdLine;
11186 char *pszCmdLine;
11187
11188 /* Make a copy of the argument vector that we'll be quoting. */
11189 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
11190 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
11191
11192 /* Quote the arguments that need it. */
11193 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
11194
11195 /* figure out cmd line length. */
11196 cbCmdLine = 0;
11197 for (i = 0; i < cArgs; i++)
11198 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
11199 *pcbCmdLine = cbCmdLine;
11200
11201 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
11202 if (pszCmdLine)
11203 {
11204 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
11205 if (papszQuotedArgs[0] != papszArgs[0])
11206 free(papszQuotedArgs[0]);
11207
11208 for (i = 1; i < cArgs; i++)
11209 {
11210 *psz++ = ' ';
11211 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
11212 if (papszQuotedArgs[i] != papszArgs[i])
11213 free(papszQuotedArgs[i]);
11214 }
11215 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
11216
11217 *psz++ = '\0';
11218 *psz++ = '\0';
11219 }
11220
11221 return pszCmdLine;
11222}
11223
11224
11225
11226static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
11227 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11228 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11229{
11230 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11231 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11232 wchar_t *pwcPool;
11233 KSIZE cbStrings;
11234 KSIZE cwc;
11235 KSIZE cbCmdLine;
11236 KU32 i;
11237
11238 /* Simple stuff. */
11239 pSandbox->rcExitCode = 256;
11240 pSandbox->pTool = pTool;
11241 pSandbox->idMainThread = GetCurrentThreadId();
11242 pSandbox->pgmptr = (char *)pTool->pszPath;
11243 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
11244#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11245 if (pSandbox->StdOut.fIsConsole)
11246 pSandbox->StdOut.u.Con.cwcBuf = 0;
11247 else
11248 pSandbox->StdOut.u.Fully.cchBuf = 0;
11249 if (pSandbox->StdErr.fIsConsole)
11250 pSandbox->StdErr.u.Con.cwcBuf = 0;
11251 else
11252 pSandbox->StdErr.u.Fully.cchBuf = 0;
11253 pSandbox->Combined.cwcBuf = 0;
11254 pSandbox->Combined.cFlushes = 0;
11255#endif
11256 pSandbox->fNoPchCaching = fNoPchCaching;
11257 pSandbox->cArgs = cArgs;
11258 pSandbox->papszArgs = (char **)papszArgs;
11259 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
11260 if (!pSandbox->pszCmdLine)
11261 return KERR_NO_MEMORY;
11262
11263 /*
11264 * Convert command line and argv to UTF-16.
11265 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
11266 */
11267 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
11268 if (!pSandbox->papwszArgs)
11269 return KERR_NO_MEMORY;
11270 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
11271 for (i = 0; i < cArgs; i++)
11272 {
11273 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
11274 pSandbox->papwszArgs[i] = pwcPool;
11275 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11276 pwcPool++;
11277 }
11278 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11279 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11280
11281 /*
11282 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11283 */
11284 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11285 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11286 if (!pSandbox->pwszCmdLine)
11287 return KERR_NO_MEMORY;
11288 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11289
11290 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11291 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11292 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11293
11294 /*
11295 * Setup the environment.
11296 */
11297 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11298 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11299 {
11300 KU32 iDst = 0;
11301 for (i = 0; i < cEnvVars; i++)
11302 {
11303 const char *pszVar = papszEnvVars[i];
11304 KSIZE cchVar = kHlpStrLen(pszVar);
11305 const char *pszEqual;
11306 if ( cchVar > 0
11307 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11308 {
11309 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11310 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11311 if (pszCopy && pwszCopy)
11312 {
11313 pSandbox->papszEnvVars[iDst] = pszCopy;
11314 pSandbox->environ[iDst] = pszCopy;
11315 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11316 pSandbox->wenviron[iDst] = pwszCopy;
11317
11318 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11319 if ( (pszEqual - pszVar) == 4
11320 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11321 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11322 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11323 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11324 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11325 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11326
11327 iDst++;
11328 }
11329 else
11330 {
11331 kHlpFree(pszCopy);
11332 kHlpFree(pwszCopy);
11333 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11334 }
11335 }
11336 else
11337 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11338 }
11339 pSandbox->papszEnvVars[iDst] = NULL;
11340 pSandbox->environ[iDst] = NULL;
11341 pSandbox->papwszEnvVars[iDst] = NULL;
11342 pSandbox->wenviron[iDst] = NULL;
11343 }
11344 else
11345 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11346
11347 /*
11348 * Invalidate the volatile parts of cache (kBuild output directory,
11349 * temporary directory, whatever).
11350 */
11351 kFsCacheInvalidateCustomBoth(g_pFsCache);
11352
11353#ifdef WITH_HISTORY
11354 /*
11355 * Record command line in debug history.
11356 */
11357 kHlpFree(g_apszHistory[g_iHistoryNext]);
11358 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11359 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11360#endif
11361
11362 return 0;
11363}
11364
11365
11366/**
11367 * Does sandbox cleanup between jobs.
11368 *
11369 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11370 * influence the result, so that kmk can get on with things ASAP.
11371 *
11372 * @param pSandbox The sandbox.
11373 */
11374static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11375{
11376 PROCESS_MEMORY_COUNTERS MemInfo;
11377 PKWVIRTALLOC pTracker;
11378 PKWHEAP pHeap;
11379 PKWLOCALSTORAGE pLocalStorage;
11380#ifdef WITH_HASH_MD5_CACHE
11381 PKWHASHMD5 pHash;
11382#endif
11383#ifdef WITH_TEMP_MEMORY_FILES
11384 PKWFSTEMPFILE pTempFile;
11385#endif
11386 PKWEXITCALLACK pExitCallback;
11387
11388 /*
11389 * First stuff that may cause code to run.
11390 */
11391
11392 /* Do exit callback first. */
11393 pExitCallback = g_Sandbox.pExitCallbackHead;
11394 g_Sandbox.pExitCallbackHead = NULL;
11395 while (pExitCallback)
11396 {
11397 PKWEXITCALLACK pNext = pExitCallback->pNext;
11398 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11399 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11400 __try
11401 {
11402 pExitCallback->pfnCallback();
11403 }
11404 __except (EXCEPTION_EXECUTE_HANDLER)
11405 {
11406 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11407 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11408 kHlpAssertFailed();
11409 }
11410 kHlpFree(pExitCallback);
11411 pExitCallback = pNext;
11412 }
11413
11414 /* Free left behind FlsAlloc leaks. */
11415 pLocalStorage = g_Sandbox.pFlsAllocHead;
11416 g_Sandbox.pFlsAllocHead = NULL;
11417 while (pLocalStorage)
11418 {
11419 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11420 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11421 FlsFree(pLocalStorage->idx);
11422 kHlpFree(pLocalStorage);
11423 pLocalStorage = pNext;
11424 }
11425
11426 /* Free left behind TlsAlloc leaks. */
11427 pLocalStorage = g_Sandbox.pTlsAllocHead;
11428 g_Sandbox.pTlsAllocHead = NULL;
11429 while (pLocalStorage)
11430 {
11431 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11432 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11433 TlsFree(pLocalStorage->idx);
11434 kHlpFree(pLocalStorage);
11435 pLocalStorage = pNext;
11436 }
11437
11438
11439 /*
11440 * Then free resources associated with the sandbox run.
11441 */
11442
11443 /* Open handles, except fixed handles (stdout and stderr). */
11444 EnterCriticalSection(&pSandbox->HandlesLock);
11445 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11446 {
11447 KU32 idxHandle = pSandbox->cHandles;
11448 while (idxHandle-- > 0)
11449 if (pSandbox->papHandles[idxHandle] == NULL)
11450 { /* likely */ }
11451 else
11452 {
11453 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11454 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11455 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11456 {
11457 pSandbox->papHandles[idxHandle] = NULL;
11458 pSandbox->cLeakedHandles++;
11459
11460 switch (pHandle->enmType)
11461 {
11462 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11463 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11464 idxHandle, pHandle->hHandle, pHandle->cRefs));
11465 break;
11466 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11467 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11468 idxHandle, pHandle->hHandle, pHandle->cRefs));
11469 break;
11470 case KWHANDLETYPE_OUTPUT_BUF:
11471 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11472 idxHandle, pHandle->hHandle, pHandle->cRefs));
11473 break;
11474#ifdef WITH_TEMP_MEMORY_FILES
11475 case KWHANDLETYPE_TEMP_FILE:
11476 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11477 idxHandle, pHandle->hHandle, pHandle->cRefs));
11478 pHandle->u.pTempFile->cActiveHandles--;
11479 break;
11480 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11481 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11482 idxHandle, pHandle->hHandle, pHandle->cRefs));
11483 pHandle->u.pTempFile->cActiveHandles--;
11484 break;
11485#endif
11486 default:
11487 kHlpAssertFailed();
11488 }
11489 if (--pHandle->cRefs == 0)
11490 kHlpFree(pHandle);
11491 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11492 break;
11493 }
11494 }
11495 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11496 }
11497 LeaveCriticalSection(&pSandbox->HandlesLock);
11498
11499 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11500 g_Sandbox.cMemMappings = 0;
11501
11502#ifdef WITH_TEMP_MEMORY_FILES
11503 /* The temporary files aren't externally visible, they're all in memory. */
11504 pTempFile = pSandbox->pTempFileHead;
11505 pSandbox->pTempFileHead = NULL;
11506 while (pTempFile)
11507 {
11508 PKWFSTEMPFILE pNext = pTempFile->pNext;
11509 KU32 iSeg = pTempFile->cSegs;
11510 while (iSeg-- > 0)
11511 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11512 kHlpFree(pTempFile->paSegs);
11513 pTempFile->pNext = NULL;
11514 kHlpFree(pTempFile);
11515
11516 pTempFile = pNext;
11517 }
11518#endif
11519
11520 /* Free left behind HeapCreate leaks. */
11521 pHeap = g_Sandbox.pHeapHead;
11522 g_Sandbox.pHeapHead = NULL;
11523 while (pHeap != NULL)
11524 {
11525 PKWHEAP pNext = pHeap->pNext;
11526 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11527 HeapDestroy(pHeap->hHeap);
11528 pHeap = pNext;
11529 }
11530
11531 /* Free left behind VirtualAlloc leaks. */
11532 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11533 pTracker = g_Sandbox.pVirtualAllocHead;
11534 g_Sandbox.pVirtualAllocHead = NULL;
11535 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11536 while (pTracker)
11537 {
11538 PKWVIRTALLOC pNext = pTracker->pNext;
11539 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11540
11541#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11542 if (pTracker->idxPreAllocated != KU32_MAX)
11543 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11544 else
11545#endif
11546 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11547 kHlpFree(pTracker);
11548 pTracker = pNext;
11549 }
11550
11551 /* Free the environment. */
11552 if (pSandbox->papszEnvVars)
11553 {
11554 KU32 i;
11555 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11556 kHlpFree(pSandbox->papszEnvVars[i]);
11557 pSandbox->environ[0] = NULL;
11558 pSandbox->papszEnvVars[0] = NULL;
11559
11560 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11561 kHlpFree(pSandbox->papwszEnvVars[i]);
11562 pSandbox->wenviron[0] = NULL;
11563 pSandbox->papwszEnvVars[0] = NULL;
11564 }
11565
11566#ifdef WITH_HASH_MD5_CACHE
11567 /*
11568 * Hash handles.
11569 */
11570 pHash = pSandbox->pHashHead;
11571 pSandbox->pHashHead = NULL;
11572 while (pHash)
11573 {
11574 PKWHASHMD5 pNext = pHash->pNext;
11575 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11576 kHlpFree(pHash);
11577 pHash = pNext;
11578 }
11579#endif
11580
11581 /*
11582 * Check the memory usage. If it's getting high, trigger a respawn
11583 * after the next job.
11584 */
11585 MemInfo.WorkingSetSize = 0;
11586 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11587 {
11588 /* The first time thru, we figure out approximately when to restart
11589 based on installed RAM and CPU threads. */
11590 static KU64 s_cbMaxWorkingSet = 0;
11591 if (s_cbMaxWorkingSet != 0)
11592 { /* likely */ }
11593 else
11594 {
11595 SYSTEM_INFO SysInfo;
11596 MEMORYSTATUSEX GlobalMemInfo;
11597 const char *pszValue;
11598
11599 /* Calculate a reasonable estimate. */
11600 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11601 GetNativeSystemInfo(&SysInfo);
11602
11603 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11604 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11605 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11606#if K_ARCH_BITS >= 64
11607 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11608#else
11609 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11610#endif
11611 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11612 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11613
11614 /* User limit. */
11615 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11616 if (pszValue != NULL)
11617 {
11618 char *pszNext;
11619 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11620 if (*pszNext == '\0' || *pszNext == 'M')
11621 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11622 else if (*pszNext == 'K')
11623 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11624 else if (*pszNext == 'G')
11625 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11626 else
11627 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11628 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11629 }
11630
11631 /* Clamp it a little. */
11632 if (s_cbMaxWorkingSet < 168*1024*1024)
11633 s_cbMaxWorkingSet = 168*1024*1024;
11634#if K_ARCH_BITS < 64
11635 else
11636 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11637 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11638 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11639 : 1536*1024*1024 /* got 4GB VA */);
11640#endif
11641 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11642 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11643 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11644 }
11645
11646 /* Finally the check. */
11647 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11648 {
11649 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11650 g_fRestart = K_TRUE;
11651 }
11652 }
11653
11654 /*
11655 * The CRT has a max of 8192 handles, so we better restart after a while if
11656 * someone is leaking handles or we risk running out of descriptors.
11657 *
11658 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11659 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11660 */
11661 if (pSandbox->cLeakedHandles > 6000)
11662 {
11663 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11664 g_fRestart = K_TRUE;
11665 }
11666}
11667
11668
11669/**
11670 * Does essential cleanups and restoring, anything externally visible.
11671 *
11672 * All cleanups that aren't externally visible are postponed till after we've
11673 * informed kmk of the result, so it can be done in the dead time between jobs.
11674 *
11675 * @param pSandbox The sandbox.
11676 */
11677static void kwSandboxCleanup(PKWSANDBOX pSandbox)
11678{
11679 /*
11680 * Restore the parent command line string.
11681 */
11682 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11683 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11684 pProcParams->CommandLine = pSandbox->SavedCommandLine;
11685#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11686 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
11687 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
11688#endif
11689}
11690
11691
11692static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11693 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11694{
11695 int rcExit = 42;
11696 int rc;
11697
11698 /*
11699 * Initialize the sandbox environment.
11700 */
11701 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
11702 if (rc == 0)
11703 {
11704 if (g_cVerbose > 2)
11705 fprintf(stderr, "kWorker: info: Executing (sandboxed): %s\n", g_Sandbox.pszCmdLine);
11706
11707 /*
11708 * Do module initialization.
11709 */
11710#if 0
11711 //kwSandboxResetModuleVisited();
11712 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
11713#else
11714 kwSandboxResetModuleState();
11715#endif
11716 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
11717 if (rc == 0)
11718 {
11719 /*
11720 * Call the main function.
11721 */
11722#if K_ARCH == K_ARCH_AMD64
11723 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
11724#elif K_ARCH == K_ARCH_X86_32
11725 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
11726#else
11727# error "Port me!"
11728#endif
11729
11730 /* Save the NT TIB first (should do that here, not in some other function). */
11731 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
11732 pSandbox->TibMainThread = *pTib;
11733
11734 /* Make the call in a guarded fashion. */
11735#if K_ARCH == K_ARCH_AMD64
11736 /* AMD64 */
11737 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
11738 __try
11739 {
11740 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11741 if (setjmp(pSandbox->JmpBuf) == 0)
11742 {
11743 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11744 pSandbox->fRunning = K_TRUE;
11745 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
11746 pSandbox->fRunning = K_FALSE;
11747 }
11748 else
11749 rcExit = pSandbox->rcExitCode;
11750 }
11751#elif K_ARCH == K_ARCH_X86_32
11752 /* x86 (see _tmainCRTStartup) */
11753 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
11754 __try
11755 {
11756 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11757 if (setjmp(pSandbox->JmpBuf) == 0)
11758 {
11759 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11760 pSandbox->fRunning = K_TRUE;
11761 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
11762 pSandbox->fRunning = K_FALSE;
11763 }
11764 else
11765 rcExit = pSandbox->rcExitCode;
11766 }
11767#endif
11768 __except (EXCEPTION_EXECUTE_HANDLER)
11769 {
11770 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
11771#ifdef WITH_HISTORY
11772 {
11773 KU32 cPrinted = 0;
11774 while (cPrinted++ < 5)
11775 {
11776 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
11777 if (g_apszHistory[idx])
11778 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
11779 }
11780 }
11781#endif
11782 rcExit = 512;
11783 }
11784 pSandbox->fRunning = K_FALSE;
11785
11786 /* Now, restore the NT TIB. */
11787 *pTib = pSandbox->TibMainThread;
11788 }
11789 else
11790 rcExit = 42 + 4;
11791
11792 /*
11793 * Flush and clean up the essential bits only, postpone whatever we
11794 * can till after we've replied to kmk.
11795 */
11796#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11797 kwSandboxConsoleFlushAll(&g_Sandbox);
11798#endif
11799 kwSandboxCleanup(&g_Sandbox);
11800 /** @todo Flush sandboxed native CRTs too. */
11801 }
11802 else
11803 rcExit = 42 + 3;
11804
11805 return rcExit;
11806}
11807
11808
11809/**
11810 * Does the post command part of a job (optional).
11811 *
11812 * @returns The exit code of the job.
11813 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11814 * @param papszPostCmdArgs The post command and its argument.
11815 */
11816static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11817{
11818 const char *pszCmd = papszPostCmdArgs[0];
11819
11820 /* Allow the kmk builtin prefix. */
11821 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
11822 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
11823 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
11824
11825 /* Command switch. */
11826 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
11827 {
11828 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
11829 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
11830 }
11831
11832 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
11833}
11834
11835
11836/**
11837 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11838 */
11839static unsigned kwGetCurrentProcessorGroup(void)
11840{
11841 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
11842 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
11843 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
11844 if (pfnGetter)
11845 {
11846 GROUP_AFFINITY GroupAffinity;
11847 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
11848 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
11849 return GroupAffinity.Group;
11850 }
11851 return 0;
11852}
11853
11854
11855/**
11856 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11857 */
11858static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
11859{
11860 KSIZE cchRet = 0;
11861 HANDLE hToken;
11862 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
11863 {
11864 DWORD cbRet;
11865 TOKEN_STATISTICS TokenStats;
11866 memset(&TokenStats, 0, sizeof(TokenStats));
11867 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
11868 cchRet = sprintf(pszValue, "%" KX64_PRI,
11869 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
11870 else
11871 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
11872 CloseHandle(hToken);
11873 }
11874 else
11875 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
11876 return cchRet;
11877}
11878
11879
11880/**
11881 * Look for and expand the special environment variable.
11882 *
11883 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
11884 * couldn't accuratly determine. Currently the following variables are
11885 * implemented:
11886 * - "@@PROCESSOR_GROUP@@" - The processor group number.
11887 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
11888 * - "@@PID@@" - The kWorker process ID.
11889 * - "@@@@" - Escaped "@@".
11890 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
11891 */
11892static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
11893{
11894 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
11895 KU32 i = cEnvVars;
11896 while (i-- > 0)
11897 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
11898 && papszEnvVars[i][cchSpecialEnv] == '=')
11899 {
11900 /* We will expand stuff like @@NAME@@ */
11901 const char *pszValue = papszEnvVars[i];
11902 KSIZE offDst = 0;
11903 char szTmp[1024];
11904 for (;;)
11905 {
11906 const char *pszAt = kHlpStrChr(pszValue, '@');
11907 while (pszAt && pszAt[1] != '@')
11908 pszAt = kHlpStrChr(pszAt + 1, '@');
11909 if (pszAt)
11910 {
11911 KSIZE cchSrc = pszAt - pszValue;
11912 if (offDst + cchSrc < sizeof(szTmp))
11913 {
11914 char szSrc[64];
11915
11916 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11917 offDst += cchSrc;
11918 pszValue = pszAt + 2;
11919
11920 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11921 {
11922 pszValue += 15;
11923 if (g_iProcessGroup == -1)
11924 g_iProcessGroup = kwGetCurrentProcessorGroup();
11925 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11926 }
11927 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11928 {
11929 pszValue += 19;
11930 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11931 }
11932 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11933 {
11934 pszValue += 5;
11935 cchSrc = sprintf(szSrc, "%d", getpid());
11936 }
11937 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
11938 {
11939 pszValue += 2;
11940 szSrc[0] = '@';
11941 szSrc[1] = '@';
11942 szSrc[2] = '\0';
11943 cchSrc = 2;
11944 }
11945 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
11946 {
11947 static unsigned int s_iCounter = 0;
11948 pszValue += 15;
11949 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
11950 }
11951 else
11952 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
11953 pszValue - 2);
11954 if (offDst + cchSrc < sizeof(szTmp))
11955 {
11956 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
11957 offDst += cchSrc;
11958 continue;
11959 }
11960 }
11961 }
11962 else
11963 {
11964 KSIZE cchSrc = kHlpStrLen(pszValue);
11965 if (offDst + cchSrc < sizeof(szTmp))
11966 {
11967 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11968 offDst += cchSrc;
11969 break;
11970 }
11971 }
11972 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
11973 }
11974 szTmp[offDst] = '\0';
11975
11976 /* Return a copy of it: */
11977 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
11978 if (papszEnvVars[i])
11979 {
11980 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
11981 return 0;
11982 }
11983 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
11984 }
11985
11986 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
11987}
11988
11989
11990/**
11991 * Part 2 of the "JOB" command handler.
11992 *
11993 * @returns The exit code of the job.
11994 * @param pszExecutable The executable to execute.
11995 * @param pszCwd The current working directory of the job.
11996 * @param cArgs The number of arguments.
11997 * @param papszArgs The argument vector.
11998 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11999 * @param cEnvVars The number of environment variables.
12000 * @param papszEnvVars The environment vector.
12001 * @param pszSpecialEnv Name of special environment variable that
12002 * requires selective expansion here.
12003 * @param fNoPchCaching Whether to disable precompiled header file
12004 * caching. Avoid trouble when creating them.
12005 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12006 * @param papszPostCmdArgs The post command and its argument.
12007 */
12008static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
12009 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12010 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
12011 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12012{
12013 int rcExit;
12014 PKWTOOL pTool;
12015 char *pszSpecialEnvFree = NULL;
12016
12017 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
12018 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
12019#ifdef KW_LOG_ENABLED
12020 {
12021 KU32 i;
12022 for (i = 0; i < cArgs; i++)
12023 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
12024 for (i = 0; i < cPostCmdArgs; i++)
12025 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
12026 }
12027#endif
12028 g_cJobs++;
12029
12030 /*
12031 * Expand pszSpecialEnv if present.
12032 */
12033 if (pszSpecialEnv && *pszSpecialEnv)
12034 {
12035 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
12036 if (!rcExit)
12037 { /* likely */ }
12038 else
12039 return rcExit;
12040 }
12041
12042 /*
12043 * Lookup the tool.
12044 */
12045 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
12046 if (pTool)
12047 {
12048 /*
12049 * Change the directory if we're going to execute the job inside
12050 * this process. Then invoke the tool type specific handler.
12051 */
12052 switch (pTool->enmType)
12053 {
12054 case KWTOOLTYPE_SANDBOXED:
12055 case KWTOOLTYPE_WATCOM:
12056 {
12057 /* Change dir. */
12058 KFSLOOKUPERROR enmError;
12059 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
12060 if ( pNewCurDir == g_pCurDirObj
12061 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
12062 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12063 else if (SetCurrentDirectoryA(pszCwd))
12064 {
12065 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
12066 g_pCurDirObj = pNewCurDir;
12067 }
12068 else
12069 {
12070 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
12071 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12072 rcExit = 42 + 1;
12073 break;
12074 }
12075
12076 /* Call specific handler. */
12077 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
12078 {
12079 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
12080 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
12081 cEnvVars, papszEnvVars, fNoPchCaching);
12082 }
12083 else
12084 {
12085 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
12086 rcExit = 42 + 2;
12087 }
12088 break;
12089 }
12090
12091 case KWTOOLTYPE_EXEC:
12092 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
12093 rcExit = 42 + 2;
12094 break;
12095
12096 default:
12097 kHlpAssertFailed();
12098 kwErrPrintf("Internal tool type corruption!!\n");
12099 rcExit = 42 + 2;
12100 g_fRestart = K_TRUE;
12101 break;
12102 }
12103
12104 /*
12105 * Do the post command, if present.
12106 */
12107 if (cPostCmdArgs && rcExit == 0)
12108 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
12109 }
12110 else
12111 rcExit = 42 + 1;
12112 if (pszSpecialEnvFree)
12113 {
12114 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
12115 kHlpFree(pszSpecialEnvFree);
12116 }
12117 return rcExit;
12118}
12119
12120
12121/**
12122 * Handles a "JOB" command.
12123 *
12124 * @returns The exit code of the job.
12125 * @param pszMsg Points to the "JOB" command part of the message.
12126 * @param cbMsg Number of message bytes at @a pszMsg. There are
12127 * 4 more zero bytes after the message body to
12128 * simplify parsing.
12129 */
12130static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
12131{
12132 int rcExit = 42;
12133
12134 /*
12135 * Unpack the message.
12136 */
12137 const char *pszExecutable;
12138 KSIZE cbTmp;
12139
12140 pszMsg += sizeof("JOB");
12141 cbMsg -= sizeof("JOB");
12142
12143 /* Executable name. */
12144 pszExecutable = pszMsg;
12145 cbTmp = kHlpStrLen(pszMsg) + 1;
12146 pszMsg += cbTmp;
12147 if ( cbTmp < cbMsg
12148 && cbTmp > 2)
12149 {
12150 const char *pszCwd;
12151 cbMsg -= cbTmp;
12152
12153 /* Current working directory. */
12154 pszCwd = pszMsg;
12155 cbTmp = kHlpStrLen(pszMsg) + 1;
12156 pszMsg += cbTmp;
12157 if ( cbTmp + sizeof(KU32) < cbMsg
12158 && cbTmp >= 2)
12159 {
12160 KU32 cArgs;
12161 cbMsg -= cbTmp;
12162
12163 /* Argument count. */
12164 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
12165 pszMsg += sizeof(cArgs);
12166 cbMsg -= sizeof(cArgs);
12167
12168 if (cArgs > 0 && cArgs < 4096)
12169 {
12170 /* The argument vector. */
12171 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
12172 if (papszArgs)
12173 {
12174 KU32 i;
12175 for (i = 0; i < cArgs; i++)
12176 {
12177 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
12178 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
12179 pszMsg += cbTmp;
12180 if (cbTmp < cbMsg)
12181 cbMsg -= cbTmp;
12182 else
12183 {
12184 cbMsg = 0;
12185 break;
12186 }
12187
12188 }
12189 papszArgs[cArgs] = 0;
12190
12191 /* Environment variable count. */
12192 if (cbMsg > sizeof(KU32))
12193 {
12194 KU32 cEnvVars;
12195 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
12196 pszMsg += sizeof(cEnvVars);
12197 cbMsg -= sizeof(cEnvVars);
12198
12199 if (cEnvVars >= 0 && cEnvVars < 4096)
12200 {
12201 /* The argument vector. */
12202 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
12203 if (papszEnvVars)
12204 {
12205 for (i = 0; i < cEnvVars; i++)
12206 {
12207 papszEnvVars[i] = pszMsg;
12208 cbTmp = kHlpStrLen(pszMsg) + 1;
12209 pszMsg += cbTmp;
12210 if (cbTmp < cbMsg)
12211 cbMsg -= cbTmp;
12212 else
12213 {
12214 cbMsg = 0;
12215 break;
12216 }
12217 }
12218 papszEnvVars[cEnvVars] = 0;
12219
12220 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
12221 if (cbMsg >= sizeof(KU8) * 2)
12222 {
12223 KBOOL fWatcomBrainDamange = *pszMsg++;
12224 KBOOL fNoPchCaching = *pszMsg++;
12225 cbMsg -= 2;
12226
12227 /* Name of special enviornment variable requiring selective expansion. */
12228 if (cbMsg >= 1)
12229 {
12230 const char *pszSpecialEnv = pszMsg;
12231 cbTmp = kHlpStrLen(pszMsg);
12232 pszMsg += cbTmp + 1;
12233 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
12234
12235 /* Post command argument count (can be zero). */
12236 if (cbMsg >= sizeof(KU32))
12237 {
12238 KU32 cPostCmdArgs;
12239 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
12240 pszMsg += sizeof(cPostCmdArgs);
12241 cbMsg -= sizeof(cPostCmdArgs);
12242
12243 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
12244 {
12245 char const *apszPostCmdArgs[32+1];
12246 for (i = 0; i < cPostCmdArgs; i++)
12247 {
12248 apszPostCmdArgs[i] = pszMsg;
12249 cbTmp = kHlpStrLen(pszMsg) + 1;
12250 pszMsg += cbTmp;
12251 if ( cbTmp < cbMsg
12252 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
12253 cbMsg -= cbTmp;
12254 else
12255 {
12256 cbMsg = KSIZE_MAX;
12257 break;
12258 }
12259 }
12260 if (cbMsg == 0)
12261 {
12262 apszPostCmdArgs[cPostCmdArgs] = NULL;
12263
12264 /*
12265 * The next step.
12266 */
12267 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
12268 cArgs, papszArgs, fWatcomBrainDamange,
12269 cEnvVars, papszEnvVars, pszSpecialEnv,
12270 fNoPchCaching,
12271 cPostCmdArgs, apszPostCmdArgs);
12272 }
12273 else if (cbMsg == KSIZE_MAX)
12274 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
12275 else
12276 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
12277 }
12278 else
12279 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
12280 }
12281 else
12282 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
12283 }
12284 else
12285 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12286 }
12287 else
12288 kwErrPrintf("Detected bogus message unpacking flags!\n");
12289 kHlpFree((void *)papszEnvVars);
12290 }
12291 else
12292 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12293 }
12294 else
12295 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12296 }
12297 else
12298 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12299 kHlpFree((void *)papszArgs);
12300 }
12301 else
12302 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12303 }
12304 else
12305 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12306 }
12307 else
12308 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12309 }
12310 else
12311 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12312 return rcExit;
12313}
12314
12315
12316/**
12317 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12318 *
12319 * @retval 0 on success.
12320 * @retval -1 on error (fully bitched).
12321 *
12322 * @param hPipe The pipe handle.
12323 * @param pvBuf The buffer to write out out.
12324 * @param cbToWrite The number of bytes to write.
12325 */
12326static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12327{
12328 KU8 const *pbBuf = (KU8 const *)pvBuf;
12329 KU32 cbLeft = cbToWrite;
12330 while (g_rcCtrlC == 0)
12331 {
12332 DWORD cbActuallyWritten = 0;
12333 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12334 {
12335 cbLeft -= cbActuallyWritten;
12336 if (!cbLeft)
12337 return 0;
12338 pbBuf += cbActuallyWritten;
12339 }
12340 else
12341 {
12342 DWORD dwErr = GetLastError();
12343 if (cbLeft == cbToWrite)
12344 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12345 else
12346 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12347 return -1;
12348 }
12349 }
12350 return -1;
12351}
12352
12353
12354/**
12355 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12356 *
12357 * @retval 0 on success.
12358 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12359 * @retval -1 on error (fully bitched).
12360 * @param hPipe The pipe handle.
12361 * @param pvBuf The buffer to read into.
12362 * @param cbToRead The number of bytes to read.
12363 * @param fShutdownOkay Whether connection shutdown while reading the
12364 * first byte is okay or not.
12365 */
12366static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12367{
12368 KU8 *pbBuf = (KU8 *)pvBuf;
12369 KU32 cbLeft = cbToRead;
12370 while (g_rcCtrlC == 0)
12371 {
12372 DWORD cbActuallyRead = 0;
12373 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12374 {
12375 cbLeft -= cbActuallyRead;
12376 if (!cbLeft)
12377 return 0;
12378 pbBuf += cbActuallyRead;
12379 }
12380 else
12381 {
12382 DWORD dwErr = GetLastError();
12383 if (cbLeft == cbToRead)
12384 {
12385 if ( fMayShutdown
12386 && dwErr == ERROR_BROKEN_PIPE)
12387 return 1;
12388 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12389 }
12390 else
12391 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12392 return -1;
12393 }
12394 }
12395 return -1;
12396}
12397
12398
12399/**
12400 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12401 *
12402 * @returns pszBuf
12403 * @param pszBuf The buffer (sufficiently large).
12404 * @param uValue The value.
12405 */
12406static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12407{
12408 char szTmp[64];
12409 char *psz = &szTmp[63];
12410 int cch = 4;
12411
12412 *psz-- = '\0';
12413 do
12414 {
12415 if (--cch == 0)
12416 {
12417 *psz-- = ' ';
12418 cch = 3;
12419 }
12420 *psz-- = (uValue % 10) + '0';
12421 uValue /= 10;
12422 } while (uValue != 0);
12423
12424 return strcpy(pszBuf, psz + 1);
12425}
12426
12427
12428/**
12429 * Prints statistics.
12430 */
12431static void kwPrintStats(void)
12432{
12433 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12434 MEMORYSTATUSEX MemStatus;
12435 IO_COUNTERS IoCounters;
12436 DWORD cHandles;
12437 KSIZE cMisses;
12438 char szBuf[16*1024];
12439 int off = 0;
12440 char szPrf[24];
12441 char sz1[64];
12442 char sz2[64];
12443 char sz3[64];
12444 char sz4[64];
12445
12446 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12447
12448 szBuf[off++] = '\n';
12449
12450 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12451 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12452 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12453 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12454 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12455
12456 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12457 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12458
12459 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12460 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12461 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12462
12463 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12464 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12465 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12466
12467 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12468 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12469 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12470 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12471 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12472 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12473 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12474
12475 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12476 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12477 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12478 kwFmtU64(sz1, g_pFsCache->cObjects),
12479 kwFmtU64(sz2, g_pFsCache->cbObjects),
12480 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12481 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12482 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12483 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12484 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12485 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12486#ifdef KFSCACHE_CFG_UTF16
12487 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12488 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12489 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12490 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12491 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12492#endif
12493 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12494 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12495 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12496 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12497 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12498
12499 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12500 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12501 kwFmtU64(sz1, g_pFsCache->cLookups),
12502 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12503 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12504 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12505 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12506 kwFmtU64(sz4, cMisses),
12507 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12508
12509 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12510 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12511 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12512 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12513 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12514 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12515 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12516 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12517
12518
12519 /*
12520 * Process & Memory details.
12521 */
12522 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12523 cHandles = 0;
12524 MemInfo.cb = sizeof(MemInfo);
12525 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12526 memset(&MemInfo, 0, sizeof(MemInfo));
12527 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12528 kwFmtU64(sz1, cHandles),
12529 kwFmtU64(sz2, MemInfo.PageFaultCount),
12530 kwFmtU64(sz3, MemInfo.PagefileUsage),
12531 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12532 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12533 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12534 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12535 kwFmtU64(sz3, MemInfo.PrivateUsage));
12536 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12537 szPrf,
12538 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12539 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12540 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12541 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12542
12543 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12544 memset(&IoCounters, 0, sizeof(IoCounters));
12545 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12546 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12547 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12548 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12549 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12550 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12551 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12552 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12553 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12554
12555 MemStatus.dwLength = sizeof(MemStatus);
12556 if (!GlobalMemoryStatusEx(&MemStatus))
12557 memset(&MemStatus, 0, sizeof(MemStatus));
12558 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12559 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12560 MemStatus.ullAvailVirtual);
12561 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12562
12563 maybe_con_fwrite(szBuf, off, 1, stdout);
12564 fflush(stdout);
12565}
12566
12567
12568/**
12569 * Handles what comes after --test.
12570 *
12571 * @returns Exit code.
12572 * @param argc Number of arguments after --test.
12573 * @param argv Arguments after --test.
12574 */
12575static int kwTestRun(int argc, char **argv)
12576{
12577 int i;
12578 int j;
12579 int rcExit;
12580 int cRepeats;
12581 char szCwd[MAX_PATH];
12582 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12583 KU32 cEnvVars;
12584 char **papszEnvVars;
12585 const char *pszSpecialEnv = "";
12586 const char *pszSpecialEnvFull = NULL;
12587 KBOOL fWatcomBrainDamange = K_FALSE;
12588 KBOOL fNoPchCaching = K_FALSE;
12589
12590 /*
12591 * Parse arguments.
12592 */
12593 /* Repeat count. */
12594 i = 0;
12595 if (i >= argc)
12596 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12597 if (strcmp(argv[i], "--") != 0)
12598 {
12599 cRepeats = atoi(argv[i]);
12600 if (cRepeats <= 0)
12601 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12602 i++;
12603
12604 /* Optional directory change. */
12605 if ( i < argc
12606 && ( strcmp(argv[i], "--chdir") == 0
12607 || strcmp(argv[i], "-C") == 0 ) )
12608 {
12609 i++;
12610 if (i >= argc)
12611 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12612 pszCwd = argv[i++];
12613 }
12614
12615 /* Optional watcom flag directory change. */
12616 if ( i < argc
12617 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12618 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12619 {
12620 fWatcomBrainDamange = K_TRUE;
12621 i++;
12622 }
12623
12624 /* Optional watcom flag directory change. */
12625 if ( i < argc
12626 && strcmp(argv[i], "--no-pch-caching") == 0)
12627 {
12628 fNoPchCaching = K_TRUE;
12629 i++;
12630 }
12631
12632 /* Optional directory change. */
12633 if ( i < argc
12634 && ( strcmp(argv[i], "--set-special") == 0
12635 || strcmp(argv[i], "-s") == 0 ) )
12636 {
12637 i++;
12638 if (i >= argc)
12639 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12640 pszSpecialEnvFull = argv[i++];
12641 putenv(pszSpecialEnvFull);
12642 pszSpecialEnv = strdup(pszSpecialEnvFull);
12643 *strchr(pszSpecialEnv, '=') = '\0';
12644 }
12645
12646 /* Trigger breakpoint */
12647 if ( i < argc
12648 && strcmp(argv[i], "--breakpoint") == 0)
12649 {
12650 __debugbreak();
12651 i++;
12652 }
12653
12654 /* Check for '--'. */
12655 if (i >= argc)
12656 return kwErrPrintfRc(2, "Missing '--'\n");
12657 if (strcmp(argv[i], "--") != 0)
12658 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
12659 i++;
12660 }
12661 else
12662 {
12663 cRepeats = 1;
12664 i++;
12665 }
12666 if (i >= argc)
12667 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
12668
12669 /*
12670 * Duplicate the environment.
12671 */
12672 cEnvVars = 0;
12673 while (environ[cEnvVars] != NULL)
12674 cEnvVars++;
12675 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
12676
12677 /*
12678 * Do the job.
12679 */
12680 rcExit = 0;
12681 for (j = 0; j < cRepeats; j++)
12682 {
12683 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
12684 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
12685 argc - i, &argv[i], fWatcomBrainDamange,
12686 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
12687 0, NULL);
12688 KW_LOG(("rcExit=%d\n", rcExit));
12689 kwSandboxCleanupLate(&g_Sandbox);
12690 }
12691
12692 if (getenv("KWORKER_STATS") != NULL)
12693 kwPrintStats();
12694
12695# ifdef WITH_LOG_FILE
12696 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12697 CloseHandle(g_hLogFile);
12698# endif
12699 return rcExit;
12700}
12701
12702
12703/**
12704 * Reads @a pszFile into memory and chops it up into an argument vector.
12705 *
12706 * @returns Pointer to the argument vector on success, NULL on failure.
12707 * @param pszFile The file to load.
12708 * @param pcArgs Where to return the number of arguments.
12709 * @param ppszFileContent Where to return the allocation.
12710 */
12711static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
12712{
12713 char **papszArgs = NULL;
12714 FILE *pFile = fopen(pszFile, "r");
12715 if (pFile)
12716 {
12717 long cbFile;
12718 if ( fseek(pFile, 0, SEEK_END) == 0
12719 && (cbFile = ftell(pFile)) >= 0
12720 && fseek(pFile, 0, SEEK_SET) == 0)
12721 {
12722 char *pszFile = kHlpAllocZ(cbFile + 3);
12723 if (pszFile)
12724 {
12725 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
12726 if ( feof(pFile)
12727 && !ferror(pFile))
12728 {
12729 size_t off = 0;
12730 int cArgs = 0;
12731 int cAllocated = 0;
12732 char ch;
12733
12734 pszFile[cbRead] = '\0';
12735 pszFile[cbRead + 1] = '\0';
12736 pszFile[cbRead + 2] = '\0';
12737
12738 while ((ch = pszFile[off]) != '\0')
12739 {
12740 char *pszArg;
12741 switch (ch)
12742 {
12743 case ' ':
12744 case '\t':
12745 case '\n':
12746 case '\r':
12747 off++;
12748 continue;
12749
12750 case '\\':
12751 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
12752 {
12753 off += 2;
12754 continue;
12755 }
12756 /* fall thru */
12757 default:
12758 pszArg = &pszFile[off];
12759 do
12760 ch = pszFile[++off];
12761 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
12762 pszFile[off++] = '\0';
12763 break;
12764
12765 case '\'':
12766 pszArg = &pszFile[++off];
12767 while ((ch = pszFile[off]) != '\0' && ch != '\'')
12768 off++;
12769 pszFile[off++] = '\0';
12770 break;
12771
12772 case '\"': /** @todo escape sequences */
12773 pszArg = &pszFile[++off];
12774 while ((ch = pszFile[off]) != '\0' && ch != '"')
12775 off++;
12776 pszFile[off++] = '\0';
12777 break;
12778 }
12779 if (cArgs + 1 >= cAllocated)
12780 {
12781 void *pvNew;
12782 cAllocated = cAllocated ? cAllocated * 2 : 16;
12783 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
12784 if (pvNew)
12785 papszArgs = (char **)pvNew;
12786 else
12787 {
12788 kHlpFree(papszArgs);
12789 papszArgs = NULL;
12790 break;
12791 }
12792 }
12793 papszArgs[cArgs] = pszArg;
12794 papszArgs[++cArgs] = NULL;
12795 }
12796 *pcArgs = cArgs;
12797 }
12798 else
12799 kwErrPrintf("Error reading '%s'!\n", pszFile);
12800 }
12801 else
12802 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
12803 }
12804 else
12805 kwErrPrintf("Error seeking '%s'!\n", pszFile);
12806 fclose(pFile);
12807 }
12808 else
12809 kwErrPrintf("Error opening '%s'!\n", pszFile);
12810 return papszArgs;
12811}
12812
12813/**
12814 * Appends a string to an string vector (arguments or enviornment).
12815 *
12816 * @returns 0 on success, non-zero on failure (exit code).
12817 * @param ppapszVector Pointer to the string pointer array.
12818 * @param pcEntries Pointer to the array size.
12819 * @param pszAppend The string to append.
12820 */
12821static int kwFullTestVectorAppend(const char ***ppapszVector, KU32 *pcEntries, char const *pszAppend)
12822{
12823 KU32 cEntries = *pcEntries;
12824 if (!(cEntries & 15))
12825 {
12826 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
12827 if (pvNew)
12828 *ppapszVector = (const char **)pvNew;
12829 else
12830 return kwErrPrintfRc(2, "Out of memory!\n");
12831 }
12832 (*ppapszVector)[cEntries] = pszAppend;
12833 (*ppapszVector)[++cEntries] = NULL;
12834 *pcEntries = cEntries;
12835 return 0;
12836}
12837
12838
12839/**
12840 * Parses arguments for --full-test.
12841 *
12842 * @returns 0 on success, non-zero on failure (exit code).
12843 */
12844static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
12845 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
12846{
12847 PKWONETEST pCur = *ppHead;
12848 int i;
12849 for (i = 0; i < argc; i++)
12850 {
12851 int rc = 0;
12852 const char *pszArg = argv[i];
12853 if (*pszArg == 'k')
12854 {
12855 if (kHlpStrComp(pszArg, "kSubmit") == 0)
12856 {
12857 if (*piState != 0)
12858 {
12859 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
12860 if (!pCur)
12861 return kwErrPrintfRc(2, "Out of memory!\n");
12862 pCur->fVirgin = K_TRUE;
12863 pCur->pszCwd = pszDefaultCwd;
12864 pCur->cRuns = 1;
12865 pCur->pNext = *ppHead;
12866 *ppHead = pCur;
12867 *piState = 0;
12868 }
12869 else if (!pCur->fVirgin)
12870 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
12871 pCur->pszJobSrc = pszJobSrc;
12872 pCur->iJobSrc = i;
12873 continue; /* (to stay virgin) */
12874 }
12875
12876 /* Ignore "kWorker 378172/62:" sequences that kmk/kSubmit spews out on failure. */
12877 if ( kHlpStrComp(pszArg, "kWorker") == 0
12878 && i + 1 < argc
12879 && (unsigned)(argv[i + 1][0] - '0') <= 9)
12880 {
12881 i++;
12882 continue;
12883 }
12884 }
12885
12886 if ( *pszArg == '-'
12887 && ( *piState == 0
12888 || pszArg[1] == '@'))
12889 {
12890 const char *pszValue = NULL;
12891 char ch = *++pszArg;
12892 pszArg++;
12893 if (ch == '-')
12894 {
12895 ch = '\0';
12896 if (*pszArg == '\0') /* -- */
12897 *piState = 2;
12898 /* Translate or handle long options: */
12899 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
12900 ch = 'E';
12901 else if (kHlpStrComp(pszArg, "special-env") == 0)
12902 ch = 's';
12903 else if (kHlpStrComp(pszArg, "default-env") == 0)
12904 {
12905 unsigned i;
12906 pCur->cEnvVars = 0;
12907 for (i = 0; environ[i] && rc == 0; i++)
12908 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
12909 }
12910 else if (kHlpStrComp(pszArg, "chdir") == 0)
12911 ch = 'C';
12912 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
12913 ch = 'P';
12914 else if (kHlpStrComp(pszArg, "response-file") == 0)
12915 ch = '@';
12916 else if (kHlpStrComp(pszArg, "runs") == 0)
12917 ch = 'R';
12918 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
12919 pCur->fWatcomBrainDamange = K_TRUE;
12920 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
12921 pCur->fNoPchCaching = K_TRUE;
12922 else if (kHlpStrComp(pszArg, "executable") == 0)
12923 ch = 'e';
12924 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12925 {
12926 __debugbreak();
12927 continue; /* (to stay virgin) */
12928 }
12929 else
12930 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12931 pszArg = "";
12932 }
12933
12934 while (ch != '\0' && rc == 0)
12935 {
12936 /* Fetch value if needed: */
12937 switch (ch)
12938 {
12939 case '@':
12940 case 'e':
12941 case 'E':
12942 case 's':
12943 case 'C':
12944 case 'R':
12945 if (*pszArg == ':' || *pszArg == '=')
12946 pszValue = &pszArg[1];
12947 else if (*pszArg)
12948 pszValue = pszArg;
12949 else if (i + 1 < argc)
12950 pszValue = argv[++i];
12951 else
12952 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
12953 pszArg = "";
12954 break;
12955 }
12956
12957 /* Handle the option: */
12958 switch (ch)
12959 {
12960 case 'E':
12961 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
12962 break;
12963 case 'C':
12964 pCur->pszCwd = pszValue;
12965 break;
12966 case 's':
12967 pCur->pszSpecialEnv = pszValue;
12968 break;
12969 case 'e':
12970 pCur->pszExecutable = pszValue;
12971 break;
12972 case 'P':
12973 *piState = 1;
12974 if (*pszArg)
12975 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
12976 break;
12977 case 'R':
12978 pCur->cRuns = atoi(pszValue);
12979 if ((int)pCur->cRuns < 0)
12980 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
12981 break;
12982 case '@':
12983 if (cRecursions < 5)
12984 {
12985 char *pszLeaked = NULL;
12986 int cArgs = 0;
12987 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
12988 if (papszArgsLeaked)
12989 {
12990 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
12991 cRecursions + 1, pszValue);
12992 pCur = *ppHead;
12993 }
12994 else
12995 return 2;
12996 }
12997 else
12998 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
12999 break;
13000 }
13001
13002 /* next */
13003 ch = *pszArg++;
13004 }
13005 }
13006 else if (*piState == 2)
13007 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
13008 else if (*piState == 1)
13009 {
13010 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
13011 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
13012 else
13013 *piState = 2;
13014 }
13015 else
13016 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
13017 if (rc)
13018 return rc;
13019 pCur->fVirgin = K_FALSE;
13020 }
13021 return 0;
13022}
13023
13024
13025/**
13026 * Handles what comes after --full-test.
13027 *
13028 * @returns Exit code.
13029 * @param argc Number of arguments after --full-test.
13030 * @param argv Arguments after --full-test.
13031 */
13032static int kwFullTestRun(int argc, char **argv)
13033{
13034 char szDefaultCwd[MAX_PATH];
13035 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
13036 KWONETEST FirstTest;
13037 PKWONETEST pHead = &FirstTest;
13038 PKWONETEST pCur;
13039 int iState = 0;
13040 int rcExit;
13041
13042 /*
13043 * Parse arguments.
13044 */
13045 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
13046 FirstTest.pszJobSrc = "command-line";
13047 FirstTest.iJobSrc = 1;
13048 FirstTest.fVirgin = K_TRUE;
13049 FirstTest.pszCwd = pszDefaultCwd;
13050 FirstTest.cRuns = 1;
13051
13052 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
13053 if (rcExit)
13054 return rcExit;
13055
13056 /*
13057 * Do the job. LIFO ordering (see kSubmit).
13058 */
13059 for (pCur = pHead; pCur; pCur = pCur->pNext)
13060 {
13061 if (!pCur->pszExecutable && pCur->papszArgs)
13062 pCur->pszExecutable = pCur->papszArgs[0];
13063 if ( pCur->pszExecutable
13064 && pCur->cArgs > 0
13065 && pCur->cEnvVars > 0)
13066 {
13067 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
13068 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
13069 unsigned iRun;
13070
13071 for (iRun = 0; iRun < pCur->cRuns; iRun++)
13072 {
13073 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
13074 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
13075 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
13076 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
13077
13078 KW_LOG(("rcExit=%d\n", rcExit));
13079 kwSandboxCleanupLate(&g_Sandbox);
13080
13081 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
13082 }
13083 kHlpFree(papszEnvVarsCopy);
13084 }
13085 else
13086 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
13087 pCur->pszExecutable ? "" : " No executable!",
13088 pCur->cArgs < 1 ? " No arguments!" : "",
13089 pCur->cEnvVars < 1 ? " No environment!" : "",
13090 pCur->iJobSrc, pCur->pszJobSrc);
13091 }
13092
13093 if (getenv("KWORKER_STATS") != NULL)
13094 kwPrintStats();
13095
13096# ifdef WITH_LOG_FILE
13097 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13098 CloseHandle(g_hLogFile);
13099# endif
13100 return rcExit;
13101}
13102
13103
13104/**
13105 * Helper for main() argument handling that sets the processor group if
13106 * possible.
13107 */
13108static void kwSetProcessorGroup(unsigned long uGroup)
13109{
13110 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
13111 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
13112 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
13113
13114 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
13115 if (pfnSetThreadGroupAffinity)
13116 {
13117 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
13118 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
13119 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
13120 if (NewAff.Mask && (WORD)uGroup == uGroup)
13121 {
13122 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
13123 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
13124 }
13125 else if (GetLastError() == NO_ERROR)
13126 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
13127 else
13128 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
13129 uGroup, GetLastError());
13130 }
13131 else
13132 {
13133 OSVERSIONINFOA VerInfo = {0};
13134 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
13135 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
13136 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
13137 }
13138}
13139
13140
13141int main(int argc, char **argv)
13142{
13143 KSIZE cbMsgBuf = 0;
13144 KU8 *pbMsgBuf = NULL;
13145 int i;
13146 HANDLE hPipe = INVALID_HANDLE_VALUE;
13147 const char *pszTmp;
13148 KFSLOOKUPERROR enmIgnored;
13149 DWORD dwType;
13150#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
13151 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
13152 kwSandboxVecXcptEmulateChained);
13153#endif
13154#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13155 HANDLE hCurProc = GetCurrentProcess();
13156 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
13157 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
13158#endif
13159
13160
13161#ifdef WITH_FIXED_VIRTUAL_ALLOCS
13162 /*
13163 * Reserve memory for cl.exe
13164 */
13165 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
13166 {
13167 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
13168 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
13169 g_aFixedVirtualAllocs[i].cbFixed,
13170 MEM_RESERVE, PAGE_READWRITE);
13171 if ( !g_aFixedVirtualAllocs[i].pvReserved
13172 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
13173 {
13174 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
13175 GetLastError());
13176 if (g_aFixedVirtualAllocs[i].pvReserved)
13177 {
13178 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
13179 g_aFixedVirtualAllocs[i].pvReserved = NULL;
13180 }
13181 }
13182 }
13183#endif
13184/// quick and dirty...
13185LoadLibraryA("E:\\vbox\\svn\\trunk\\tools\\win.amd64\\vcc\\v14.2\\Tools\\MSVC\\14.26.28801\\bin\\Hostx64\\x64\\mspdbcore.dll");
13186
13187 /*
13188 * Register our Control-C and Control-Break handlers.
13189 */
13190 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
13191 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
13192
13193 /*
13194 * Create the cache and mark the temporary directory as using the custom revision.
13195 */
13196 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
13197 if (!g_pFsCache)
13198 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
13199
13200 pszTmp = getenv("TEMP");
13201 if (pszTmp && *pszTmp != '\0')
13202 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13203 pszTmp = getenv("TMP");
13204 if (pszTmp && *pszTmp != '\0')
13205 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13206 pszTmp = getenv("TMPDIR");
13207 if (pszTmp && *pszTmp != '\0')
13208 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13209
13210 /*
13211 * Make g_abDefLdBuf executable.
13212 */
13213 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
13214 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
13215 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
13216 InitializeCriticalSection(&g_Sandbox.HandlesLock);
13217 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
13218
13219#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13220 /*
13221 * Get and duplicate the console handles.
13222 */
13223 /* Standard output. */
13224 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
13225 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
13226 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13227 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
13228 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
13229 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
13230 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13231 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13232 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
13233 g_Sandbox.HandleStdOut.cRefs = 0x10001;
13234 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
13235 g_Sandbox.HandleStdOut.tidOwner = KU32_MAX;
13236 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
13237 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
13238 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
13239 {
13240 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
13241 g_Sandbox.cFixedHandles++;
13242 else
13243 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
13244 }
13245 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13246 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
13247
13248 /* Standard error. */
13249 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
13250 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
13251 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13252 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
13253 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
13254 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
13255 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13256 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13257 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
13258 g_Sandbox.HandleStdErr.cRefs = 0x10001;
13259 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
13260 g_Sandbox.HandleStdErr.tidOwner = KU32_MAX;
13261 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
13262 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
13263 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
13264 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
13265 {
13266 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
13267 g_Sandbox.cFixedHandles++;
13268 else
13269 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
13270 }
13271 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13272 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
13273
13274 /* Combined console buffer. */
13275 if (g_Sandbox.StdErr.fIsConsole)
13276 {
13277 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
13278 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13279 }
13280 else if (g_Sandbox.StdOut.fIsConsole)
13281 {
13282 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
13283 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13284 }
13285 else
13286 {
13287 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
13288 g_Sandbox.Combined.uCodepage = CP_ACP;
13289 }
13290 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
13291#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
13292
13293
13294 /*
13295 * Parse arguments.
13296 */
13297 for (i = 1; i < argc; i++)
13298 {
13299 if (strcmp(argv[i], "--pipe") == 0)
13300 {
13301 i++;
13302 if (i < argc)
13303 {
13304 char *pszEnd = NULL;
13305 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13306 if ( *argv[i]
13307 && pszEnd != NULL
13308 && *pszEnd == '\0'
13309 && u64Value != 0
13310 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13311 && (uintptr_t)u64Value == u64Value)
13312 hPipe = (HANDLE)(uintptr_t)u64Value;
13313 else
13314 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13315 }
13316 else
13317 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13318 }
13319 else if (strcmp(argv[i], "--volatile") == 0)
13320 {
13321 i++;
13322 if (i < argc)
13323 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13324 else
13325 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13326 }
13327 else if (strcmp(argv[i], "--test") == 0)
13328 return kwTestRun(argc - i - 1, &argv[i + 1]);
13329 else if (strcmp(argv[i], "--full-test") == 0)
13330 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13331 else if (strcmp(argv[i], "--priority") == 0)
13332 {
13333 i++;
13334 if (i < argc)
13335 {
13336 char *pszEnd = NULL;
13337 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13338 if ( *argv[i]
13339 && pszEnd != NULL
13340 && *pszEnd == '\0'
13341 && uValue >= 1
13342 && uValue <= 5)
13343 {
13344 DWORD dwClass;
13345 int dwPriority;
13346 switch (uValue)
13347 {
13348 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13349 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13350 default:
13351 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13352 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13353 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13354 }
13355 SetPriorityClass(GetCurrentProcess(), dwClass);
13356 if (dwPriority != INT_MAX)
13357 SetThreadPriority(GetCurrentThread(), dwPriority);
13358 }
13359 else
13360 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13361 }
13362 else
13363 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13364 }
13365 else if (strcmp(argv[i], "--group") == 0)
13366 {
13367 i++;
13368 if (i < argc)
13369 {
13370 char *pszEnd = NULL;
13371 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13372 if ( *argv[i]
13373 && pszEnd != NULL
13374 && *pszEnd == '\0'
13375 && uValue == (WORD)uValue)
13376 kwSetProcessorGroup(uValue);
13377 else
13378 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13379 }
13380 else
13381 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13382 }
13383 else if ( strcmp(argv[i], "--verbose") == 0
13384 || strcmp(argv[i], "-v") == 0)
13385 g_cVerbose++;
13386 else if ( strcmp(argv[i], "--help") == 0
13387 || strcmp(argv[i], "-h") == 0
13388 || strcmp(argv[i], "-?") == 0)
13389 {
13390 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13391 "usage: kWorker <--help|-h>\n"
13392 "usage: kWorker <--version|-V>\n"
13393 "usage: kWorker [--volatile dir] --full-test kSubmit ...\n"
13394 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13395 "\n"
13396 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13397 return 0;
13398 }
13399 else if ( strcmp(argv[i], "--version") == 0
13400 || strcmp(argv[i], "-V") == 0)
13401 return kbuild_version(argv[0]);
13402 else
13403 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13404 }
13405
13406 /*
13407 * If no --pipe argument, then assume its standard input.
13408 * We need to carefully replace the CRT stdin with a handle to "nul".
13409 */
13410 if (hPipe == INVALID_HANDLE_VALUE)
13411 {
13412 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13413 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13414 {
13415 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13416 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13417 {
13418 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13419 if (fdNul >= 0)
13420 {
13421 if (_dup2(fdNul, 0) >= 0)
13422 {
13423 close(fdNul);
13424 hPipe = hDuplicate;
13425 }
13426 else
13427 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13428 }
13429 else
13430 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13431 }
13432 else
13433 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13434 }
13435 else
13436 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13437 GetFileType(hPipe), GetLastError());
13438 }
13439 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13440 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13441 GetFileType(hPipe), GetLastError());
13442 g_hPipe = hPipe;
13443
13444 /*
13445 * Serve the pipe.
13446 */
13447 for (;;)
13448 {
13449 KU32 cbMsg = 0;
13450 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13451 if (rc == 0)
13452 {
13453 /* Make sure the message length is within sane bounds. */
13454 if ( cbMsg > 4
13455 && cbMsg <= 256*1024*1024)
13456 {
13457 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13458 if (cbMsg + 4 <= cbMsgBuf)
13459 { /* likely */ }
13460 else
13461 {
13462 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13463 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13464 if (!pbMsgBuf)
13465 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13466 }
13467
13468 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13469 *(KU32 *)pbMsgBuf = cbMsg;
13470 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13471 if (rc == 0)
13472 {
13473 const char *psz;
13474
13475 pbMsgBuf[cbMsg] = '\0';
13476 pbMsgBuf[cbMsg + 1] = '\0';
13477 pbMsgBuf[cbMsg + 2] = '\0';
13478 pbMsgBuf[cbMsg + 3] = '\0';
13479
13480 /* The first string after the header is the command. */
13481 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13482 if ( strcmp(psz, "JOB") == 0
13483 && g_rcCtrlC == 0)
13484 {
13485 struct
13486 {
13487 KI32 rcExitCode;
13488 KU8 bExiting;
13489 KU8 abZero[3];
13490 } Reply;
13491 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13492 Reply.bExiting = g_fRestart;
13493 Reply.abZero[0] = 0;
13494 Reply.abZero[1] = 0;
13495 Reply.abZero[2] = 0;
13496 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13497 if ( rc == 0
13498 && !g_fRestart)
13499 {
13500 kwSandboxCleanupLate(&g_Sandbox);
13501 if (g_rcCtrlC == 0)
13502 continue;
13503 }
13504 }
13505 else
13506 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13507 }
13508 }
13509 else
13510 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13511 }
13512
13513 /*
13514 * If we're exitting because we're restarting, we need to delay till
13515 * kmk/kSubmit has read the result. Windows documentation says it
13516 * immediately discards pipe buffers once the pipe is broken by the
13517 * server (us). So, We flush the buffer and queues a 1 byte read
13518 * waiting for kSubmit to close the pipe when it receives the
13519 * bExiting = K_TRUE result.
13520 */
13521 if (g_fRestart)
13522 {
13523 DWORD cbIgnored = 1;
13524 KU8 b;
13525 FlushFileBuffers(hPipe);
13526 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13527 }
13528
13529 CloseHandle(hPipe);
13530#ifdef WITH_LOG_FILE
13531 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13532 CloseHandle(g_hLogFile);
13533#endif
13534 if (getenv("KWORKER_STATS") != NULL)
13535 kwPrintStats();
13536 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13537 }
13538}
13539
13540
13541/** @page pg_kWorker kSubmit / kWorker
13542 *
13543 * @section sec_kWorker_Motivation Motivation / Inspiration
13544 *
13545 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13546 * builds on machines "infested" by Anti Virus protection and disk encryption
13547 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13548 * got "infected".
13549 *
13550 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13551 * mainly a bunch of tiny assembly and C files being compiler a million times.
13552 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13553 * own toolchain from within the same process, saving a lot of process creation
13554 * and teardown overhead.
13555 *
13556 *
13557 * @section sec_kWorker_kSubmit About kSubmit
13558 *
13559 * When wanting to execute a job in a kWorker instance, it must be submitted
13560 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13561 * built into kmk and does not exist as an external program. The reason for
13562 * this is that it keep track of the kWorker instances.
13563 *
13564 * The kSubmit command has the --32-bit and --64-bit options for selecting
13565 * between 32-bit and 64-bit worker instance. We generally assume the user of
13566 * the command knows which bit count the executable has, so kSubmit is spared
13567 * the extra work of finding out.
13568 *
13569 * The kSubmit command shares a environment and current directory manipulation
13570 * with the kRedirect command, but not the file redirection. So long no file
13571 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13572 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13573 * and/or current directory changes to work.
13574 *
13575 * Unlike the kRedirect command, the kSubmit command can also specify an
13576 * internall post command to be executed after the main command succeeds.
13577 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13578 * information from Microsoft COFF object files and Watcom OMF object files and
13579 * is scheduled to replace kDepIDB.
13580 *
13581 *
13582 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13583 *
13584 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13585 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13586 * it. When the job is completed, kWorker writes a short reply with the exit
13587 * code and an internal status indicating whether it is going to restart.
13588 *
13589 * The kWorker intance will reply to kSubmit before completing all the internal
13590 * cleanup work, so as not to delay the next job execution unnecessarily. This
13591 * includes checking its own memory consumption and checking whether it needs
13592 * restarting. So, a decision to restart unfortunately have to wait till after
13593 * the next job has completed. This is a little bit unfortunate if the next job
13594 * requires a lot of memory and kWorker has already leaked/used a lot.
13595 *
13596 *
13597 * @section sec_kWorker_How_Works How kWorker Works
13598 *
13599 * kWorker will load the executable specified by kSubmit into memory and call
13600 * it's entrypoint in a lightly sandbox'ed environment.
13601 *
13602 *
13603 * @subsection ssec_kWorker_Loaing Image loading
13604 *
13605 * kWorker will manually load all the executable images into memory, fix them
13606 * up, and make a copy of the virgin image so it can be restored using memcpy
13607 * the next time it is used.
13608 *
13609 * Imported functions are monitored and replacements used for a few of them.
13610 * These replacements are serve the following purposes:
13611 * - Provide a different command line.
13612 * - Provide a different environment.
13613 * - Intercept process termination.
13614 * - Intercept thread creation (only linker is allowed to create threads).
13615 * - Intercept file reading for caching (header files, ++) as file system
13616 * access is made even slower by anti-virus software.
13617 * - Intercept crypto hash APIs to cache MD5 digests of header files
13618 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13619 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13620 * in memory as writing files grows expensive with encryption and
13621 * anti-virus software active.
13622 * - Intercept some file system queries to use the kFsCache instead of
13623 * going to the kernel and slowly worm thru the AV filter driver.
13624 * - Intercept standard output/error and console writes to aggressivly
13625 * buffer the output. The MS CRT does not buffer either when it goes to
13626 * the console, resulting in terrible performance and mixing up output
13627 * with other compile jobs.
13628 * This also allows us to filter out the annoying source file announcements
13629 * by cl.exe.
13630 * - Intercept VirtualAlloc and VirtualFree to prevent
13631 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13632 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13633 * the callbacks run after each job.
13634 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13635 * executables and tools using custom heaps (like the microsoft linker).
13636 * [exectuable images only]
13637 * - Intercept atexit and _onexit registration to be able run them after
13638 * each job instead of crashing as kWorker exits. This also helps avoid
13639 * some leaks. [executable image only]
13640 *
13641 * DLLs falls into two categories, system DLLs which we always load using the
13642 * native loader, and tool DLLs which can be handled like the executable or
13643 * optionally using the native loader. We maintain a hardcoded white listing of
13644 * tool DLLs we trust to load using the native loader.
13645 *
13646 * Imports of natively loaded DLLs are processed too, but we only replace a
13647 * subset of the functions compared to natively loaded excutable and DLL images.
13648 *
13649 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13650 * This is to speed up job execution.
13651 *
13652 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13653 * for each job run, but so far this hasn't been necessary.
13654 *
13655 *
13656 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
13657 *
13658 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
13659 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
13660 * intermediate representation between the first (c1/c1xx.dll) and second pass
13661 * (c2.dll).
13662 *
13663 * kWorker helps the compiler as best as it can. Given a little knowledge about
13664 * stable and volatile file system areas, it can do a lot of caching that a
13665 * normal compiler driver cannot easily do when given a single file.
13666 *
13667 *
13668 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
13669 *
13670 * The preprocessor part will open and process header files exactly as they are
13671 * encountered in the source files. If string.h is included by the main source
13672 * and five other header files, it will be searched for (include path), opened,
13673 * read, MD5-summed, and pre-processed six times. The last five times is just a
13674 * waste of time because of the guards or \#pragma once. A smart compiler would
13675 * make a little extra effort and realize this.
13676 *
13677 * kWorker will cache help the preprocessor by remembering places where the
13678 * header was not found with help of kFsCache, and cache the file in memory when
13679 * found. The first part is taken care of by intercepting GetFileAttributesW,
13680 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
13681 * cached, the file is kept open and the CreateFileW call returns a duplicate of
13682 * that handle. An internal handle table is used by ReadFile and CloseFile to
13683 * keep track of intercepted handles (also used for temporary file, temporary
13684 * file mappings, console buffering, and standard out/err buffering).
13685 *
13686 * PS. The header search optimization also comes in handy when cl.exe goes on
13687 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
13688 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
13689 * optionally compile the three pass DLLs as executables during development
13690 * and problem analysis.
13691 *
13692 *
13693 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
13694 *
13695 * The issues of the temporary files is pretty severe on the Dell machine used
13696 * for benchmarking with full AV and encryption. The synthetic benchmark
13697 * improved by 30% when kWorker implemented measures to keep them entirely in
13698 * memory.
13699 *
13700 * kWorker implement these by recognizing the filename pattern in CreateFileW
13701 * and creating/opening the given file as needed. The handle returned is a
13702 * duplicate of the current process, thus giving us a good chance of catching
13703 * API calls we're not intercepting.
13704 *
13705 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
13706 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
13707 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
13708 * UnmapViewOfFile.
13709 *
13710 *
13711 * @section sec_kWorker_Numbers Some measurements.
13712 *
13713 * - r2881 building src/VBox/Runtime:
13714 * - without: 2m01.016388s = 120.016388 s
13715 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
13716 * - r2884 building vbox/debug (r110512):
13717 * - without: 11m14.446609s = 674.446609 s
13718 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
13719 * - r2896 building vbox/debug (r110577):
13720 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
13721 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
13722 * MS Defender as AV):
13723 * - without: 10m24.990389s = 624.990389s
13724 * - with: 08m04.738184s = 484.738184s
13725 * - delta: 624.99s - 484.74s = 140.25s
13726 * - saved: 140.25/624.99 = 22% faster
13727 *
13728 *
13729 * @subsection subsec_kWorker_Early_Numbers Early Experiments
13730 *
13731 * These are some early experiments doing 1024 compilations of
13732 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
13733 * main function:
13734 *
13735 * Skylake (W10/amd64, only stdandard MS defender):
13736 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
13737 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
13738 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
13739 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
13740 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
13741 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
13742 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
13743 *
13744 * Dell (W7/amd64, infected by mcafee):
13745 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
13746 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
13747 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
13748 *
13749 * The command line:
13750 * @code{.cpp}
13751 "\"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"
13752 * @endcode
13753 */
13754
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