VirtualBox

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

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

kWorker: Need more small TLS DLLs when moc uses kSubmit too.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 521.2 KB
Line 
1/* $Id: kWorker.c 3371 2020-06-10 10:58:58Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33//#undef NDEBUG
34//#define K_STRICT 1
35//#define KW_LOG_ENABLED
36
37#define PSAPI_VERSION 1
38#include <k/kHlp.h>
39#include <k/kLdr.h>
40
41#include <stdio.h>
42#include <intrin.h>
43#include <setjmp.h>
44#include <ctype.h>
45#include <errno.h>
46#include <process.h>
47
48#include "nt/ntstat.h"
49#include "kbuild_version.h"
50
51#include "nt/ntstuff.h"
52#include "nt/nthlp.h"
53#include <psapi.h>
54
55#include "nt/kFsCache.h"
56#include "nt_fullpath.h"
57#include "win_get_processor_group_active_mask.h"
58#include "quote_argv.h"
59#include "md5.h"
60#include "console.h"
61
62#include "../kmk/kmkbuiltin.h"
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68/** @def WITH_TEMP_MEMORY_FILES
69 * Enables temporary memory files for cl.exe. */
70#define WITH_TEMP_MEMORY_FILES
71
72/** @def WITH_HASH_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 { L"kWorkerTls1K08.dll", K_FALSE },
1128 { L"kWorkerTls1K09.dll", K_FALSE },
1129 { L"kWorkerTls1K10.dll", K_FALSE },
1130 { L"kWorkerTls1K11.dll", K_FALSE },
1131 { L"kWorkerTls1K12.dll", K_FALSE },
1132 { L"kWorkerTls1K13.dll", K_FALSE },
1133 { L"kWorkerTls1K14.dll", K_FALSE },
1134 { L"kWorkerTls1K15.dll", K_FALSE },
1135};
1136
1137/** The 64KB TLS DLLs. */
1138static KWTLSDLL g_aTls64KDlls[] =
1139{
1140 { L"kWorkerTls64K.dll", K_FALSE },
1141 { L"kWorkerTls64K01.dll", K_FALSE },
1142 { L"kWorkerTls64K02.dll", K_FALSE },
1143 { L"kWorkerTls64K03.dll", K_FALSE },
1144 { L"kWorkerTls64K04.dll", K_FALSE },
1145 { L"kWorkerTls64K05.dll", K_FALSE },
1146 { L"kWorkerTls64K06.dll", K_FALSE },
1147 { L"kWorkerTls64K07.dll", K_FALSE },
1148};
1149
1150/** The 128KB TLS DLLs. */
1151static KWTLSDLL g_aTls128KDlls[] =
1152{
1153 { L"kWorkerTls128K.dll", K_FALSE },
1154 { L"kWorkerTls128K01.dll", K_FALSE },
1155 { L"kWorkerTls128K02.dll", K_FALSE },
1156 { L"kWorkerTls128K03.dll", K_FALSE },
1157 { L"kWorkerTls128K04.dll", K_FALSE },
1158 { L"kWorkerTls128K05.dll", K_FALSE },
1159 { L"kWorkerTls128K06.dll", K_FALSE },
1160 { L"kWorkerTls128K07.dll", K_FALSE },
1161};
1162
1163/** The 512KB TLS DLLs. */
1164static KWTLSDLL g_aTls512KDlls[] =
1165{
1166 { L"kWorkerTls512K.dll", K_FALSE },
1167 { L"kWorkerTls512K01.dll", K_FALSE },
1168 { L"kWorkerTls512K02.dll", K_FALSE },
1169 { L"kWorkerTls512K03.dll", K_FALSE },
1170 { L"kWorkerTls512K04.dll", K_FALSE },
1171 { L"kWorkerTls512K05.dll", K_FALSE },
1172 { L"kWorkerTls512K06.dll", K_FALSE },
1173 { L"kWorkerTls512K07.dll", K_FALSE },
1174};
1175
1176/** The TLS DLLs grouped by size. */
1177static KWTLSDLLENTRY const g_aTlsDlls[] =
1178{
1179 { 1024, K_ELEMENTS(g_aTls1KDlls), g_aTls1KDlls },
1180 { 64*1024, K_ELEMENTS(g_aTls64KDlls), g_aTls64KDlls },
1181 { 128*1024, K_ELEMENTS(g_aTls128KDlls), g_aTls128KDlls },
1182 { 512*1024, K_ELEMENTS(g_aTls512KDlls), g_aTls512KDlls },
1183};
1184
1185/** CRT slots.
1186 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1187static KWCRTSLOT g_aCrtSlots[32];
1188
1189/** windbg .reload statements. vs */
1190char g_szReloads[4096];
1191/** Current offset into g_szReloads. */
1192KU32 volatile g_cchReloads;
1193
1194/** The file system cache. */
1195static PKFSCACHE g_pFsCache;
1196/** The current directory (referenced). */
1197static PKFSOBJ g_pCurDirObj = NULL;
1198#ifdef KBUILD_OS_WINDOWS
1199/** The windows system32 directory (referenced). */
1200static PKFSDIR g_pWinSys32 = NULL;
1201#endif
1202
1203/** Verbosity level. */
1204static int g_cVerbose = 2;
1205
1206/** Whether we should restart the worker. */
1207static KBOOL g_fRestart = K_FALSE;
1208
1209/** The process group this worker is tied to (--group option), -1 if none. */
1210static KI32 g_iProcessGroup = -1;
1211
1212/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1213static int volatile g_rcCtrlC = 0;
1214
1215/** The communication pipe handle. We break this when we see Ctrl-C such. */
1216#ifdef KBUILD_OS_WINDOWS
1217static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1218#else
1219static int g_hPipe = -1;
1220#endif
1221
1222
1223/* Further down. */
1224extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1225extern KU32 const g_cSandboxReplacements;
1226
1227extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1228extern KU32 const g_cSandboxNativeReplacements;
1229
1230extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1231extern KU32 const g_cSandboxGetProcReplacements;
1232
1233
1234/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1235 * cover the default executable link address of 0x400000.
1236 * @remarks Early main() makes it read+write+executable. Attempts as having
1237 * it as a separate section failed because the linker insists on
1238 * writing out every zero in the uninitialized section, resulting in
1239 * really big binaries. */
1240__declspec(align(0x1000))
1241static KU8 g_abDefLdBuf[16*1024*1024];
1242
1243#ifdef WITH_LOG_FILE
1244/** Log file handle. */
1245static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1246#endif
1247
1248
1249#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1250/** Virtual address space reserved for CL.EXE heap manager.
1251 *
1252 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1253 * address. It's among other things used for precompiled headers, which
1254 * seemingly have addresses hardcoded into them and won't work if mapped
1255 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1256 * for it. (The /Zm option may affect this allocation.)
1257 */
1258static struct
1259{
1260 /** The memory address we need. */
1261 KUPTR const uFixed;
1262 /** How much we need to fix. */
1263 KSIZE const cbFixed;
1264 /** What we actually got, NULL if given back. */
1265 void *pvReserved;
1266 /** Whether it is in use or not. */
1267 KBOOL fInUse;
1268} g_aFixedVirtualAllocs[] =
1269{
1270# if K_ARCH == K_ARCH_X86_32
1271 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1272 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1273 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1274# else
1275 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1276# endif
1277};
1278#endif
1279
1280
1281#ifdef WITH_HISTORY
1282/** The job history. */
1283static char *g_apszHistory[32];
1284/** Index of the next history entry. */
1285static unsigned g_iHistoryNext = 0;
1286#endif
1287
1288
1289/** Number of jobs executed. */
1290static KU32 g_cJobs;
1291/** Number of tools. */
1292static KU32 g_cTools;
1293/** Number of modules. */
1294static KU32 g_cModules;
1295/** Number of non-native modules. */
1296static KU32 g_cNonNativeModules;
1297/** Number of read-cached files. */
1298static KU32 g_cReadCachedFiles;
1299/** Total size of read-cached files. */
1300static KSIZE g_cbReadCachedFiles;
1301
1302/** Total number of ReadFile calls. */
1303static KSIZE g_cReadFileCalls;
1304/** Total bytes read via ReadFile. */
1305static KSIZE g_cbReadFileTotal;
1306/** Total number of read from read-cached files. */
1307static KSIZE g_cReadFileFromReadCached;
1308/** Total bytes read from read-cached files. */
1309static KSIZE g_cbReadFileFromReadCached;
1310/** Total number of read from in-memory temporary files. */
1311static KSIZE g_cReadFileFromInMemTemp;
1312/** Total bytes read from in-memory temporary files. */
1313static KSIZE g_cbReadFileFromInMemTemp;
1314
1315/** Total number of WriteFile calls. */
1316static KSIZE g_cWriteFileCalls;
1317/** Total bytes written via WriteFile. */
1318static KSIZE g_cbWriteFileTotal;
1319/** Total number of written to from in-memory temporary files. */
1320static KSIZE g_cWriteFileToInMemTemp;
1321/** Total bytes written to in-memory temporary files. */
1322static KSIZE g_cbWriteFileToInMemTemp;
1323
1324
1325/*********************************************************************************************************************************
1326* Internal Functions *
1327*********************************************************************************************************************************/
1328static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1329static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1330 const char *pszSearchPath, PKWMODULE *ppMod);
1331static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1332static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName);
1333static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule);
1334static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod);
1335static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1336static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1337static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile);
1338static PKWHANDLE kwSandboxHandleGet(HANDLE hFile);
1339K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle);
1340#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1341static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1342#endif
1343static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1344
1345
1346
1347
1348/**
1349 * Debug printing.
1350 * @param pszFormat Debug format string.
1351 * @param ... Format argument.
1352 */
1353static void kwDbgPrintfV(const char *pszFormat, va_list va)
1354{
1355 if (g_cVerbose >= 2)
1356 {
1357 DWORD const dwSavedErr = GetLastError();
1358#ifdef WITH_LOG_FILE
1359 DWORD dwIgnored;
1360 char szTmp[2048];
1361 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1362 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1363 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1364 cch += cchPrefix;
1365 else
1366 {
1367 cch = sizeof(szTmp) - 1;
1368 szTmp[cch] = '\0';
1369 }
1370
1371 if (g_hLogFile == INVALID_HANDLE_VALUE)
1372 {
1373 wchar_t wszFilename[128];
1374 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1375 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1376 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1377 }
1378
1379 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1380#else
1381 fprintf(stderr, "debug: ");
1382 vfprintf(stderr, pszFormat, va);
1383#endif
1384
1385 SetLastError(dwSavedErr);
1386 }
1387}
1388
1389
1390/**
1391 * Debug printing.
1392 * @param pszFormat Debug format string.
1393 * @param ... Format argument.
1394 */
1395static void kwDbgPrintf(const char *pszFormat, ...)
1396{
1397 if (g_cVerbose >= 2)
1398 {
1399 va_list va;
1400 va_start(va, pszFormat);
1401 kwDbgPrintfV(pszFormat, va);
1402 va_end(va);
1403 }
1404}
1405
1406
1407/**
1408 * Debugger printing.
1409 * @param pszFormat Debug format string.
1410 * @param ... Format argument.
1411 */
1412static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1413{
1414 if (IsDebuggerPresent())
1415 {
1416 DWORD const dwSavedErr = GetLastError();
1417 char szTmp[2048];
1418
1419 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1420 OutputDebugStringA(szTmp);
1421
1422 SetLastError(dwSavedErr);
1423 }
1424}
1425
1426
1427/**
1428 * Debugger printing.
1429 * @param pszFormat Debug format string.
1430 * @param ... Format argument.
1431 */
1432static void kwDebuggerPrintf(const char *pszFormat, ...)
1433{
1434 va_list va;
1435 va_start(va, pszFormat);
1436 kwDebuggerPrintfV(pszFormat, va);
1437 va_end(va);
1438}
1439
1440
1441
1442/**
1443 * Error printing.
1444 * @param pszFormat Message format string.
1445 * @param ... Format argument.
1446 */
1447static void kwErrPrintfV(const char *pszFormat, va_list va)
1448{
1449 DWORD const dwSavedErr = GetLastError();
1450
1451#if defined(KW_LOG_ENABLED) && defined(WITH_LOG_FILE)
1452 va_list vaCopy;
1453# if defined(va_copy) || !defined(_MSC_VER) || _MSC_VER >= 1700 /*??*/
1454 va_copy(vaCopy, va);
1455# else
1456 vaCopy = va;
1457# endif
1458 kwDebuggerPrintf("kWorker: error: ");
1459 kwDebuggerPrintfV(pszFormat, vaCopy);
1460#endif
1461
1462 fprintf(stderr, "kWorker: error: ");
1463 vfprintf(stderr, pszFormat, va);
1464 fflush(stderr); /* In case it's a pipe. */
1465
1466 SetLastError(dwSavedErr);
1467}
1468
1469
1470/**
1471 * Error printing.
1472 * @param pszFormat Message format string.
1473 * @param ... Format argument.
1474 */
1475static void kwErrPrintf(const char *pszFormat, ...)
1476{
1477 va_list va;
1478 va_start(va, pszFormat);
1479 kwErrPrintfV(pszFormat, va);
1480 va_end(va);
1481}
1482
1483
1484/**
1485 * Error printing.
1486 * @return rc;
1487 * @param rc Return value
1488 * @param pszFormat Message format string.
1489 * @param ... Format argument.
1490 */
1491static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1492{
1493 va_list va;
1494 va_start(va, pszFormat);
1495 kwErrPrintfV(pszFormat, va);
1496 va_end(va);
1497 return rc;
1498}
1499
1500
1501#ifdef K_STRICT
1502
1503KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1504{
1505 DWORD const dwSavedErr = GetLastError();
1506
1507 fprintf(stderr,
1508 "\n"
1509 "!!Assertion failed!!\n"
1510 "Expression: %s\n"
1511 "Function : %s\n"
1512 "File: %s\n"
1513 "Line: %d\n"
1514 , pszExpr, pszFunction, pszFile, iLine);
1515
1516 SetLastError(dwSavedErr);
1517}
1518
1519
1520KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1521{
1522 DWORD const dwSavedErr = GetLastError();
1523 va_list va;
1524
1525 va_start(va, pszFormat);
1526 fprintf(stderr, pszFormat, va);
1527 va_end(va);
1528
1529 SetLastError(dwSavedErr);
1530}
1531
1532#endif /* K_STRICT */
1533
1534
1535/**
1536 * Hashes a string.
1537 *
1538 * @returns 32-bit string hash.
1539 * @param pszString String to hash.
1540 */
1541static KU32 kwStrHash(const char *pszString)
1542{
1543 /* This algorithm was created for sdbm (a public-domain reimplementation of
1544 ndbm) database library. it was found to do well in scrambling bits,
1545 causing better distribution of the keys and fewer splits. it also happens
1546 to be a good general hashing function with good distribution. the actual
1547 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1548 is the faster version used in gawk. [there is even a faster, duff-device
1549 version] the magic constant 65599 was picked out of thin air while
1550 experimenting with different constants, and turns out to be a prime.
1551 this is one of the algorithms used in berkeley db (see sleepycat) and
1552 elsewhere. */
1553 KU32 uHash = 0;
1554 KU32 uChar;
1555 while ((uChar = (unsigned char)*pszString++) != 0)
1556 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1557 return uHash;
1558}
1559
1560
1561/**
1562 * Hashes a string.
1563 *
1564 * @returns The string length.
1565 * @param pszString String to hash.
1566 * @param puHash Where to return the 32-bit string hash.
1567 */
1568static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1569{
1570 const char * const pszStart = pszString;
1571 KU32 uHash = 0;
1572 KU32 uChar;
1573 while ((uChar = (unsigned char)*pszString) != 0)
1574 {
1575 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1576 pszString++;
1577 }
1578 *puHash = uHash;
1579 return pszString - pszStart;
1580}
1581
1582
1583/**
1584 * Hashes a string.
1585 *
1586 * @returns The string length in wchar_t units.
1587 * @param pwszString String to hash.
1588 * @param puHash Where to return the 32-bit string hash.
1589 */
1590static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1591{
1592 const wchar_t * const pwszStart = pwszString;
1593 KU32 uHash = 0;
1594 KU32 uChar;
1595 while ((uChar = *pwszString) != 0)
1596 {
1597 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1598 pwszString++;
1599 }
1600 *puHash = uHash;
1601 return pwszString - pwszStart;
1602}
1603
1604
1605/**
1606 * Converts the given string to unicode.
1607 *
1608 * @returns Length of the resulting string in wchar_t's.
1609 * @param pszSrc The source string.
1610 * @param pwszDst The destination buffer.
1611 * @param cwcDst The size of the destination buffer in wchar_t's.
1612 */
1613static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1614{
1615 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1616 KSIZE offDst = 0;
1617 while (offDst < cwcDst)
1618 {
1619 char ch = *pszSrc++;
1620 pwszDst[offDst++] = ch;
1621 if (!ch)
1622 return offDst - 1;
1623 kHlpAssert((unsigned)ch < 127);
1624 }
1625
1626 pwszDst[offDst - 1] = '\0';
1627 return offDst;
1628}
1629
1630
1631/**
1632 * Converts the given string to UTF-16, allocating the buffer.
1633 *
1634 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1635 * the source string.
1636 * @param pchSrc The source string.
1637 * @param cchSrc The length of the source string.
1638 */
1639static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1640{
1641 DWORD const dwErrSaved = GetLastError();
1642 KSIZE cwcBuf = cchSrc + 1;
1643 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1644 if (pwszBuf)
1645 {
1646 if (cchSrc > 0)
1647 {
1648 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1649 if (cwcRet > 0)
1650 {
1651 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1652 pwszBuf[cwcRet] = '\0';
1653 }
1654 else
1655 {
1656 kHlpFree(pwszBuf);
1657
1658 /* Figure the length and allocate the right buffer size. */
1659 SetLastError(NO_ERROR);
1660 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1661 if (cwcRet)
1662 {
1663 cwcBuf = cwcRet + 2;
1664 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1665 if (pwszBuf)
1666 {
1667 SetLastError(NO_ERROR);
1668 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1669 if (cwcRet)
1670 {
1671 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1672 pwszBuf[cwcRet] = '\0';
1673 }
1674 else
1675 {
1676 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1677 kHlpFree(pwszBuf);
1678 pwszBuf = NULL;
1679 }
1680 }
1681 }
1682 else
1683 {
1684 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1685 pwszBuf = NULL;
1686 }
1687 }
1688 }
1689 else
1690 pwszBuf[0] = '\0';
1691 }
1692 SetLastError(dwErrSaved);
1693 return pwszBuf;
1694}
1695
1696
1697/**
1698 * Converts the given UTF-16 to a normal string.
1699 *
1700 * @returns Length of the resulting string.
1701 * @param pwszSrc The source UTF-16 string.
1702 * @param pszDst The destination buffer.
1703 * @param cbDst The size of the destination buffer in bytes.
1704 */
1705static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1706{
1707 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1708 KSIZE offDst = 0;
1709 while (offDst < cbDst)
1710 {
1711 wchar_t wc = *pwszSrc++;
1712 pszDst[offDst++] = (char)wc;
1713 if (!wc)
1714 return offDst - 1;
1715 kHlpAssert((unsigned)wc < 127);
1716 }
1717
1718 pszDst[offDst - 1] = '\0';
1719 return offDst;
1720}
1721
1722
1723/**
1724 * Converts the given UTF-16 to ASSI, allocating the buffer.
1725 *
1726 * @returns Pointer to the new heap allocation containing the ANSI version of
1727 * the source string.
1728 * @param pwcSrc The source string.
1729 * @param cwcSrc The length of the source string.
1730 */
1731static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1732{
1733 DWORD const dwErrSaved = GetLastError();
1734 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1735 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1736 if (pszBuf)
1737 {
1738 if (cwcSrc > 0)
1739 {
1740 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1741 if (cchRet > 0)
1742 {
1743 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1744 pszBuf[cchRet] = '\0';
1745 }
1746 else
1747 {
1748 kHlpFree(pszBuf);
1749
1750 /* Figure the length and allocate the right buffer size. */
1751 SetLastError(NO_ERROR);
1752 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1753 if (cchRet)
1754 {
1755 cbBuf = cchRet + 2;
1756 pszBuf = (char *)kHlpAlloc(cbBuf);
1757 if (pszBuf)
1758 {
1759 SetLastError(NO_ERROR);
1760 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1761 if (cchRet)
1762 {
1763 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1764 pszBuf[cchRet] = '\0';
1765 }
1766 else
1767 {
1768 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1769 kHlpFree(pszBuf);
1770 pszBuf = NULL;
1771 }
1772 }
1773 }
1774 else
1775 {
1776 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1777 pszBuf = NULL;
1778 }
1779 }
1780 }
1781 else
1782 pszBuf[0] = '\0';
1783 }
1784 SetLastError(dwErrSaved);
1785 return pszBuf;
1786}
1787
1788
1789
1790/** UTF-16 string length. */
1791static KSIZE kwUtf16Len(wchar_t const *pwsz)
1792{
1793 KSIZE cwc = 0;
1794 while (*pwsz != '\0')
1795 cwc++, pwsz++;
1796 return cwc;
1797}
1798
1799/**
1800 * Copy out the UTF-16 string following the convension of GetModuleFileName
1801 */
1802static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1803{
1804 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1805 if (cwcSrc + 1 <= cwcDst)
1806 {
1807 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1808 return (DWORD)cwcSrc;
1809 }
1810 if (cwcDst > 0)
1811 {
1812 KSIZE cwcDstTmp = cwcDst - 1;
1813 pwszDst[cwcDstTmp] = '\0';
1814 if (cwcDstTmp > 0)
1815 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1816 }
1817 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1818 return (DWORD)cwcDst;
1819}
1820
1821
1822/**
1823 * Copy out the ANSI string following the convension of GetModuleFileName
1824 */
1825static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1826{
1827 KSIZE cchSrc = kHlpStrLen(pszSrc);
1828 if (cchSrc + 1 <= cbDst)
1829 {
1830 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1831 return (DWORD)cchSrc;
1832 }
1833 if (cbDst > 0)
1834 {
1835 KSIZE cbDstTmp = cbDst - 1;
1836 pszDst[cbDstTmp] = '\0';
1837 if (cbDstTmp > 0)
1838 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1839 }
1840 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1841 return (DWORD)cbDst;
1842}
1843
1844
1845/**
1846 * Normalizes the path so we get a consistent hash.
1847 *
1848 * @returns status code.
1849 * @param pszPath The path.
1850 * @param pszNormPath The output buffer.
1851 * @param cbNormPath The size of the output buffer.
1852 */
1853static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1854{
1855 KFSLOOKUPERROR enmError;
1856 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1857 if (pFsObj)
1858 {
1859 KBOOL fRc;
1860 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1861 kFsCacheObjRelease(g_pFsCache, pFsObj);
1862 if (fRc)
1863 return 0;
1864 return KERR_BUFFER_OVERFLOW;
1865 }
1866 return KERR_FILE_NOT_FOUND;
1867}
1868
1869
1870/**
1871 * Get the pointer to the filename part of the path.
1872 *
1873 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1874 * @returns Pointer to the terminator char if no filename.
1875 * @param pszPath The path to parse.
1876 */
1877static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1878{
1879 const wchar_t *pwszLast = NULL;
1880 for (;;)
1881 {
1882 wchar_t wc = *pwszPath;
1883#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1884 if (wc == '/' || wc == '\\' || wc == ':')
1885 {
1886 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1887 /* nothing */;
1888 pwszLast = pwszPath;
1889 }
1890#else
1891 if (wc == '/')
1892 {
1893 while ((wc = *++pszFilename) == '/')
1894 /* betsuni */;
1895 pwszLast = pwszPath;
1896 }
1897#endif
1898 if (!wc)
1899 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1900 pwszPath++;
1901 }
1902}
1903
1904
1905
1906/**
1907 * Retains a new reference to the given module
1908 * @returns pMod
1909 * @param pMod The module to retain.
1910 */
1911static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1912{
1913 kHlpAssert(pMod->cRefs > 0);
1914 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1915 pMod->cRefs++;
1916 return pMod;
1917}
1918
1919
1920/**
1921 * Releases a module reference.
1922 *
1923 * @param pMod The module to release.
1924 */
1925static void kwLdrModuleRelease(PKWMODULE pMod)
1926{
1927 if (--pMod->cRefs == 0)
1928 {
1929 /* Make sure it doesn't receive any more native TLS callbacks.if non-native. */
1930 if (!pMod->fNative && pMod->u.Manual.ppTlsWorkerModuleVar)
1931 {
1932 *pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1933 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1934 }
1935
1936 /* Unlink it from the hash table. */
1937 if (!pMod->fExe)
1938 {
1939 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1940 if (g_apModules[idx] == pMod)
1941 g_apModules[idx] = pMod->pNextHash;
1942 else
1943 {
1944 PKWMODULE pPrev = g_apModules[idx];
1945 kHlpAssert(pPrev != NULL);
1946 while (pPrev->pNextHash != pMod)
1947 {
1948 pPrev = pPrev->pNextHash;
1949 kHlpAssert(pPrev != NULL);
1950 }
1951 pPrev->pNextHash = pMod->pNextHash;
1952 }
1953 }
1954
1955 /* Unlink it from the list. */
1956 if (pMod != g_pModuleHead)
1957 {
1958 PKWMODULE pPrev = g_pModuleHead;
1959 while (pPrev)
1960 {
1961 if (pPrev->pNextList == pMod)
1962 {
1963 pPrev->pNextList = pMod->pNextList;
1964 if (!pMod->pNextList)
1965 g_ppModuleNext = &pPrev->pNextList;
1966 break;
1967 }
1968 pPrev = pPrev->pNextList;
1969 }
1970 kHlpAssert(pPrev != NULL);
1971 }
1972 else
1973 {
1974 g_pModuleHead = pMod->pNextList;
1975 if (!pMod->pNextList)
1976 g_ppModuleNext = &g_pModuleHead;
1977 }
1978
1979 /* Release import modules. */
1980 if (!pMod->fNative)
1981 {
1982 KSIZE idx = pMod->u.Manual.cImpMods;
1983 while (idx-- > 0)
1984 if (pMod->u.Manual.apImpMods[idx])
1985 {
1986 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1987 pMod->u.Manual.apImpMods[idx] = NULL;
1988 }
1989 }
1990
1991 /* Free our resources. */
1992 kLdrModClose(pMod->pLdrMod);
1993 pMod->pLdrMod = NULL;
1994
1995 if (!pMod->fNative)
1996 {
1997 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1998 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1999 }
2000
2001 if (pMod->iCrtSlot != KU8_MAX)
2002 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
2003
2004 if (pMod->pszMsPdbSrvEndpoint)
2005 {
2006 kHlpFree(pMod->pszMsPdbSrvEndpoint);
2007 pMod->pszMsPdbSrvEndpoint = NULL;
2008 }
2009
2010 kHlpFree(pMod);
2011 }
2012 else
2013 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
2014}
2015
2016
2017/**
2018 * Links the module into the module hash table.
2019 *
2020 * @returns pMod
2021 * @param pMod The module to link.
2022 */
2023static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
2024{
2025 if (!pMod->fExe)
2026 {
2027 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
2028 pMod->pNextHash = g_apModules[idx];
2029 g_apModules[idx] = pMod;
2030 }
2031
2032 pMod->pNextList = NULL;
2033 *g_ppModuleNext = pMod;
2034 g_ppModuleNext = &pMod->pNextList;
2035
2036 return pMod;
2037}
2038
2039
2040/**
2041 * Replaces imports for this module according to g_aSandboxNativeReplacements.
2042 *
2043 * @param pMod The natively loaded module to process.
2044 */
2045static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
2046{
2047 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
2048 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
2049 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
2050 IMAGE_NT_HEADERS const *pNtHdrs;
2051 IMAGE_DATA_DIRECTORY const *pDirEnt;
2052
2053 kHlpAssert(pMod->fNative);
2054
2055 /*
2056 * Locate the export descriptors.
2057 */
2058 /* MZ header. */
2059 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
2060 {
2061 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
2062 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
2063 }
2064 else
2065 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
2066
2067 /* Check PE header. */
2068 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2069 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
2070
2071 /* Locate the import descriptor array. */
2072 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2073 if ( pDirEnt->Size > 0
2074 && pDirEnt->VirtualAddress != 0)
2075 {
2076 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
2077 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
2078 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
2079 KU8 *pbProtRange = NULL;
2080 SIZE_T cbProtRange = 0;
2081 DWORD fOldProt = 0;
2082 KU32 const cbPage = 0x1000;
2083 BOOL fRc;
2084
2085
2086 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
2087 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
2088 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
2089
2090 /*
2091 * Walk the import descriptor array.
2092 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
2093 */
2094 while ( cLeft-- > 0
2095 && pImpDesc->Name > 0
2096 && pImpDesc->FirstThunk > 0)
2097 {
2098 KU32 iThunk;
2099 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
2100 PKWMODULE pImportMod = NULL;
2101 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
2102 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
2103 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
2104 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
2105 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
2106 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
2107 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
2108
2109 /* Iterate the thunks. */
2110 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
2111 {
2112 KUPTR const off = paOrgThunks[iThunk].u1.Function;
2113 kHlpAssertReturnVoid(off < cbImage);
2114 if (!IMAGE_SNAP_BY_ORDINAL(off))
2115 {
2116 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
2117 KSIZE const cchSymbol = kHlpStrLen((const char *)&pName->Name[0]);
2118 KU32 i = g_cSandboxNativeReplacements;
2119 while (i-- > 0)
2120 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
2121 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
2122 {
2123 if ( !g_aSandboxNativeReplacements[i].pszModule
2124 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
2125 {
2126 KWLDR_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
2127
2128 /* The .rdata section is normally read-only, so we need to make it writable first. */
2129 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
2130 {
2131 /* Restore previous .rdata page. */
2132 if (fOldProt)
2133 {
2134 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
2135 kHlpAssert(fRc);
2136 fOldProt = 0;
2137 }
2138
2139 /* Query attributes for the current .rdata page. */
2140 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
2141 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
2142 kHlpAssert(cbProtRange);
2143 if (cbProtRange)
2144 {
2145 switch (ProtInfo.Protect)
2146 {
2147 case PAGE_READWRITE:
2148 case PAGE_WRITECOPY:
2149 case PAGE_EXECUTE_READWRITE:
2150 case PAGE_EXECUTE_WRITECOPY:
2151 /* Already writable, nothing to do. */
2152 fRc = TRUE;
2153 break;
2154
2155 default:
2156 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2157 case PAGE_READONLY:
2158 cbProtRange = cbPage;
2159 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2160 break;
2161
2162 case PAGE_EXECUTE:
2163 case PAGE_EXECUTE_READ:
2164 cbProtRange = cbPage;
2165 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2166 break;
2167 }
2168 kHlpAssertStmt(fRc, fOldProt = 0);
2169 }
2170 }
2171
2172 /*
2173 * Unslotted replacements are simple.
2174 */
2175 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2176 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2177 else
2178 {
2179 /*
2180 * Must find our module entry for this module, possibly creating one.
2181 */
2182 if (!pImportMod)
2183 {
2184 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2185 K_TRUE /*fAlwaysPresent*/);
2186 if (!pImportMod)
2187 {
2188 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2189 pszImport, pMod->pszPath);
2190 break;
2191 }
2192 }
2193 paThunks[iThunk].u1.AddressOfData
2194 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2195 }
2196 break;
2197 }
2198 }
2199 }
2200 }
2201
2202
2203 /* Next import descriptor. */
2204 pImpDesc++;
2205 }
2206
2207
2208 if (fOldProt)
2209 {
2210 DWORD fIgnore = 0;
2211 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2212 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2213 }
2214 }
2215
2216}
2217
2218
2219/**
2220 * Creates a module from a native kLdr module handle.
2221 *
2222 * @returns Module w/ 1 reference on success, NULL on failure.
2223 * @param pLdrMod The native kLdr module.
2224 * @param pszPath The normalized path to the module.
2225 * @param cbPath The module path length with terminator.
2226 * @param uHashPath The module path hash.
2227 * @param fDoReplacements Whether to do import replacements on this
2228 * module.
2229 */
2230static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2231 KBOOL fDoReplacements, PKWMODULE pVirtualApiMod)
2232{
2233 /*
2234 * Create the entry.
2235 */
2236 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2237 if (pMod)
2238 {
2239 pMod->pwszPath = (wchar_t *)(pMod + 1);
2240 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2241 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2242 pMod->uHashPath = uHashPath;
2243 pMod->cRefs = 1;
2244 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2245 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2246 pMod->fExe = K_FALSE;
2247 pMod->fNative = K_TRUE;
2248 pMod->pLdrMod = pLdrMod;
2249 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2250 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2251 pMod->iCrtSlot = KU8_MAX;
2252 pMod->fNeedReInit = K_FALSE;
2253 pMod->pszMsPdbSrvEndpoint = NULL;
2254 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2255 pMod->pVirtualApiMod = pVirtualApiMod;
2256 if (pVirtualApiMod)
2257 kwLdrModuleRetain(pVirtualApiMod);
2258
2259 if (fDoReplacements)
2260 {
2261 DWORD const dwSavedErr = GetLastError();
2262 kwLdrModuleDoNativeImportReplacements(pMod);
2263 SetLastError(dwSavedErr);
2264 }
2265
2266 KWLDR_LOG(("New module: %p LB %#010x %s (native%s%s)\n",
2267 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath,
2268 pVirtualApiMod ? ", virtual api => " : "", pVirtualApiMod ? pVirtualApiMod->pszPath : ""));
2269 g_cModules++;
2270 return kwLdrModuleLink(pMod);
2271 }
2272 return NULL;
2273}
2274
2275
2276
2277/**
2278 * Creates a module using the native loader.
2279 *
2280 * @returns Module w/ 1 reference on success, NULL on failure.
2281 * @param pszPath The normalized path to the module.
2282 * @param uHashPath The module path hash.
2283 * @param fDoReplacements Whether to do import replacements on this
2284 * module.
2285 */
2286static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2287{
2288 PKLDRMOD pLdrMod;
2289 int rc;
2290
2291 /*
2292 * HACK ALERT! Make sure the application path is searched when looking for
2293 * imports in the module we're loading.
2294 */
2295 /** @todo improve on this hack! */
2296 PKWMODULE pExe = g_Sandbox.pTool ? g_Sandbox.pTool->u.Sandboxed.pExe : NULL;
2297 if (pExe)
2298 {
2299 /* HACK ALERT! */
2300 wchar_t *pwzFilename = (wchar_t *)&pExe->pwszPath[pExe->offFilenameW];
2301 wchar_t wcSaved = pExe->pwszPath[pExe->offFilenameW];
2302 *pwzFilename = '\0';
2303 if (!SetDllDirectoryW(pExe->pwszPath))
2304 kwErrPrintf("SetDllDirectoryW failed: %u\n", GetLastError());
2305 KW_LOG(("kwLdrModuleCreateNative: Applied SetDllDirectoryW hack (%ls)\n", pExe->pwszPath));
2306 *pwzFilename = wcSaved;
2307 }
2308 else
2309 KW_LOG(("kwLdrModuleCreateNative: Warning! Too early for SetDllDirectoryW hack\n"));
2310
2311
2312 /*
2313 * Load the library and create a module structure for it.
2314 */
2315 rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2316 if (rc == 0)
2317 {
2318 KSIZE cchPath = kHlpStrLen(pszPath);
2319 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, cchPath + 1, uHashPath,
2320 fDoReplacements, NULL /*pVirtualApiMod*/);
2321 if (pMod)
2322 return pMod;
2323 kLdrModClose(pLdrMod);
2324 }
2325 return NULL;
2326}
2327
2328
2329/**
2330 * Checks if the given name could be a virtual API module or not.
2331 */
2332static KBOOL kwLdrIsVirtualApiModule(const char *pszName, KSIZE cchName)
2333{
2334 if (cchName <= 7)
2335 return K_FALSE;
2336 switch (*pszName)
2337 {
2338 default:
2339 return K_FALSE;
2340 case 'a':
2341 case 'A':
2342 if (pszName[1] != 'p' && pszName[1] != 'P')
2343 return K_FALSE;
2344 if (pszName[2] != 'i' && pszName[2] != 'I')
2345 return K_FALSE;
2346 break;
2347 case 'e':
2348 case 'E':
2349 if (pszName[1] != 'x' && pszName[1] != 'X')
2350 return K_FALSE;
2351 if (pszName[2] != 't' && pszName[2] != 'T')
2352 return K_FALSE;
2353 break;
2354 }
2355 if (pszName[3] != '-')
2356 return K_FALSE;
2357 if (pszName[4] != 'm' && pszName[4] != 'M')
2358 return K_FALSE;
2359 if (pszName[5] != 's' && pszName[5] != 'S')
2360 return K_FALSE;
2361 if (pszName[6] != '-')
2362 return K_FALSE;
2363 return K_TRUE;
2364}
2365
2366
2367/**
2368 * Try load what seems to be a virtual API DLL.
2369 *
2370 * This is a worker for kwLdrModuleResolveAndLookup and
2371 * kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule.
2372 *
2373 * @returns Pointer to module on success, NULL on failure.
2374 * @param pszName The name of the module. This must be
2375 * normalized already!
2376 * @param cchName The length of the name.
2377 */
2378static PKWMODULE kwLdrModuleTryLoadVirtualDll(const char *pszName, KSIZE cchName)
2379{
2380 HMODULE hModule;
2381
2382 /*
2383 * Look it up in the hash table.
2384 */
2385 KU32 const uHashPath = kwStrHash(pszName);
2386 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2387 PKWMODULE pMod = g_apModules[idxHash];
2388 if (pMod)
2389 {
2390 do
2391 {
2392 if ( pMod->uHashPath == uHashPath
2393 && kHlpStrComp(pMod->pszPath, pszName) == 0)
2394 return kwLdrModuleRetain(pMod);
2395 pMod = pMod->pNextHash;
2396 } while (pMod);
2397 }
2398
2399 /*
2400 * Not found. Try load it.
2401 */
2402 hModule = LoadLibraryA(pszName);
2403 if (!hModule)
2404 {
2405 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s failed (%u)\n", pszName, GetLastError()));
2406 return NULL;
2407 }
2408
2409 /*
2410 * Loaded successfully. Create a module for the real module.
2411 */
2412 pMod = kwLdrModuleForLoadedNativeByHandle(hModule, K_FALSE /*fEnsureCrtSlot*/, pszName);
2413 if (pMod)
2414 {
2415 /* Create a module for the virtual API name too, unless it is actually a real DLL. */
2416 if (stricmp(&pMod->pszPath[pMod->offFilename], pszName) != 0)
2417 {
2418 PKLDRMOD pLdrMod;
2419 int rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2420 if (rc == 0)
2421 {
2422 PKWMODULE pVirtMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszName, cchName + 1, kwStrHash(pszName),
2423 K_FALSE /*fDoReplacements*/, pMod /*pVirtualApiMod*/);
2424 if (pVirtMod)
2425 {
2426 kwLdrModuleRelease(pMod);
2427 pMod = pVirtMod;
2428 }
2429 else
2430 {
2431 kLdrModClose(pLdrMod);
2432 kwErrPrintf("out of memory\n");
2433 }
2434 }
2435 else
2436 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
2437 }
2438 else
2439 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s -> %s - A real DLL!\n", pszName, pMod->pszPath));
2440 }
2441
2442 return pMod;
2443}
2444
2445
2446/**
2447 * Sets up the quick zero & copy tables for the non-native module.
2448 *
2449 * This is a worker for kwLdrModuleCreateNonNative.
2450 *
2451 * @param pMod The module.
2452 */
2453static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2454{
2455 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2456 KU32 cSegs = pMod->pLdrMod->cSegments;
2457 KU32 iSeg;
2458
2459 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2460 pMod->u.Manual.cQuickCopyChunks = 0;
2461 pMod->u.Manual.cQuickZeroChunks = 0;
2462
2463 for (iSeg = 0; iSeg < cSegs; iSeg++)
2464 switch (paSegs[iSeg].enmProt)
2465 {
2466 case KPROT_READWRITE:
2467 case KPROT_WRITECOPY:
2468 case KPROT_EXECUTE_READWRITE:
2469 case KPROT_EXECUTE_WRITECOPY:
2470 if (paSegs[iSeg].cbMapped)
2471 {
2472 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2473 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2474 {
2475 /*
2476 * Check for trailing zero words.
2477 */
2478 KSIZE cbTrailingZeros;
2479 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2480 && (paSegs[iSeg].cbMapped & 7) == 0
2481 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2482 {
2483 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2484 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2485 KSIZE idxFirstZero = cNatural;
2486 while (idxFirstZero > 0)
2487 if (pauNatural[--idxFirstZero] == 0)
2488 { /* likely */ }
2489 else
2490 {
2491 idxFirstZero++;
2492 break;
2493 }
2494 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2495 if (cbTrailingZeros < 128)
2496 cbTrailingZeros = 0;
2497 }
2498 else
2499 cbTrailingZeros = 0;
2500
2501 /*
2502 * Add quick copy entry.
2503 */
2504 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2505 {
2506 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2507 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2508 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2509 pMod->u.Manual.cQuickCopyChunks = (KU8)(iChunk + 1);
2510 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2511 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2512 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2513 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2514 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2515 }
2516
2517 /*
2518 * Add quick zero entry.
2519 */
2520 if (cbTrailingZeros)
2521 {
2522 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2523 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2524 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2525 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2526 pMod->u.Manual.cQuickZeroChunks = (KU8)(iZero + 1);
2527 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2528 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2529 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2530 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2531 }
2532 }
2533 else
2534 {
2535 /*
2536 * We're out of quick copy table entries, so just copy the whole darn thing.
2537 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2538 */
2539 kHlpAssertFailed();
2540 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2541 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2542 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2543 pMod->u.Manual.cQuickCopyChunks = 1;
2544 KWLDR_LOG(("Quick copy not possible!\n"));
2545 return;
2546 }
2547 }
2548 break;
2549
2550 default:
2551 break;
2552 }
2553}
2554
2555
2556/**
2557 * Called from the TLS allocation DLL when ever the native loader wants to issue
2558 * a TLS callback after the initial kwLdrTlsAllocationHook callout.
2559 *
2560 * @param hDll The DLL handle.
2561 * @param dwReason The callback reason.
2562 * @param pvContext Some context value that seems to always be NULL.
2563 * @param pMod Out internal module.
2564 */
2565static void kwLdrTlsNativeLoaderCallback(void *hDll, DWORD dwReason, void *pvContext, PKWMODULE pMod)
2566{
2567 if ( pMod
2568 && pMod->u.Manual.enmState == KWMODSTATE_READY)
2569 {
2570 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p\n",
2571 hDll, dwReason, pvContext, pMod));
2572 if (pMod->u.Manual.cTlsCallbacks)
2573 {
2574 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
2575 do
2576 {
2577 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: Calling TLS callback %p(%p, %#x, %p) - %s\n",
2578 *ppfnCallback, pMod->hOurMod, dwReason, pvContext, pMod->pszPath));
2579 (*ppfnCallback)(pMod->hOurMod, dwReason, pvContext);
2580 ppfnCallback++;
2581 } while (*ppfnCallback);
2582 }
2583 }
2584 else
2585 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p - skipped\n",
2586 hDll, dwReason, pvContext, pMod));
2587}
2588
2589
2590/**
2591 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2592 *
2593 * @returns Address of the callback function (kwLdrTlsNativeLoaderCallback).
2594 * @param hDll The DLL handle.
2595 * @param idxTls The allocated TLS index.
2596 * @param pabInitData The init data in the TLS allocation DLL
2597 * (g_abInitData).
2598 * @param ppWorkerModuleVar Pointer to the variable holding the pMod
2599 * callback parameter value (g_pvWorkerModule).
2600 *
2601 * @see KWLDRTLSALLOCATIONHOOK in kWorkerTlsXxxxK.c
2602 */
2603__declspec(dllexport) KUPTR kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, KU8 *pabInitData, PKWMODULE *ppWorkerModuleVar)
2604{
2605 /*
2606 * Do the module initialization thing first.
2607 */
2608 PKWMODULE pMod = g_pModPendingTlsAlloc;
2609 if (pMod)
2610 {
2611 if ( pMod->u.Manual.idxTls == KU32_MAX
2612 && pMod->u.Manual.pabTlsInitData == NULL)
2613 {
2614 pMod->u.Manual.idxTls = idxTls;
2615 pMod->u.Manual.pabTlsInitData = pabInitData;
2616 pMod->u.Manual.ppTlsWorkerModuleVar = ppWorkerModuleVar;
2617 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2618
2619#if 0 /** @todo this doesn't work W10 18363 */
2620 {
2621 /*
2622 * Try sabotage the DLL name so we can load this module again.
2623 */
2624 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2625 LIST_ENTRY *pHead;
2626 LIST_ENTRY *pCur;
2627
2628 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2629 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2630 {
2631 LDR_DATA_TABLE_ENTRY *pMte;
2632 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2633 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2634 {
2635 PUNICODE_STRING pStr = &pMte->FullDllName;
2636 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2637 pStr->Buffer[--off]++;
2638 pStr->Buffer[--off]++;
2639 pStr->Buffer[--off]++;
2640 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2641 break;
2642 }
2643 }
2644 }
2645#endif
2646
2647 /*
2648 * Don't return a callback function unless the module has callbacks to service.
2649 */
2650 if (pMod->u.Manual.cTlsCallbacks > 0)
2651 {
2652 *ppWorkerModuleVar = pMod;
2653 return (KUPTR)kwLdrTlsNativeLoaderCallback;
2654 }
2655 return 0;
2656 }
2657 KWLDR_LOG(("kwLdrTlsAllocationHook: WTF? pMod=%p: idxTls=%#x pabTlsInitData=%p\n",
2658 pMod, pMod->u.Manual.idxTls, pMod->u.Manual.pabTlsInitData));
2659 }
2660 return 0;
2661}
2662
2663
2664/**
2665 * Allocates and initializes TLS variables.
2666 *
2667 * @returns 0 on success, non-zero failure.
2668 * @param pMod The module.
2669 */
2670static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2671{
2672 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2673 IMAGE_NT_HEADERS const *pNtHdrs;
2674 IMAGE_DATA_DIRECTORY const *pTlsDir;
2675
2676 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2677 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2678 else
2679 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2680 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2681
2682 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2683 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2684 {
2685 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2686 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2687 KU32 iEntry;
2688 KU32 iTlsDll;
2689 KU32 iTlsDllSub;
2690 KUPTR offIndex;
2691 KUPTR offCallbacks;
2692 KUPTR const *puCallbacks;
2693 KSIZE cbData;
2694 const wchar_t *pwszTlsDll;
2695 HMODULE hmodTlsDll;
2696
2697 /*
2698 * Check and log.
2699 */
2700 for (iEntry = 0; iEntry < cEntries; iEntry++)
2701 {
2702 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2703 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2704 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2705 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2706 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2707 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2708 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2709
2710 if (offIndex >= pMod->cbImage)
2711 {
2712 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2713 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2714 return -1;
2715 }
2716 if (offCallbacks >= pMod->cbImage)
2717 {
2718 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2719 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2720 return -1;
2721 }
2722 while (*puCallbacks != 0)
2723 {
2724 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2725 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2726 puCallbacks++;
2727 }
2728 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2729 {
2730 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2731 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2732 return -1;
2733 }
2734 }
2735
2736 if (cEntries > 1)
2737 {
2738 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2739 return -1;
2740 }
2741
2742 /*
2743 * Make the allocation by loading a new instance of one of the TLS dlls.
2744 * The DLL will make a call to kwLdrTlsAllocationHook.
2745 */
2746 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2747 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2748 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2749 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2750
2751 /** @todo find better strategy here. Like temporary copy or whatever when
2752 * there is more than a single user. */
2753 for (iTlsDll = 0; cbData > g_aTlsDlls[iTlsDll].cbTls;)
2754 if (++iTlsDll >= K_ELEMENTS(g_aTlsDlls))
2755 {
2756 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2757 return -1;
2758 }
2759 for (iTlsDllSub = 0; g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed;)
2760 if (++iTlsDllSub >= g_aTlsDlls[iTlsDll].cDlls)
2761 {
2762 kwErrPrintf("No unused TLS DLLs for %s of size %u!\n", pMod->pszPath, (unsigned)cbData);
2763 return -1;
2764 }
2765
2766 g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed = K_TRUE;
2767 pwszTlsDll = g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].pwszName;
2768
2769 pMod->u.Manual.pabTlsInitData = NULL;
2770 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2771 pMod->u.Manual.idxTls = KU32_MAX;
2772
2773 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2774 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2775 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2776 pMod->u.Manual.cTlsCallbacks = 0;
2777 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2778 pMod->u.Manual.cTlsCallbacks++;
2779 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2780
2781 g_pModPendingTlsAlloc = pMod;
2782 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2783 g_pModPendingTlsAlloc = NULL;
2784 if (hmodTlsDll == NULL)
2785 {
2786 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2787 return -1;
2788 }
2789 if (pMod->u.Manual.idxTls == KU32_MAX)
2790 {
2791 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2792 return -1;
2793 }
2794
2795 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2796 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x pabTlsInitData=%p\n",
2797 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData, pMod->u.Manual.pabTlsInitData));
2798
2799 kHlpAssert(pMod->u.Manual.pabTlsInitData);
2800 if (pMod->u.Manual.pabTlsInitData && pMod->u.Manual.cbTlsInitData)
2801 kHlpMemCopy(pMod->u.Manual.pabTlsInitData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData],
2802 pMod->u.Manual.cbTlsInitData);
2803 }
2804 return 0;
2805}
2806
2807
2808/**
2809 * Creates a module using the our own loader.
2810 *
2811 * @returns Module w/ 1 reference on success, NULL on failure.
2812 * @param pszPath The normalized path to the module.
2813 * @param uHashPath The module path hash.
2814 * @param fExe K_TRUE if this is an executable image, K_FALSE
2815 * if not. Executable images does not get entered
2816 * into the global module table.
2817 * @param pExeMod The executable module of the process (for
2818 * resolving imports). NULL if fExe is set.
2819 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2820 */
2821static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2822 PKWMODULE pExeMod, const char *pszSearchPath)
2823{
2824 /*
2825 * Open the module and check the type.
2826 */
2827 PKLDRMOD pLdrMod;
2828 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2829 if (rc == 0)
2830 {
2831 switch (pLdrMod->enmType)
2832 {
2833 case KLDRTYPE_EXECUTABLE_FIXED:
2834 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2835 case KLDRTYPE_EXECUTABLE_PIC:
2836 if (!fExe)
2837 rc = KERR_GENERAL_FAILURE;
2838 break;
2839
2840 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2841 case KLDRTYPE_SHARED_LIBRARY_PIC:
2842 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2843 if (fExe)
2844 rc = KERR_GENERAL_FAILURE;
2845 break;
2846
2847 default:
2848 rc = KERR_GENERAL_FAILURE;
2849 break;
2850 }
2851 if (rc == 0)
2852 {
2853 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2854 if (cImports >= 0)
2855 {
2856 /*
2857 * Create the entry.
2858 */
2859 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2860 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2861 + sizeof(pMod) * cImports
2862 + cbPath
2863 + cbPath * 2 * sizeof(wchar_t));
2864 if (pMod)
2865 {
2866 KBOOL fFixed;
2867
2868 pMod->cRefs = 1;
2869 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2870 pMod->uHashPath = uHashPath;
2871 pMod->fExe = fExe;
2872 pMod->fNative = K_FALSE;
2873 pMod->pLdrMod = pLdrMod;
2874 pMod->iCrtSlot = KU8_MAX;
2875 pMod->fNeedReInit = K_FALSE;
2876 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2877 pMod->pszMsPdbSrvEndpoint = NULL;
2878 pMod->u.Manual.cImpMods = (KU32)cImports;
2879#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2880 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2881#endif
2882 pMod->u.Manual.fUseLdBuf = K_FALSE;
2883 pMod->u.Manual.fCanDoQuick = K_FALSE;
2884 pMod->u.Manual.cQuickZeroChunks = 0;
2885 pMod->u.Manual.cQuickCopyChunks = 0;
2886 pMod->u.Manual.pabTlsInitData = NULL;
2887 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2888 pMod->u.Manual.idxTls = KU32_MAX;
2889 pMod->u.Manual.offTlsInitData = KU32_MAX;
2890 pMod->u.Manual.cbTlsInitData = 0;
2891 pMod->u.Manual.cbTlsAlloc = 0;
2892 pMod->u.Manual.cTlsCallbacks = 0;
2893 pMod->u.Manual.offTlsCallbacks = 0;
2894 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2895 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2896 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2897 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2898
2899 /*
2900 * Figure out where to load it and get memory there.
2901 */
2902 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2903 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2904 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2905 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2906 if ( !fFixed
2907 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2908 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2909 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2910 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2911 else
2912 pMod->u.Manual.fUseLdBuf = K_TRUE;
2913 if (rc == 0)
2914 {
2915 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2916 if (rc == 0)
2917 {
2918 KI32 iImp;
2919 KU32 cchReloads;
2920
2921 /*
2922 * Link the module (unless it's an executable image) and process the imports.
2923 */
2924 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2925 kwLdrModuleLink(pMod);
2926 KWLDR_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2927 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2928 KWLDR_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2929 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2930 cchReloads = g_cchReloads;
2931 if (cchReloads + 80 < sizeof(g_szReloads))
2932 {
2933 cchReloads += _snprintf(&g_szReloads[cchReloads], sizeof(g_szReloads) - cchReloads,
2934 "%s.reload /f %s=%p\n", cchReloads ? "; " : "",
2935 pMod->pszPath, pMod->u.Manual.pbLoad);
2936 g_cchReloads = cchReloads;
2937 }
2938
2939 for (iImp = 0; iImp < cImports; iImp++)
2940 {
2941 char szName[1024];
2942 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2943 if (rc == 0)
2944 {
2945 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2946 &pMod->u.Manual.apImpMods[iImp]);
2947 if (rc == 0)
2948 continue;
2949 }
2950 break;
2951 }
2952
2953 if (rc == 0)
2954 {
2955 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2956 kwLdrModuleGetImportCallback, pMod);
2957 if (rc == 0)
2958 {
2959#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2960 /*
2961 * Find the function table. No validation here because the
2962 * loader did that already, right...
2963 */
2964 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2965 IMAGE_NT_HEADERS const *pNtHdrs;
2966 IMAGE_DATA_DIRECTORY const *pXcptDir;
2967 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2968 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2969 else
2970 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2971 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2972 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2973 if (pXcptDir->Size > 0)
2974 {
2975 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2976 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2977 == pXcptDir->Size);
2978 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2979 }
2980 else
2981 {
2982 pMod->u.Manual.cFunctions = 0;
2983 pMod->u.Manual.paFunctions = NULL;
2984 }
2985#endif
2986
2987 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2988
2989 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2990 if (rc == 0)
2991 {
2992 /*
2993 * Final finish.
2994 */
2995 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2996 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2997 pMod->u.Manual.enmReInitState = KWMODSTATE_NEEDS_BITS;
2998 if ( g_Sandbox.pTool
2999 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
3000 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3001 && !pMod->fExe)
3002 pMod->u.Manual.enmReInitState = KWMODSTATE_READY;
3003 g_cModules++;
3004 g_cNonNativeModules++;
3005 return pMod;
3006 }
3007 }
3008 else
3009 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
3010 }
3011
3012 kwLdrModuleRelease(pMod);
3013 return NULL;
3014 }
3015
3016 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
3017 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3018 }
3019 else if (fFixed)
3020 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
3021 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
3022 else
3023 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3024 }
3025 }
3026 }
3027 kLdrModClose(pLdrMod);
3028 }
3029 else
3030 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
3031 return NULL;
3032}
3033
3034
3035/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
3036static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
3037 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
3038{
3039 PKWMODULE pCurMod = (PKWMODULE)pvUser;
3040 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
3041 int rc;
3042 K_NOREF(pMod);
3043
3044 if (pImpMod->fNative)
3045 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
3046 iSymbol, pchSymbol, cchSymbol, pszVersion,
3047 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3048 puValue, pfKind);
3049 else
3050 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
3051 iSymbol, pchSymbol, cchSymbol, pszVersion,
3052 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3053 puValue, pfKind);
3054 if (rc == 0)
3055 {
3056 KU32 i = g_cSandboxReplacements;
3057 while (i-- > 0)
3058 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
3059 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
3060 {
3061 if ( !g_aSandboxReplacements[i].pszModule
3062 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
3063 {
3064 if ( pCurMod->fExe
3065 || !g_aSandboxReplacements[i].fOnlyExe)
3066 {
3067 KWLDR_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
3068 if (!g_aSandboxReplacements[i].fCrtSlotArray)
3069 *puValue = g_aSandboxReplacements[i].pfnReplacement;
3070 else
3071 {
3072 if (pImpMod->iCrtSlot == KU8_MAX)
3073 {
3074 rc = kwLdrModuleCreateCrtSlot(pImpMod);
3075 if (rc)
3076 KWLDR_LOG(("kwLdrModuleGetImportCallback: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
3077 }
3078 *puValue = ((KUPTR *)g_aSandboxReplacements[i].pfnReplacement)[pImpMod->iCrtSlot];
3079 }
3080 }
3081 break;
3082 }
3083 }
3084 }
3085
3086 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
3087 KWLDR_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
3088 return rc;
3089
3090}
3091
3092
3093/**
3094 * Gets the main entrypoint for a module.
3095 *
3096 * @returns 0 on success, KERR on failure
3097 * @param pMod The module.
3098 * @param puAddrMain Where to return the address.
3099 */
3100static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
3101{
3102 KLDRADDR uLdrAddrMain;
3103 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
3104 if (rc == 0)
3105 {
3106 *puAddrMain = (KUPTR)uLdrAddrMain;
3107 return 0;
3108 }
3109 return rc;
3110}
3111
3112
3113/**
3114 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
3115 *
3116 * @returns K_TRUE/K_FALSE.
3117 * @param pszFilename The filename (no path).
3118 * @param enmLocation The location.
3119 */
3120static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
3121{
3122 if (enmLocation != KWLOCATION_SYSTEM32)
3123 return K_TRUE;
3124 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3125 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3126 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3127 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3128 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3129 || kHlpStrNICompAscii(pszFilename, TUPLE("api-ms-win-crt-")) == 0
3130#if 0 /* for debugging, only for debugging. */
3131 || kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3132 || kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3133 || kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3134#endif
3135 ;
3136}
3137
3138
3139/**
3140 * Lazily initializes the g_pWinSys32 variable.
3141 */
3142static PKFSDIR kwLdrResolveWinSys32(void)
3143{
3144 KFSLOOKUPERROR enmError;
3145 PKFSDIR pWinSys32;
3146
3147 /* Get the path first. */
3148 char szSystem32[MAX_PATH];
3149 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
3150 {
3151 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
3152 strcpy(szSystem32, "C:\\Windows\\System32");
3153 }
3154
3155 /* Look it up and verify it. */
3156 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
3157 if (pWinSys32)
3158 {
3159 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
3160 {
3161 g_pWinSys32 = pWinSys32;
3162 return pWinSys32;
3163 }
3164
3165 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
3166 }
3167 else
3168 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
3169 return NULL;
3170}
3171
3172
3173/**
3174 * Whether we can load this DLL natively or not.
3175 *
3176 * @returns K_TRUE/K_FALSE.
3177 * @param pszFilename The filename (no path).
3178 * @param enmLocation The location.
3179 * @param pszFullPath The full filename and path.
3180 */
3181static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
3182{
3183 if (enmLocation == KWLOCATION_SYSTEM32)
3184 return K_TRUE;
3185 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
3186 return K_TRUE;
3187 if ( enmLocation == KWLOCATION_UNKNOWN
3188 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3189 return K_TRUE;
3190
3191 /* If the location is unknown, we must check if it's some dynamic loading
3192 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
3193 if (enmLocation == KWLOCATION_UNKNOWN)
3194 {
3195 PKFSDIR pWinSys32 = g_pWinSys32;
3196 if (!pWinSys32)
3197 pWinSys32 = kwLdrResolveWinSys32();
3198 if (pWinSys32)
3199 {
3200 KFSLOOKUPERROR enmError;
3201 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
3202 if (pFsObj)
3203 {
3204 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
3205 kFsCacheObjRelease(g_pFsCache, pFsObj);
3206 if (fInWinSys32)
3207 return K_TRUE;
3208 }
3209 }
3210 }
3211
3212 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3213 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3214 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3215 || kHlpStrNICompAscii(pszFilename, TUPLE("tbbmalloc")) == 0
3216 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3217 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3218 || ( enmLocation != KWLOCATION_UNKNOWN
3219 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3220#if 0 /* for debugging, only for debugging. */
3221 //|| kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3222 //|| kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3223 //|| kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3224#endif
3225 ;
3226}
3227
3228
3229/**
3230 * Check if the path leads to a regular file (that exists).
3231 *
3232 * @returns K_TRUE / K_FALSE
3233 * @param pszPath Path to the file to check out.
3234 */
3235static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
3236{
3237 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
3238 KSIZE cchPath = kHlpStrLen(pszPath);
3239 if ( cchPath > 3
3240 && pszPath[cchPath - 4] == '.'
3241 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
3242 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
3243 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
3244 {
3245 KFSLOOKUPERROR enmError;
3246 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3247 if (pFsObj)
3248 {
3249 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
3250 kFsCacheObjRelease(g_pFsCache, pFsObj);
3251 return fRc;
3252 }
3253 }
3254 else
3255 {
3256 BirdStat_T Stat;
3257 int rc = birdStatFollowLink(pszPath, &Stat);
3258 if (rc == 0)
3259 {
3260 if (S_ISREG(Stat.st_mode))
3261 return K_TRUE;
3262 }
3263 }
3264 return K_FALSE;
3265}
3266
3267
3268/**
3269 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
3270 *
3271 * If the file exists, we consult the module hash table before trying to load it
3272 * off the disk.
3273 *
3274 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
3275 * failure.
3276 * @param pszPath The name of the import module.
3277 * @param enmLocation The location we're searching. This is used in
3278 * the heuristics for determining if we can use the
3279 * native loader or need to sandbox the DLL.
3280 * @param pExe The executable (optional).
3281 * @param pszSearchPath The PATH to search (optional).
3282 */
3283static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
3284{
3285 /*
3286 * Does the file exists and is it a regular file?
3287 */
3288 if (kwLdrModuleIsRegularFile(pszPath))
3289 {
3290 /*
3291 * Yes! Normalize it and look it up in the hash table.
3292 */
3293 char szNormPath[1024];
3294 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
3295 if (rc == 0)
3296 {
3297 const char *pszName;
3298 KU32 const uHashPath = kwStrHash(szNormPath);
3299 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3300 PKWMODULE pMod = g_apModules[idxHash];
3301 if (pMod)
3302 {
3303 do
3304 {
3305 if ( pMod->uHashPath == uHashPath
3306 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3307 return kwLdrModuleRetain(pMod);
3308 pMod = pMod->pNextHash;
3309 } while (pMod);
3310 }
3311
3312 /*
3313 * Not in the hash table, so we have to load it from scratch.
3314 */
3315 pszName = kHlpGetFilename(szNormPath);
3316 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
3317 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
3318 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
3319 else
3320 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
3321 if (pMod)
3322 return pMod;
3323 return (PKWMODULE)~(KUPTR)0;
3324 }
3325 }
3326 return NULL;
3327}
3328
3329
3330/**
3331 * Gets a reference to the module by the given name.
3332 *
3333 * We must do the search path thing, as our hash table may multiple DLLs with
3334 * the same base name due to different tools version and similar. We'll use a
3335 * modified search sequence, though. No point in searching the current
3336 * directory for instance.
3337 *
3338 * @returns 0 on success, KERR on failure.
3339 * @param pszName The name of the import module.
3340 * @param pExe The executable (optional).
3341 * @param pImporter The module doing the importing (optional).
3342 * @param pszSearchPath The PATH to search (optional).
3343 * @param ppMod Where to return the module pointer w/ reference.
3344 */
3345static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
3346 const char *pszSearchPath, PKWMODULE *ppMod)
3347{
3348 KSIZE const cchName = kHlpStrLen(pszName);
3349 char szPath[1024];
3350 char *psz;
3351 PKWMODULE pMod = NULL;
3352 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
3353 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
3354
3355 /* Virtual API module. Normalize and try load it. */
3356 if (pMod == NULL && cchName > 7 && kwLdrIsVirtualApiModule(pszName, cchName))
3357 {
3358 if (cchName + cchSuffix >= sizeof(szPath))
3359 return KERR_BUFFER_OVERFLOW;
3360 kHlpMemCopy(szPath, pszName, cchName);
3361 if (fNeedSuffix)
3362 kHlpMemCopy(&szPath[cchName], ".dll", sizeof(".dll"));
3363 szPath[cchName + cchSuffix] = '\0';
3364 _strlwr(szPath);
3365 pMod = kwLdrModuleTryLoadVirtualDll(szPath, cchName + cchSuffix);
3366 }
3367
3368 /* The import path. */
3369 if (pMod == NULL && pImporter != NULL)
3370 {
3371 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
3372 return KERR_BUFFER_OVERFLOW;
3373
3374 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
3375 if (fNeedSuffix)
3376 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3377 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
3378 }
3379
3380 /* Application directory first. */
3381 if (pMod == NULL && pExe != NULL && pExe != pImporter)
3382 {
3383 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
3384 return KERR_BUFFER_OVERFLOW;
3385 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
3386 if (fNeedSuffix)
3387 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3388 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
3389 }
3390
3391 /* The windows directory. */
3392 if (pMod == NULL)
3393 {
3394 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
3395 if ( cchDir <= 2
3396 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
3397 return KERR_BUFFER_OVERFLOW;
3398 szPath[cchDir++] = '\\';
3399 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
3400 if (fNeedSuffix)
3401 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3402 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3403 }
3404
3405 /* The path. */
3406 if ( pMod == NULL
3407 && pszSearchPath)
3408 {
3409 const char *pszCur = pszSearchPath;
3410 while (*pszCur != '\0')
3411 {
3412 /* Find the end of the component */
3413 KSIZE cch = 0;
3414 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
3415 cch++;
3416
3417 if ( cch > 0 /* wrong, but whatever */
3418 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
3419 {
3420 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
3421 if ( szPath[cch - 1] != ':'
3422 && szPath[cch - 1] != '/'
3423 && szPath[cch - 1] != '\\')
3424 *pszDst++ = '\\';
3425 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
3426 if (fNeedSuffix)
3427 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
3428 *pszDst = '\0';
3429
3430 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3431 if (pMod)
3432 break;
3433 }
3434
3435 /* Advance */
3436 pszCur += cch;
3437 while (*pszCur == ';')
3438 pszCur++;
3439 }
3440 }
3441
3442 /* Return. */
3443 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3444 {
3445 *ppMod = pMod;
3446 return 0;
3447 }
3448 *ppMod = NULL;
3449 return KERR_GENERAL_FAILURE;
3450}
3451
3452
3453/**
3454 * Creates a CRT slot for the given module.
3455 *
3456 * @returns 0 on success, non-zero on failure.
3457 * @param pModule The module.
3458 */
3459static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3460{
3461 KSIZE iSlot;
3462 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3463 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3464 if (g_aCrtSlots[iSlot].pModule == NULL)
3465 {
3466 KLDRADDR uAddr;
3467 int rc;
3468
3469 /* Do the linking: */
3470 g_aCrtSlots[iSlot].pModule = pModule;
3471 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3472 pModule->iCrtSlot = (KU8)iSlot;
3473
3474 /* resolve symbols: */
3475 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3476 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3477 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3478 if (rc != 0)
3479 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3480
3481 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "_beginthreadex", 14,
3482 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3483 *(KUPTR *)&g_aCrtSlots[iSlot].pfnBeginThreadEx = rc == 0 ? (KUPTR)uAddr : 0;
3484 //if (rc != 0)
3485 // kwErrPrintf("Failed to resolved '_beginthreadex' in '%s': %d\n", pModule->pszPath, rc);
3486
3487 return 0;
3488 }
3489 kwErrPrintf("Out of CRT slots!\n");
3490 return KERR_NO_MEMORY;
3491}
3492
3493
3494/**
3495 * Locates the module structure for an already loaded native module.
3496 *
3497 * This will create a module structure if needed.
3498 *
3499 * @returns Pointer to the module structure on success, NULL on failure.
3500 * @param hModule The native module handle.
3501 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3502 * @param pszLogName The name to use for logging/errors.
3503 */
3504static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName)
3505{
3506 /*
3507 * Get a normalized path for it.
3508 */
3509 char szModPath[1024];
3510 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3511 {
3512 char szNormPath[1024];
3513 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3514 if (rc == 0)
3515 {
3516 /*
3517 * Hash the path and look it up.
3518 */
3519 KU32 uHashPath;
3520 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3521 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3522 PKWMODULE pMod = g_apModules[idxHash];
3523 if (pMod)
3524 {
3525 do
3526 {
3527 if ( pMod->uHashPath == uHashPath
3528 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3529 {
3530 kwLdrModuleRetain(pMod);
3531 break;
3532 }
3533 pMod = pMod->pNextHash;
3534 } while (pMod);
3535 }
3536
3537 /*
3538 * If not in the hash table, so create a module entry.
3539 */
3540 if (!pMod)
3541 {
3542 PKLDRMOD pLdrMod;
3543 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3544 if (rc == 0)
3545 {
3546 /** @todo more accurately determine location */
3547 const char *pszFilename = kHlpGetFilename(szNormPath);
3548 KBOOL fDoReplacements = kwLdrModuleShouldDoNativeReplacements(pszFilename, KWLOCATION_SYSTEM32);
3549 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3550 fDoReplacements, NULL /*pVirtualApiMod*/);
3551 if (!pMod)
3552 {
3553 kLdrModClose(pLdrMod);
3554 kwErrPrintf("out of memory\n");
3555 }
3556 }
3557 else
3558 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszLogName, rc);
3559 }
3560 if (pMod)
3561 {
3562 /*
3563 * Create a CRT slot for the module if necessary.
3564 */
3565 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3566 return pMod;
3567 rc = kwLdrModuleCreateCrtSlot(pMod);
3568 if (rc == 0)
3569 return pMod;
3570 kwLdrModuleRelease(pMod);
3571 }
3572 }
3573 else
3574 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszLogName, GetLastError());
3575 }
3576 else
3577 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszLogName, GetLastError());
3578 return NULL;
3579}
3580
3581
3582/**
3583 * Locates the module structure for an already loaded native module.
3584 *
3585 * This will create a module structure if needed.
3586 *
3587 * @returns Pointer to the module structure on success, NULL on failure.
3588 * @param pszName The name of the module.
3589 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3590 * @param fAlwaysPresent Whether the module is expected to always be present,
3591 * or not. If not, complain less.
3592 */
3593static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3594{
3595 /*
3596 * Locate the module handle and pass it to kwLdrModuleForLoadedNativeByHandle.
3597 */
3598 HANDLE hModule = GetModuleHandleA(pszName);
3599 if (hModule)
3600 return kwLdrModuleForLoadedNativeByHandle(hModule, fEnsureCrtSlot, pszName);
3601 if (fAlwaysPresent)
3602 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3603 return NULL;
3604}
3605
3606
3607/**
3608 * Does the TLS memory initialization for a module on the current thread.
3609 *
3610 * @returns 0 on success, error on failure.
3611 * @param pMod The module.
3612 */
3613static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3614{
3615 if (pMod->u.Manual.idxTls != KU32_MAX)
3616 {
3617 PTEB pTeb = NtCurrentTeb();
3618 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3619 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3620 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3621 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3622 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3623 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3624 if (pMod->u.Manual.cbTlsInitData)
3625 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3626 }
3627 return 0;
3628}
3629
3630
3631/**
3632 * Does the TLS callbacks for a module.
3633 *
3634 * @param pMod The module.
3635 * @param dwReason The callback reason.
3636 */
3637static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3638{
3639 if (pMod->u.Manual.cTlsCallbacks)
3640 {
3641 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3642 do
3643 {
3644 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *ppfnCallback, pMod->hOurMod, dwReason));
3645 (*ppfnCallback)(pMod->hOurMod, dwReason, 0);
3646 } while (*++ppfnCallback);
3647 }
3648}
3649
3650
3651/**
3652 * Does module initialization starting at @a pMod.
3653 *
3654 * This is initially used on the executable. Later it is used by the
3655 * LoadLibrary interceptor.
3656 *
3657 * @returns 0 on success, error on failure.
3658 * @param pMod The module to initialize.
3659 */
3660static int kwLdrModuleInitTree(PKWMODULE pMod)
3661{
3662 int rc = 0;
3663 if (!pMod->fNative)
3664 {
3665 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3666 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3667
3668 /*
3669 * Need to copy bits?
3670 */
3671 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3672 {
3673 if (pMod->u.Manual.fUseLdBuf)
3674 {
3675#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3676 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3677 {
3678 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3679 kHlpAssert(fRc); K_NOREF(fRc);
3680 }
3681#endif
3682 g_pModPrevInLdBuf = g_pModInLdBuf;
3683 g_pModInLdBuf = pMod;
3684 }
3685
3686 /* Do quick zeroing and copying when we can. */
3687 pMod->u.Manual.fCanDoQuick = K_FALSE;
3688 if ( pMod->u.Manual.fCanDoQuick
3689 && ( !pMod->u.Manual.fUseLdBuf
3690 || g_pModPrevInLdBuf == pMod))
3691 {
3692 /* Zero first. */
3693 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3694 switch (pMod->u.Manual.cQuickZeroChunks)
3695 {
3696 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3697 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3698 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3699 case 0: break;
3700 }
3701
3702 /* Then copy. */
3703 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3704 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3705 switch (pMod->u.Manual.cQuickCopyChunks)
3706 {
3707 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3708 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3709 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3710 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3711 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3712 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3713 case 0: break;
3714 }
3715 }
3716 /* Must copy the whole image. */
3717 else
3718 {
3719 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3720 pMod->u.Manual.fCanDoQuick = K_TRUE;
3721 }
3722 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3723 }
3724
3725#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3726 /*
3727 * Need to register function table?
3728 */
3729 if ( !pMod->u.Manual.fRegisteredFunctionTable
3730 && pMod->u.Manual.cFunctions > 0)
3731 {
3732 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3733 pMod->u.Manual.cFunctions,
3734 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3735 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3736 }
3737#endif
3738
3739
3740 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3741 {
3742 /*
3743 * Must do imports first, but mark our module as being initialized to avoid
3744 * endless recursion should there be a dependency loop.
3745 */
3746 KSIZE iImp;
3747 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3748
3749 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3750 {
3751 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3752 if (rc != 0)
3753 return rc;
3754 }
3755
3756 /* Do TLS allocations for module init? */
3757 rc = kwLdrCallTlsAllocateAndInit(pMod);
3758 if (rc != 0)
3759 return rc;
3760 if (pMod->u.Manual.cTlsCallbacks > 0)
3761 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3762
3763 /* Finally call the entry point. */
3764 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3765 if (rc == 0)
3766 pMod->u.Manual.enmState = KWMODSTATE_READY;
3767 else
3768 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3769 }
3770 }
3771 /*
3772 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3773 * _MSPDBSRV_ENDPOINT_ changes value.
3774 */
3775 else if (pMod->fNeedReInit)
3776 {
3777 int rc2;
3778 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3779 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3780 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3781 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3782 if (!rc && !rc2)
3783 { /* likely */ }
3784 else
3785 {
3786 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3787 if (rc2 && !rc)
3788 rc = rc2;
3789 }
3790 pMod->fNeedReInit = K_FALSE;
3791 }
3792 return rc;
3793}
3794
3795
3796/**
3797 * Looks up a module handle for a tool.
3798 *
3799 * @returns Referenced loader module on success, NULL on if not found.
3800 * @param pTool The tool.
3801 * @param hmod The module handle.
3802 */
3803static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3804{
3805 KUPTR const uHMod = (KUPTR)hmod;
3806 PKWMODULE *papMods;
3807 KU32 iEnd;
3808 KU32 i;
3809 PKWDYNLOAD pDynLoad;
3810
3811 if (pTool)
3812 { /* likely */ }
3813 else
3814 return NULL;
3815
3816 /* The executable. */
3817 if ( hmod == NULL
3818 || (pTool->u.Sandboxed.pExe && pTool->u.Sandboxed.pExe->hOurMod == hmod))
3819 {
3820 if (pTool->u.Sandboxed.pExe)
3821 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3822 return NULL;
3823 }
3824
3825 /*
3826 * Binary lookup using the module table.
3827 */
3828 papMods = pTool->u.Sandboxed.papModules;
3829 iEnd = pTool->u.Sandboxed.cModules;
3830 if (iEnd)
3831 {
3832 KU32 iStart = 0;
3833 i = iEnd / 2;
3834 for (;;)
3835 {
3836 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
3837 if (uHMod < uHModCur)
3838 {
3839 iEnd = i--;
3840 if (iStart <= i)
3841 { }
3842 else
3843 break;
3844 }
3845 else if (uHMod != uHModCur)
3846 {
3847 iStart = ++i;
3848 if (i < iEnd)
3849 { }
3850 else
3851 break;
3852 }
3853 /* We've got a match. Always return the non-virtual module (first) when there is one. */
3854 else if (!papMods[i]->pVirtualApiMod)
3855 return kwLdrModuleRetain(papMods[i]);
3856 else
3857 {
3858 while (i > 0 && papMods[i - 1]->pVirtualApiMod && papMods[i - 1]->hOurMod == hmod)
3859 i--;
3860 return kwLdrModuleRetain(papMods[i]);
3861 }
3862
3863 i = iStart + (iEnd - iStart) / 2;
3864 }
3865
3866#ifndef NDEBUG
3867 iStart = pTool->u.Sandboxed.cModules;
3868 while (--iStart > 0)
3869 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3870 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3871#endif
3872 }
3873
3874 /*
3875 * Dynamically loaded images.
3876 */
3877 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3878 if (pDynLoad->hmod == hmod)
3879 {
3880 if (pDynLoad->pMod)
3881 return kwLdrModuleRetain(pDynLoad->pMod);
3882 KWFS_TODO();
3883 return NULL;
3884 }
3885
3886 return NULL;
3887}
3888
3889/**
3890 * Adds the given module to the tool import table.
3891 *
3892 * @returns 0 on success, non-zero on failure.
3893 * @param pTool The tool.
3894 * @param pMod The module.
3895 */
3896static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3897{
3898 /*
3899 * Binary lookup. Locating the right slot for it, return if already there.
3900 */
3901 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3902 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3903 KU32 iEnd = pTool->u.Sandboxed.cModules;
3904 KU32 i;
3905 if (iEnd)
3906 {
3907 KU32 iStart = 0;
3908 i = iEnd / 2;
3909 for (;;)
3910 {
3911 PKWMODULE pCurMod = papMods[i];
3912 KUPTR const uHModCur = (KUPTR)pCurMod->hOurMod;
3913 if (uHMod < uHModCur)
3914 {
3915 iEnd = i;
3916 if (iStart < i)
3917 { }
3918 else
3919 break;
3920 }
3921 else if (uHMod != uHModCur)
3922 {
3923 iStart = ++i;
3924 if (i < iEnd)
3925 { }
3926 else
3927 break;
3928 }
3929 else
3930 {
3931 /* Already there in the table. The non-virtual module must be the first
3932 entry if we've got duplicate hmod values because of virtual modules. */
3933 if (pMod != pCurMod)
3934 {
3935 /* Skip to the last module with the same hmod. */
3936 while (i + 1 < iEnd && (KUPTR)(pCurMod = papMods[i + 1])->hOurMod == uHMod)
3937 {
3938 if (pMod == pCurMod)
3939 return 0;
3940 i++;
3941 }
3942
3943 /* Then scan backwards till the first one. */
3944 while (i > 0 && (KUPTR)(pCurMod = papMods[i - 1])->hOurMod == uHMod)
3945 {
3946 if (pMod == pCurMod)
3947 return 0;
3948 i--;
3949 }
3950 pCurMod = papMods[i];
3951 if (pMod != pCurMod)
3952 {
3953 if (pMod->pVirtualApiMod && !pCurMod->pVirtualApiMod)
3954 i++;
3955 break;
3956 }
3957 }
3958 return 0;
3959 }
3960
3961 i = iStart + (iEnd - iStart) / 2;
3962 }
3963#ifndef NDEBUG
3964 iStart = pTool->u.Sandboxed.cModules;
3965 while (--iStart > 0)
3966 {
3967 kHlpAssert(papMods[iStart] != pMod);
3968 kHlpAssert( (KUPTR)papMods[iStart]->hOurMod != uHMod
3969 || pMod->pVirtualApiMod
3970 || papMods[iStart]->pVirtualApiMod);
3971 }
3972 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod <= uHMod);
3973 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod >= uHMod);
3974#endif
3975 }
3976 else
3977 i = 0;
3978
3979 /*
3980 * Grow the table?
3981 */
3982 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3983 {
3984 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3985 if (!pvNew)
3986 return KERR_NO_MEMORY;
3987 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3988 }
3989
3990 /* Insert it. */
3991 if (i != pTool->u.Sandboxed.cModules)
3992 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3993 papMods[i] = kwLdrModuleRetain(pMod);
3994 pTool->u.Sandboxed.cModules++;
3995 KWLDR_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3996 return 0;
3997}
3998
3999
4000/**
4001 * Adds the given module and all its imports to the
4002 *
4003 * @returns 0 on success, non-zero on failure.
4004 * @param pTool The tool.
4005 * @param pMod The module.
4006 */
4007static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
4008{
4009 int rc = kwToolAddModule(pTool, pMod);
4010 if (pMod->pVirtualApiMod && rc == 0)
4011 rc = kwToolAddModule(pTool, pMod->pVirtualApiMod);
4012 if (!pMod->fNative && rc == 0)
4013 {
4014 KSIZE iImp = pMod->u.Manual.cImpMods;
4015 while (iImp-- > 0)
4016 {
4017 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
4018 if (rc == 0)
4019 { }
4020 else
4021 break;
4022 }
4023 }
4024
4025 return 0;
4026}
4027
4028
4029/**
4030 * Creates a tool entry and inserts it.
4031 *
4032 * @returns Pointer to the tool entry. NULL on failure.
4033 * @param pToolFsObj The file object of the tool. The created tool
4034 * will be associated with it.
4035 *
4036 * A reference is donated by the caller and must be
4037 * released.
4038 * @param pszSearchPath The PATH environment variable value, or NULL.
4039 */
4040static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
4041{
4042 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
4043 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
4044 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
4045 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
4046 if (pTool)
4047 {
4048 KBOOL fRc;
4049 pTool->pwszPath = (wchar_t const *)(pTool + 1);
4050 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
4051 kHlpAssert(fRc); K_NOREF(fRc);
4052
4053 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
4054 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
4055 kHlpAssert(fRc);
4056
4057 pTool->enmType = KWTOOLTYPE_SANDBOXED;
4058 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
4059 NULL /*pEexeMod*/, pszSearchPath);
4060 if (pTool->u.Sandboxed.pExe)
4061 {
4062 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
4063 if (rc == 0)
4064 {
4065 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
4066 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
4067 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
4068 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
4069 else
4070 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
4071 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
4072 }
4073 else
4074 {
4075 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
4076 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
4077 pTool->u.Sandboxed.pExe = NULL;
4078 pTool->enmType = KWTOOLTYPE_EXEC;
4079 }
4080 }
4081 else
4082 pTool->enmType = KWTOOLTYPE_EXEC;
4083
4084 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4085 g_cTools++;
4086 return pTool;
4087 }
4088 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4089 return NULL;
4090}
4091
4092
4093/**
4094 * Looks up the given tool, creating a new tool table entry if necessary.
4095 *
4096 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
4097 * @param pszExe The executable for the tool (not normalized).
4098 * @param cEnvVars Number of environment varibles.
4099 * @param papszEnvVars Environment variables. For getting the PATH.
4100 */
4101static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
4102{
4103 /*
4104 * We associate the tools instances with the file system objects.
4105 *
4106 * We'd like to do the lookup without invaliding the volatile parts of the
4107 * cache, thus the double lookup here. The cache gets invalidate later on.
4108 */
4109 KFSLOOKUPERROR enmError;
4110 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4111 if ( !pToolFsObj
4112 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
4113 {
4114 kFsCacheInvalidateCustomBoth(g_pFsCache);
4115 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4116 }
4117 if (pToolFsObj)
4118 {
4119 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
4120 {
4121 const char *pszSearchPath;
4122 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
4123 if (pTool)
4124 {
4125 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4126 return pTool;
4127 }
4128
4129 /*
4130 * Need to create a new tool.
4131 */
4132 pszSearchPath = NULL;
4133 while (cEnvVars-- > 0)
4134 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
4135 {
4136 pszSearchPath = &papszEnvVars[cEnvVars][5];
4137 break;
4138 }
4139
4140 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
4141 if (pTool)
4142 return pTool;
4143
4144 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
4145 }
4146 else
4147 {
4148 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4149 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
4150 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
4151 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
4152 }
4153 }
4154 else
4155 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
4156 return NULL;
4157}
4158
4159
4160
4161/*
4162 *
4163 * File system cache.
4164 * File system cache.
4165 * File system cache.
4166 *
4167 */
4168
4169
4170/**
4171 * This is for kDep.
4172 */
4173int kwFsPathExists(const char *pszPath)
4174{
4175 BirdTimeSpec_T TsIgnored;
4176 KFSLOOKUPERROR enmError;
4177 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
4178 if (pFsObj)
4179 {
4180 kFsCacheObjRelease(g_pFsCache, pFsObj);
4181 return 1;
4182 }
4183 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
4184}
4185
4186
4187/* duplicated in dir-nt-bird.c */
4188void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
4189{
4190 KFSLOOKUPERROR enmError;
4191 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
4192 if (pPathObj)
4193 {
4194 KSIZE off = pPathObj->cchParent;
4195 if (off > 0)
4196 {
4197 KSIZE offEnd = off + pPathObj->cchName;
4198 if (offEnd < cbFull)
4199 {
4200 PKFSDIR pAncestor;
4201
4202 pszFull[off + pPathObj->cchName] = '\0';
4203 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
4204
4205 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4206 {
4207 kHlpAssert(off > 1);
4208 kHlpAssert(pAncestor != NULL);
4209 kHlpAssert(pAncestor->Obj.cchName > 0);
4210 pszFull[--off] = '/';
4211 off -= pAncestor->Obj.cchName;
4212 kHlpAssert(pAncestor->Obj.cchParent == off);
4213 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4214 }
4215 kFsCacheObjRelease(g_pFsCache, pPathObj);
4216 return;
4217 }
4218 }
4219 else
4220 {
4221 if ((size_t)pPathObj->cchName + 1 < cbFull)
4222 {
4223 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
4224 pszFull[pPathObj->cchName] = '/';
4225 pszFull[pPathObj->cchName + 1] = '\0';
4226
4227 kFsCacheObjRelease(g_pFsCache, pPathObj);
4228 return;
4229 }
4230 }
4231
4232 /* do fallback. */
4233 kHlpAssertFailed();
4234 kFsCacheObjRelease(g_pFsCache, pPathObj);
4235 }
4236
4237 nt_fullpath(pszPath, pszFull, cbFull);
4238}
4239
4240
4241/**
4242 * Helper for getting the extension of a UTF-16 path.
4243 *
4244 * @returns Pointer to the extension or the terminator.
4245 * @param pwszPath The path.
4246 * @param pcwcExt Where to return the length of the extension.
4247 */
4248static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
4249{
4250 wchar_t const *pwszName = pwszPath;
4251 wchar_t const *pwszExt = NULL;
4252 for (;;)
4253 {
4254 wchar_t const wc = *pwszPath++;
4255 if (wc == '.')
4256 pwszExt = pwszPath;
4257 else if (wc == '/' || wc == '\\' || wc == ':')
4258 {
4259 pwszName = pwszPath;
4260 pwszExt = NULL;
4261 }
4262 else if (wc == '\0')
4263 {
4264 if (pwszExt)
4265 {
4266 *pcwcExt = pwszPath - pwszExt - 1;
4267 return pwszExt;
4268 }
4269 *pcwcExt = 0;
4270 return pwszPath - 1;
4271 }
4272 }
4273}
4274
4275
4276
4277/**
4278 * Parses the argument string passed in as pszSrc.
4279 *
4280 * @returns size of the processed arguments.
4281 * @param pszSrc Pointer to the commandline that's to be parsed.
4282 * @param pcArgs Where to return the number of arguments.
4283 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
4284 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
4285 *
4286 * @remarks Lifted from startuphacks-win.c
4287 */
4288static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
4289{
4290 int bs;
4291 char chQuote;
4292 char *pfFlags;
4293 int cbArgs;
4294 int cArgs;
4295
4296#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
4297#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
4298#define WHITE(c) ((c) == ' ' || (c) == '\t')
4299
4300#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
4301#define _ARG_RESPONSE 0x02 /* Argument read from response file */
4302#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
4303#define _ARG_ENV 0x08 /* Argument from environment */
4304#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
4305
4306 cArgs = 0;
4307 cbArgs = 0;
4308
4309#if 0
4310 /* argv[0] */
4311 PUTC((char)_ARG_NONZERO);
4312 PUTV;
4313 for (;;)
4314 {
4315 PUTC(*pszSrc);
4316 if (*pszSrc == 0)
4317 break;
4318 ++pszSrc;
4319 }
4320 ++pszSrc;
4321#endif
4322
4323 for (;;)
4324 {
4325 while (WHITE(*pszSrc))
4326 ++pszSrc;
4327 if (*pszSrc == 0)
4328 break;
4329 pfFlags = pchPool;
4330 PUTC((unsigned char)_ARG_NONZERO);
4331 PUTV;
4332 bs = 0; chQuote = 0;
4333 for (;;)
4334 {
4335 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
4336 {
4337 while (bs >= 2)
4338 {
4339 PUTC('\\');
4340 bs -= 2;
4341 }
4342 if (bs & 1)
4343 PUTC(*pszSrc);
4344 else
4345 {
4346 chQuote = chQuote ? 0 : *pszSrc;
4347 if (pfFlags != NULL)
4348 *pfFlags |= _ARG_DQUOTE;
4349 }
4350 bs = 0;
4351 }
4352 else if (*pszSrc == '\\')
4353 ++bs;
4354 else
4355 {
4356 while (bs != 0)
4357 {
4358 PUTC('\\');
4359 --bs;
4360 }
4361 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
4362 break;
4363 PUTC(*pszSrc);
4364 }
4365 ++pszSrc;
4366 }
4367 PUTC(0);
4368 }
4369
4370 *pcArgs = cArgs;
4371 return cbArgs;
4372}
4373
4374
4375
4376
4377/*
4378 *
4379 * Process and thread related APIs.
4380 * Process and thread related APIs.
4381 * Process and thread related APIs.
4382 *
4383 */
4384
4385/** Common worker for ExitProcess(), exit() and friends. */
4386static void WINAPI kwSandboxDoExit(int uExitCode)
4387{
4388 if (g_Sandbox.idMainThread == GetCurrentThreadId())
4389 {
4390 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
4391
4392 g_Sandbox.rcExitCode = (int)uExitCode;
4393
4394 /* Before we jump, restore the TIB as we're not interested in any
4395 exception chain stuff installed by the sandboxed executable. */
4396 *pTib = g_Sandbox.TibMainThread;
4397 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
4398
4399 longjmp(g_Sandbox.JmpBuf, 1);
4400 }
4401 KWFS_TODO();
4402}
4403
4404
4405/** ExitProcess replacement. */
4406static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
4407{
4408 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
4409 kwSandboxDoExit((int)uExitCode);
4410}
4411
4412
4413/** ExitProcess replacement. */
4414static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
4415{
4416 if (hProcess == GetCurrentProcess())
4417 kwSandboxDoExit(uExitCode);
4418 KWFS_TODO();
4419 return TerminateProcess(hProcess, uExitCode);
4420}
4421
4422
4423/** Normal CRT exit(). */
4424static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
4425{
4426 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
4427 kwSandboxDoExit(rcExitCode);
4428}
4429
4430
4431/** Quick CRT _exit(). */
4432static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
4433{
4434 /* Quick. */
4435 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
4436 kwSandboxDoExit(rcExitCode);
4437}
4438
4439
4440/** Return to caller CRT _cexit(). */
4441static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
4442{
4443 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
4444 kwSandboxDoExit(rcExitCode);
4445}
4446
4447
4448/** Quick return to caller CRT _c_exit(). */
4449static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
4450{
4451 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
4452 kwSandboxDoExit(rcExitCode);
4453}
4454
4455
4456/** Runtime error and exit _amsg_exit(). */
4457static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
4458{
4459 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
4460 kwSandboxDoExit(255);
4461}
4462
4463
4464/** CRT - terminate(). */
4465static void __cdecl kwSandbox_msvcrt_terminate(void)
4466{
4467 KW_LOG(("\nRuntime - terminate!\n"));
4468 kwSandboxDoExit(254);
4469}
4470
4471
4472/** CRT - _onexit */
4473static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
4474{
4475 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4476 {
4477 PKWEXITCALLACK pCallback;
4478 KW_LOG(("_onexit(%p)\n", pfnFunc));
4479 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4480
4481 pCallback = kHlpAlloc(sizeof(*pCallback));
4482 if (pCallback)
4483 {
4484 pCallback->pfnCallback = pfnFunc;
4485 pCallback->fAtExit = K_FALSE;
4486 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4487 g_Sandbox.pExitCallbackHead = pCallback;
4488 return pfnFunc;
4489 }
4490 return NULL;
4491 }
4492 //KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
4493 //return pfnFunc;
4494}
4495
4496
4497/** CRT - atexit */
4498static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
4499{
4500 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4501 {
4502 PKWEXITCALLACK pCallback;
4503 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4504 KW_LOG(("atexit(%p)\n", pfnFunc));
4505
4506 pCallback = kHlpAlloc(sizeof(*pCallback));
4507 if (pCallback)
4508 {
4509 pCallback->pfnCallback = (_onexit_t)pfnFunc;
4510 pCallback->fAtExit = K_TRUE;
4511 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4512 g_Sandbox.pExitCallbackHead = pCallback;
4513 return 0;
4514 }
4515 return -1;
4516 }
4517 //KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4518 //return 0;
4519}
4520
4521
4522/** Kernel32 - SetConsoleCtrlHandler(). */
4523static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4524{
4525 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4526 return TRUE;
4527}
4528
4529
4530/** The CRT internal __getmainargs() API. */
4531static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4532 int dowildcard, int const *piNewMode)
4533{
4534 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4535 *pargc = g_Sandbox.cArgs;
4536 *pargv = g_Sandbox.papszArgs;
4537 *penvp = g_Sandbox.environ;
4538
4539 /** @todo startinfo points at a newmode (setmode) value. */
4540 return 0;
4541}
4542
4543
4544/** The CRT internal __wgetmainargs() API. */
4545static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4546 int dowildcard, int const *piNewMode)
4547{
4548 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4549 *pargc = g_Sandbox.cArgs;
4550 *pargv = g_Sandbox.papwszArgs;
4551 *penvp = g_Sandbox.wenviron;
4552
4553 /** @todo startinfo points at a newmode (setmode) value. */
4554 return 0;
4555}
4556
4557
4558
4559/** Kernel32 - GetCommandLineA() */
4560static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4561{
4562 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4563 return g_Sandbox.pszCmdLine;
4564}
4565
4566
4567/** Kernel32 - GetCommandLineW() */
4568static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4569{
4570 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4571 return g_Sandbox.pwszCmdLine;
4572}
4573
4574
4575/** Kernel32 - GetStartupInfoA() */
4576static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4577{
4578 KW_LOG(("GetStartupInfoA\n"));
4579 GetStartupInfoA(pStartupInfo);
4580 pStartupInfo->lpReserved = NULL;
4581 pStartupInfo->lpTitle = NULL;
4582 pStartupInfo->lpReserved2 = NULL;
4583 pStartupInfo->cbReserved2 = 0;
4584}
4585
4586
4587/** Kernel32 - GetStartupInfoW() */
4588static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4589{
4590 KW_LOG(("GetStartupInfoW\n"));
4591 GetStartupInfoW(pStartupInfo);
4592 pStartupInfo->lpReserved = NULL;
4593 pStartupInfo->lpTitle = NULL;
4594 pStartupInfo->lpReserved2 = NULL;
4595 pStartupInfo->cbReserved2 = 0;
4596}
4597
4598
4599/** CRT - __p___argc(). */
4600static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4601{
4602 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4603 return &g_Sandbox.cArgs;
4604}
4605
4606
4607/** CRT - __p___argv(). */
4608static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4609{
4610 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4611 return &g_Sandbox.papszArgs;
4612}
4613
4614
4615/** CRT - __p___sargv(). */
4616static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4617{
4618 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4619 return &g_Sandbox.papwszArgs;
4620}
4621
4622
4623/** CRT - __p__acmdln(). */
4624static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4625{
4626 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4627 return (char **)&g_Sandbox.pszCmdLine;
4628}
4629
4630
4631/** CRT - __p__acmdln(). */
4632static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4633{
4634 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4635 return &g_Sandbox.pwszCmdLine;
4636}
4637
4638
4639/** CRT - __p__pgmptr(). */
4640static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4641{
4642 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4643 return &g_Sandbox.pgmptr;
4644}
4645
4646
4647/** CRT - __p__wpgmptr(). */
4648static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4649{
4650 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4651 return &g_Sandbox.wpgmptr;
4652}
4653
4654
4655/** CRT - _get_pgmptr(). */
4656static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4657{
4658 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4659 *ppszValue = g_Sandbox.pgmptr;
4660 return 0;
4661}
4662
4663
4664/** CRT - _get_wpgmptr(). */
4665static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4666{
4667 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4668 *ppwszValue = g_Sandbox.wpgmptr;
4669 return 0;
4670}
4671
4672/** Just in case. */
4673static void kwSandbox_msvcrt__wincmdln(void)
4674{
4675 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4676 KWFS_TODO();
4677}
4678
4679
4680/** Just in case. */
4681static void kwSandbox_msvcrt__wwincmdln(void)
4682{
4683 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4684 KWFS_TODO();
4685}
4686
4687/** CreateThread interceptor. */
4688static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4689 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4690 DWORD fFlags, PDWORD pidThread)
4691{
4692 HANDLE hThread = NULL;
4693 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4694 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4695 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4696 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4697 {
4698 /* Allow link::DbgThread. */
4699 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4700 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4701 }
4702 else
4703 KWFS_TODO();
4704 return hThread;
4705}
4706
4707
4708/** _beginthread - create a new thread. */
4709static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4710{
4711 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4712 KWFS_TODO();
4713 return 0;
4714}
4715
4716
4717/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4718static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4719 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4720 unsigned fCreate, unsigned *pidThread)
4721{
4722 /*
4723 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4724 * whatever it needs to.
4725 */
4726 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4727 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4728 pfnThreadProc, pvUser, fCreate, pidThread));
4729 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4730 {
4731 uintptr_t rcRet;
4732 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4733 if (!s_pfnReal)
4734 {
4735 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4736 if (!s_pfnReal)
4737 {
4738 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4739 __debugbreak();
4740 }
4741 }
4742 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4743 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4744 return rcRet;
4745 }
4746
4747 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4748 KWFS_TODO();
4749 return 0;
4750}
4751
4752
4753/** _beginthreadex - create a new thread. */
4754static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex_wrapped(void *pvSecAttr, unsigned cbStack,
4755 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4756 unsigned fCreate, unsigned *pidThread, PKWCRTSLOT pSlot)
4757{
4758 /*
4759 * Since the VC++ 12 (VS 2013) compiler, the 2nd pass is now threaded.
4760 * Let it do whatever it needs to.
4761 */
4762 KW_LOG(("kwSandbox_msvcrt__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4763 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4764 pfnThreadProc, pvUser, fCreate, pidThread));
4765 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4766 && pSlot->pfnBeginThreadEx)
4767 {
4768 uintptr_t rcRet = pSlot->pfnBeginThreadEx(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4769 KW_LOG(("kwSandbox_msvcrt__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4770 return rcRet;
4771 }
4772
4773 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4774 KWFS_TODO();
4775 return 0;
4776}
4777
4778CRT_SLOT_FUNCTION_WRAPPER(uintptr_t __cdecl, kwSandbox_msvcrt__beginthreadex,
4779 (void *pvSecAttr, unsigned cbStack, unsigned (__stdcall *pfnThreadProc)(void *),
4780 void *pvUser, unsigned fCreate, unsigned *pidThread),
4781 (pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread, &g_aCrtSlots[iCrtSlot]));
4782
4783
4784
4785/*
4786 *
4787 * Environment related APIs.
4788 * Environment related APIs.
4789 * Environment related APIs.
4790 *
4791 */
4792
4793/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4794static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4795{
4796 char *pszzEnv;
4797 char *pszCur;
4798 KSIZE cbNeeded = 1;
4799 KSIZE iVar = 0;
4800
4801 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4802
4803 /* Figure how space much we need first. */
4804 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4805 cbNeeded += kHlpStrLen(pszCur) + 1;
4806
4807 /* Allocate it. */
4808 pszzEnv = kHlpAlloc(cbNeeded);
4809 if (pszzEnv)
4810 {
4811 char *psz = pszzEnv;
4812 iVar = 0;
4813 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4814 {
4815 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4816 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4817 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4818 }
4819 *psz++ = '\0';
4820 kHlpAssert((KUPTR)(psz - pszzEnv) == cbNeeded);
4821 }
4822
4823 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4824#if 0
4825 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4826 pszCur = pszzEnv;
4827 iVar = 0;
4828 while (*pszCur)
4829 {
4830 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4831 iVar++;
4832 pszCur += kHlpStrLen(pszCur) + 1;
4833 }
4834 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4835 pszCur++;
4836 fprintf(stderr, "ended at %p, after %u bytes (expected %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4837#endif
4838 return pszzEnv;
4839}
4840
4841
4842/** Kernel32 - GetEnvironmentStrings */
4843static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4844{
4845 KW_LOG(("GetEnvironmentStrings!\n"));
4846 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4847}
4848
4849
4850/** Kernel32 - GetEnvironmentStringsW */
4851static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4852{
4853 wchar_t *pwszzEnv;
4854 wchar_t *pwszCur;
4855 KSIZE cwcNeeded = 1;
4856 KSIZE iVar = 0;
4857
4858 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4859
4860 /* Figure how space much we need first. */
4861 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4862 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4863
4864 /* Allocate it. */
4865 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4866 if (pwszzEnv)
4867 {
4868 wchar_t *pwsz = pwszzEnv;
4869 iVar = 0;
4870 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4871 {
4872 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4873 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4874 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4875 }
4876 *pwsz++ = '\0';
4877 kHlpAssert((KUPTR)(pwsz - pwszzEnv) == cwcNeeded);
4878 }
4879
4880 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4881 return pwszzEnv;
4882}
4883
4884
4885/** Kernel32 - FreeEnvironmentStringsA */
4886static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4887{
4888 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4889 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4890 kHlpFree(pszzEnv);
4891 return TRUE;
4892}
4893
4894
4895/** Kernel32 - FreeEnvironmentStringsW */
4896static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4897{
4898 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4899 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4900 kHlpFree(pwszzEnv);
4901 return TRUE;
4902}
4903
4904
4905/**
4906 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4907 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4908 *
4909 * @returns 0 on success, non-zero on failure.
4910 * @param pSandbox The sandbox.
4911 * @param cMin Minimum size, including terminator.
4912 */
4913static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4914{
4915 void *pvNew;
4916 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4917 KSIZE cNew = cOld + 256;
4918 while (cNew < cMin)
4919 cNew += 256;
4920
4921 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4922 if (pvNew)
4923 {
4924 pSandbox->environ = (char **)pvNew;
4925 pSandbox->environ[cOld] = NULL;
4926
4927 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4928 if (pvNew)
4929 {
4930 pSandbox->papszEnvVars = (char **)pvNew;
4931 pSandbox->papszEnvVars[cOld] = NULL;
4932
4933 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4934 if (pvNew)
4935 {
4936 pSandbox->wenviron = (wchar_t **)pvNew;
4937 pSandbox->wenviron[cOld] = NULL;
4938
4939 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4940 if (pvNew)
4941 {
4942 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4943 pSandbox->papwszEnvVars[cOld] = NULL;
4944
4945 pSandbox->cEnvVarsAllocated = cNew;
4946 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4947 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4948 return 0;
4949 }
4950 }
4951 }
4952 }
4953 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4954 return KERR_NO_MEMORY;
4955}
4956
4957
4958/**
4959 * Sets an environment variable, ANSI style.
4960 *
4961 * @returns 0 on success, non-zero on failure.
4962 * @param pSandbox The sandbox.
4963 * @param pchVar The variable name.
4964 * @param cchVar The length of the name.
4965 * @param pszValue The value.
4966 */
4967static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4968{
4969 /* Allocate and construct the new strings. */
4970 KSIZE cchTmp = kHlpStrLen(pszValue);
4971 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4972 if (pszNew)
4973 {
4974 wchar_t *pwszNew;
4975 kHlpMemCopy(pszNew, pchVar, cchVar);
4976 pszNew[cchVar] = '=';
4977 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4978 cchTmp += cchVar + 1;
4979 pszNew[cchTmp] = '\0';
4980
4981 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4982 if (pwszNew)
4983 {
4984 /* Look it up. */
4985 KSIZE iVar = 0;
4986 char *pszEnv;
4987 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4988 {
4989 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4990 && pszEnv[cchVar] == '=')
4991 {
4992 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4993 " iVar=%d: %p='%s' and %p='%ls'\n",
4994 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4995 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4996 iVar, pszNew, pszNew, pwszNew, pwszNew));
4997
4998 kHlpFree(pSandbox->papszEnvVars[iVar]);
4999 pSandbox->papszEnvVars[iVar] = pszNew;
5000 pSandbox->environ[iVar] = pszNew;
5001
5002 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5003 pSandbox->papwszEnvVars[iVar] = pwszNew;
5004 pSandbox->wenviron[iVar] = pwszNew;
5005 return 0;
5006 }
5007 iVar++;
5008 }
5009
5010 /* Not found, do we need to grow the table first? */
5011 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5012 kwSandboxGrowEnv(pSandbox, iVar + 2);
5013 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5014 {
5015 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5016
5017 pSandbox->papszEnvVars[iVar + 1] = NULL;
5018 pSandbox->papszEnvVars[iVar] = pszNew;
5019 pSandbox->environ[iVar + 1] = NULL;
5020 pSandbox->environ[iVar] = pszNew;
5021
5022 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5023 pSandbox->papwszEnvVars[iVar] = pwszNew;
5024 pSandbox->wenviron[iVar + 1] = NULL;
5025 pSandbox->wenviron[iVar] = pwszNew;
5026 return 0;
5027 }
5028
5029 kHlpFree(pwszNew);
5030 }
5031 kHlpFree(pszNew);
5032 }
5033 KW_LOG(("Out of memory!\n"));
5034 return 0;
5035}
5036
5037
5038/**
5039 * Sets an environment variable, UTF-16 style.
5040 *
5041 * @returns 0 on success, non-zero on failure.
5042 * @param pSandbox The sandbox.
5043 * @param pwcVar The variable name.
5044 * @param cwcVar The length of the name.
5045 * @param pwszValue The value.
5046 */
5047static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
5048{
5049 /* Allocate and construct the new strings. */
5050 KSIZE cwcTmp = kwUtf16Len(pwszValue);
5051 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
5052 if (pwszNew)
5053 {
5054 char *pszNew;
5055 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
5056 pwszNew[cwcVar] = '=';
5057 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
5058 cwcTmp += cwcVar + 1;
5059 pwszNew[cwcVar] = '\0';
5060
5061 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
5062 if (pszNew)
5063 {
5064 /* Look it up. */
5065 KSIZE iVar = 0;
5066 wchar_t *pwszEnv;
5067 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5068 {
5069 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
5070 && pwszEnv[cwcVar] == '=')
5071 {
5072 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5073 " iVar=%d: %p='%s' and %p='%ls'\n",
5074 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5075 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5076 iVar, pszNew, pszNew, pwszNew, pwszNew));
5077
5078 kHlpFree(pSandbox->papszEnvVars[iVar]);
5079 pSandbox->papszEnvVars[iVar] = pszNew;
5080 pSandbox->environ[iVar] = pszNew;
5081
5082 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5083 pSandbox->papwszEnvVars[iVar] = pwszNew;
5084 pSandbox->wenviron[iVar] = pwszNew;
5085 return 0;
5086 }
5087 iVar++;
5088 }
5089
5090 /* Not found, do we need to grow the table first? */
5091 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5092 kwSandboxGrowEnv(pSandbox, iVar + 2);
5093 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5094 {
5095 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5096
5097 pSandbox->papszEnvVars[iVar + 1] = NULL;
5098 pSandbox->papszEnvVars[iVar] = pszNew;
5099 pSandbox->environ[iVar + 1] = NULL;
5100 pSandbox->environ[iVar] = pszNew;
5101
5102 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5103 pSandbox->papwszEnvVars[iVar] = pwszNew;
5104 pSandbox->wenviron[iVar + 1] = NULL;
5105 pSandbox->wenviron[iVar] = pwszNew;
5106 return 0;
5107 }
5108
5109 kHlpFree(pwszNew);
5110 }
5111 kHlpFree(pszNew);
5112 }
5113 KW_LOG(("Out of memory!\n"));
5114 return 0;
5115}
5116
5117
5118/** ANSI unsetenv worker. */
5119static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5120{
5121 KSIZE iVar = 0;
5122 char *pszEnv;
5123 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5124 {
5125 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5126 && pszEnv[cchVar] == '=')
5127 {
5128 KSIZE cVars = iVar;
5129 while (pSandbox->papszEnvVars[cVars])
5130 cVars++;
5131 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
5132 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
5133
5134 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5135 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5136 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5137
5138 kHlpFree(pSandbox->papszEnvVars[iVar]);
5139 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5140 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5141 pSandbox->papszEnvVars[cVars] = NULL;
5142 pSandbox->environ[cVars] = NULL;
5143
5144 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5145 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5146 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5147 pSandbox->papwszEnvVars[cVars] = NULL;
5148 pSandbox->wenviron[cVars] = NULL;
5149 return 0;
5150 }
5151 iVar++;
5152 }
5153 return KERR_ENVVAR_NOT_FOUND;
5154}
5155
5156
5157/** UTF-16 unsetenv worker. */
5158static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5159{
5160 KSIZE iVar = 0;
5161 wchar_t *pwszEnv;
5162 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5163 {
5164 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5165 && pwszEnv[cwcVar] == '=')
5166 {
5167 KSIZE cVars = iVar;
5168 while (pSandbox->papwszEnvVars[cVars])
5169 cVars++;
5170 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
5171 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
5172
5173 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5174 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5175 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5176
5177 kHlpFree(pSandbox->papszEnvVars[iVar]);
5178 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5179 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5180 pSandbox->papszEnvVars[cVars] = NULL;
5181 pSandbox->environ[cVars] = NULL;
5182
5183 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5184 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5185 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5186 pSandbox->papwszEnvVars[cVars] = NULL;
5187 pSandbox->wenviron[cVars] = NULL;
5188 return 0;
5189 }
5190 iVar++;
5191 }
5192 return KERR_ENVVAR_NOT_FOUND;
5193}
5194
5195
5196
5197/** ANSI getenv worker. */
5198static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5199{
5200 KSIZE iVar = 0;
5201 char *pszEnv;
5202 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
5203 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5204 && pszEnv[cchVar] == '=')
5205 return &pszEnv[cchVar + 1];
5206 return NULL;
5207}
5208
5209
5210/** UTF-16 getenv worker. */
5211static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5212{
5213 KSIZE iVar = 0;
5214 wchar_t *pwszEnv;
5215 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
5216 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5217 && pwszEnv[cwcVar] == '=')
5218 return &pwszEnv[cwcVar + 1];
5219 return NULL;
5220}
5221
5222
5223/** Kernel32 - GetEnvironmentVariableA() */
5224static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
5225{
5226 char *pszFoundValue;
5227 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5228
5229 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5230 if (pszFoundValue)
5231 {
5232 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
5233 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
5234 return cchRet;
5235 }
5236 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
5237 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5238 return 0;
5239}
5240
5241
5242/** Kernel32 - GetEnvironmentVariableW() */
5243static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
5244{
5245 wchar_t *pwszFoundValue;
5246 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5247
5248 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5249 if (pwszFoundValue)
5250 {
5251 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
5252 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
5253 return cchRet;
5254 }
5255 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
5256 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5257 return 0;
5258}
5259
5260
5261/** Kernel32 - SetEnvironmentVariableA() */
5262static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
5263{
5264 int rc;
5265 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5266
5267 if (pszValue)
5268 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5269 else
5270 {
5271 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5272 rc = 0; //??
5273 }
5274 if (rc == 0)
5275 {
5276 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
5277 return TRUE;
5278 }
5279 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5280 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
5281 return FALSE;
5282}
5283
5284
5285/** Kernel32 - SetEnvironmentVariableW() */
5286static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
5287{
5288 int rc;
5289 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5290
5291 if (pwszValue)
5292 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5293 else
5294 {
5295 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5296 rc = 0; //??
5297 }
5298 if (rc == 0)
5299 {
5300 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
5301 return TRUE;
5302 }
5303 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5304 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
5305 return FALSE;
5306}
5307
5308
5309/** Kernel32 - ExpandEnvironmentStringsA() */
5310static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
5311{
5312 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5313 KWFS_TODO();
5314 return 0;
5315}
5316
5317
5318/** Kernel32 - ExpandEnvironmentStringsW() */
5319static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
5320{
5321 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5322 KWFS_TODO();
5323 return 0;
5324}
5325
5326
5327/** CRT - _putenv(). */
5328static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
5329{
5330 int rc;
5331 char const *pszEqual;
5332 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5333
5334 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
5335 if (pszEqual)
5336 {
5337 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
5338 if (rc == 0)
5339 { }
5340 else
5341 rc = -1;
5342 }
5343 else
5344 {
5345 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
5346 rc = 0;
5347 }
5348 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
5349 return rc;
5350}
5351
5352
5353/** CRT - _wputenv(). */
5354static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
5355{
5356 int rc;
5357 wchar_t const *pwszEqual;
5358 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5359
5360 pwszEqual = wcschr(pwszVarEqualValue, '=');
5361 if (pwszEqual)
5362 {
5363 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
5364 if (rc == 0)
5365 { }
5366 else
5367 rc = -1;
5368 }
5369 else
5370 {
5371 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
5372 rc = 0;
5373 }
5374 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
5375 return rc;
5376}
5377
5378
5379/** CRT - _putenv_s(). */
5380static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
5381{
5382 char const *pszEqual;
5383 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5384
5385 pszEqual = kHlpStrChr(pszVar, '=');
5386 if (pszEqual == NULL)
5387 {
5388 if (pszValue)
5389 {
5390 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5391 if (rc == 0)
5392 {
5393 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
5394 return 0;
5395 }
5396 }
5397 else
5398 {
5399 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5400 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
5401 return 0;
5402 }
5403 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
5404 return ENOMEM;
5405 }
5406 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
5407 return EINVAL;
5408}
5409
5410
5411/** CRT - _wputenv_s(). */
5412static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
5413{
5414 wchar_t const *pwszEqual;
5415 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5416
5417 pwszEqual = wcschr(pwszVar, '=');
5418 if (pwszEqual == NULL)
5419 {
5420 if (pwszValue)
5421 {
5422 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5423 if (rc == 0)
5424 {
5425 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
5426 return 0;
5427 }
5428 }
5429 else
5430 {
5431 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5432 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
5433 return 0;
5434 }
5435 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
5436 return ENOMEM;
5437 }
5438 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
5439 return EINVAL;
5440}
5441
5442
5443/** CRT - get pointer to the __initenv variable (initial environment). */
5444static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
5445{
5446 KW_LOG(("__p___initenv\n"));
5447 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5448 KWFS_TODO();
5449 return &g_Sandbox.initenv;
5450}
5451
5452
5453/** CRT - get pointer to the __winitenv variable (initial environment). */
5454static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
5455{
5456 KW_LOG(("__p___winitenv\n"));
5457 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5458 KWFS_TODO();
5459 return &g_Sandbox.winitenv;
5460}
5461
5462
5463/** CRT - get pointer to the _environ variable (current environment). */
5464static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
5465{
5466 KW_LOG(("__p__environ\n"));
5467 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5468 return &g_Sandbox.environ;
5469}
5470
5471
5472/** CRT - get pointer to the _wenviron variable (current environment). */
5473static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
5474{
5475 KW_LOG(("__p__wenviron\n"));
5476 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5477 return &g_Sandbox.wenviron;
5478}
5479
5480
5481/** CRT - get the _environ variable (current environment).
5482 * @remarks Not documented or prototyped? */
5483static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
5484{
5485 KWFS_TODO(); /** @todo check the callers expectations! */
5486 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5487 *ppapszEnviron = g_Sandbox.environ;
5488 return 0;
5489}
5490
5491
5492/** CRT - get the _wenviron variable (current environment).
5493 * @remarks Not documented or prototyped? */
5494static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
5495{
5496 KWFS_TODO(); /** @todo check the callers expectations! */
5497 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5498 *ppapwszEnviron = g_Sandbox.wenviron;
5499 return 0;
5500}
5501
5502
5503/** CRT - _wdupenv_s() (see _tdupenv_s(). */
5504static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
5505 PKWCRTSLOT pSlot)
5506{
5507 errno_t rc;
5508 wchar_t *pwszValue;
5509 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5510
5511 if (ppwszValue)
5512 {
5513 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
5514 if (pwszValue)
5515 {
5516 size_t cwcValue = wcslen(pwszValue);
5517 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
5518 if (pwszDst)
5519 {
5520 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
5521 pwszDst[cwcValue] = '\0';
5522 *ppwszValue = pwszDst;
5523 if (pcwcValue)
5524 *pcwcValue = cwcValue;
5525 rc = 0;
5526 }
5527 else
5528 {
5529 *ppwszValue = NULL;
5530 if (pcwcValue)
5531 *pcwcValue = 0;
5532 rc = ENOMEM;
5533 }
5534 }
5535 else
5536 {
5537 *ppwszValue = NULL;
5538 if (pcwcValue)
5539 *pcwcValue = 0;
5540 rc = 0;
5541 }
5542 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5543 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5544 }
5545 else
5546 {
5547 /*
5548 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5549 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5550 * was set to a value.
5551 */
5552 if (pcwcValue)
5553 *pcwcValue = 0;
5554 rc = EINVAL;
5555 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5556 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5557 }
5558 return rc;
5559}
5560CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5561 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5562 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5563
5564
5565
5566/*
5567 *
5568 * Loader related APIs
5569 * Loader related APIs
5570 * Loader related APIs
5571 *
5572 */
5573
5574/**
5575 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5576 */
5577static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5578{
5579 /* Load it first. */
5580 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5581 if (hmod)
5582 {
5583 pDynLoad->hmod = hmod;
5584 pDynLoad->pMod = NULL; /* indicates special */
5585
5586 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5587 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5588 KWLDR_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5589 }
5590 else
5591 kHlpFree(pDynLoad);
5592 return hmod;
5593}
5594
5595
5596/**
5597 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5598 */
5599static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5600{
5601 static const char s_szDll[] = ".dll";
5602 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5603 PKWMODULE pMod;
5604 char szNormPath[256];
5605
5606 /*
5607 * Lower case it and make sure it ends with .dll.
5608 */
5609 if (cbFilename > sizeof(szNormPath))
5610 {
5611 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5612 return NULL;
5613 }
5614 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5615 _strlwr(szNormPath);
5616 kHlpAssert(cbFilename > 7 /* api-ms- */ );
5617 if (strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5618 {
5619 if (cbFilename + sizeof(s_szDll) - 1 > sizeof(szNormPath))
5620 {
5621 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5622 return NULL;
5623 }
5624
5625 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5626 cbFilename += sizeof(s_szDll) - 1;
5627 }
5628
5629 /*
5630 * Try load it.
5631 */
5632 pMod = kwLdrModuleTryLoadVirtualDll(szNormPath, cbFilename - 1);
5633 if (pMod)
5634 {
5635 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5636
5637 pDynLoad->pMod = pMod;
5638 pDynLoad->hmod = pMod->hOurMod;
5639
5640 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5641 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5642 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5643 return pDynLoad->hmod;
5644 }
5645 kHlpFree(pDynLoad);
5646 return NULL;
5647}
5648
5649
5650/** Kernel32 - LoadLibraryExA() */
5651static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5652{
5653 KSIZE cchFilename = kHlpStrLen(pszFilename);
5654 const char *pszSearchPath;
5655 PKWDYNLOAD pDynLoad;
5656 PKWMODULE pMod;
5657 int rc;
5658 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5659 //fprintf(stderr, "LoadLibraryExA: %s, %#x\n", pszFilename, fFlags);
5660
5661 /*
5662 * Deal with a couple of extremely unlikely special cases right away.
5663 */
5664 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5665 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5666 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5667 { /* likely */ }
5668 else
5669 {
5670 KWFS_TODO();
5671 return LoadLibraryExA(pszFilename, hFile, fFlags);
5672 }
5673
5674 /*
5675 * Check if we've already got a dynload entry for this one.
5676 */
5677 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5678 if ( pDynLoad->cchRequest == cchFilename
5679 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5680 {
5681 if (pDynLoad->pMod)
5682 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5683 else
5684 rc = 0;
5685 if (rc == 0)
5686 {
5687 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5688 return pDynLoad->hmod;
5689 }
5690 SetLastError(ERROR_DLL_INIT_FAILED);
5691 return NULL;
5692 }
5693
5694 /*
5695 * Allocate a dynload entry for the request.
5696 */
5697 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5698 if (pDynLoad)
5699 {
5700 pDynLoad->cchRequest = cchFilename;
5701 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5702 }
5703 else
5704 {
5705 KWLDR_LOG(("LoadLibraryExA: Out of memory!\n"));
5706 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5707 return NULL;
5708 }
5709
5710 /*
5711 * Deal with resource / data DLLs.
5712 */
5713 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5714 | LOAD_LIBRARY_AS_DATAFILE
5715 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5716 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5717 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5718
5719 /*
5720 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5721 */
5722 if ( kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5723 && kHlpIsFilenameOnly(pszFilename))
5724 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5725
5726 /*
5727 * Normal library loading.
5728 * We start by being very lazy and reusing the code for resolving imports.
5729 */
5730 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5731 if (!kHlpIsFilenameOnly(pszFilename))
5732 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5733#if 1 /* HACK ALERT! We run into trouble with a 2nd mspdb140.dll instance (x64 + x86), so use the one already loaded. A call
5734 * to NdrClientCall2 at ConnectToServer+0x426 fails with E_INVALIDARG. Problems with multiple connections from same PID? */
5735 else if ( strcmp(pszFilename, "mspdb140.dll") == 0
5736 && GetModuleHandleA(pszFilename) != NULL)
5737 {
5738 pMod = kwLdrModuleForLoadedNativeByHandle(GetModuleHandleA(pszFilename), K_FALSE, pszFilename);
5739 KWLDR_LOG(("LoadLibraryExA: mspdb140 hack: pMod=%p\n", pMod));
5740 }
5741#endif
5742 else
5743 {
5744 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5745 if (rc != 0)
5746 pMod = NULL;
5747 }
5748 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5749 {
5750 /* Enter it into the tool module table and dynamic link request cache. */
5751 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5752
5753 pDynLoad->pMod = pMod;
5754 pDynLoad->hmod = pMod->hOurMod;
5755
5756 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5757 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5758
5759 /*
5760 * Make sure it's initialized (need to link it first since DllMain may
5761 * use loader APIs).
5762 */
5763 rc = kwLdrModuleInitTree(pMod);
5764 if (rc == 0)
5765 {
5766 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5767 return pDynLoad->hmod;
5768 }
5769
5770 SetLastError(ERROR_DLL_INIT_FAILED);
5771 }
5772 else
5773 {
5774 KWFS_TODO();
5775 kHlpFree(pDynLoad);
5776 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5777 }
5778 return NULL;
5779}
5780
5781
5782/** Kernel32 - LoadLibraryExA() for native overloads */
5783static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5784{
5785 char szPath[1024];
5786 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5787
5788 /*
5789 * We may have to help resolved unqualified DLLs living in the executable directory.
5790 */
5791 if ( kHlpIsFilenameOnly(pszFilename)
5792 && g_Sandbox.pTool
5793 && g_Sandbox.pTool->u.Sandboxed.pExe)
5794 {
5795 KSIZE const cchFilename = kHlpStrLen(pszFilename);
5796#define MY_IMATCH(a_szName) (cchFilename == sizeof(a_szName) - 1 && kHlpStrICompAscii(pszFilename, a_szName) == 0)
5797 if ( !kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5798 && !MY_IMATCH("ntdll")
5799 && !MY_IMATCH("kernel32")
5800 && !MY_IMATCH("ntdll.dll")
5801 && !MY_IMATCH("kernelbase")
5802 && !MY_IMATCH("kernel32.dll")
5803 && !MY_IMATCH("kernelbase.dll")
5804 )
5805#undef MY_IMATCH
5806 {
5807 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5808 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5809 {
5810 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5811 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5812 if (kwFsPathExists(szPath))
5813 {
5814 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5815 pszFilename = szPath;
5816 }
5817 }
5818
5819 if (pszFilename != szPath)
5820 {
5821 KSIZE cchSuffix = 0;
5822 KBOOL fNeedSuffix = K_FALSE;
5823 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5824 kHlpAssert(pszCur);
5825 if (pszCur)
5826 {
5827 while (*pszCur != '\0')
5828 {
5829 /* Find the end of the component */
5830 KSIZE cch = 0;
5831 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5832 cch++;
5833
5834 if ( cch > 0 /* wrong, but whatever */
5835 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5836 {
5837 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5838 if ( szPath[cch - 1] != ':'
5839 && szPath[cch - 1] != '/'
5840 && szPath[cch - 1] != '\\')
5841 *pszDst++ = '\\';
5842 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5843 if (fNeedSuffix)
5844 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5845 *pszDst = '\0';
5846
5847 if (kwFsPathExists(szPath))
5848 {
5849 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5850 pszFilename = szPath;
5851 break;
5852 }
5853 }
5854
5855 /* Advance */
5856 pszCur += cch;
5857 while (*pszCur == ';')
5858 pszCur++;
5859 }
5860 }
5861 }
5862 }
5863 }
5864
5865 return LoadLibraryExA(pszFilename, hFile, fFlags);
5866}
5867
5868
5869/** Kernel32 - LoadLibraryExW() for native overloads */
5870static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5871{
5872 char szTmp[4096];
5873 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5874 if (cchTmp < sizeof(szTmp))
5875 return kwSandbox_Kernel32_Native_LoadLibraryExA(szTmp, hFile, fFlags);
5876
5877 KWFS_TODO();
5878 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5879 return NULL;
5880}
5881
5882
5883/** Kernel32 - LoadLibraryExW() */
5884static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5885{
5886 char szTmp[4096];
5887 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5888 if (cchTmp < sizeof(szTmp))
5889 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5890
5891 KWFS_TODO();
5892 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5893 return NULL;
5894}
5895
5896/** Kernel32 - LoadLibraryA() */
5897static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5898{
5899 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5900}
5901
5902
5903/** Kernel32 - LoadLibraryW() */
5904static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5905{
5906 char szTmp[4096];
5907 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5908 if (cchTmp < sizeof(szTmp))
5909 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5910 KWFS_TODO();
5911 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5912 return NULL;
5913}
5914
5915
5916/** Kernel32 - FreeLibrary() */
5917static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5918{
5919 /* Ignored, we like to keep everything loaded. */
5920 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5921 return TRUE;
5922}
5923
5924
5925/** Worker for GetModuleHandleA/W for handling cached modules. */
5926static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5927{
5928 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5929 if (hmod)
5930 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5931 hmod, g_aGetModuleHandleCache[i].pszName));
5932 else
5933 {
5934 /*
5935 * The first time around we have to make sure we have a module table
5936 * entry for it, if not we add one. We need to add it to the tools
5937 * module list to for it to work.
5938 */
5939 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
5940 g_aGetModuleHandleCache[i].fAlwaysPresent);
5941 if (pMod)
5942 {
5943 hmod = pMod->hOurMod;
5944 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5945 {
5946 kwToolAddModule(g_Sandbox.pTool, pMod);
5947 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5948 hmod, g_aGetModuleHandleCache[i].pszName));
5949 }
5950 else
5951 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5952 hmod, g_aGetModuleHandleCache[i].pszName));
5953
5954 }
5955 }
5956 return hmod;
5957}
5958
5959
5960/** Kernel32 - GetModuleHandleA() */
5961static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5962{
5963 KSIZE i;
5964 KSIZE cchModule;
5965 PKWDYNLOAD pDynLoad;
5966 KSIZE cchSuffix;
5967 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5968 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5969
5970 /*
5971 * The executable.
5972 */
5973 if (pszModule == NULL)
5974 {
5975 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5976 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5977 }
5978
5979 /*
5980 * If no path of suffix, pretend it ends with .DLL.
5981 */
5982 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5983
5984 /*
5985 * Cache of system modules we've seen queried.
5986 */
5987 cchModule = kHlpStrLen(pszModule);
5988 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5989 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5990 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5991 || ( cchSuffix > 0
5992 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5993 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5994 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5995 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5996
5997 /*
5998 * Modules we've dynamically loaded.
5999 */
6000 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6001 if (pDynLoad->pMod)
6002 {
6003 const char *pszPath = pDynLoad->pMod->pszPath;
6004 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
6005 if ( stricmp(pszPath, pszModule) == 0
6006 || stricmp(pszName, pszModule) == 0
6007 || ( cchSuffix > 0
6008 && strnicmp(pszName, pszModule, cchModule) == 0
6009 && stricmp(&pszName[cchModule], ".dll") == 0))
6010 {
6011 if ( pDynLoad->pMod->fNative
6012 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6013 {
6014 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
6015 return pDynLoad->hmod;
6016 }
6017 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
6018 SetLastError(ERROR_MOD_NOT_FOUND);
6019 return NULL;
6020 }
6021 }
6022
6023 /*
6024 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6025 * to and go via the g_aGetModuleHandleCache cache.
6026 */
6027/** @todo virtual api DLLs */
6028 if (kHlpStrNICompAscii(pszModule, "api-ms-win-", 11) == 0)
6029 {
6030 HMODULE hmod = GetModuleHandleA(pszModule);
6031 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
6032 if (hmod)
6033 {
6034 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6035 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
6036 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6037 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
6038 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6039 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
6040 if (hmod == GetModuleHandleW(L"UCRTBASE.DLL"))
6041 return kwSandbox_Kernel32_GetModuleHandleA("UCRTBASE.DLL");
6042 }
6043 else
6044 dwErr = GetLastError();
6045 }
6046
6047 kwErrPrintf("pszModule=%s\n", pszModule);
6048 KWFS_TODO();
6049 SetLastError(ERROR_MOD_NOT_FOUND);
6050 return NULL;
6051}
6052
6053
6054/** Kernel32 - GetModuleHandleW() */
6055static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
6056{
6057 KSIZE i;
6058 KSIZE cwcModule;
6059 PKWDYNLOAD pDynLoad;
6060 KSIZE cwcSuffix;
6061 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6062 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6063
6064 /*
6065 * The executable.
6066 */
6067 if (pwszModule == NULL)
6068 {
6069 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
6070 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
6071 }
6072
6073 /*
6074 * If no path of suffix, pretend it ends with .DLL.
6075 */
6076 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
6077
6078 /*
6079 * Cache of system modules we've seen queried.
6080 */
6081 cwcModule = kwUtf16Len(pwszModule);
6082 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6083 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
6084 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
6085 || ( cwcSuffix > 0
6086 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
6087 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
6088 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
6089 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
6090
6091 /*
6092 * Modules we've dynamically loaded.
6093 */
6094 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6095 if (pDynLoad->pMod)
6096 {
6097 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
6098 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
6099 if ( _wcsicmp(pwszPath, pwszModule) == 0
6100 || _wcsicmp(pwszName, pwszModule) == 0
6101 || ( cwcSuffix
6102 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
6103 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
6104 {
6105 if ( pDynLoad->pMod->fNative
6106 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6107 {
6108 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
6109 return pDynLoad->hmod;
6110 }
6111 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
6112 SetLastError(ERROR_MOD_NOT_FOUND);
6113 return NULL;
6114 }
6115 }
6116
6117 /*
6118 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6119 * to and go via the g_aGetModuleHandleCache cache.
6120 */
6121 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
6122 {
6123 HMODULE hmod = GetModuleHandleW(pwszModule);
6124 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
6125 if (hmod)
6126 {
6127 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6128 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
6129 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6130 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
6131 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6132 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
6133 }
6134 else
6135 dwErr = GetLastError();
6136 }
6137
6138 kwErrPrintf("pwszModule=%ls\n", pwszModule);
6139 KWFS_TODO();
6140 SetLastError(dwErr);
6141 return NULL;
6142}
6143
6144
6145/** Used to debug dynamically resolved procedures. */
6146static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
6147{
6148#ifdef _MSC_VER
6149 __debugbreak();
6150#else
6151 KWFS_TODO();
6152#endif
6153 return ~(UINT)0;
6154}
6155
6156
6157#ifndef NDEBUG
6158/*
6159 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
6160 */
6161# if K_ARCH == K_ARCH_X86_32
6162static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
6163# else
6164static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
6165# endif
6166typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
6167typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
6168typedef struct KWCXINTERCEPTORENTRY
6169{
6170 PFNINVOKECOMPILERPASSW pfnOrg;
6171 PKWMODULE pModule;
6172 PFNINVOKECOMPILERPASSW pfnWrap;
6173} KWCXINTERCEPTORENTRY;
6174
6175static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
6176 KWCXINTERCEPTORENTRY *pEntry)
6177{
6178 int i;
6179 KIPTR rcExit;
6180 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
6181 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
6182 for (i = 0; i < cArgs; i++)
6183 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
6184
6185 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
6186
6187 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
6188 return rcExit;
6189}
6190
6191static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
6192static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
6193static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
6194
6195static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
6196{
6197 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
6198 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
6199 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
6200};
6201
6202static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6203{
6204 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
6205}
6206
6207static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6208{
6209 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
6210}
6211
6212static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6213{
6214 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
6215}
6216
6217#endif /* !NDEBUG */
6218
6219
6220/** Kernel32 - GetProcAddress() */
6221static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6222{
6223 KSIZE i;
6224 PKWMODULE pMod;
6225 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6226
6227 /*
6228 * Try locate the module.
6229 */
6230 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6231 if (pMod)
6232 {
6233 KLDRADDR uValue;
6234 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
6235 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
6236 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
6237 KU32_MAX /*iSymbol*/,
6238 pszProc,
6239 kHlpStrLen(pszProc),
6240 NULL /*pszVersion*/,
6241 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
6242 &uValue,
6243 NULL /*pfKind*/);
6244 if (rc == 0)
6245 {
6246 //static int s_cDbgGets = 0;
6247 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
6248 KU32 i = g_cSandboxGetProcReplacements;
6249 while (i-- > 0)
6250 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
6251 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
6252 {
6253 if ( !g_aSandboxGetProcReplacements[i].pszModule
6254 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
6255 {
6256 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
6257 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
6258 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
6259 {
6260 if (!g_aSandboxReplacements[i].fCrtSlotArray)
6261 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
6262 else
6263 {
6264 if (pMod->iCrtSlot == KU8_MAX)
6265 {
6266 int rc = kwLdrModuleCreateCrtSlot(pMod);
6267 if (rc)
6268 {
6269 KW_LOG(("GetProcAddress: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
6270 SetLastError(ERROR_INTERNAL_ERROR);
6271 return NULL;
6272 }
6273 }
6274 uValue = ((KUPTR *)g_aSandboxGetProcReplacements[i].pfnReplacement)[pMod->iCrtSlot];
6275 }
6276
6277 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6278 }
6279 kwLdrModuleRelease(pMod);
6280 return (FARPROC)(KUPTR)uValue;
6281 }
6282 }
6283
6284#ifndef NDEBUG
6285 /* Intercept the compiler pass method, dumping arguments. */
6286 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
6287 {
6288 KU32 i;
6289 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6290 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6291 {
6292 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6293 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6294 break;
6295 }
6296 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6297 while (i-- > 0)
6298 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6299 {
6300 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6301 g_aCxInterceptorEntries[i].pModule = pMod;
6302 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6303 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6304 break;
6305 }
6306 }
6307#endif
6308 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6309 kwLdrModuleRelease(pMod);
6310 //s_cDbgGets++;
6311 //if (s_cGets >= 3)
6312 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6313 return (FARPROC)(KUPTR)uValue;
6314 }
6315
6316 KWFS_TODO();
6317 SetLastError(ERROR_PROC_NOT_FOUND);
6318 kwLdrModuleRelease(pMod);
6319 return NULL;
6320 }
6321
6322 /*
6323 * Hmm... could be a cached module-by-name.
6324 */
6325 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6326 if (g_aGetModuleHandleCache[i].hmod == hmod)
6327 return GetProcAddress(hmod, pszProc);
6328
6329 KWFS_TODO();
6330 return GetProcAddress(hmod, pszProc);
6331}
6332
6333
6334#ifndef NDEBUG
6335/** Kernel32 - GetProcAddress() - native replacement for debugging only. */
6336static FARPROC WINAPI kwSandbox_Kernel32_Native_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6337{
6338 FARPROC pfnRet = GetProcAddress(hmod, pszProc);
6339 KWLDR_LOG(("kwSandbox_Kernel32_Native_GetProcAddress(%p, %s) -> %p\n", hmod, pszProc, pfnRet));
6340 return pfnRet;
6341}
6342#endif
6343
6344
6345/** Kernel32 - GetModuleFileNameA() */
6346static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6347{
6348 PKWMODULE pMod;
6349 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6350
6351 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6352 if (pMod != NULL)
6353 {
6354 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6355 kwLdrModuleRelease(pMod);
6356 return cbRet;
6357 }
6358 KWFS_TODO();
6359 return 0;
6360}
6361
6362
6363/** Kernel32 - GetModuleFileNameW() */
6364static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6365{
6366 PKWMODULE pMod;
6367 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6368
6369 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6370 if (pMod)
6371 {
6372 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6373 kwLdrModuleRelease(pMod);
6374 return cwcRet;
6375 }
6376
6377 KWFS_TODO();
6378 return 0;
6379}
6380
6381
6382/** NtDll - RtlPcToFileHeader
6383 * This is necessary for msvcr100.dll!CxxThrowException. */
6384static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6385{
6386 PVOID pvRet;
6387
6388 /*
6389 * Do a binary lookup of the module table for the current tool.
6390 * This will give us a
6391 */
6392 if (g_Sandbox.fRunning)
6393 {
6394 KUPTR const uPC = (KUPTR)pvPC;
6395 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6396 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6397 KU32 i;
6398 if (iEnd)
6399 {
6400 KU32 iStart = 0;
6401 i = iEnd / 2;
6402 for (;;)
6403 {
6404 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6405 if (uPC < uHModCur)
6406 {
6407 iEnd = i;
6408 if (iStart < i)
6409 { }
6410 else
6411 break;
6412 }
6413 else if (uPC != uHModCur)
6414 {
6415 iStart = ++i;
6416 if (i < iEnd)
6417 { }
6418 else
6419 break;
6420 }
6421 else
6422 {
6423 /* This isn't supposed to happen. */
6424 break;
6425 }
6426
6427 i = iStart + (iEnd - iStart) / 2;
6428 }
6429
6430 /* For reasons of simplicity (= copy & paste), we end up with the
6431 module after the one we're interested in here. */
6432 i--;
6433 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6434 && papMods[i]->pLdrMod)
6435 {
6436 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6437 if (uRvaPC < papMods[i]->cbImage)
6438 {
6439 *ppvImageBase = papMods[i]->hOurMod;
6440 pvRet = papMods[i]->hOurMod;
6441 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6442 return pvRet;
6443 }
6444 }
6445 }
6446 else
6447 i = 0;
6448 }
6449
6450 /*
6451 * Call the regular API.
6452 */
6453 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6454 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6455 return pvRet;
6456}
6457
6458
6459/*
6460 *
6461 * File access APIs (for speeding them up).
6462 * File access APIs (for speeding them up).
6463 * File access APIs (for speeding them up).
6464 *
6465 */
6466
6467
6468/**
6469 * Converts a lookup error to a windows error code.
6470 *
6471 * @returns The windows error code.
6472 * @param enmError The lookup error.
6473 */
6474static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6475{
6476 switch (enmError)
6477 {
6478 case KFSLOOKUPERROR_NOT_FOUND:
6479 case KFSLOOKUPERROR_NOT_DIR:
6480 return ERROR_FILE_NOT_FOUND;
6481
6482 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6483 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6484 case KFSLOOKUPERROR_PATH_TOO_SHORT:
6485 return ERROR_PATH_NOT_FOUND;
6486
6487 case KFSLOOKUPERROR_PATH_TOO_LONG:
6488 return ERROR_FILENAME_EXCED_RANGE;
6489
6490 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6491 return ERROR_NOT_ENOUGH_MEMORY;
6492
6493 default:
6494 return ERROR_PATH_NOT_FOUND;
6495 }
6496}
6497
6498#ifdef WITH_TEMP_MEMORY_FILES
6499
6500/**
6501 * Checks for a cl.exe temporary file.
6502 *
6503 * There are quite a bunch of these. They seems to be passing data between the
6504 * first and second compiler pass. Since they're on disk, they get subjected to
6505 * AV software screening and normal file consistency rules. So, not necessarily
6506 * a very efficient way of handling reasonably small amounts of data.
6507 *
6508 * We make the files live in virtual memory by intercepting their opening,
6509 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6510 *
6511 * @returns K_TRUE / K_FALSE
6512 * @param pwszFilename The file name being accessed.
6513 */
6514static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6515{
6516 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6517 if (pwszName)
6518 {
6519 /* The name starts with _CL_... */
6520 if ( pwszName[0] == '_'
6521 && pwszName[1] == 'C'
6522 && pwszName[2] == 'L'
6523 && pwszName[3] == '_' )
6524 {
6525 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6526 this check by just checking that it's alpha numerical ascii from here on. */
6527 wchar_t wc;
6528 pwszName += 4;
6529 while ((wc = *pwszName++) != '\0')
6530 {
6531 if (wc < 127 && iswalnum(wc))
6532 { /* likely */ }
6533 else
6534 return K_FALSE;
6535 }
6536 return K_TRUE;
6537 }
6538
6539 /* In VC2019 there is also one {UUID} file in temp: */
6540 if (pwszName[0] == '{')
6541 {
6542 KSIZE cwcName = kwUtf16Len(pwszName);
6543 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6544 && pwszName[37] == '}'
6545 && iswalnum(pwszName[1]) // 4
6546 && iswalnum(pwszName[2]) // 4
6547 && iswalnum(pwszName[3]) // 6
6548 && iswalnum(pwszName[4]) // 5
6549 && iswalnum(pwszName[5]) // d
6550 && iswalnum(pwszName[6]) // d
6551 && iswalnum(pwszName[7]) // d
6552 && iswalnum(pwszName[8]) // 9
6553 && pwszName[9] == '-' // -
6554 && iswalnum(pwszName[10]) // e
6555 && iswalnum(pwszName[11]) // 4
6556 && iswalnum(pwszName[12]) // 9
6557 && iswalnum(pwszName[13]) // 4
6558 && pwszName[14] == '-' // -
6559 && iswalnum(pwszName[15]) // 4
6560 && iswalnum(pwszName[16]) // 7
6561 && iswalnum(pwszName[17]) // 1
6562 && iswalnum(pwszName[18]) // b
6563 && pwszName[19] == '-' // -
6564 && iswalnum(pwszName[20]) // 9
6565 && iswalnum(pwszName[21]) // 9
6566 && iswalnum(pwszName[22]) // 6
6567 && iswalnum(pwszName[23]) // b
6568 && pwszName[24] == '-' // -
6569 && iswalnum(pwszName[25]) // 9
6570 && iswalnum(pwszName[26]) // b
6571 && iswalnum(pwszName[27]) // 5
6572 && iswalnum(pwszName[28]) // 5
6573 && iswalnum(pwszName[29]) // 6
6574 && iswalnum(pwszName[30]) // e
6575 && iswalnum(pwszName[31]) // 2
6576 && iswalnum(pwszName[32]) // 5
6577 && iswalnum(pwszName[33]) // a
6578 && iswalnum(pwszName[34]) // 3
6579 && iswalnum(pwszName[35]) // f
6580 && iswalnum(pwszName[36])) // 8
6581 return K_TRUE;
6582 }
6583 }
6584 return K_FALSE;
6585}
6586
6587
6588/**
6589 * Creates a handle to a temporary file.
6590 *
6591 * @returns The handle on success.
6592 * INVALID_HANDLE_VALUE and SetLastError on failure.
6593 * @param pTempFile The temporary file.
6594 * @param dwDesiredAccess The desired access to the handle.
6595 * @param fMapping Whether this is a mapping (K_TRUE) or file
6596 * (K_FALSE) handle type.
6597 */
6598static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6599{
6600 /*
6601 * Create a handle to the temporary file.
6602 */
6603 HANDLE hFile = INVALID_HANDLE_VALUE;
6604 HANDLE hProcSelf = GetCurrentProcess();
6605 if (DuplicateHandle(hProcSelf, hProcSelf,
6606 hProcSelf, &hFile,
6607 SYNCHRONIZE, FALSE,
6608 0 /*dwOptions*/))
6609 {
6610 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6611 if (pHandle)
6612 {
6613 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6614 pHandle->cRefs = 1;
6615 pHandle->offFile = 0;
6616 pHandle->hHandle = hFile;
6617 pHandle->dwDesiredAccess = dwDesiredAccess;
6618 pHandle->tidOwner = KU32_MAX;
6619 pHandle->u.pTempFile = pTempFile;
6620 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6621 {
6622 pTempFile->cActiveHandles++;
6623 kHlpAssert(pTempFile->cActiveHandles >= 1);
6624 kHlpAssert(pTempFile->cActiveHandles <= 2);
6625 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6626 return hFile;
6627 }
6628
6629 kHlpFree(pHandle);
6630 }
6631 else
6632 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6633 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6634 }
6635 else
6636 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6637 return INVALID_HANDLE_VALUE;
6638}
6639
6640
6641static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6642 KBOOL *pfFallback)
6643{
6644 HANDLE hFile;
6645 DWORD dwErr;
6646
6647 /*
6648 * Check if we've got an existing temp file.
6649 * ASSUME exact same path for now.
6650 */
6651 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6652 PKWFSTEMPFILE pTempFile;
6653 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6654 {
6655 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6656 if ( pTempFile->cwcPath == cwcFilename
6657 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6658 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6659 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6660 break;
6661 }
6662
6663 /*
6664 * Create a new temporary file instance if not found.
6665 */
6666 *pfFallback = K_FALSE;
6667 if (pTempFile == NULL)
6668 {
6669 KSIZE cbFilename;
6670
6671 switch (dwCreationDisposition)
6672 {
6673 case CREATE_ALWAYS:
6674 case OPEN_ALWAYS:
6675 case CREATE_NEW:
6676 dwErr = NO_ERROR;
6677 break;
6678
6679 case OPEN_EXISTING:
6680 case TRUNCATE_EXISTING:
6681 *pfFallback = K_TRUE;
6682 kHlpAssertFailed();
6683 SetLastError(ERROR_FILE_NOT_FOUND);
6684 return INVALID_HANDLE_VALUE;
6685
6686 default:
6687 kHlpAssertFailed();
6688 SetLastError(ERROR_INVALID_PARAMETER);
6689 return INVALID_HANDLE_VALUE;
6690 }
6691
6692 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6693 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6694 if (pTempFile)
6695 {
6696 pTempFile->cwcPath = (KU16)cwcFilename;
6697 pTempFile->cbFile = 0;
6698 pTempFile->cbFileAllocated = 0;
6699 pTempFile->cActiveHandles = 0;
6700 pTempFile->cMappings = 0;
6701 pTempFile->cSegs = 0;
6702 pTempFile->paSegs = NULL;
6703 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6704
6705 pTempFile->pNext = g_Sandbox.pTempFileHead;
6706 g_Sandbox.pTempFileHead = pTempFile;
6707 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6708 }
6709 else
6710 {
6711 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6712 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6713 return INVALID_HANDLE_VALUE;
6714 }
6715 }
6716 else
6717 {
6718 switch (dwCreationDisposition)
6719 {
6720 case OPEN_EXISTING:
6721 dwErr = NO_ERROR;
6722 break;
6723 case OPEN_ALWAYS:
6724 dwErr = ERROR_ALREADY_EXISTS;
6725 break;
6726
6727 case TRUNCATE_EXISTING:
6728 case CREATE_ALWAYS:
6729 kHlpAssertFailed();
6730 pTempFile->cbFile = 0;
6731 dwErr = ERROR_ALREADY_EXISTS;
6732 break;
6733
6734 case CREATE_NEW:
6735 kHlpAssertFailed();
6736 SetLastError(ERROR_FILE_EXISTS);
6737 return INVALID_HANDLE_VALUE;
6738
6739 default:
6740 kHlpAssertFailed();
6741 SetLastError(ERROR_INVALID_PARAMETER);
6742 return INVALID_HANDLE_VALUE;
6743 }
6744 }
6745
6746 /*
6747 * Create a handle to the temporary file.
6748 */
6749 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6750 if (hFile != INVALID_HANDLE_VALUE)
6751 SetLastError(dwErr);
6752 return hFile;
6753}
6754
6755#endif /* WITH_TEMP_MEMORY_FILES */
6756
6757/**
6758 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6759 *
6760 * @returns K_TRUE if cacheable, K_FALSE if not.
6761 * @param wcFirst The first extension character.
6762 * @param wcSecond The second extension character.
6763 * @param wcThird The third extension character.
6764 * @param fAttrQuery Set if it's for an attribute query, clear if for
6765 * file creation.
6766 */
6767static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6768{
6769 /* C++ header without an extension or a directory. */
6770 if (wcFirst == '\0')
6771 {
6772 /** @todo exclude temporary files... */
6773 return K_TRUE;
6774 }
6775
6776 /* C Header: .h */
6777 if (wcFirst == 'h' || wcFirst == 'H')
6778 {
6779 if (wcSecond == '\0')
6780 return K_TRUE;
6781
6782 /* C++ Header: .hpp, .hxx */
6783 if ( (wcSecond == 'p' || wcSecond == 'P')
6784 && (wcThird == 'p' || wcThird == 'P'))
6785 return K_TRUE;
6786 if ( (wcSecond == 'x' || wcSecond == 'X')
6787 && (wcThird == 'x' || wcThird == 'X'))
6788 return K_TRUE;
6789 }
6790 /* Misc starting with i. */
6791 else if (wcFirst == 'i' || wcFirst == 'I')
6792 {
6793 if (wcSecond != '\0')
6794 {
6795 if (wcSecond == 'n' || wcSecond == 'N')
6796 {
6797 /* C++ inline header: .inl */
6798 if (wcThird == 'l' || wcThird == 'L')
6799 return K_TRUE;
6800
6801 /* Assembly include file: .inc */
6802 if (wcThird == 'c' || wcThird == 'C')
6803 return K_TRUE;
6804 }
6805 }
6806 }
6807 /* Assembly header: .mac */
6808 else if (wcFirst == 'm' || wcFirst == 'M')
6809 {
6810 if (wcSecond == 'a' || wcSecond == 'A')
6811 {
6812 if (wcThird == 'c' || wcThird == 'C')
6813 return K_TRUE;
6814 }
6815 }
6816#ifdef WITH_PCH_CACHING
6817 /* Precompiled header: .pch */
6818 else if (wcFirst == 'p' || wcFirst == 'P')
6819 {
6820 if (wcSecond == 'c' || wcSecond == 'C')
6821 {
6822 if (wcThird == 'h' || wcThird == 'H')
6823 return !g_Sandbox.fNoPchCaching;
6824 }
6825 }
6826#endif
6827#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6828 /* Linker - Object file: .obj */
6829 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6830 {
6831 if (wcSecond == 'b' || wcSecond == 'B')
6832 {
6833 if (wcThird == 'j' || wcThird == 'J')
6834 return K_TRUE;
6835 }
6836 }
6837#endif
6838 else if (fAttrQuery)
6839 {
6840 /* Dynamic link library: .dll */
6841 if (wcFirst == 'd' || wcFirst == 'D')
6842 {
6843 if (wcSecond == 'l' || wcSecond == 'L')
6844 {
6845 if (wcThird == 'l' || wcThird == 'L')
6846 return K_TRUE;
6847 }
6848 }
6849 /* Executable file: .exe */
6850 else if (wcFirst == 'e' || wcFirst == 'E')
6851 {
6852 if (wcSecond == 'x' || wcSecond == 'X')
6853 {
6854 if (wcThird == 'e' || wcThird == 'E')
6855 return K_TRUE;
6856 }
6857 }
6858 /* Response file: .rsp */
6859 else if (wcFirst == 'r' || wcFirst == 'R')
6860 {
6861 if (wcSecond == 's' || wcSecond == 'S')
6862 {
6863 if (wcThird == 'p' || wcThird == 'P')
6864 return !g_Sandbox.fNoPchCaching;
6865 }
6866 }
6867 /* Linker: */
6868 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6869 {
6870 /* Object file: .obj */
6871 if (wcFirst == 'o' || wcFirst == 'O')
6872 {
6873 if (wcSecond == 'b' || wcSecond == 'B')
6874 {
6875 if (wcThird == 'j' || wcThird == 'J')
6876 return K_TRUE;
6877 }
6878 }
6879 /* Library file: .lib */
6880 else if (wcFirst == 'l' || wcFirst == 'L')
6881 {
6882 if (wcSecond == 'i' || wcSecond == 'I')
6883 {
6884 if (wcThird == 'b' || wcThird == 'B')
6885 return K_TRUE;
6886 }
6887 }
6888 /* Linker definition file: .def */
6889 else if (wcFirst == 'd' || wcFirst == 'D')
6890 {
6891 if (wcSecond == 'e' || wcSecond == 'E')
6892 {
6893 if (wcThird == 'f' || wcThird == 'F')
6894 return K_TRUE;
6895 }
6896 }
6897 }
6898 }
6899
6900 return K_FALSE;
6901}
6902
6903
6904/**
6905 * Checks if the file extension indicates that the file/dir is something we
6906 * ought to cache.
6907 *
6908 * @returns K_TRUE if cachable, K_FALSE if not.
6909 * @param pszExt The kHlpGetExt result.
6910 * @param fAttrQuery Set if it's for an attribute query, clear if for
6911 * file creation.
6912 */
6913static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6914{
6915 wchar_t const wcFirst = *pszExt;
6916 if (wcFirst)
6917 {
6918 wchar_t const wcSecond = pszExt[1];
6919 if (wcSecond)
6920 {
6921 wchar_t const wcThird = pszExt[2];
6922 if (pszExt[3] == '\0')
6923 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6924 return K_FALSE;
6925 }
6926 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6927 }
6928 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6929}
6930
6931
6932/**
6933 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6934 * should be cached.
6935 *
6936 * @returns K_TRUE if cachable, K_FALSE if not.
6937 * @param pwszPath The UTF-16 path to examine.
6938 * @param fAttrQuery Set if it's for an attribute query, clear if for
6939 * file creation.
6940 */
6941static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6942{
6943 KSIZE cwcExt;
6944 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6945 switch (cwcExt)
6946 {
6947 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6948 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6949 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6950 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6951 }
6952 return K_FALSE;
6953}
6954
6955
6956
6957/**
6958 * Creates a new
6959 *
6960 * @returns
6961 * @param pFsObj .
6962 * @param pwszFilename .
6963 */
6964static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6965{
6966 HANDLE hFile;
6967 MY_IO_STATUS_BLOCK Ios;
6968 MY_OBJECT_ATTRIBUTES ObjAttr;
6969 MY_UNICODE_STRING UniStr;
6970 MY_NTSTATUS rcNt;
6971 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6972
6973 /*
6974 * Open the file relative to the parent directory.
6975 */
6976 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6977 kHlpAssert(pFsObj->pParent);
6978 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6979
6980 Ios.Information = ~(ULONG_PTR)0;
6981 Ios.u.Status = -1;
6982
6983 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6984 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6985 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6986
6987 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6988
6989 rcNt = g_pfnNtCreateFile(&hFile,
6990 GENERIC_READ | SYNCHRONIZE,
6991 &ObjAttr,
6992 &Ios,
6993 NULL, /*cbFileInitialAlloc */
6994 FILE_ATTRIBUTE_NORMAL,
6995 FILE_SHARE_READ,
6996 FILE_OPEN,
6997 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6998 NULL, /*pEaBuffer*/
6999 0); /*cbEaBuffer*/
7000 if (MY_NT_SUCCESS(rcNt))
7001 {
7002 /*
7003 * Read the whole file into memory.
7004 */
7005 LARGE_INTEGER cbFile;
7006 if (GetFileSizeEx(hFile, &cbFile))
7007 {
7008 if ( cbFile.QuadPart >= 0
7009#ifdef WITH_PCH_CACHING
7010 && ( cbFile.QuadPart < 16*1024*1024
7011 || ( cbFile.QuadPart < 96*1024*1024
7012 && pFsObj->cchName > 4
7013 && !g_Sandbox.fNoPchCaching
7014 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
7015#endif
7016 )
7017 {
7018 KU32 cbCache = (KU32)cbFile.QuadPart;
7019 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
7020 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
7021 if (hMapping != NULL)
7022 {
7023 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
7024 if (pbCache)
7025 {
7026 /*
7027 * Create the cached file object.
7028 */
7029 PKFSWCACHEDFILE pCachedFile;
7030 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
7031 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
7032 sizeof(*pCachedFile) + cbPath);
7033 if (pCachedFile)
7034 {
7035 pCachedFile->hCached = hFile;
7036 pCachedFile->hSection = hMapping;
7037 pCachedFile->cbCached = cbCache;
7038 pCachedFile->pbCached = pbCache;
7039 pCachedFile->pFsObj = pFsObj;
7040 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
7041 kFsCacheObjRetain(pFsObj);
7042
7043 g_cReadCachedFiles++;
7044 g_cbReadCachedFiles += cbCache;
7045
7046 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
7047 return pCachedFile;
7048 }
7049
7050 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
7051 }
7052 else
7053 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
7054 CloseHandle(hMapping);
7055 }
7056 else
7057 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
7058 }
7059 else
7060 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
7061 }
7062 else
7063 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
7064 g_pfnNtClose(hFile);
7065 }
7066 else
7067 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
7068 return NULL;
7069}
7070
7071
7072/**
7073 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
7074 */
7075static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
7076 KBOOL fIsFileHandle, HANDLE *phFile)
7077{
7078 HANDLE hProcSelf = GetCurrentProcess();
7079 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
7080 hProcSelf, phFile,
7081 dwDesiredAccess, fInheritHandle,
7082 0 /*dwOptions*/))
7083 {
7084 /*
7085 * Create handle table entry for the duplicate handle.
7086 */
7087 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
7088 if (pHandle)
7089 {
7090 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
7091 pHandle->cRefs = 1;
7092 pHandle->offFile = 0;
7093 pHandle->hHandle = *phFile;
7094 pHandle->dwDesiredAccess = dwDesiredAccess;
7095 pHandle->tidOwner = KU32_MAX;
7096 pHandle->u.pCachedFile = pCachedFile;
7097 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
7098 return K_TRUE;
7099
7100 kHlpFree(pHandle);
7101 }
7102 else
7103 KWFS_LOG(("Out of memory for handle!\n"));
7104
7105 CloseHandle(*phFile);
7106 *phFile = INVALID_HANDLE_VALUE;
7107 }
7108 else
7109 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
7110 return K_FALSE;
7111}
7112
7113
7114/**
7115 * Kernel32 - Common code for CreateFileW and CreateFileA.
7116 */
7117static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
7118{
7119 *phFile = INVALID_HANDLE_VALUE;
7120
7121 /*
7122 * At the moment we only handle existing files.
7123 */
7124 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
7125 {
7126 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
7127 kHlpAssert(pFsObj->fHaveStats);
7128 if ( pCachedFile != NULL
7129 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
7130 {
7131 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
7132 return K_TRUE;
7133 }
7134 }
7135 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
7136
7137 /* Do fallback, please. */
7138 return K_FALSE;
7139}
7140
7141
7142/** Kernel32 - CreateFileA */
7143static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7144 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7145 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7146{
7147 HANDLE hFile;
7148
7149 /*
7150 * Check for include files and similar that we do read-only caching of.
7151 */
7152 if (dwCreationDisposition == OPEN_EXISTING)
7153 {
7154 if ( dwDesiredAccess == GENERIC_READ
7155 || dwDesiredAccess == FILE_GENERIC_READ)
7156 {
7157 if (dwShareMode & FILE_SHARE_READ)
7158 {
7159 if ( !pSecAttrs
7160 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7161 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7162 {
7163 const char *pszExt = kHlpGetExt(pszFilename);
7164 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
7165 {
7166 KFSLOOKUPERROR enmError;
7167 PKFSOBJ pFsObj;
7168 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7169
7170 pFsObj = kFsCacheLookupA(g_pFsCache, pszFilename, &enmError);
7171 if (pFsObj)
7172 {
7173 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7174 {
7175 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7176 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7177 kFsCacheObjRelease(g_pFsCache, pFsObj);
7178 if (fRc)
7179 {
7180 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
7181 return hFile;
7182 }
7183 }
7184 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7185 {
7186 KWFS_LOG(("CreateFileA(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7187 SetLastError(ERROR_FILE_NOT_FOUND);
7188 return INVALID_HANDLE_VALUE;
7189 }
7190 /* Always fall back on missing files in volatile areas. */
7191 }
7192 /* These are for nasm and yasm header searching. Cache will already
7193 have checked the directories for the file, no need to call
7194 CreateFile to do it again. */
7195 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7196 {
7197 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7198 SetLastError(ERROR_FILE_NOT_FOUND);
7199 return INVALID_HANDLE_VALUE;
7200 }
7201 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7202 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7203 {
7204 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
7205 SetLastError(ERROR_PATH_NOT_FOUND);
7206 return INVALID_HANDLE_VALUE;
7207 }
7208
7209 /* fallback */
7210 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7211 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7212 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
7213 return hFile;
7214 }
7215 }
7216 }
7217 }
7218 }
7219
7220 /*
7221 * Okay, normal.
7222 */
7223 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7224 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7225 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7226
7227 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7228 return hFile;
7229}
7230
7231
7232/** Kernel32 - CreateFileW */
7233static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7234 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7235 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7236{
7237 HANDLE hFile;
7238
7239#ifdef WITH_TEMP_MEMORY_FILES
7240 /*
7241 * Check for temporary files (cl.exe only).
7242 */
7243 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7244 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7245 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7246 && kwFsIsClTempFileW(pwszFilename))
7247 {
7248 KBOOL fFallback = K_FALSE;
7249 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7250 if (!fFallback)
7251 {
7252 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7253 return hFile;
7254 }
7255 }
7256#endif
7257
7258 /*
7259 * Check for include files and similar that we do read-only caching of.
7260 */
7261 if (dwCreationDisposition == OPEN_EXISTING)
7262 {
7263 if ( dwDesiredAccess == GENERIC_READ
7264 || dwDesiredAccess == FILE_GENERIC_READ)
7265 {
7266 if (dwShareMode & FILE_SHARE_READ)
7267 {
7268 if ( !pSecAttrs
7269 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7270 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7271 {
7272 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7273 {
7274 KFSLOOKUPERROR enmError;
7275 PKFSOBJ pFsObj;
7276 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7277
7278 pFsObj = kFsCacheLookupW(g_pFsCache, pwszFilename, &enmError);
7279 if (pFsObj)
7280 {
7281 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7282 {
7283 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7284 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7285 kFsCacheObjRelease(g_pFsCache, pFsObj);
7286 if (fRc)
7287 {
7288 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7289 return hFile;
7290 }
7291 }
7292 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7293 {
7294 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7295 SetLastError(ERROR_FILE_NOT_FOUND);
7296#if 0
7297 if ( pFsObj->cchName > sizeof("generated.h")
7298 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - sizeof("generated.h") + 1], "generated.h") == 0)
7299 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; pFsObj->fFlags=%#x\n", pwszFilename, pFsObj->fFlags);
7300#endif
7301 return INVALID_HANDLE_VALUE;
7302 }
7303 /* Always fall back on missing files in volatile areas. */
7304 }
7305 /* These are for nasm and yasm style header searching. Cache will
7306 already have checked the directories for the file, no need to call
7307 CreateFile to do it again. */
7308 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7309 {
7310#if 0
7311 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7312 if ( cwcFilename > sizeof("generated.h")
7313 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7314 L"generated.h", sizeof(L"generated.h")) == 0)
7315 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; (KFSLOOKUPERROR_NOT_FOUND)\n", pwszFilename, pFsObj->fFlags);
7316#endif
7317 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7318 SetLastError(ERROR_FILE_NOT_FOUND);
7319 return INVALID_HANDLE_VALUE;
7320 }
7321 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7322 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7323 {
7324#if 0
7325 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7326 if ( cwcFilename > sizeof("generated.h")
7327 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7328 L"generated.h", sizeof(L"generated.h")) == 0)
7329 kwErrPrintf("CreateFileW(%ls) -> ERROR_PATH_NOT_FOUND; (%d)\n", pwszFilename, enmError);
7330#endif
7331 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7332 SetLastError(ERROR_PATH_NOT_FOUND);
7333 return INVALID_HANDLE_VALUE;
7334 }
7335
7336 /* fallback */
7337 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7338 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7339 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7340 return hFile;
7341 }
7342 }
7343 else
7344 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7345 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7346 }
7347 else
7348 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7349 }
7350 else
7351 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7352 }
7353 else
7354 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7355
7356 /*
7357 * Okay, normal.
7358 */
7359 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7360 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7361 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7362
7363 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7364 return hFile;
7365}
7366
7367
7368
7369/** Kernel32 - SetFilePointer */
7370static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7371{
7372 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7373 if (pHandle != NULL)
7374 {
7375 KU32 cbFile;
7376 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7377 switch (pHandle->enmType)
7378 {
7379 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7380 cbFile = pHandle->u.pCachedFile->cbCached;
7381 break;
7382#ifdef WITH_TEMP_MEMORY_FILES
7383 case KWHANDLETYPE_TEMP_FILE:
7384 cbFile = pHandle->u.pTempFile->cbFile;
7385 break;
7386 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7387#endif
7388 case KWHANDLETYPE_OUTPUT_BUF:
7389 default:
7390 kHlpAssertFailed();
7391 kwSandboxHandlePut(pHandle);
7392 SetLastError(ERROR_INVALID_FUNCTION);
7393 return INVALID_SET_FILE_POINTER;
7394 }
7395
7396 switch (dwMoveMethod)
7397 {
7398 case FILE_BEGIN:
7399 break;
7400 case FILE_CURRENT:
7401 offMove += pHandle->offFile;
7402 break;
7403 case FILE_END:
7404 offMove += cbFile;
7405 break;
7406 default:
7407 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7408 kwSandboxHandlePut(pHandle);
7409 SetLastError(ERROR_INVALID_PARAMETER);
7410 return INVALID_SET_FILE_POINTER;
7411 }
7412 if (offMove >= 0)
7413 {
7414 if (offMove >= (KSSIZE)cbFile)
7415 {
7416#ifdef WITH_TEMP_MEMORY_FILES
7417 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7418 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7419#endif
7420 offMove = (KSSIZE)cbFile;
7421#ifdef WITH_TEMP_MEMORY_FILES
7422 /* For writable files, seeking beyond the end is fine, but check that we've got
7423 the type range for the request. */
7424 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7425 {
7426 kHlpAssertMsgFailed(("%#llx\n", offMove));
7427 kwSandboxHandlePut(pHandle);
7428 SetLastError(ERROR_SEEK);
7429 return INVALID_SET_FILE_POINTER;
7430 }
7431#endif
7432 }
7433 pHandle->offFile = (KU32)offMove;
7434 }
7435 else
7436 {
7437 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7438 kwSandboxHandlePut(pHandle);
7439 SetLastError(ERROR_NEGATIVE_SEEK);
7440 return INVALID_SET_FILE_POINTER;
7441 }
7442 if (pcbMoveHi)
7443 *pcbMoveHi = (KU64)offMove >> 32;
7444 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7445 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7446 kwSandboxHandlePut(pHandle);
7447 SetLastError(NO_ERROR);
7448 return (KU32)offMove;
7449 }
7450
7451 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7452 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7453}
7454
7455
7456/** Kernel32 - SetFilePointerEx */
7457static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7458 DWORD dwMoveMethod)
7459{
7460 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7461 if (pHandle != NULL)
7462 {
7463 KI64 offMyMove = offMove.QuadPart;
7464 KU32 cbFile;
7465 switch (pHandle->enmType)
7466 {
7467 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7468 cbFile = pHandle->u.pCachedFile->cbCached;
7469 break;
7470#ifdef WITH_TEMP_MEMORY_FILES
7471 case KWHANDLETYPE_TEMP_FILE:
7472 cbFile = pHandle->u.pTempFile->cbFile;
7473 break;
7474 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7475#endif
7476 case KWHANDLETYPE_OUTPUT_BUF:
7477 default:
7478 kHlpAssertFailed();
7479 kwSandboxHandlePut(pHandle);
7480 SetLastError(ERROR_INVALID_FUNCTION);
7481 return FALSE;
7482 }
7483
7484 switch (dwMoveMethod)
7485 {
7486 case FILE_BEGIN:
7487 break;
7488 case FILE_CURRENT:
7489 offMyMove += pHandle->offFile;
7490 break;
7491 case FILE_END:
7492 offMyMove += cbFile;
7493 break;
7494 default:
7495 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7496 kwSandboxHandlePut(pHandle);
7497 SetLastError(ERROR_INVALID_PARAMETER);
7498 return FALSE;
7499 }
7500 if (offMyMove >= 0)
7501 {
7502 if (offMyMove >= (KSSIZE)cbFile)
7503 {
7504#ifdef WITH_TEMP_MEMORY_FILES
7505 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7506 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7507#endif
7508 offMyMove = (KSSIZE)cbFile;
7509#ifdef WITH_TEMP_MEMORY_FILES
7510 /* For writable files, seeking beyond the end is fine, but check that we've got
7511 the type range for the request. */
7512 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7513 {
7514 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7515 kwSandboxHandlePut(pHandle);
7516 SetLastError(ERROR_SEEK);
7517 return FALSE;
7518 }
7519#endif
7520 }
7521 pHandle->offFile = (KU32)offMyMove;
7522 }
7523 else
7524 {
7525 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7526 kwSandboxHandlePut(pHandle);
7527 SetLastError(ERROR_NEGATIVE_SEEK);
7528 return FALSE;
7529 }
7530 if (poffNew)
7531 poffNew->QuadPart = offMyMove;
7532 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7533 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7534 kwSandboxHandlePut(pHandle);
7535 return TRUE;
7536 }
7537 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7538 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7539}
7540
7541
7542/** Kernel32 - ReadFile */
7543static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7544 LPOVERLAPPED pOverlapped)
7545{
7546 BOOL fRet;
7547 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7548 g_cReadFileCalls++;
7549 if (pHandle != NULL)
7550 {
7551 switch (pHandle->enmType)
7552 {
7553 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7554 {
7555 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7556 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7557 if (cbActually > cbToRead)
7558 cbActually = cbToRead;
7559
7560#ifdef WITH_HASH_MD5_CACHE
7561 if (g_Sandbox.pHashHead)
7562 {
7563 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7564 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7565 g_Sandbox.LastHashRead.cbRead = cbActually;
7566 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7567 }
7568#endif
7569
7570 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7571 pHandle->offFile += cbActually;
7572
7573 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7574 *pcbActuallyRead = cbActually;
7575
7576 g_cbReadFileFromReadCached += cbActually;
7577 g_cbReadFileTotal += cbActually;
7578 g_cReadFileFromReadCached++;
7579
7580 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7581 kwSandboxHandlePut(pHandle);
7582 return TRUE;
7583 }
7584
7585#ifdef WITH_TEMP_MEMORY_FILES
7586 case KWHANDLETYPE_TEMP_FILE:
7587 {
7588 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7589 KU32 cbActually;
7590 if (pHandle->offFile < pTempFile->cbFile)
7591 {
7592 cbActually = pTempFile->cbFile - pHandle->offFile;
7593 if (cbActually > cbToRead)
7594 cbActually = cbToRead;
7595
7596 /* Copy the data. */
7597 if (cbActually > 0)
7598 {
7599 KU32 cbLeft;
7600 KU32 offSeg;
7601 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7602
7603 /* Locate the segment containing the byte at offFile. */
7604 KU32 iSeg = pTempFile->cSegs - 1;
7605 kHlpAssert(pTempFile->cSegs > 0);
7606 while (paSegs[iSeg].offData > pHandle->offFile)
7607 iSeg--;
7608
7609 /* Copy out the data. */
7610 cbLeft = cbActually;
7611 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7612 for (;;)
7613 {
7614 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7615 if (cbAvail >= cbLeft)
7616 {
7617 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7618 break;
7619 }
7620
7621 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7622 cbLeft -= cbAvail;
7623 offSeg = 0;
7624 iSeg++;
7625 kHlpAssert(iSeg < pTempFile->cSegs);
7626 }
7627
7628 /* Update the file offset. */
7629 pHandle->offFile += cbActually;
7630 }
7631 }
7632 /* Read does not commit file space, so return zero bytes. */
7633 else
7634 cbActually = 0;
7635
7636 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7637 *pcbActuallyRead = cbActually;
7638
7639 g_cbReadFileTotal += cbActually;
7640 g_cbReadFileFromInMemTemp += cbActually;
7641 g_cReadFileFromInMemTemp++;
7642
7643 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7644 kwSandboxHandlePut(pHandle);
7645 return TRUE;
7646 }
7647
7648 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7649#endif /* WITH_TEMP_MEMORY_FILES */
7650 case KWHANDLETYPE_OUTPUT_BUF:
7651 default:
7652 kHlpAssertFailed();
7653 kwSandboxHandlePut(pHandle);
7654 SetLastError(ERROR_INVALID_FUNCTION);
7655 *pcbActuallyRead = 0;
7656 return FALSE;
7657 }
7658 }
7659
7660 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7661 if (fRet && pcbActuallyRead)
7662 g_cbReadFileTotal += *pcbActuallyRead;
7663 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7664 return fRet;
7665}
7666
7667
7668/** Kernel32 - ReadFileEx */
7669static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7670 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7671{
7672 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7673
7674 KWFS_LOG(("ReadFile(%p)\n", hFile));
7675 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7676}
7677
7678#ifdef WITH_STD_OUT_ERR_BUFFERING
7679
7680/**
7681 * Write something to a handle, making sure everything is actually written.
7682 *
7683 * @param hHandle Where to write it to.
7684 * @param pchBuf What to write
7685 * @param cchToWrite How much to write.
7686 */
7687static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7688{
7689 if (cchToWrite > 0)
7690 {
7691 DWORD cchWritten = 0;
7692 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7693 {
7694 if (cchWritten == cchToWrite)
7695 { /* likely */ }
7696 else
7697 {
7698 do
7699 {
7700 pchBuf += cchWritten;
7701 cchToWrite -= cchWritten;
7702 cchWritten = 0;
7703 } while ( cchToWrite > 0
7704 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7705 }
7706 }
7707 else
7708 kHlpAssertFailed();
7709 }
7710}
7711
7712
7713/**
7714 * Worker for WriteFile when the output isn't going to the console.
7715 *
7716 * @param pSandbox The sandbox.
7717 * @param pOutBuf The output buffer.
7718 * @param pchBuffer What to write.
7719 * @param cchToWrite How much to write.
7720 */
7721static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7722{
7723 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7724 { /* likely */ }
7725 else
7726 {
7727 /* No realloc, max size is 64KB. */
7728 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7729 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7730 if (!pOutBuf->u.Fully.pchBuf)
7731 {
7732 while ( !pOutBuf->u.Fully.pchBuf
7733 && pOutBuf->u.Fully.cchBufAlloc > 64)
7734 {
7735 pOutBuf->u.Fully.cchBufAlloc /= 2;
7736 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7737 }
7738 if (!pOutBuf->u.Fully.pchBuf)
7739 {
7740 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7741 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7742 }
7743 }
7744 }
7745
7746 /*
7747 * Special case: Output ends with newline and fits in the buffer.
7748 */
7749 if ( cchToWrite > 1
7750 && pchBuffer[cchToWrite - 1] == '\n'
7751 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7752 {
7753 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7754 pOutBuf->u.Fully.cchBuf += cchToWrite;
7755 }
7756 else
7757 {
7758 /*
7759 * Work thru the text line by line, flushing the buffer when
7760 * appropriate. The buffer is not a line buffer here, it's a
7761 * full buffer.
7762 */
7763 while (cchToWrite > 0)
7764 {
7765 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7766 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7767 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7768 {
7769 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7770 pOutBuf->u.Fully.cchBuf += cchLine;
7771 }
7772 /*
7773 * Option one: Flush the buffer and the current line.
7774 *
7775 * We choose this one when the line won't ever fit, or when we have
7776 * an incomplete line in the buffer.
7777 */
7778 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7779 || pOutBuf->u.Fully.cchBuf == 0
7780 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7781 {
7782 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7783 if (pOutBuf->u.Fully.cchBuf > 0)
7784 {
7785 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7786 pOutBuf->u.Fully.cchBuf = 0;
7787 }
7788 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7789 }
7790 /*
7791 * Option two: Only flush the lines in the buffer.
7792 */
7793 else
7794 {
7795 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7796 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7797 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7798 pOutBuf->u.Fully.cchBuf = cchLine;
7799 }
7800
7801 /* advance */
7802 pchBuffer += cchLine;
7803 cchToWrite -= cchLine;
7804 }
7805 }
7806}
7807
7808#endif /* WITH_STD_OUT_ERR_BUFFERING */
7809
7810#ifdef WITH_TEMP_MEMORY_FILES
7811static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7812{
7813 KU32 cbMinFile = offFile + cbNeeded;
7814 if (cbMinFile >= offFile)
7815 {
7816 /* Calc how much space we've already allocated and */
7817 if (cbMinFile <= pTempFile->cbFileAllocated)
7818 return K_TRUE;
7819
7820 /* Grow the file. */
7821 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7822 {
7823 int rc;
7824 KU32 cSegs = pTempFile->cSegs;
7825 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7826 do
7827 {
7828 /* grow the segment array? */
7829 if ((cSegs % 16) == 0)
7830 {
7831 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7832 if (!pvNew)
7833 return K_FALSE;
7834 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7835 }
7836
7837 /* Use page alloc here to simplify mapping later. */
7838 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7839 if (rc == 0)
7840 { /* likely */ }
7841 else
7842 {
7843 cbNewSeg = 64*1024;
7844 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7845 if (rc != 0)
7846 {
7847 kHlpAssertFailed();
7848 return K_FALSE;
7849 }
7850 }
7851 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7852 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7853 pTempFile->cbFileAllocated += cbNewSeg;
7854 pTempFile->cSegs = ++cSegs;
7855
7856 } while (pTempFile->cbFileAllocated < cbMinFile);
7857
7858 return K_TRUE;
7859 }
7860 }
7861
7862 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7863 return K_FALSE;
7864}
7865#endif /* WITH_TEMP_MEMORY_FILES */
7866
7867
7868#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7869/** Kernel32 - WriteFile */
7870static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7871 LPOVERLAPPED pOverlapped)
7872{
7873 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7874 BOOL fRet;
7875 g_cWriteFileCalls++;
7876 if (pHandle != NULL)
7877 {
7878 switch (pHandle->enmType)
7879 {
7880# ifdef WITH_TEMP_MEMORY_FILES
7881 case KWHANDLETYPE_TEMP_FILE:
7882 {
7883 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7884
7885 kHlpAssert(!pOverlapped);
7886 kHlpAssert(pcbActuallyWritten);
7887
7888 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7889 {
7890 KU32 cbLeft;
7891 KU32 offSeg;
7892
7893 /* Locate the segment containing the byte at offFile. */
7894 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7895 KU32 iSeg = pTempFile->cSegs - 1;
7896 kHlpAssert(pTempFile->cSegs > 0);
7897 while (paSegs[iSeg].offData > pHandle->offFile)
7898 iSeg--;
7899
7900 /* Copy in the data. */
7901 cbLeft = cbToWrite;
7902 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7903 for (;;)
7904 {
7905 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7906 if (cbAvail >= cbLeft)
7907 {
7908 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7909 break;
7910 }
7911
7912 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7913 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7914 cbLeft -= cbAvail;
7915 offSeg = 0;
7916 iSeg++;
7917 kHlpAssert(iSeg < pTempFile->cSegs);
7918 }
7919
7920 /* Update the file offset. */
7921 pHandle->offFile += cbToWrite;
7922 if (pHandle->offFile > pTempFile->cbFile)
7923 pTempFile->cbFile = pHandle->offFile;
7924
7925 *pcbActuallyWritten = cbToWrite;
7926
7927 g_cbWriteFileTotal += cbToWrite;
7928 g_cbWriteFileToInMemTemp += cbToWrite;
7929 g_cWriteFileToInMemTemp++;
7930
7931 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7932 kwSandboxHandlePut(pHandle);
7933 return TRUE;
7934 }
7935
7936 kHlpAssertFailed();
7937 kwSandboxHandlePut(pHandle);
7938 *pcbActuallyWritten = 0;
7939 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7940 return FALSE;
7941 }
7942# endif
7943
7944 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7945 kHlpAssertFailed();
7946 kwSandboxHandlePut(pHandle);
7947 SetLastError(ERROR_ACCESS_DENIED);
7948 *pcbActuallyWritten = 0;
7949 return FALSE;
7950
7951# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7952 /*
7953 * Standard output & error.
7954 */
7955 case KWHANDLETYPE_OUTPUT_BUF:
7956 {
7957 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7958 if (pOutBuf->fIsConsole)
7959 {
7960 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7961 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7962 }
7963 else
7964 {
7965# ifdef WITH_STD_OUT_ERR_BUFFERING
7966 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7967 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7968 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7969# else
7970 kHlpAssertFailed();
7971# endif
7972 }
7973 if (pcbActuallyWritten)
7974 *pcbActuallyWritten = cbToWrite;
7975 g_cbWriteFileTotal += cbToWrite;
7976 kwSandboxHandlePut(pHandle);
7977 return TRUE;
7978 }
7979# endif
7980
7981 default:
7982#ifdef WITH_TEMP_MEMORY_FILES
7983 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7984#endif
7985 kHlpAssertFailed();
7986 kwSandboxHandlePut(pHandle);
7987 SetLastError(ERROR_INVALID_FUNCTION);
7988 *pcbActuallyWritten = 0;
7989 return FALSE;
7990 }
7991 }
7992
7993 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7994 if (fRet && pcbActuallyWritten)
7995 g_cbWriteFileTotal += *pcbActuallyWritten;
7996 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7997 return fRet;
7998}
7999
8000
8001/** Kernel32 - WriteFileEx */
8002static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
8003 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
8004{
8005 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
8006
8007 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
8008 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
8009}
8010
8011#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
8012
8013#ifdef WITH_TEMP_MEMORY_FILES
8014
8015/** Kernel32 - SetEndOfFile; */
8016static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
8017{
8018 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8019 if (pHandle != NULL)
8020 {
8021 switch (pHandle->enmType)
8022 {
8023 case KWHANDLETYPE_TEMP_FILE:
8024 {
8025 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8026 if ( pHandle->offFile > pTempFile->cbFile
8027 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
8028 {
8029 kHlpAssertFailed();
8030 kwSandboxHandlePut(pHandle);
8031 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8032 return FALSE;
8033 }
8034
8035 pTempFile->cbFile = pHandle->offFile;
8036 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
8037 kwSandboxHandlePut(pHandle);
8038 return TRUE;
8039 }
8040
8041 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8042 kHlpAssertFailed();
8043 kwSandboxHandlePut(pHandle);
8044 SetLastError(ERROR_ACCESS_DENIED);
8045 return FALSE;
8046
8047# ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8048 case KWHANDLETYPE_OUTPUT_BUF:
8049 kHlpAssertFailed();
8050 kwSandboxHandlePut(pHandle);
8051 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
8052 return FALSE;
8053# endif
8054
8055 default:
8056 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8057 kHlpAssertFailed();
8058 kwSandboxHandlePut(pHandle);
8059 SetLastError(ERROR_INVALID_FUNCTION);
8060 return FALSE;
8061 }
8062 }
8063
8064 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
8065 return SetEndOfFile(hFile);
8066}
8067
8068
8069/** Kernel32 - GetFileType */
8070static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
8071{
8072 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8073 if (pHandle != NULL)
8074 {
8075 switch (pHandle->enmType)
8076 {
8077 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8078 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
8079 kwSandboxHandlePut(pHandle);
8080 return FILE_TYPE_DISK;
8081
8082 case KWHANDLETYPE_TEMP_FILE:
8083 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
8084 kwSandboxHandlePut(pHandle);
8085 return FILE_TYPE_DISK;
8086
8087#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8088 case KWHANDLETYPE_OUTPUT_BUF:
8089 {
8090 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8091 DWORD fRet;
8092 if (pOutBuf->fFileType != KU8_MAX)
8093 {
8094 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
8095 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
8096 }
8097 else
8098 {
8099 fRet = GetFileType(hFile);
8100 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
8101 }
8102 kwSandboxHandlePut(pHandle);
8103 return fRet;
8104 }
8105#endif
8106 }
8107 kwSandboxHandlePut(pHandle);
8108 }
8109
8110 KWFS_LOG(("GetFileType(%p)\n", hFile));
8111 return GetFileType(hFile);
8112}
8113
8114
8115/** Kernel32 - GetFileSize */
8116static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
8117{
8118 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8119 if (pHandle != NULL)
8120 {
8121 if (pcbHighDword)
8122 *pcbHighDword = 0;
8123 SetLastError(NO_ERROR);
8124 switch (pHandle->enmType)
8125 {
8126 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8127 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8128 kwSandboxHandlePut(pHandle);
8129 return pHandle->u.pCachedFile->cbCached;
8130
8131 case KWHANDLETYPE_TEMP_FILE:
8132 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8133 kwSandboxHandlePut(pHandle);
8134 return pHandle->u.pTempFile->cbFile;
8135
8136 case KWHANDLETYPE_OUTPUT_BUF:
8137 /* do default */
8138 break;
8139
8140 default:
8141 kHlpAssertFailed();
8142 kwSandboxHandlePut(pHandle);
8143 SetLastError(ERROR_INVALID_FUNCTION);
8144 return INVALID_FILE_SIZE;
8145 }
8146 kwSandboxHandlePut(pHandle);
8147 }
8148
8149 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
8150 return GetFileSize(hFile, pcbHighDword);
8151}
8152
8153
8154/** Kernel32 - GetFileSizeEx */
8155static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
8156{
8157 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8158 if (pHandle != NULL)
8159 {
8160 switch (pHandle->enmType)
8161 {
8162 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8163 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8164 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
8165 kwSandboxHandlePut(pHandle);
8166 return TRUE;
8167
8168 case KWHANDLETYPE_TEMP_FILE:
8169 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8170 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
8171 kwSandboxHandlePut(pHandle);
8172 return TRUE;
8173
8174 case KWHANDLETYPE_OUTPUT_BUF:
8175 /* do default */
8176 break;
8177
8178 default:
8179 kHlpAssertFailed();
8180 kwSandboxHandlePut(pHandle);
8181 SetLastError(ERROR_INVALID_FUNCTION);
8182 return INVALID_FILE_SIZE;
8183 }
8184 kwSandboxHandlePut(pHandle);
8185 }
8186
8187 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
8188 return GetFileSizeEx(hFile, pcbFile);
8189}
8190
8191
8192/** Kernel32 - CreateFileMappingW */
8193static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
8194 DWORD fProtect, DWORD dwMaximumSizeHigh,
8195 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
8196{
8197 HANDLE hMapping;
8198 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8199 if (pHandle != NULL)
8200 {
8201 switch (pHandle->enmType)
8202 {
8203 case KWHANDLETYPE_TEMP_FILE:
8204 {
8205 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8206 if ( ( fProtect == PAGE_READONLY
8207 || fProtect == PAGE_EXECUTE_READ)
8208 && dwMaximumSizeHigh == 0
8209 && ( dwMaximumSizeLow == 0
8210 || dwMaximumSizeLow == pTempFile->cbFile)
8211 && pwszName == NULL)
8212 {
8213 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
8214 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
8215 kwSandboxHandlePut(pHandle);
8216 return hMapping;
8217 }
8218 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
8219 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8220 kwSandboxHandlePut(pHandle);
8221 SetLastError(ERROR_ACCESS_DENIED);
8222 return INVALID_HANDLE_VALUE;
8223 }
8224
8225 /* moc.exe benefits from this. */
8226 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8227 {
8228 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8229 if ( ( fProtect == PAGE_READONLY
8230 || fProtect == PAGE_EXECUTE_READ)
8231 && dwMaximumSizeHigh == 0
8232 && ( dwMaximumSizeLow == 0
8233 || dwMaximumSizeLow == pCachedFile->cbCached)
8234 && pwszName == NULL)
8235 {
8236 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
8237 K_FALSE /*fIsFileHandle*/, &hMapping))
8238 { /* likely */ }
8239 else
8240 hMapping = NULL;
8241 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8242 kwSandboxHandlePut(pHandle);
8243 return hMapping;
8244 }
8245
8246 /* Do fallback (for .pch). */
8247 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8248 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8249 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8250
8251 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8252 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8253 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8254 kwSandboxHandlePut(pHandle);
8255 return hMapping;
8256 }
8257
8258 /** @todo read cached memory mapped files too for moc. */
8259 }
8260 kwSandboxHandlePut(pHandle);
8261 }
8262
8263 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8264 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8265 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8266 return hMapping;
8267}
8268
8269
8270/** Kernel32 - MapViewOfFile */
8271static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8272 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8273{
8274 PVOID pvRet;
8275 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8276 if (pHandle != NULL)
8277 {
8278 KU32 idxMapping;
8279
8280 /*
8281 * Ensure one free entry in the mapping tracking table first,
8282 * since this is common to both temporary and cached files.
8283 */
8284 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8285 { /* likely */ }
8286 else
8287 {
8288 void *pvNew;
8289 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8290 if (cNew)
8291 cNew *= 2;
8292 else
8293 cNew = 32;
8294 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8295 if (pvNew)
8296 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8297 else
8298 {
8299 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8300 kwSandboxHandlePut(pHandle);
8301 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8302 return NULL;
8303 }
8304 g_Sandbox.cMemMappingsAlloc = cNew;
8305 }
8306
8307 /*
8308 * Type specific work.
8309 */
8310 switch (pHandle->enmType)
8311 {
8312 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8313 case KWHANDLETYPE_TEMP_FILE:
8314 case KWHANDLETYPE_OUTPUT_BUF:
8315 default:
8316 kHlpAssertFailed();
8317 kwSandboxHandlePut(pHandle);
8318 SetLastError(ERROR_INVALID_OPERATION);
8319 return NULL;
8320
8321 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8322 {
8323 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8324 if ( dwDesiredAccess == FILE_MAP_READ
8325 && offFileHigh == 0
8326 && offFileLow == 0
8327 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8328 {
8329 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8330 if (pTempFile->cSegs != 1)
8331 {
8332 KU32 iSeg;
8333 KU32 cbLeft;
8334 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8335 KU8 *pbAll = NULL;
8336 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8337 if (rc != 0)
8338 {
8339 kHlpAssertFailed();
8340 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8341 return NULL;
8342 }
8343
8344 cbLeft = pTempFile->cbFile;
8345 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8346 {
8347 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8348 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8349 cbLeft -= cbToCopy;
8350 }
8351
8352 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8353 {
8354 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8355 pTempFile->paSegs[iSeg].pbData = NULL;
8356 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8357 }
8358
8359 pTempFile->cSegs = 1;
8360 pTempFile->cbFileAllocated = cbAll;
8361 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8362 pTempFile->paSegs[0].pbData = pbAll;
8363 pTempFile->paSegs[0].offData = 0;
8364 }
8365
8366 pTempFile->cMappings++;
8367 kHlpAssert(pTempFile->cMappings == 1);
8368
8369 pvRet = pTempFile->paSegs[0].pbData;
8370 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8371 break;
8372 }
8373
8374 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8375 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8376 kwSandboxHandlePut(pHandle);
8377 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8378 return NULL;
8379 }
8380
8381 /*
8382 * This is simple in comparison to the above temporary file code.
8383 */
8384 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8385 {
8386 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8387 if ( dwDesiredAccess == FILE_MAP_READ
8388 && offFileHigh == 0
8389 && offFileLow == 0
8390 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8391 {
8392 pvRet = pCachedFile->pbCached;
8393 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8394 break;
8395 }
8396 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8397 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8398 kwSandboxHandlePut(pHandle);
8399 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8400 return NULL;
8401 }
8402 }
8403
8404 /*
8405 * Insert into the mapping tracking table. This is common
8406 * and we should only get here with a non-NULL pvRet.
8407 *
8408 * Note! We could look for duplicates and do ref counting, but it's
8409 * easier to just append for now.
8410 */
8411 kHlpAssert(pvRet != NULL);
8412 idxMapping = g_Sandbox.cMemMappings;
8413 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8414
8415 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8416 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8417 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8418 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8419 g_Sandbox.cMemMappings++;
8420
8421 kwSandboxHandlePut(pHandle);
8422 return pvRet;
8423 }
8424
8425 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8426 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8427 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8428 return pvRet;
8429}
8430
8431
8432/** Kernel32 - MapViewOfFileEx */
8433static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8434 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8435{
8436 PVOID pvRet;
8437 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8438 if (pHandle != NULL)
8439 {
8440 switch (pHandle->enmType)
8441 {
8442 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8443 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8444 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8445 if (!pvMapAddr)
8446 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8447 else
8448 {
8449 kHlpAssertFailed();
8450 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8451 }
8452 kwSandboxHandlePut(pHandle);
8453 return NULL;
8454
8455 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8456 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8457 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8458 if (!pvMapAddr)
8459 {
8460 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8461 kwSandboxHandlePut(pHandle);
8462 return pvRet;
8463 }
8464 /* We can use fallback here as the handle is an actual section handle. */
8465 break;
8466
8467 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8468 case KWHANDLETYPE_TEMP_FILE:
8469 case KWHANDLETYPE_OUTPUT_BUF:
8470 default:
8471 kHlpAssertFailed();
8472 kwSandboxHandlePut(pHandle);
8473 SetLastError(ERROR_INVALID_OPERATION);
8474 return NULL;
8475 }
8476 kwSandboxHandlePut(pHandle);
8477 }
8478
8479 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8480 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8481 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8482 return pvRet;
8483
8484}
8485
8486/** Kernel32 - UnmapViewOfFile */
8487static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8488{
8489 /*
8490 * Consult the memory mapping tracker.
8491 */
8492 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8493 KU32 idxMapping = g_Sandbox.cMemMappings;
8494 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8495 while (idxMapping-- > 0)
8496 if (paMemMappings[idxMapping].pvMapping == pvBase)
8497 {
8498 /* Type specific stuff. */
8499 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8500 {
8501 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8502 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8503 }
8504 else
8505 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8506
8507 /* Deref and probably free it. */
8508 if (--paMemMappings[idxMapping].cRefs == 0)
8509 {
8510 g_Sandbox.cMemMappings--;
8511 if (idxMapping != g_Sandbox.cMemMappings)
8512 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8513 }
8514 return TRUE;
8515 }
8516
8517 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8518 return UnmapViewOfFile(pvBase);
8519}
8520
8521/** @todo UnmapViewOfFileEx */
8522
8523#endif /* WITH_TEMP_MEMORY_FILES */
8524
8525
8526/** Kernel32 - DuplicateHandle */
8527static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8528 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8529{
8530 BOOL fRet;
8531
8532 /*
8533 * We must catch our handles being duplicated.
8534 */
8535 if (hSrcProc == GetCurrentProcess())
8536 {
8537 PKWHANDLE pHandle = kwSandboxHandleGet(hSrc);
8538 if (pHandle)
8539 {
8540 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8541 if (fRet)
8542 {
8543 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8544 {
8545 pHandle->cRefs++;
8546 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8547 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8548 pHandle->enmType, pHandle->cRefs));
8549 }
8550 else
8551 {
8552 fRet = FALSE;
8553 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8554 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8555 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8556 }
8557 }
8558 else
8559 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8560 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8561 kwSandboxHandlePut(pHandle);
8562 return fRet;
8563 }
8564 }
8565
8566 /*
8567 * Not one of ours, just do what the caller asks and log it.
8568 */
8569 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8570 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8571 fInheritHandle, dwOptions, fRet, *phNew));
8572 return fRet;
8573}
8574
8575
8576/** Kernel32 - CloseHandle */
8577static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8578{
8579 BOOL fRet;
8580 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8581 PKWHANDLE pHandle = kwSandboxHandleGet(hObject);
8582 if (pHandle)
8583 {
8584 /* Prevent the closing of the standard output and error handles. */
8585 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8586 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) /* why this?!? */)
8587 {
8588 fRet = CloseHandle(hObject);
8589 if (fRet)
8590 {
8591 EnterCriticalSection(&g_Sandbox.HandlesLock);
8592 pHandle = g_Sandbox.papHandles[idxHandle];
8593 g_Sandbox.papHandles[idxHandle] = NULL;
8594 g_Sandbox.cActiveHandles--;
8595 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8596 if (--pHandle->cRefs == 0)
8597 {
8598#ifdef WITH_TEMP_MEMORY_FILES
8599 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8600 {
8601 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8602 pHandle->u.pTempFile->cActiveHandles--;
8603 }
8604#endif
8605 kHlpFree(pHandle);
8606 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8607 }
8608 else
8609 {
8610 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8611 kwSandboxHandlePut(pHandle);
8612 }
8613 LeaveCriticalSection(&g_Sandbox.HandlesLock);
8614 return fRet;
8615 }
8616 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8617 }
8618 else
8619 {
8620#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8621 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8622 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8623#else
8624 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of stdXXX!\n", hObject));
8625#endif
8626 fRet = TRUE;
8627 }
8628 kwSandboxHandlePut(pHandle);
8629 return fRet;
8630 }
8631
8632 fRet = CloseHandle(hObject);
8633 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8634 return fRet;
8635}
8636
8637
8638/** Kernel32 - GetFileAttributesA. */
8639static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8640{
8641 DWORD fRet;
8642 const char *pszExt = kHlpGetExt(pszFilename);
8643 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8644 {
8645 KFSLOOKUPERROR enmError;
8646 PKFSOBJ pFsObj;
8647 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8648
8649 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8650 if (pFsObj)
8651 {
8652 kHlpAssert(pFsObj->fHaveStats);
8653 fRet = pFsObj->Stats.st_attribs;
8654 kFsCacheObjRelease(g_pFsCache, pFsObj);
8655 }
8656 else
8657 {
8658 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8659 fRet = INVALID_FILE_ATTRIBUTES;
8660 }
8661
8662 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8663 return fRet;
8664 }
8665
8666 fRet = GetFileAttributesA(pszFilename);
8667 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8668 return fRet;
8669}
8670
8671
8672/** Kernel32 - GetFileAttributesW. */
8673static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8674{
8675 DWORD fRet;
8676 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8677 {
8678 KFSLOOKUPERROR enmError;
8679 PKFSOBJ pFsObj;
8680 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8681
8682 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8683 if (pFsObj)
8684 {
8685 kHlpAssert(pFsObj->fHaveStats);
8686 fRet = pFsObj->Stats.st_attribs;
8687 kFsCacheObjRelease(g_pFsCache, pFsObj);
8688 }
8689 else
8690 {
8691 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8692 fRet = INVALID_FILE_ATTRIBUTES;
8693 }
8694#ifndef NDEBUG
8695 {
8696 DWORD fCheck = GetFileAttributesW(pwszFilename);
8697 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8698 }
8699#endif
8700 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8701 return fRet;
8702 }
8703
8704 fRet = GetFileAttributesW(pwszFilename);
8705 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8706 return fRet;
8707}
8708
8709
8710/** Kernel32 - GetFileAttributesExA. */
8711static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8712 WIN32_FILE_ATTRIBUTE_DATA *pData)
8713{
8714 BOOL fRet;
8715 const char *pszExt = kHlpGetExt(pszFilename);
8716 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8717 {
8718 KFSLOOKUPERROR enmError;
8719 PKFSOBJ pFsObj;
8720 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8721
8722 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8723 if (pFsObj)
8724 {
8725 kHlpAssert(pFsObj->fHaveStats);
8726 if (enmLevel == GetFileExInfoStandard)
8727 {
8728 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8729 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8730 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8731 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8732 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8733 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8734 kFsCacheObjRelease(g_pFsCache, pFsObj);
8735 fRet = TRUE;
8736 }
8737 else
8738 {
8739 kFsCacheObjRelease(g_pFsCache, pFsObj);
8740 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8741 }
8742 }
8743 else
8744 {
8745 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8746 fRet = FALSE;
8747 }
8748
8749#ifdef K_STRICT
8750 {
8751 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8752 DWORD const dwErrSaved = GetLastError();
8753 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8754 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8755 if (fRetCheck && fRet)
8756 {
8757# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8758 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8759 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8760 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8761 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8762 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8763 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8764 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8765 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8766 }
8767 else
8768 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8769 SetLastError(dwErrSaved);
8770 }
8771#endif
8772 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8773 return fRet;
8774 }
8775
8776 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8777 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8778 return fRet;
8779}
8780
8781
8782/** Kernel32 - GetFileAttributesExW. */
8783static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8784 WIN32_FILE_ATTRIBUTE_DATA *pData)
8785{
8786 BOOL fRet;
8787 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8788 {
8789 KFSLOOKUPERROR enmError;
8790 PKFSOBJ pFsObj;
8791 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8792
8793 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8794 if (pFsObj)
8795 {
8796 kHlpAssert(pFsObj->fHaveStats);
8797 if (enmLevel == GetFileExInfoStandard)
8798 {
8799 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8800 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8801 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8802 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8803 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8804 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8805 kFsCacheObjRelease(g_pFsCache, pFsObj);
8806 fRet = TRUE;
8807 }
8808 else
8809 {
8810 kFsCacheObjRelease(g_pFsCache, pFsObj);
8811 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8812 }
8813 }
8814 else
8815 {
8816 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8817 fRet = FALSE;
8818 }
8819
8820#ifdef K_STRICT
8821 {
8822 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8823 DWORD const dwErrSaved = GetLastError();
8824 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8825 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8826 if (fRetCheck && fRet)
8827 {
8828# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8829 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8830 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8831 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8832 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8833 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8834 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8835 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8836 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8837 }
8838 else
8839 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8840 SetLastError(dwErrSaved);
8841 }
8842#endif
8843 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8844 return fRet;
8845 }
8846
8847 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8848 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8849 return fRet;
8850}
8851
8852
8853/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8854 * directory containing each include file. We cache the result to speed
8855 * things up a little. */
8856static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8857{
8858 DWORD cwcRet;
8859 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8860 {
8861 KFSLOOKUPERROR enmError;
8862 PKFSOBJ pObj;
8863 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8864
8865 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8866 if (pObj)
8867 {
8868 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8869 {
8870 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8871 {
8872 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8873
8874 /* Should preserve trailing slash on directory paths. */
8875 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8876 {
8877 if ( cwcRet + 1 < cwcShortPath
8878 && pwszShortPath[cwcRet - 1] != '\\')
8879 {
8880 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8881 if ( cwcIn > 0
8882 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8883 {
8884 pwszShortPath[cwcRet++] = '\\';
8885 pwszShortPath[cwcRet] = '\0';
8886 }
8887 }
8888 }
8889
8890 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8891 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8892 kFsCacheObjRelease(g_pFsCache, pObj);
8893 return cwcRet;
8894 }
8895
8896 /* fall back for complicated cases. */
8897 }
8898 kFsCacheObjRelease(g_pFsCache, pObj);
8899 }
8900 }
8901 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8902 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8903 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8904 return cwcRet;
8905}
8906
8907
8908#ifdef WITH_TEMP_MEMORY_FILES
8909/** Kernel32 - DeleteFileW
8910 * Skip deleting the in-memory files. */
8911static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8912{
8913 BOOL fRc;
8914 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8915 && kwFsIsClTempFileW(pwszFilename))
8916 {
8917 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8918 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8919 fRc = TRUE;
8920 }
8921 else
8922 {
8923 fRc = DeleteFileW(pwszFilename);
8924 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8925 }
8926 return fRc;
8927}
8928#endif /* WITH_TEMP_MEMORY_FILES */
8929
8930
8931
8932#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8933
8934/*
8935 *
8936 * Console output buffering.
8937 * Console output buffering.
8938 * Console output buffering.
8939 *
8940 */
8941
8942
8943/**
8944 * Write a wide char string to the console.
8945 *
8946 * @param pSandbox The sandbox which output buffer to flush.
8947 */
8948static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8949{
8950 if (cwcToWrite > 0)
8951 {
8952 DWORD cwcWritten = 0;
8953 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8954 {
8955 if (cwcWritten == cwcToWrite)
8956 { /* likely */ }
8957 else
8958 {
8959 DWORD off = 0;
8960 do
8961 {
8962 off += cwcWritten;
8963 cwcWritten = 0;
8964 } while ( off < cwcToWrite
8965 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8966 kHlpAssert(off == cwcWritten);
8967 }
8968 }
8969 else
8970 kHlpAssertFailed();
8971 pSandbox->Combined.cFlushes++;
8972 }
8973}
8974
8975
8976/**
8977 * Flushes the combined console output buffer.
8978 *
8979 * @param pSandbox The sandbox which output buffer to flush.
8980 */
8981static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8982{
8983 if (pSandbox->Combined.cwcBuf > 0)
8984 {
8985 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8986 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8987 pSandbox->Combined.cwcBuf = 0;
8988 }
8989}
8990
8991
8992/**
8993 * For handling combined buffer overflow cases line by line.
8994 *
8995 * @param pSandbox The sandbox.
8996 * @param pwcBuf What to add to the combined buffer. Usually a
8997 * line, unless we're really low on buffer space.
8998 * @param cwcBuf The length of what to add.
8999 * @param fBrokenLine Whether this is a broken line.
9000 */
9001static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
9002{
9003 if (fBrokenLine)
9004 kwSandboxConsoleFlushCombined(pSandbox);
9005 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
9006 {
9007 kwSandboxConsoleFlushCombined(pSandbox);
9008 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
9009 }
9010 else
9011 {
9012 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
9013 pSandbox->Combined.cwcBuf += cwcBuf;
9014 }
9015}
9016
9017
9018/**
9019 * Called to final flush a line buffer via the combined buffer (if applicable).
9020 *
9021 * @param pSandbox The sandbox.
9022 * @param pLineBuf The line buffer.
9023 * @param pszName The line buffer name (for logging)
9024 */
9025static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
9026{
9027 if (pLineBuf->fIsConsole)
9028 {
9029 if (pLineBuf->u.Con.cwcBuf > 0)
9030 {
9031 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
9032
9033 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
9034 {
9035 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
9036 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
9037 }
9038 else
9039 {
9040 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
9041 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
9042 }
9043 pLineBuf->u.Con.cwcBuf = 0;
9044 }
9045 }
9046#ifdef WITH_STD_OUT_ERR_BUFFERING
9047 else if (pLineBuf->u.Fully.cchBuf > 0)
9048 {
9049 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
9050
9051 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
9052 pLineBuf->u.Fully.cchBuf = 0;
9053 }
9054#endif
9055}
9056
9057
9058/**
9059 * Called at the end of sandboxed execution to flush both stream buffers.
9060 *
9061 * @param pSandbox The sandbox.
9062 */
9063static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
9064{
9065 /*
9066 * First do the cl.exe source file supression trick, if applicable.
9067 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
9068 * handle.
9069 */
9070 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9071 && pSandbox->Combined.cFlushes == 0)
9072 {
9073 if ( pSandbox->StdOut.fIsConsole
9074 || pSandbox->StdErr.fIsConsole)
9075 {
9076 if ( pSandbox->Combined.cwcBuf >= 3
9077 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
9078 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
9079 {
9080 KI32 off = pSandbox->Combined.cwcBuf - 1;
9081 if (pSandbox->Combined.wszBuf[off] == '\n')
9082 {
9083 KBOOL fOk = K_TRUE;
9084 while (off-- > 0)
9085 {
9086 wchar_t const wc = pSandbox->Combined.wszBuf[off];
9087 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
9088 { /* likely */ }
9089 else
9090 {
9091 fOk = K_FALSE;
9092 break;
9093 }
9094 }
9095 if (fOk)
9096 {
9097 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
9098 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9099 pSandbox->Combined.cwcBuf = 0;
9100 return;
9101 }
9102 }
9103 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
9104 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9105 }
9106 }
9107#ifdef WITH_STD_OUT_ERR_BUFFERING
9108 /*
9109 * Otherwise, it goes to standard output (redirected).
9110 */
9111 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
9112 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
9113 {
9114 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
9115 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
9116 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
9117
9118 if (pchBuf[off] == '\n')
9119 {
9120 KBOOL fOk = K_TRUE;
9121 if (pchBuf[off - 1] == '\r')
9122 off--;
9123 while (off-- > 0)
9124 {
9125 char const ch = pchBuf[off];
9126 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
9127 { /* likely */ }
9128 else
9129 {
9130 fOk = K_FALSE;
9131 break;
9132 }
9133 }
9134 if (fOk)
9135 {
9136 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
9137 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9138 pSandbox->StdOut.u.Fully.cchBuf = 0;
9139 return;
9140 }
9141 }
9142 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
9143 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9144 }
9145#endif
9146 }
9147
9148 /*
9149 * Flush the two line buffer, then the combined buffer.
9150 */
9151 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
9152 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
9153 kwSandboxConsoleFlushCombined(pSandbox);
9154}
9155
9156
9157/**
9158 * Writes a string to the given output stream.
9159 *
9160 * @param pSandbox The sandbox.
9161 * @param pLineBuf The line buffer for the output stream.
9162 * @param pwcBuffer The buffer to write.
9163 * @param cwcToWrite The number of wchar_t's in the buffer.
9164 */
9165static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
9166{
9167 kHlpAssert(pLineBuf->fIsConsole);
9168 if (cwcToWrite > 0)
9169 {
9170 /*
9171 * First, find the start of the last incomplete line so we can figure
9172 * out how much line buffering we need to do.
9173 */
9174 KU32 cchLastIncompleteLine;
9175 KU32 offLastIncompleteLine = cwcToWrite;
9176 while ( offLastIncompleteLine > 0
9177 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
9178 offLastIncompleteLine--;
9179 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
9180
9181 /* Was there anything to line buffer? */
9182 if (offLastIncompleteLine < cwcToWrite)
9183 {
9184 /* Need to grow the line buffer? */
9185 KU32 cwcNeeded = offLastIncompleteLine == 0
9186 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
9187 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
9188 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
9189 {
9190 void *pvNew;
9191 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
9192 while (cwcNew < cwcNeeded)
9193 cwcNew *= 2;
9194 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
9195 if (pvNew)
9196 {
9197 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9198 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
9199 }
9200 else
9201 {
9202 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
9203 if (pvNew)
9204 {
9205 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9206 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
9207 }
9208 else
9209 {
9210 /* This isn't perfect, but it will have to do for now. */
9211 if (pLineBuf->u.Con.cwcBuf > 0)
9212 {
9213 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9214 K_TRUE /*fBrokenLine*/);
9215 pLineBuf->u.Con.cwcBuf = 0;
9216 }
9217 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
9218 return;
9219 }
9220 }
9221 }
9222
9223 /*
9224 * Handle the case where we only add to the line buffer.
9225 */
9226 if (offLastIncompleteLine == 0)
9227 {
9228 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
9229 pLineBuf->u.Con.cwcBuf += cwcToWrite;
9230 return;
9231 }
9232 }
9233
9234 /*
9235 * If there is sufficient combined buffer to handle this request, this is rather simple.
9236 */
9237 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
9238 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9239 {
9240 if (pLineBuf->u.Con.cwcBuf > 0)
9241 {
9242 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9243 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9244 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9245 pLineBuf->u.Con.cwcBuf = 0;
9246 }
9247
9248 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9249 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9250 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9251 }
9252 else
9253 {
9254 /*
9255 * Do line-by-line processing of the input, flusing the combined buffer
9256 * when it becomes necessary. We may have to write lines
9257 */
9258 KU32 off = 0;
9259 KU32 offNextLine = 0;
9260
9261 /* If there are buffered chars, we handle the first line outside the
9262 main loop. We must try our best outputting it as a complete line. */
9263 if (pLineBuf->u.Con.cwcBuf > 0)
9264 {
9265 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9266 offNextLine++;
9267 offNextLine++;
9268 kHlpAssert(offNextLine <= offLastIncompleteLine);
9269
9270 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9271 {
9272 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9273 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9274 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9275 pLineBuf->u.Con.cwcBuf = 0;
9276
9277 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9278 pSandbox->Combined.cwcBuf += offNextLine;
9279 }
9280 else
9281 {
9282 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9283 if (cwcLeft > 0)
9284 {
9285 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9286 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9287 pLineBuf->u.Con.cwcBuf += cwcCopy;
9288 off += cwcCopy;
9289 }
9290 if (pLineBuf->u.Con.cwcBuf > 0)
9291 {
9292 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9293 K_TRUE /*fBrokenLine*/);
9294 pLineBuf->u.Con.cwcBuf = 0;
9295 }
9296 if (off < offNextLine)
9297 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9298 }
9299 off = offNextLine;
9300 }
9301
9302 /* Deal with the remaining lines */
9303 while (off < offLastIncompleteLine)
9304 {
9305 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9306 offNextLine++;
9307 offNextLine++;
9308 kHlpAssert(offNextLine <= offLastIncompleteLine);
9309 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9310 off = offNextLine;
9311 }
9312 }
9313
9314 /*
9315 * Buffer any remaining incomplete line chars.
9316 */
9317 if (cchLastIncompleteLine)
9318 {
9319 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9320 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9321 }
9322 }
9323}
9324
9325
9326/**
9327 * Worker for WriteConsoleA and WriteFile.
9328 *
9329 * @param pSandbox The sandbox.
9330 * @param pLineBuf The line buffer.
9331 * @param pchBuffer What to write.
9332 * @param cchToWrite How much to write.
9333 */
9334static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9335{
9336 /*
9337 * Convert it to wide char and use the 'W' to do the work.
9338 */
9339 int cwcRet;
9340 KU32 cwcBuf = cchToWrite * 2 + 1;
9341 wchar_t *pwcBufFree = NULL;
9342 wchar_t *pwcBuf;
9343 kHlpAssert(pLineBuf->fIsConsole);
9344
9345 if (cwcBuf <= 4096)
9346 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9347 else
9348 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9349
9350 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9351 if (cwcRet > 0)
9352 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9353 else
9354 {
9355 DWORD cchWritten;
9356 kHlpAssertFailed();
9357
9358 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9359 if (pLineBuf->u.Con.cwcBuf > 0)
9360 {
9361 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9362 pLineBuf->u.Con.cwcBuf = 0;
9363 }
9364 kwSandboxConsoleFlushCombined(pSandbox);
9365
9366 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9367 {
9368 if (cchWritten >= cchToWrite)
9369 { /* likely */ }
9370 else
9371 {
9372 KU32 off = 0;
9373 do
9374 {
9375 off += cchWritten;
9376 cchWritten = 0;
9377 } while ( off < cchToWrite
9378 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9379 }
9380 }
9381 }
9382
9383 if (pwcBufFree)
9384 kHlpFree(pwcBufFree);
9385}
9386
9387
9388/** Kernel32 - WriteConsoleA */
9389BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9390 PVOID pvReserved)
9391{
9392 BOOL fRc;
9393 PKWOUTPUTSTREAMBUF pLineBuf;
9394 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9395
9396 if (hConOutput == g_Sandbox.StdErr.hOutput)
9397 pLineBuf = &g_Sandbox.StdErr;
9398 else
9399 pLineBuf = &g_Sandbox.StdOut;
9400 if (pLineBuf->fIsConsole)
9401 {
9402 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9403
9404 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9405 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9406 if (pcbWritten)
9407 *pcbWritten = cbToWrite;
9408 fRc = TRUE;
9409 }
9410 else
9411 {
9412 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9413 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9414 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9415 }
9416 return fRc;
9417}
9418
9419
9420/** Kernel32 - WriteConsoleW */
9421BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9422 PVOID pvReserved)
9423{
9424 BOOL fRc;
9425 PKWOUTPUTSTREAMBUF pLineBuf;
9426 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9427
9428 if (hConOutput == g_Sandbox.StdErr.hOutput)
9429 pLineBuf = &g_Sandbox.StdErr;
9430 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9431 pLineBuf = &g_Sandbox.StdOut;
9432 else
9433 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9434 if (pLineBuf->fIsConsole)
9435 {
9436 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9437
9438 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9439 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9440 if (pcwcWritten)
9441 *pcwcWritten = cwcToWrite;
9442 fRc = TRUE;
9443 }
9444 else
9445 {
9446 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9447 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9448 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9449 }
9450 return fRc;
9451}
9452
9453#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9454
9455
9456
9457/*
9458 *
9459 * Virtual memory leak prevension.
9460 * Virtual memory leak prevension.
9461 * Virtual memory leak prevension.
9462 *
9463 */
9464
9465#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9466
9467/** For debug logging. */
9468# ifndef NDEBUG
9469static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9470{
9471 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9472 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9473 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9474 if (cbMemInfo != 0)
9475 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9476 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9477 MemInfo.BaseAddress,
9478 MemInfo.AllocationBase,
9479 MemInfo.RegionSize,
9480 MemInfo.State,
9481 MemInfo.Protect,
9482 MemInfo.Type));
9483}
9484# else
9485# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9486# endif
9487
9488/**
9489 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9490 *
9491 * @param idxFixed The fixed allocation index to "free".
9492 */
9493static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9494{
9495 BOOL fRc;
9496 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9497 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9498 kHlpAssert(fRc); K_NOREF(fRc);
9499 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9500 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9501}
9502
9503#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9504
9505
9506/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9507 * location (~78MB in 32-bit 2010 compiler). */
9508static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9509{
9510 PVOID pvMem;
9511 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9512 {
9513 KU32 idxPreAllocated = KU32_MAX;
9514
9515#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9516 /*
9517 * Look for a pre-reserved CL.exe heap allocation.
9518 */
9519 pvMem = NULL;
9520 if ( pvAddr != 0
9521 && (fAllocType & MEM_RESERVE))
9522 {
9523 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9524 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9525 while (idxFixed-- > 0)
9526 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9527 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9528 {
9529 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9530 {
9531 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9532 {
9533 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9534 pvMem = pvAddr;
9535 idxPreAllocated = idxFixed;
9536 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9537 pvAddr, cb, fAllocType, fProt, pvMem));
9538 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9539 SetLastError(NO_ERROR);
9540 break;
9541 }
9542 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9543 }
9544 else
9545 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9546 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9547 }
9548 }
9549 if (!pvMem)
9550#endif
9551 {
9552 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9553 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9554 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9555 if ( pvAddr
9556 && pvAddr != pvMem
9557 && !( fAllocType == MEM_RESERVE /* After mapping the PCH, VS2019 ends up here (happens */
9558 && fProt == PAGE_READWRITE /* in real cl.exe runs too). Just shut it up to avoid confusion. */
9559#if K_ARCH_BITS >= 64
9560 && cb > 0x10000000 /* seen 67c00000, 33e00000, ++ */
9561#else
9562 && cb > 0x04000000 /* no idea */
9563#endif
9564 )
9565 )
9566 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9567 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9568 }
9569
9570 if (pvMem)
9571 {
9572 /*
9573 * Track it.
9574 */
9575 PKWVIRTALLOC pTracker;
9576
9577 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9578 pTracker = g_Sandbox.pVirtualAllocHead;
9579 while ( pTracker
9580 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9581 pTracker = pTracker->pNext;
9582 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9583 if (!pTracker)
9584 {
9585 DWORD dwErr = GetLastError();
9586 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9587 if (pTracker)
9588 {
9589 pTracker->pvAlloc = pvMem;
9590 pTracker->cbAlloc = cb;
9591 pTracker->idxPreAllocated = idxPreAllocated;
9592 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9593 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9594 g_Sandbox.pVirtualAllocHead = pTracker;
9595 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9596 }
9597 SetLastError(dwErr);
9598 }
9599 }
9600 }
9601 else
9602 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9603 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9604 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9605 return pvMem;
9606}
9607
9608
9609/** Kernel32 - VirtualFree. */
9610static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9611{
9612 BOOL fRc;
9613 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9614 {
9615 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9616 if (dwFreeType & MEM_RELEASE)
9617 {
9618 PKWVIRTALLOC pTracker;
9619 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9620 pTracker = g_Sandbox.pVirtualAllocHead;
9621 if (pTracker)
9622 {
9623 if (pTracker->pvAlloc == pvAddr)
9624 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9625 else
9626 {
9627 PKWVIRTALLOC pPrev;
9628 do
9629 {
9630 pPrev = pTracker;
9631 pTracker = pTracker->pNext;
9632 } while (pTracker && pTracker->pvAlloc != pvAddr);
9633 if (pTracker)
9634 pPrev->pNext = pTracker->pNext;
9635 }
9636 if (pTracker)
9637 {
9638#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9639 if (pTracker->idxPreAllocated != KU32_MAX)
9640 {
9641 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9642 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9643 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9644 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9645 kHlpFree(pTracker);
9646 return TRUE;
9647 }
9648#endif
9649
9650 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9651 if (fRc)
9652 kHlpFree(pTracker);
9653 else
9654 {
9655 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9656 g_Sandbox.pVirtualAllocHead = pTracker;
9657 }
9658 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9659 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9660 return fRc;
9661 }
9662
9663 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9664 }
9665 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9666 }
9667 }
9668
9669#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9670 /*
9671 * Protect our fixed allocations (this isn't just paranoia, btw.).
9672 */
9673 if (dwFreeType & MEM_RELEASE)
9674 {
9675 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9676 while (idxFixed-- > 0)
9677 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9678 {
9679 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9680 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9681 return TRUE;
9682 }
9683 }
9684#endif
9685
9686 /*
9687 * Not tracker or not actually free the virtual range.
9688 */
9689 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9690 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9691 return fRc;
9692}
9693
9694
9695/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9696HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9697{
9698 HANDLE hHeap;
9699 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9700
9701 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9702 if (hHeap != NULL)
9703 {
9704 DWORD dwErr = GetLastError();
9705 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9706 if (pTracker)
9707 {
9708 pTracker->hHeap = hHeap;
9709 pTracker->pNext = g_Sandbox.pHeapHead;
9710 g_Sandbox.pHeapHead = pTracker;
9711 }
9712
9713 SetLastError(dwErr);
9714 }
9715 return hHeap;
9716
9717}
9718
9719
9720/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9721BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9722{
9723 BOOL fRc = HeapDestroy(hHeap);
9724 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9725 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9726 if (fRc)
9727 {
9728 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9729 if (pTracker)
9730 {
9731 if (pTracker->hHeap == hHeap)
9732 g_Sandbox.pHeapHead = pTracker->pNext;
9733 else
9734 {
9735 PKWHEAP pPrev;
9736 do
9737 {
9738 pPrev = pTracker;
9739 pTracker = pTracker->pNext;
9740 } while (pTracker && pTracker->hHeap == hHeap);
9741 if (pTracker)
9742 pPrev->pNext = pTracker->pNext;
9743 }
9744 if (pTracker)
9745 kHlpFree(pTracker);
9746 else
9747 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9748 }
9749 }
9750
9751 return fRc;
9752}
9753
9754
9755
9756/*
9757 *
9758 * Thread/Fiber local storage leak prevention.
9759 * Thread/Fiber local storage leak prevention.
9760 * Thread/Fiber local storage leak prevention.
9761 *
9762 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9763 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9764 * we're leaking these indexes, but more importantely we crash during
9765 * worker exit since the callback is triggered multiple times.
9766 */
9767
9768
9769/** Kernel32 - FlsAlloc */
9770DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9771{
9772 DWORD idxFls = FlsAlloc(pfnCallback);
9773 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9774 if (idxFls != FLS_OUT_OF_INDEXES)
9775 {
9776 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9777 if (pTracker)
9778 {
9779 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9780 pTracker->idx = idxFls;
9781 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9782 g_Sandbox.pFlsAllocHead = pTracker;
9783 }
9784 }
9785
9786 return idxFls;
9787}
9788
9789/** Kernel32 - FlsFree */
9790BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9791{
9792 BOOL fRc = FlsFree(idxFls);
9793 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9794 if (fRc)
9795 {
9796 PKWLOCALSTORAGE pTracker;
9797 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9798
9799 pTracker = g_Sandbox.pFlsAllocHead;
9800 if (pTracker)
9801 {
9802 if (pTracker->idx == idxFls)
9803 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9804 else
9805 {
9806 PKWLOCALSTORAGE pPrev;
9807 do
9808 {
9809 pPrev = pTracker;
9810 pTracker = pTracker->pNext;
9811 } while (pTracker && pTracker->idx != idxFls);
9812 if (pTracker)
9813 pPrev->pNext = pTracker->pNext;
9814 }
9815 if (pTracker)
9816 {
9817 pTracker->idx = FLS_OUT_OF_INDEXES;
9818 pTracker->pNext = NULL;
9819 kHlpFree(pTracker);
9820 }
9821 }
9822 }
9823 return fRc;
9824}
9825
9826
9827/** Kernel32 - TlsAlloc */
9828DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9829{
9830 DWORD idxTls = TlsAlloc();
9831 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9832 if (idxTls != TLS_OUT_OF_INDEXES)
9833 {
9834 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9835 if (pTracker)
9836 {
9837 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9838 pTracker->idx = idxTls;
9839 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9840 g_Sandbox.pTlsAllocHead = pTracker;
9841 }
9842 }
9843
9844 return idxTls;
9845}
9846
9847/** Kernel32 - TlsFree */
9848BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9849{
9850 BOOL fRc = TlsFree(idxTls);
9851 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9852 if (fRc)
9853 {
9854 PKWLOCALSTORAGE pTracker;
9855 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9856
9857 pTracker = g_Sandbox.pTlsAllocHead;
9858 if (pTracker)
9859 {
9860 if (pTracker->idx == idxTls)
9861 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9862 else
9863 {
9864 PKWLOCALSTORAGE pPrev;
9865 do
9866 {
9867 pPrev = pTracker;
9868 pTracker = pTracker->pNext;
9869 } while (pTracker && pTracker->idx != idxTls);
9870 if (pTracker)
9871 pPrev->pNext = pTracker->pNext;
9872 }
9873 if (pTracker)
9874 {
9875 pTracker->idx = TLS_OUT_OF_INDEXES;
9876 pTracker->pNext = NULL;
9877 kHlpFree(pTracker);
9878 }
9879 }
9880 }
9881 return fRc;
9882}
9883
9884
9885
9886/*
9887 *
9888 * Header file hashing.
9889 * Header file hashing.
9890 * Header file hashing.
9891 *
9892 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9893 * indicated that ~12% of the time was spent doing MD5 caluclation when
9894 * rebuiling openssl. The hashing it done right after reading the source
9895 * via ReadFile, same buffers and sizes.
9896 */
9897
9898#ifdef WITH_HASH_MD5_CACHE
9899
9900/** AdvApi32 - CryptCreateHash */
9901static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9902 HCRYPTHASH *phHash)
9903{
9904 BOOL fRc;
9905
9906 /*
9907 * Only do this for cl.exe when it request normal MD5.
9908 */
9909 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9910 {
9911 /** @todo do generic caching of hash results. Need SHA-1 and SHA-256 for
9912 * more recent compilers. */
9913 if (idAlg == CALG_MD5)
9914 {
9915 if (hKey == 0)
9916 {
9917 if (dwFlags == 0)
9918 {
9919 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9920 if (pHash)
9921 {
9922 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9923 pHash->uMagic = KWHASHMD5_MAGIC;
9924 pHash->cbHashed = 0;
9925 pHash->fGoneBad = K_FALSE;
9926 pHash->fFallbackMode = K_FALSE;
9927 pHash->fFinal = K_FALSE;
9928
9929 /* link it. */
9930 pHash->pNext = g_Sandbox.pHashHead;
9931 g_Sandbox.pHashHead = pHash;
9932
9933 *phHash = (KUPTR)pHash;
9934 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9935 hProv, *phHash, TRUE));
9936 return TRUE;
9937 }
9938
9939 kwErrPrintf("CryptCreateHash: out of memory!\n");
9940 }
9941 else
9942 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9943 }
9944 else
9945 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9946 }
9947 //else
9948 // kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9949 }
9950
9951 /*
9952 * Fallback.
9953 */
9954 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9955 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9956 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9957 return fRc;
9958}
9959
9960
9961/** AdvApi32 - CryptHashData */
9962static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9963{
9964 BOOL fRc;
9965 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9966 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9967 while (pHash && (KUPTR)pHash != hHash)
9968 pHash = pHash->pNext;
9969 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9970 hHash, pHash, pbData, cbData, dwFlags));
9971 if (pHash)
9972 {
9973 /*
9974 * Validate the state.
9975 */
9976 if ( pHash->uMagic == KWHASHMD5_MAGIC
9977 && !pHash->fFinal)
9978 {
9979 if (!pHash->fFallbackMode)
9980 {
9981 /*
9982 * Does this match the previous ReadFile call to a cached file?
9983 * If it doesn't, try falling back.
9984 */
9985 if ( g_Sandbox.LastHashRead.cbRead == cbData
9986 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9987 {
9988 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9989 if ( pCachedFile
9990 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9991 {
9992
9993 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9994 {
9995 if ( pHash->pCachedFile == NULL
9996 && pHash->cbHashed == 0)
9997 pHash->pCachedFile = pCachedFile;
9998 if (pHash->pCachedFile == pCachedFile)
9999 {
10000 pHash->cbHashed += cbData;
10001 g_Sandbox.LastHashRead.pCachedFile = NULL;
10002 g_Sandbox.LastHashRead.pvRead = NULL;
10003 g_Sandbox.LastHashRead.cbRead = 0;
10004 g_Sandbox.LastHashRead.offRead = 0;
10005 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
10006 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
10007 return TRUE;
10008 }
10009
10010 /* Note! it's possible to fall back here too, if necessary. */
10011 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
10012 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
10013 }
10014 else
10015 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
10016 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
10017 }
10018 else if (!pCachedFile)
10019 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
10020 else
10021 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
10022 }
10023 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
10024 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
10025 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
10026 if (pHash->cbHashed == 0)
10027 pHash->fFallbackMode = K_TRUE;
10028 if (pHash->fFallbackMode)
10029 {
10030 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
10031 pHash->fFallbackMode = K_TRUE;
10032 MD5Init(&pHash->Md5Ctx);
10033 MD5Update(&pHash->Md5Ctx, pbData, cbData);
10034 pHash->cbHashed = cbData;
10035 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
10036 hHash, pbData, cbData, dwFlags));
10037 return TRUE;
10038 }
10039 pHash->fGoneBad = K_TRUE;
10040 SetLastError(ERROR_INVALID_PARAMETER);
10041 fRc = FALSE;
10042 }
10043 else
10044 {
10045 /* fallback. */
10046 MD5Update(&pHash->Md5Ctx, pbData, cbData);
10047 pHash->cbHashed += cbData;
10048 fRc = TRUE;
10049 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
10050 hHash, pbData, cbData, dwFlags));
10051 }
10052 }
10053 /*
10054 * Bad handle state.
10055 */
10056 else
10057 {
10058 if (pHash->uMagic != KWHASHMD5_MAGIC)
10059 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
10060 else
10061 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
10062 SetLastError((DWORD)NTE_BAD_HASH);
10063 fRc = FALSE;
10064 }
10065 }
10066 else
10067 {
10068
10069 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
10070 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
10071 }
10072 return fRc;
10073}
10074
10075
10076/** AdvApi32 - CryptGetHashParam */
10077static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
10078 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
10079{
10080 BOOL fRc;
10081 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
10082 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10083 while (pHash && (KUPTR)pHash != hHash)
10084 pHash = pHash->pNext;
10085 if (pHash)
10086 {
10087 if (pHash->uMagic == KWHASHMD5_MAGIC)
10088 {
10089 if (dwFlags == 0)
10090 {
10091 DWORD cbRet;
10092 void *pvRet;
10093 union
10094 {
10095 DWORD dw;
10096 } uBuf;
10097
10098 switch (dwParam)
10099 {
10100 case HP_HASHVAL:
10101 {
10102 /* Check the hash progress. */
10103 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
10104 if (pCachedFile)
10105 {
10106 if ( pCachedFile->cbCached == pHash->cbHashed
10107 && !pHash->fGoneBad)
10108 {
10109 if (pCachedFile->fValidMd5)
10110 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
10111 else
10112 {
10113 MD5Init(&pHash->Md5Ctx);
10114 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
10115 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
10116 pCachedFile->fValidMd5 = K_TRUE;
10117 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
10118 }
10119 pvRet = pCachedFile->abMd5Digest;
10120 }
10121 else
10122 {
10123 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
10124 from what I can tell, so just deal with it. */
10125 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
10126 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
10127 pHash, pCachedFile, pCachedFile->szPath));
10128 pHash->fFallbackMode = K_TRUE;
10129 pHash->pCachedFile = NULL;
10130 MD5Init(&pHash->Md5Ctx);
10131 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
10132 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
10133 pvRet = pHash->abDigest;
10134 }
10135 pHash->fFinal = K_TRUE;
10136 cbRet = 16;
10137 break;
10138 }
10139 else if (pHash->fFallbackMode)
10140 {
10141 if (!pHash->fFinal)
10142 {
10143 pHash->fFinal = K_TRUE;
10144 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
10145 }
10146 pvRet = pHash->abDigest;
10147 cbRet = 16;
10148 break;
10149 }
10150 else
10151 {
10152 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
10153 SetLastError(ERROR_INVALID_SERVER_STATE);
10154 }
10155 return FALSE;
10156 }
10157
10158 case HP_HASHSIZE:
10159 uBuf.dw = 16;
10160 pvRet = &uBuf;
10161 cbRet = sizeof(DWORD);
10162 break;
10163
10164 case HP_ALGID:
10165 uBuf.dw = CALG_MD5;
10166 pvRet = &uBuf;
10167 cbRet = sizeof(DWORD);
10168 break;
10169
10170 default:
10171 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
10172 SetLastError((DWORD)NTE_BAD_TYPE);
10173 return FALSE;
10174 }
10175
10176 /*
10177 * Copy out cbRet from pvRet.
10178 */
10179 if (pbData)
10180 {
10181 if (*pcbData >= cbRet)
10182 {
10183 *pcbData = cbRet;
10184 kHlpMemCopy(pbData, pvRet, cbRet);
10185 if (cbRet == 4)
10186 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
10187 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
10188 else if (cbRet == 16)
10189 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",
10190 dwParam, pHash, pHash->pCachedFile, cbRet,
10191 pbData[0], pbData[1], pbData[2], pbData[3],
10192 pbData[4], pbData[5], pbData[6], pbData[7],
10193 pbData[8], pbData[9], pbData[10], pbData[11],
10194 pbData[12], pbData[13], pbData[14], pbData[15]));
10195 else
10196 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
10197 dwParam, pHash, pHash->pCachedFile, cbRet));
10198 return TRUE;
10199 }
10200
10201 kHlpMemCopy(pbData, pvRet, *pcbData);
10202 }
10203 SetLastError(ERROR_MORE_DATA);
10204 *pcbData = cbRet;
10205 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
10206 }
10207 else
10208 {
10209 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
10210 SetLastError((DWORD)NTE_BAD_FLAGS);
10211 }
10212 }
10213 else
10214 {
10215 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
10216 SetLastError((DWORD)NTE_BAD_HASH);
10217 }
10218 fRc = FALSE;
10219 }
10220 /*
10221 * Regular handle.
10222 */
10223 else
10224 {
10225 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
10226 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
10227 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
10228 }
10229
10230 return fRc;
10231}
10232
10233
10234/** AdvApi32 - CryptDestroyHash */
10235static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
10236{
10237 BOOL fRc;
10238 PKWHASHMD5 pPrev = NULL;
10239 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
10240 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10241 while (pHash && (KUPTR)pHash != hHash)
10242 {
10243 pPrev = pHash;
10244 pHash = pHash->pNext;
10245 }
10246 if (pHash)
10247 {
10248 if (pHash->uMagic == KWHASHMD5_MAGIC)
10249 {
10250 pHash->uMagic = 0;
10251 if (!pPrev)
10252 g_Sandbox.pHashHead = pHash->pNext;
10253 else
10254 pPrev->pNext = pHash->pNext;
10255 kHlpFree(pHash);
10256 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
10257 fRc = TRUE;
10258 }
10259 else
10260 {
10261 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10262 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10263 SetLastError(ERROR_INVALID_HANDLE);
10264 fRc = FALSE;
10265 }
10266 }
10267 /*
10268 * Regular handle.
10269 */
10270 else
10271 {
10272 fRc = CryptDestroyHash(hHash);
10273 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10274 }
10275 return fRc;
10276}
10277
10278#endif /* WITH_HASH_MD5_CACHE */
10279
10280
10281/*
10282 *
10283 * Reuse crypt context.
10284 * Reuse crypt context.
10285 * Reuse crypt context.
10286 *
10287 *
10288 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10289 *
10290 */
10291
10292#ifdef WITH_CRYPT_CTX_REUSE
10293
10294/** AdvApi32 - CryptAcquireContextW. */
10295static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10296 DWORD dwProvType, DWORD dwFlags)
10297{
10298 BOOL fRet;
10299
10300 /*
10301 * Lookup reusable context based on the input.
10302 */
10303 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10304 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10305 KU32 iCtx = g_Sandbox.cCryptCtxs;
10306 while (iCtx-- > 0)
10307 {
10308 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10309 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10310 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10311 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10312 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10313 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10314 {
10315 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10316 {
10317 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10318 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10319 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10320 return TRUE;
10321 }
10322 }
10323 }
10324
10325 /*
10326 * Create it and enter it into the reused array if possible.
10327 */
10328 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10329 if (fRet)
10330 {
10331 iCtx = g_Sandbox.cCryptCtxs;
10332 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10333 {
10334 /* Try duplicate the input strings. */
10335 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10336 (cwcContainer + 1) * sizeof(wchar_t));
10337 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10338 {
10339 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10340 (cwcProvider + 1) * sizeof(wchar_t));
10341 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10342 {
10343 /* Add a couple of references just to be on the safe side and all that. */
10344 HCRYPTPROV hProv = *phProv;
10345 if (CryptContextAddRef(hProv, NULL, 0))
10346 {
10347 if (CryptContextAddRef(hProv, NULL, 0))
10348 {
10349 /* Okay, finish the entry and return success */
10350 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10351 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10352 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10353 g_Sandbox.cCryptCtxs = iCtx + 1;
10354
10355 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10356 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10357 return TRUE;
10358 }
10359 CryptReleaseContext(hProv, 0);
10360 }
10361 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10362
10363 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10364 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10365 }
10366 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10367 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10368 }
10369 }
10370 else
10371 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10372 }
10373
10374 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10375 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10376 return fRet;
10377}
10378
10379
10380/** AdvApi32 - CryptReleaseContext */
10381static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10382{
10383 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10384 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10385 return fRet;
10386}
10387
10388
10389/** AdvApi32 - CryptContextAddRef */
10390static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10391{
10392 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10393 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10394 return fRet;
10395}
10396
10397#endif /* WITH_CRYPT_CTX_REUSE */
10398
10399/*
10400 *
10401 * Structured exception handling.
10402 * Structured exception handling.
10403 * Structured exception handling.
10404 *
10405 */
10406#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10407
10408# define EH_NONCONTINUABLE KU32_C(0x00000001)
10409# define EH_UNWINDING KU32_C(0x00000002)
10410# define EH_EXIT_UNWIND KU32_C(0x00000004)
10411# define EH_STACK_INVALID KU32_C(0x00000008)
10412# define EH_NESTED_CALL KU32_C(0x00000010)
10413
10414typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10415 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10416typedef struct _EXCEPTION_REGISTRATION_RECORD
10417{
10418 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10419 PFNXCPTHANDLER pfnXcptHandler;
10420};
10421
10422
10423/**
10424 * Calls @a pfnHandler.
10425 */
10426static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10427 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10428 PFNXCPTHANDLER pfnHandler)
10429{
10430# if 1
10431 /* This is a more robust version that isn't subject to calling
10432 convension cleanup disputes and such. */
10433 KU32 uSavedEdi;
10434 KU32 uSavedEsi;
10435 KU32 uSavedEbx;
10436 KU32 rcHandler;
10437
10438 __asm
10439 {
10440 mov [uSavedEdi], edi
10441 mov [uSavedEsi], esi
10442 mov [uSavedEbx], ebx
10443 mov esi, esp
10444 mov edi, esp
10445 mov edi, [pXcptRec]
10446 mov edx, [pRegRec]
10447 mov eax, [pXcptCtx]
10448 mov ebx, [ppRegRec]
10449 mov ecx, [pfnHandler]
10450 sub esp, 16
10451 and esp, 0fffffff0h
10452 mov [esp ], edi
10453 mov [esp + 4], edx
10454 mov [esp + 8], eax
10455 mov [esp + 12], ebx
10456 mov edi, esi
10457 call ecx
10458 mov esp, esi
10459 cmp esp, edi
10460 je stack_ok
10461 int 3
10462 stack_ok:
10463 mov edi, [uSavedEdi]
10464 mov esi, [uSavedEsi]
10465 mov ebx, [uSavedEbx]
10466 mov [rcHandler], eax
10467 }
10468 return rcHandler;
10469# else
10470 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10471# endif
10472}
10473
10474
10475/**
10476 * Vectored exception handler that emulates x86 chained exception handler.
10477 *
10478 * This is necessary because the RtlIsValidHandler check fails for self loaded
10479 * code and prevents cl.exe from working. (On AMD64 we can register function
10480 * tables, but on X86 cooking your own handling seems to be the only viabke
10481 * alternative.)
10482 *
10483 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10484 * @param pXcptPtrs The exception details.
10485 */
10486static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10487{
10488 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10489 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10490 if (g_Sandbox.fRunning)
10491 {
10492 HANDLE const hCurProc = GetCurrentProcess();
10493 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10494 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10495 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10496 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10497 {
10498 /* Read the exception record in a safe manner. */
10499 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10500 DWORD cbActuallyRead = 0;
10501 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10502 && cbActuallyRead == sizeof(RegRec))
10503 {
10504 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10505 KU32 rcHandler;
10506 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10507 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10508 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10509 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10510 if (rcHandler == ExceptionContinueExecution)
10511 {
10512 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10513 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10514 return EXCEPTION_CONTINUE_EXECUTION;
10515 }
10516
10517 if (rcHandler == ExceptionContinueSearch)
10518 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10519 else if (rcHandler == ExceptionNestedException)
10520 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10521 else
10522 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10523 }
10524 else
10525 {
10526 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10527 break;
10528 }
10529
10530 /*
10531 * Next.
10532 */
10533 pRegRec = RegRec.pPrevRegRec;
10534 }
10535 }
10536 return EXCEPTION_CONTINUE_SEARCH;
10537}
10538
10539
10540/** NtDll,Kernel32 - RtlUnwind */
10541static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10542 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10543{
10544 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10545 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10546 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10547 if (g_Sandbox.fRunning)
10548 {
10549 HANDLE const hCurProc = GetCurrentProcess();
10550 PCONTEXT pXcptCtx = NULL;
10551 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10552
10553 /*
10554 * Update / create an exception record.
10555 */
10556 if (pXcptRec)
10557 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10558 else
10559 {
10560 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10561 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10562 pXcptRec->ExceptionCode = (DWORD)STATUS_UNWIND;
10563 pXcptRec->ExceptionFlags = EH_UNWINDING;
10564 }
10565 if (!pStopXcptRec)
10566 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10567
10568 /*
10569 * Walk the chain till we find pStopXctpRec.
10570 */
10571 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10572 && pRegRec != NULL
10573 && pRegRec != pStopXcptRec)
10574 {
10575 /* Read the exception record in a safe manner. */
10576 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10577 DWORD cbActuallyRead = 0;
10578 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10579 && cbActuallyRead == sizeof(RegRec))
10580 {
10581 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10582 KU32 rcHandler;
10583 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10584 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10585 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10586 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10587
10588 if (rcHandler == ExceptionContinueSearch)
10589 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10590 else if (rcHandler == ExceptionCollidedUnwind)
10591 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10592 else
10593 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10594 }
10595 else
10596 {
10597 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10598 break;
10599 }
10600
10601 /*
10602 * Pop next.
10603 */
10604 pTib->ExceptionList = RegRec.pPrevRegRec;
10605 pRegRec = RegRec.pPrevRegRec;
10606 }
10607 return;
10608 }
10609
10610 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10611}
10612
10613#endif /* WINDOWS + X86 */
10614
10615
10616/*
10617 *
10618 * Misc function only intercepted while debugging.
10619 * Misc function only intercepted while debugging.
10620 * Misc function only intercepted while debugging.
10621 *
10622 */
10623
10624#ifndef NDEBUG
10625
10626/** CRT - memcpy */
10627static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10628{
10629 KU8 const *pbSrc = (KU8 const *)pvSrc;
10630 KU8 *pbDst = (KU8 *)pvDst;
10631 KSIZE cbLeft = cb;
10632 while (cbLeft-- > 0)
10633 *pbDst++ = *pbSrc++;
10634 return pvDst;
10635}
10636
10637
10638/** CRT - memset */
10639static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10640{
10641 KU8 *pbDst = (KU8 *)pvDst;
10642 KSIZE cbLeft = cb;
10643 while (cbLeft-- > 0)
10644 *pbDst++ = (KU8)bFiller;
10645 return pvDst;
10646}
10647
10648#endif /* NDEBUG */
10649
10650
10651/** @todo consider hooking NtQueryDirectoryFile as c1xx.dll/c1.dll in 2019
10652 * uses it directly to read the content of include directories, however
10653 * they do it one file at the time. We already have the info in the
10654 * cache (where we do bulk reads). There are a lot of calls for the
10655 * SDK include directories, as one can imagine. */
10656
10657/**
10658 * Functions that needs replacing for sandboxed execution.
10659 */
10660KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10661{
10662 /*
10663 * Kernel32.dll and friends.
10664 */
10665 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10666 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10667
10668 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10669 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10670 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10671 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10672 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10673 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10674 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10675 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10676 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10677 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10678 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10679
10680 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10681 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10682 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10683 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10684
10685 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10686
10687 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10688 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10689 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10690 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10691 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10692 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10693 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10694 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10695 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10696 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10697 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10698
10699 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10700 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10701 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10702 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10703#ifdef WITH_TEMP_MEMORY_FILES
10704 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10705 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10706 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10707 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10708 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10709 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10710 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10711 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10712 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10713 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10714#endif
10715 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10716 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10717 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10718 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10719 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10720 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10721 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10722 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10723 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10724#ifdef WITH_TEMP_MEMORY_FILES
10725 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10726#endif
10727
10728#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10729 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10730 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10731#endif
10732
10733 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10734 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10735
10736 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10737 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10738
10739 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10740 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10741 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10742 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10743
10744 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10745
10746#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10747 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10748#endif
10749
10750#ifdef WITH_HASH_MD5_CACHE
10751 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10752 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10753 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10754 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10755#endif
10756
10757#ifdef WITH_CRYPT_CTX_REUSE
10758 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10759 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10760 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10761#endif
10762
10763 /*
10764 * MS Visual C++ CRTs.
10765 */
10766 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10767 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10768 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10769 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10770 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10771 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10772
10773 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10774 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10775 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10776
10777 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10778 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10779 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
10780
10781 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
10782 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
10783 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
10784 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
10785 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
10786 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
10787 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
10788 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
10789 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
10790 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
10791 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
10792 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
10793 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
10794 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
10795 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
10796 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
10797 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
10798 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
10799 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
10800 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
10801
10802 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
10803 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
10804 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
10805 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
10806 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
10807 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
10808 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
10809 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
10810 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
10811 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
10812 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
10813 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
10814 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
10815 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
10816
10817#ifndef NDEBUG
10818 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
10819 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
10820#endif
10821};
10822/** Number of entries in g_aReplacements. */
10823KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
10824
10825
10826/**
10827 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
10828 * execution.
10829 */
10830KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
10831{
10832 /*
10833 * Kernel32.dll and friends.
10834 */
10835 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10836 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10837
10838 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10839 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10840
10841#if 0
10842 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10843#endif
10844
10845 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10846 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10847 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10848 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10849#ifdef WITH_TEMP_MEMORY_FILES
10850 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10851 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10852 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10853 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10854 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10855 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10856 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10857 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10858 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10859 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10860#endif
10861 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10862 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10863 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10864 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10865 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10866 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10867 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10868 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10869 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10870#ifdef WITH_TEMP_MEMORY_FILES
10871 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10872#endif
10873 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10874 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10875 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExW },
10876#ifndef NDEBUG
10877 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_Native_GetProcAddress },
10878#endif
10879
10880#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10881 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10882 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10883#endif
10884
10885#ifdef WITH_HASH_MD5_CACHE
10886 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10887 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10888 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10889 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10890#endif
10891
10892 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10893
10894#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10895 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10896#endif
10897
10898 /*
10899 * MS Visual C++ CRTs.
10900 */
10901 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10902 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10903 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10904 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10905 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10906 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10907 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10908
10909#if 0 /* used by mspdbXXX.dll */
10910 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10911 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10912#endif
10913};
10914/** Number of entries in g_aSandboxNativeReplacements. */
10915KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10916
10917
10918/**
10919 * Functions that needs replacing when queried by GetProcAddress.
10920 */
10921KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10922{
10923 /*
10924 * Kernel32.dll and friends.
10925 */
10926 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10927 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10928 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10929 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10930};
10931/** Number of entries in g_aSandboxGetProcReplacements. */
10932KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10933
10934
10935/**
10936 * Control handler.
10937 *
10938 * @returns TRUE if handled, FALSE if not.
10939 * @param dwCtrlType The signal.
10940 */
10941static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10942{
10943 DWORD cbIgn;
10944 int volatile rc; /* volatile for debugging */
10945 int volatile rcPrev;
10946 const char *pszMsg;
10947 switch (dwCtrlType)
10948 {
10949 case CTRL_C_EVENT:
10950 rc = 9;
10951 pszMsg = "kWorker: Ctrl-C\r\n";
10952 break;
10953
10954 case CTRL_BREAK_EVENT:
10955 rc = 10;
10956 pszMsg = "kWorker: Ctrl-Break\r\n";
10957 break;
10958
10959 case CTRL_CLOSE_EVENT:
10960 rc = 11;
10961 pszMsg = "kWorker: console closed\r\n";
10962 break;
10963
10964 case CTRL_LOGOFF_EVENT:
10965 rc = 11;
10966 pszMsg = "kWorker: logoff event\r\n";
10967 break;
10968
10969 case CTRL_SHUTDOWN_EVENT:
10970 rc = 11;
10971 pszMsg = "kWorker: shutdown event\r\n";
10972 break;
10973
10974 default:
10975 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10976 return TRUE;
10977 }
10978
10979 /*
10980 * Terminate the process after 5 seconds.
10981 * If we get here a second time we just terminate the process ourselves.
10982 *
10983 * Note! We do no try call exit() here as it turned out to deadlock a lot
10984 * flusing file descriptors (stderr back when we first wrote to it).
10985 */
10986 rcPrev = g_rcCtrlC;
10987 g_rcCtrlC = rc;
10988 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10989 if (rcPrev == 0)
10990 {
10991 int i;
10992 for (i = 0; i < 10; i++)
10993 {
10994 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10995 Sleep(500);
10996 }
10997 }
10998 TerminateProcess(GetCurrentProcess(), rc);
10999 return TRUE;
11000}
11001
11002
11003#if 0
11004/**
11005 * Resets the KWMODULE::fVisited flag for _all_ known modules.
11006 */
11007static void kwSandboxResetModuleVisited(void)
11008{
11009 PKWMODULE pMod = g_pModuleHead;
11010 while (pMod)
11011 {
11012 pMod->fVisited = K_FALSE;
11013 pMod = pMod->pNextList;
11014 }
11015}
11016
11017
11018/**
11019 * Used by kwSandboxExec to reset the state of the module tree.
11020 *
11021 * This is done recursively.
11022 *
11023 * @param pMod The root of the tree to consider.
11024 */
11025static void kwSandboxResetModuleState(PKWMODULE pMod)
11026{
11027 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
11028 if (!pMod->fNative)
11029 {
11030 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
11031 if (!pMod->fVisited) /* Avoid loops. */
11032 {
11033 KSIZE iImp;
11034 pMod->fVisited = K_TRUE;
11035 iImp = pMod->u.Manual.cImpMods;
11036 while (iImp-- > 0)
11037 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
11038 }
11039 }
11040 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11041 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
11042 {
11043 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11044 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11045 {
11046 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11047 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11048 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11049 pMod->pszPath, pszValue ? pszValue : "<null>"));
11050 }
11051 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11052 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11053 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11054 pMod->pszPath, pszValue ? pszValue : "<null>"));
11055 else
11056 {
11057 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11058 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11059 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11060 if (pszValue != NULL)
11061 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11062 else
11063 pMod->pszMsPdbSrvEndpoint = NULL;
11064 pMod->fNeedReInit = K_TRUE;
11065 }
11066 }
11067}
11068#else
11069/**
11070 * Used by kwSandboxExec to reset the state of the module tree.
11071 */
11072static void kwSandboxResetModuleState(void)
11073{
11074 PKWMODULE pMod = g_pModuleHead;
11075 while (pMod)
11076 {
11077 if (!pMod->fNative)
11078 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
11079 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11080 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
11081 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
11082 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
11083 {
11084 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11085 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11086 {
11087 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11088 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11089 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11090 pMod->pszPath, pszValue ? pszValue : "<null>"));
11091 }
11092 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11093 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11094 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11095 pMod->pszPath, pszValue ? pszValue : "<null>"));
11096 else
11097 {
11098 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11099 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11100 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11101 if (pszValue != NULL)
11102 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11103 else
11104 pMod->pszMsPdbSrvEndpoint = NULL;
11105 pMod->fNeedReInit = K_TRUE;
11106 }
11107 }
11108
11109 pMod = pMod->pNextList;
11110 }
11111}
11112#endif
11113
11114static PPEB kwSandboxGetProcessEnvironmentBlock(void)
11115{
11116#if K_ARCH == K_ARCH_X86_32
11117 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
11118#elif K_ARCH == K_ARCH_AMD64
11119 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
11120#else
11121# error "Port me!"
11122#endif
11123}
11124
11125
11126/**
11127 * Enters the given handle into the handle table.
11128 *
11129 * @returns K_TRUE on success, K_FALSE on failure.
11130 * @param pSandbox The sandbox.
11131 * @param pHandle The handle.
11132 * @param hHandle The handle value to enter it under (for the
11133 * duplicate handle API).
11134 */
11135static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
11136{
11137 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
11138 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
11139
11140 EnterCriticalSection(&g_Sandbox.HandlesLock);
11141
11142 /*
11143 * Grow handle table.
11144 */
11145 if (idxHandle >= pSandbox->cHandles)
11146 {
11147 void *pvNew;
11148 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
11149 while (cHandles <= idxHandle)
11150 cHandles *= 2;
11151 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
11152 if (!pvNew)
11153 {
11154 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11155 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
11156 return K_FALSE;
11157 }
11158 pSandbox->papHandles = (PKWHANDLE *)pvNew;
11159 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
11160 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
11161 pSandbox->cHandles = cHandles;
11162 }
11163
11164 /*
11165 * Check that the entry is unused then insert it.
11166 */
11167 kHlpAssertStmtReturn(pSandbox->papHandles[idxHandle] == NULL, LeaveCriticalSection(&g_Sandbox.HandlesLock), K_FALSE);
11168 pSandbox->papHandles[idxHandle] = pHandle;
11169 pSandbox->cActiveHandles++;
11170 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11171 return K_TRUE;
11172}
11173
11174
11175/**
11176 * Safely looks up a handle, does not get it and it must not be 'put'.
11177 *
11178 * @returns Pointer to the handle structure if found, otherwise NULL.
11179 * @param hFile The handle to resolve.
11180 */
11181static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile)
11182{
11183 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11184 EnterCriticalSection(&g_Sandbox.HandlesLock);
11185 if (idxHandle < g_Sandbox.cHandles)
11186 {
11187 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11188 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11189 return pHandle;
11190 }
11191 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11192 return NULL;
11193}
11194
11195
11196/**
11197 * Safely gets a handle, must be "put" when done with it.
11198 *
11199 * @returns Pointer to the handle structure if found, otherwise NULL.
11200 * @param hFile The handle to resolve.
11201 */
11202static PKWHANDLE kwSandboxHandleGet(HANDLE hFile)
11203{
11204 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11205 EnterCriticalSection(&g_Sandbox.HandlesLock);
11206 if (idxHandle < g_Sandbox.cHandles)
11207 {
11208 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11209 if (pHandle)
11210 {
11211 kHlpAssertMsg(pHandle->tidOwner == KU32_MAX, ("hFile=%p tidOwner=%#x\n", hFile, pHandle->tidOwner));
11212 pHandle->tidOwner = GetCurrentThreadId();
11213 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11214 return pHandle;
11215 }
11216 }
11217 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11218 return NULL;
11219}
11220
11221
11222/**
11223 * Puts a handle returned by kwSandboxHandleGet.
11224 *
11225 * @param pHandle The handle to "put".
11226 */
11227K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle)
11228{
11229 kHlpAssertMsg(pHandle->tidOwner == GetCurrentThreadId(),
11230 ("hFile tidOwner=%#x tidMe=%#x\n", pHandle->hHandle, pHandle->tidOwner, GetCurrentThreadId()));
11231 pHandle->tidOwner = KU32_MAX;
11232}
11233
11234
11235/**
11236 * Creates a correctly quoted ANSI command line string from the given argv.
11237 *
11238 * @returns Pointer to the command line.
11239 * @param cArgs Number of arguments.
11240 * @param papszArgs The argument vector.
11241 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11242 * @param pcbCmdLine Where to return the command line length,
11243 * including one terminator.
11244 */
11245static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
11246{
11247 KU32 i;
11248 KSIZE cbCmdLine;
11249 char *pszCmdLine;
11250
11251 /* Make a copy of the argument vector that we'll be quoting. */
11252 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
11253 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
11254
11255 /* Quote the arguments that need it. */
11256 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
11257
11258 /* figure out cmd line length. */
11259 cbCmdLine = 0;
11260 for (i = 0; i < cArgs; i++)
11261 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
11262 *pcbCmdLine = cbCmdLine;
11263
11264 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
11265 if (pszCmdLine)
11266 {
11267 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
11268 if (papszQuotedArgs[0] != papszArgs[0])
11269 free(papszQuotedArgs[0]);
11270
11271 for (i = 1; i < cArgs; i++)
11272 {
11273 *psz++ = ' ';
11274 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
11275 if (papszQuotedArgs[i] != papszArgs[i])
11276 free(papszQuotedArgs[i]);
11277 }
11278 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
11279
11280 *psz++ = '\0';
11281 *psz++ = '\0';
11282 }
11283
11284 return pszCmdLine;
11285}
11286
11287
11288
11289static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
11290 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11291 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11292{
11293 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11294 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11295 wchar_t *pwcPool;
11296 KSIZE cbStrings;
11297 KSIZE cwc;
11298 KSIZE cbCmdLine;
11299 KU32 i;
11300
11301 /* Simple stuff. */
11302 pSandbox->rcExitCode = 256;
11303 pSandbox->pTool = pTool;
11304 pSandbox->idMainThread = GetCurrentThreadId();
11305 pSandbox->pgmptr = (char *)pTool->pszPath;
11306 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
11307#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11308 if (pSandbox->StdOut.fIsConsole)
11309 pSandbox->StdOut.u.Con.cwcBuf = 0;
11310 else
11311 pSandbox->StdOut.u.Fully.cchBuf = 0;
11312 if (pSandbox->StdErr.fIsConsole)
11313 pSandbox->StdErr.u.Con.cwcBuf = 0;
11314 else
11315 pSandbox->StdErr.u.Fully.cchBuf = 0;
11316 pSandbox->Combined.cwcBuf = 0;
11317 pSandbox->Combined.cFlushes = 0;
11318#endif
11319 pSandbox->fNoPchCaching = fNoPchCaching;
11320 pSandbox->cArgs = cArgs;
11321 pSandbox->papszArgs = (char **)papszArgs;
11322 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
11323 if (!pSandbox->pszCmdLine)
11324 return KERR_NO_MEMORY;
11325
11326 /*
11327 * Convert command line and argv to UTF-16.
11328 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
11329 */
11330 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
11331 if (!pSandbox->papwszArgs)
11332 return KERR_NO_MEMORY;
11333 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
11334 for (i = 0; i < cArgs; i++)
11335 {
11336 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
11337 pSandbox->papwszArgs[i] = pwcPool;
11338 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11339 pwcPool++;
11340 }
11341 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11342 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11343
11344 /*
11345 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11346 */
11347 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11348 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11349 if (!pSandbox->pwszCmdLine)
11350 return KERR_NO_MEMORY;
11351 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11352
11353 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11354 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11355 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11356
11357 /*
11358 * Setup the environment.
11359 */
11360 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11361 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11362 {
11363 KU32 iDst = 0;
11364 for (i = 0; i < cEnvVars; i++)
11365 {
11366 const char *pszVar = papszEnvVars[i];
11367 KSIZE cchVar = kHlpStrLen(pszVar);
11368 const char *pszEqual;
11369 if ( cchVar > 0
11370 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11371 {
11372 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11373 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11374 if (pszCopy && pwszCopy)
11375 {
11376 pSandbox->papszEnvVars[iDst] = pszCopy;
11377 pSandbox->environ[iDst] = pszCopy;
11378 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11379 pSandbox->wenviron[iDst] = pwszCopy;
11380
11381 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11382 if ( (pszEqual - pszVar) == 4
11383 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11384 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11385 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11386 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11387 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11388 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11389
11390 iDst++;
11391 }
11392 else
11393 {
11394 kHlpFree(pszCopy);
11395 kHlpFree(pwszCopy);
11396 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11397 }
11398 }
11399 else
11400 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11401 }
11402 pSandbox->papszEnvVars[iDst] = NULL;
11403 pSandbox->environ[iDst] = NULL;
11404 pSandbox->papwszEnvVars[iDst] = NULL;
11405 pSandbox->wenviron[iDst] = NULL;
11406 }
11407 else
11408 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11409
11410 /*
11411 * Invalidate the volatile parts of cache (kBuild output directory,
11412 * temporary directory, whatever).
11413 */
11414 kFsCacheInvalidateCustomBoth(g_pFsCache);
11415
11416#ifdef WITH_HISTORY
11417 /*
11418 * Record command line in debug history.
11419 */
11420 kHlpFree(g_apszHistory[g_iHistoryNext]);
11421 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11422 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11423#endif
11424
11425 return 0;
11426}
11427
11428
11429/**
11430 * Does sandbox cleanup between jobs.
11431 *
11432 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11433 * influence the result, so that kmk can get on with things ASAP.
11434 *
11435 * @param pSandbox The sandbox.
11436 */
11437static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11438{
11439 PROCESS_MEMORY_COUNTERS MemInfo;
11440 PKWVIRTALLOC pTracker;
11441 PKWHEAP pHeap;
11442 PKWLOCALSTORAGE pLocalStorage;
11443#ifdef WITH_HASH_MD5_CACHE
11444 PKWHASHMD5 pHash;
11445#endif
11446#ifdef WITH_TEMP_MEMORY_FILES
11447 PKWFSTEMPFILE pTempFile;
11448#endif
11449 PKWEXITCALLACK pExitCallback;
11450
11451 /*
11452 * First stuff that may cause code to run.
11453 */
11454
11455 /* Do exit callback first. */
11456 pExitCallback = g_Sandbox.pExitCallbackHead;
11457 g_Sandbox.pExitCallbackHead = NULL;
11458 while (pExitCallback)
11459 {
11460 PKWEXITCALLACK pNext = pExitCallback->pNext;
11461 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11462 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11463 __try
11464 {
11465 pExitCallback->pfnCallback();
11466 }
11467 __except (EXCEPTION_EXECUTE_HANDLER)
11468 {
11469 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11470 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11471 kHlpAssertFailed();
11472 }
11473 kHlpFree(pExitCallback);
11474 pExitCallback = pNext;
11475 }
11476
11477 /* Free left behind FlsAlloc leaks. */
11478 pLocalStorage = g_Sandbox.pFlsAllocHead;
11479 g_Sandbox.pFlsAllocHead = NULL;
11480 while (pLocalStorage)
11481 {
11482 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11483 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11484 FlsFree(pLocalStorage->idx);
11485 kHlpFree(pLocalStorage);
11486 pLocalStorage = pNext;
11487 }
11488
11489 /* Free left behind TlsAlloc leaks. */
11490 pLocalStorage = g_Sandbox.pTlsAllocHead;
11491 g_Sandbox.pTlsAllocHead = NULL;
11492 while (pLocalStorage)
11493 {
11494 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11495 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11496 TlsFree(pLocalStorage->idx);
11497 kHlpFree(pLocalStorage);
11498 pLocalStorage = pNext;
11499 }
11500
11501
11502 /*
11503 * Then free resources associated with the sandbox run.
11504 */
11505
11506 /* Open handles, except fixed handles (stdout and stderr). */
11507 EnterCriticalSection(&pSandbox->HandlesLock);
11508 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11509 {
11510 KU32 idxHandle = pSandbox->cHandles;
11511 while (idxHandle-- > 0)
11512 if (pSandbox->papHandles[idxHandle] == NULL)
11513 { /* likely */ }
11514 else
11515 {
11516 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11517 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11518 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11519 {
11520 pSandbox->papHandles[idxHandle] = NULL;
11521 pSandbox->cLeakedHandles++;
11522
11523 switch (pHandle->enmType)
11524 {
11525 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11526 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11527 idxHandle, pHandle->hHandle, pHandle->cRefs));
11528 break;
11529 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11530 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11531 idxHandle, pHandle->hHandle, pHandle->cRefs));
11532 break;
11533 case KWHANDLETYPE_OUTPUT_BUF:
11534 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11535 idxHandle, pHandle->hHandle, pHandle->cRefs));
11536 break;
11537#ifdef WITH_TEMP_MEMORY_FILES
11538 case KWHANDLETYPE_TEMP_FILE:
11539 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11540 idxHandle, pHandle->hHandle, pHandle->cRefs));
11541 pHandle->u.pTempFile->cActiveHandles--;
11542 break;
11543 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11544 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11545 idxHandle, pHandle->hHandle, pHandle->cRefs));
11546 pHandle->u.pTempFile->cActiveHandles--;
11547 break;
11548#endif
11549 default:
11550 kHlpAssertFailed();
11551 }
11552 if (--pHandle->cRefs == 0)
11553 kHlpFree(pHandle);
11554 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11555 break;
11556 }
11557 }
11558 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11559 }
11560 LeaveCriticalSection(&pSandbox->HandlesLock);
11561
11562 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11563 g_Sandbox.cMemMappings = 0;
11564
11565#ifdef WITH_TEMP_MEMORY_FILES
11566 /* The temporary files aren't externally visible, they're all in memory. */
11567 pTempFile = pSandbox->pTempFileHead;
11568 pSandbox->pTempFileHead = NULL;
11569 while (pTempFile)
11570 {
11571 PKWFSTEMPFILE pNext = pTempFile->pNext;
11572 KU32 iSeg = pTempFile->cSegs;
11573 while (iSeg-- > 0)
11574 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11575 kHlpFree(pTempFile->paSegs);
11576 pTempFile->pNext = NULL;
11577 kHlpFree(pTempFile);
11578
11579 pTempFile = pNext;
11580 }
11581#endif
11582
11583 /* Free left behind HeapCreate leaks. */
11584 pHeap = g_Sandbox.pHeapHead;
11585 g_Sandbox.pHeapHead = NULL;
11586 while (pHeap != NULL)
11587 {
11588 PKWHEAP pNext = pHeap->pNext;
11589 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11590 HeapDestroy(pHeap->hHeap);
11591 pHeap = pNext;
11592 }
11593
11594 /* Free left behind VirtualAlloc leaks. */
11595 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11596 pTracker = g_Sandbox.pVirtualAllocHead;
11597 g_Sandbox.pVirtualAllocHead = NULL;
11598 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11599 while (pTracker)
11600 {
11601 PKWVIRTALLOC pNext = pTracker->pNext;
11602 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11603
11604#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11605 if (pTracker->idxPreAllocated != KU32_MAX)
11606 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11607 else
11608#endif
11609 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11610 kHlpFree(pTracker);
11611 pTracker = pNext;
11612 }
11613
11614 /* Free the environment. */
11615 if (pSandbox->papszEnvVars)
11616 {
11617 KU32 i;
11618 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11619 kHlpFree(pSandbox->papszEnvVars[i]);
11620 pSandbox->environ[0] = NULL;
11621 pSandbox->papszEnvVars[0] = NULL;
11622
11623 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11624 kHlpFree(pSandbox->papwszEnvVars[i]);
11625 pSandbox->wenviron[0] = NULL;
11626 pSandbox->papwszEnvVars[0] = NULL;
11627 }
11628
11629#ifdef WITH_HASH_MD5_CACHE
11630 /*
11631 * Hash handles.
11632 */
11633 pHash = pSandbox->pHashHead;
11634 pSandbox->pHashHead = NULL;
11635 while (pHash)
11636 {
11637 PKWHASHMD5 pNext = pHash->pNext;
11638 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11639 kHlpFree(pHash);
11640 pHash = pNext;
11641 }
11642#endif
11643
11644 /*
11645 * Check the memory usage. If it's getting high, trigger a respawn
11646 * after the next job.
11647 */
11648 MemInfo.WorkingSetSize = 0;
11649 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11650 {
11651 /* The first time thru, we figure out approximately when to restart
11652 based on installed RAM and CPU threads. */
11653 static KU64 s_cbMaxWorkingSet = 0;
11654 if (s_cbMaxWorkingSet != 0)
11655 { /* likely */ }
11656 else
11657 {
11658 SYSTEM_INFO SysInfo;
11659 MEMORYSTATUSEX GlobalMemInfo;
11660 const char *pszValue;
11661
11662 /* Calculate a reasonable estimate. */
11663 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11664 GetNativeSystemInfo(&SysInfo);
11665
11666 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11667 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11668 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11669#if K_ARCH_BITS >= 64
11670 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11671#else
11672 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11673#endif
11674 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11675 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11676
11677 /* User limit. */
11678 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11679 if (pszValue != NULL)
11680 {
11681 char *pszNext;
11682 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11683 if (*pszNext == '\0' || *pszNext == 'M')
11684 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11685 else if (*pszNext == 'K')
11686 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11687 else if (*pszNext == 'G')
11688 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11689 else
11690 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11691 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11692 }
11693
11694 /* Clamp it a little. */
11695 if (s_cbMaxWorkingSet < 168*1024*1024)
11696 s_cbMaxWorkingSet = 168*1024*1024;
11697#if K_ARCH_BITS < 64
11698 else
11699 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11700 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11701 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11702 : 1536*1024*1024 /* got 4GB VA */);
11703#endif
11704 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11705 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11706 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11707 }
11708
11709 /* Finally the check. */
11710 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11711 {
11712 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11713 g_fRestart = K_TRUE;
11714 }
11715 }
11716
11717 /*
11718 * The CRT has a max of 8192 handles, so we better restart after a while if
11719 * someone is leaking handles or we risk running out of descriptors.
11720 *
11721 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11722 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11723 */
11724 if (pSandbox->cLeakedHandles > 6000)
11725 {
11726 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11727 g_fRestart = K_TRUE;
11728 }
11729}
11730
11731
11732/**
11733 * Does essential cleanups and restoring, anything externally visible.
11734 *
11735 * All cleanups that aren't externally visible are postponed till after we've
11736 * informed kmk of the result, so it can be done in the dead time between jobs.
11737 *
11738 * @param pSandbox The sandbox.
11739 */
11740static void kwSandboxCleanup(PKWSANDBOX pSandbox)
11741{
11742 /*
11743 * Restore the parent command line string.
11744 */
11745 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11746 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11747 pProcParams->CommandLine = pSandbox->SavedCommandLine;
11748#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11749 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
11750 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
11751#endif
11752}
11753
11754
11755static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11756 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11757{
11758 int rcExit = 42;
11759 int rc;
11760
11761 /*
11762 * Initialize the sandbox environment.
11763 */
11764 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
11765 if (rc == 0)
11766 {
11767 if (g_cVerbose > 2)
11768 fprintf(stderr, "kWorker: info: Executing (sandboxed): %s\n", g_Sandbox.pszCmdLine);
11769
11770 /*
11771 * Do module initialization.
11772 */
11773#if 0
11774 //kwSandboxResetModuleVisited();
11775 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
11776#else
11777 kwSandboxResetModuleState();
11778#endif
11779 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
11780 if (rc == 0)
11781 {
11782 /*
11783 * Call the main function.
11784 */
11785#if K_ARCH == K_ARCH_AMD64
11786 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
11787#elif K_ARCH == K_ARCH_X86_32
11788 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
11789#else
11790# error "Port me!"
11791#endif
11792
11793 /* Save the NT TIB first (should do that here, not in some other function). */
11794 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
11795 pSandbox->TibMainThread = *pTib;
11796
11797 /* Make the call in a guarded fashion. */
11798#if K_ARCH == K_ARCH_AMD64
11799 /* AMD64 */
11800 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
11801 __try
11802 {
11803 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11804 if (setjmp(pSandbox->JmpBuf) == 0)
11805 {
11806 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11807 pSandbox->fRunning = K_TRUE;
11808 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
11809 pSandbox->fRunning = K_FALSE;
11810 }
11811 else
11812 rcExit = pSandbox->rcExitCode;
11813 }
11814#elif K_ARCH == K_ARCH_X86_32
11815 /* x86 (see _tmainCRTStartup) */
11816 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
11817 __try
11818 {
11819 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11820 if (setjmp(pSandbox->JmpBuf) == 0)
11821 {
11822 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11823 pSandbox->fRunning = K_TRUE;
11824 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
11825 pSandbox->fRunning = K_FALSE;
11826 }
11827 else
11828 rcExit = pSandbox->rcExitCode;
11829 }
11830#endif
11831 __except (EXCEPTION_EXECUTE_HANDLER)
11832 {
11833 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
11834#ifdef WITH_HISTORY
11835 {
11836 KU32 cPrinted = 0;
11837 while (cPrinted++ < 5)
11838 {
11839 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
11840 if (g_apszHistory[idx])
11841 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
11842 }
11843 }
11844#endif
11845 rcExit = 512;
11846 }
11847 pSandbox->fRunning = K_FALSE;
11848
11849 /* Now, restore the NT TIB. */
11850 *pTib = pSandbox->TibMainThread;
11851 }
11852 else
11853 rcExit = 42 + 4;
11854
11855 /*
11856 * Flush and clean up the essential bits only, postpone whatever we
11857 * can till after we've replied to kmk.
11858 */
11859#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11860 kwSandboxConsoleFlushAll(&g_Sandbox);
11861#endif
11862 kwSandboxCleanup(&g_Sandbox);
11863 /** @todo Flush sandboxed native CRTs too. */
11864 }
11865 else
11866 rcExit = 42 + 3;
11867
11868 return rcExit;
11869}
11870
11871
11872/**
11873 * Does the post command part of a job (optional).
11874 *
11875 * @returns The exit code of the job.
11876 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11877 * @param papszPostCmdArgs The post command and its argument.
11878 */
11879static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11880{
11881 const char *pszCmd = papszPostCmdArgs[0];
11882
11883 /* Allow the kmk builtin prefix. */
11884 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
11885 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
11886 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
11887
11888 /* Command switch. */
11889 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
11890 {
11891 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
11892 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
11893 }
11894
11895 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
11896}
11897
11898
11899/**
11900 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11901 */
11902static unsigned kwGetCurrentProcessorGroup(void)
11903{
11904 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
11905 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
11906 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
11907 if (pfnGetter)
11908 {
11909 GROUP_AFFINITY GroupAffinity;
11910 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
11911 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
11912 return GroupAffinity.Group;
11913 }
11914 return 0;
11915}
11916
11917
11918/**
11919 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11920 */
11921static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
11922{
11923 KSIZE cchRet = 0;
11924 HANDLE hToken;
11925 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
11926 {
11927 DWORD cbRet;
11928 TOKEN_STATISTICS TokenStats;
11929 memset(&TokenStats, 0, sizeof(TokenStats));
11930 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
11931 cchRet = sprintf(pszValue, "%" KX64_PRI,
11932 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
11933 else
11934 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
11935 CloseHandle(hToken);
11936 }
11937 else
11938 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
11939 return cchRet;
11940}
11941
11942
11943/**
11944 * Look for and expand the special environment variable.
11945 *
11946 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
11947 * couldn't accuratly determine. Currently the following variables are
11948 * implemented:
11949 * - "@@PROCESSOR_GROUP@@" - The processor group number.
11950 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
11951 * - "@@PID@@" - The kWorker process ID.
11952 * - "@@@@" - Escaped "@@".
11953 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
11954 */
11955static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
11956{
11957 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
11958 KU32 i = cEnvVars;
11959 while (i-- > 0)
11960 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
11961 && papszEnvVars[i][cchSpecialEnv] == '=')
11962 {
11963 /* We will expand stuff like @@NAME@@ */
11964 const char *pszValue = papszEnvVars[i];
11965 KSIZE offDst = 0;
11966 char szTmp[1024];
11967 for (;;)
11968 {
11969 const char *pszAt = kHlpStrChr(pszValue, '@');
11970 while (pszAt && pszAt[1] != '@')
11971 pszAt = kHlpStrChr(pszAt + 1, '@');
11972 if (pszAt)
11973 {
11974 KSIZE cchSrc = pszAt - pszValue;
11975 if (offDst + cchSrc < sizeof(szTmp))
11976 {
11977 char szSrc[64];
11978
11979 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11980 offDst += cchSrc;
11981 pszValue = pszAt + 2;
11982
11983 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11984 {
11985 pszValue += 15;
11986 if (g_iProcessGroup == -1)
11987 g_iProcessGroup = kwGetCurrentProcessorGroup();
11988 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11989 }
11990 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11991 {
11992 pszValue += 19;
11993 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11994 }
11995 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11996 {
11997 pszValue += 5;
11998 cchSrc = sprintf(szSrc, "%d", getpid());
11999 }
12000 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
12001 {
12002 pszValue += 2;
12003 szSrc[0] = '@';
12004 szSrc[1] = '@';
12005 szSrc[2] = '\0';
12006 cchSrc = 2;
12007 }
12008 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
12009 {
12010 static unsigned int s_iCounter = 0;
12011 pszValue += 15;
12012 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
12013 }
12014 else
12015 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
12016 pszValue - 2);
12017 if (offDst + cchSrc < sizeof(szTmp))
12018 {
12019 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
12020 offDst += cchSrc;
12021 continue;
12022 }
12023 }
12024 }
12025 else
12026 {
12027 KSIZE cchSrc = kHlpStrLen(pszValue);
12028 if (offDst + cchSrc < sizeof(szTmp))
12029 {
12030 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12031 offDst += cchSrc;
12032 break;
12033 }
12034 }
12035 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
12036 }
12037 szTmp[offDst] = '\0';
12038
12039 /* Return a copy of it: */
12040 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
12041 if (papszEnvVars[i])
12042 {
12043 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
12044 return 0;
12045 }
12046 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
12047 }
12048
12049 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
12050}
12051
12052
12053/**
12054 * Part 2 of the "JOB" command handler.
12055 *
12056 * @returns The exit code of the job.
12057 * @param pszExecutable The executable to execute.
12058 * @param pszCwd The current working directory of the job.
12059 * @param cArgs The number of arguments.
12060 * @param papszArgs The argument vector.
12061 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
12062 * @param cEnvVars The number of environment variables.
12063 * @param papszEnvVars The environment vector.
12064 * @param pszSpecialEnv Name of special environment variable that
12065 * requires selective expansion here.
12066 * @param fNoPchCaching Whether to disable precompiled header file
12067 * caching. Avoid trouble when creating them.
12068 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12069 * @param papszPostCmdArgs The post command and its argument.
12070 */
12071static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
12072 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12073 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
12074 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12075{
12076 int rcExit;
12077 PKWTOOL pTool;
12078 char *pszSpecialEnvFree = NULL;
12079
12080 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
12081 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
12082#ifdef KW_LOG_ENABLED
12083 {
12084 KU32 i;
12085 for (i = 0; i < cArgs; i++)
12086 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
12087 for (i = 0; i < cPostCmdArgs; i++)
12088 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
12089 }
12090#endif
12091 g_cJobs++;
12092
12093 /*
12094 * Expand pszSpecialEnv if present.
12095 */
12096 if (pszSpecialEnv && *pszSpecialEnv)
12097 {
12098 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
12099 if (!rcExit)
12100 { /* likely */ }
12101 else
12102 return rcExit;
12103 }
12104
12105 /*
12106 * Lookup the tool.
12107 */
12108 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
12109 if (pTool)
12110 {
12111 /*
12112 * Change the directory if we're going to execute the job inside
12113 * this process. Then invoke the tool type specific handler.
12114 */
12115 switch (pTool->enmType)
12116 {
12117 case KWTOOLTYPE_SANDBOXED:
12118 case KWTOOLTYPE_WATCOM:
12119 {
12120 /* Change dir. */
12121 KFSLOOKUPERROR enmError;
12122 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
12123 if ( pNewCurDir == g_pCurDirObj
12124 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
12125 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12126 else if (SetCurrentDirectoryA(pszCwd))
12127 {
12128 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
12129 g_pCurDirObj = pNewCurDir;
12130 }
12131 else
12132 {
12133 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
12134 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12135 rcExit = 42 + 1;
12136 break;
12137 }
12138
12139 /* Call specific handler. */
12140 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
12141 {
12142 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
12143 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
12144 cEnvVars, papszEnvVars, fNoPchCaching);
12145 }
12146 else
12147 {
12148 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
12149 rcExit = 42 + 2;
12150 }
12151 break;
12152 }
12153
12154 case KWTOOLTYPE_EXEC:
12155 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
12156 rcExit = 42 + 2;
12157 break;
12158
12159 default:
12160 kHlpAssertFailed();
12161 kwErrPrintf("Internal tool type corruption!!\n");
12162 rcExit = 42 + 2;
12163 g_fRestart = K_TRUE;
12164 break;
12165 }
12166
12167 /*
12168 * Do the post command, if present.
12169 */
12170 if (cPostCmdArgs && rcExit == 0)
12171 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
12172 }
12173 else
12174 rcExit = 42 + 1;
12175 if (pszSpecialEnvFree)
12176 {
12177 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
12178 kHlpFree(pszSpecialEnvFree);
12179 }
12180 return rcExit;
12181}
12182
12183
12184/**
12185 * Handles a "JOB" command.
12186 *
12187 * @returns The exit code of the job.
12188 * @param pszMsg Points to the "JOB" command part of the message.
12189 * @param cbMsg Number of message bytes at @a pszMsg. There are
12190 * 4 more zero bytes after the message body to
12191 * simplify parsing.
12192 */
12193static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
12194{
12195 int rcExit = 42;
12196
12197 /*
12198 * Unpack the message.
12199 */
12200 const char *pszExecutable;
12201 KSIZE cbTmp;
12202
12203 pszMsg += sizeof("JOB");
12204 cbMsg -= sizeof("JOB");
12205
12206 /* Executable name. */
12207 pszExecutable = pszMsg;
12208 cbTmp = kHlpStrLen(pszMsg) + 1;
12209 pszMsg += cbTmp;
12210 if ( cbTmp < cbMsg
12211 && cbTmp > 2)
12212 {
12213 const char *pszCwd;
12214 cbMsg -= cbTmp;
12215
12216 /* Current working directory. */
12217 pszCwd = pszMsg;
12218 cbTmp = kHlpStrLen(pszMsg) + 1;
12219 pszMsg += cbTmp;
12220 if ( cbTmp + sizeof(KU32) < cbMsg
12221 && cbTmp >= 2)
12222 {
12223 KU32 cArgs;
12224 cbMsg -= cbTmp;
12225
12226 /* Argument count. */
12227 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
12228 pszMsg += sizeof(cArgs);
12229 cbMsg -= sizeof(cArgs);
12230
12231 if (cArgs > 0 && cArgs < 4096)
12232 {
12233 /* The argument vector. */
12234 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
12235 if (papszArgs)
12236 {
12237 KU32 i;
12238 for (i = 0; i < cArgs; i++)
12239 {
12240 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
12241 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
12242 pszMsg += cbTmp;
12243 if (cbTmp < cbMsg)
12244 cbMsg -= cbTmp;
12245 else
12246 {
12247 cbMsg = 0;
12248 break;
12249 }
12250
12251 }
12252 papszArgs[cArgs] = 0;
12253
12254 /* Environment variable count. */
12255 if (cbMsg > sizeof(KU32))
12256 {
12257 KU32 cEnvVars;
12258 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
12259 pszMsg += sizeof(cEnvVars);
12260 cbMsg -= sizeof(cEnvVars);
12261
12262 if (cEnvVars >= 0 && cEnvVars < 4096)
12263 {
12264 /* The argument vector. */
12265 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
12266 if (papszEnvVars)
12267 {
12268 for (i = 0; i < cEnvVars; i++)
12269 {
12270 papszEnvVars[i] = pszMsg;
12271 cbTmp = kHlpStrLen(pszMsg) + 1;
12272 pszMsg += cbTmp;
12273 if (cbTmp < cbMsg)
12274 cbMsg -= cbTmp;
12275 else
12276 {
12277 cbMsg = 0;
12278 break;
12279 }
12280 }
12281 papszEnvVars[cEnvVars] = 0;
12282
12283 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
12284 if (cbMsg >= sizeof(KU8) * 2)
12285 {
12286 KBOOL fWatcomBrainDamange = *pszMsg++;
12287 KBOOL fNoPchCaching = *pszMsg++;
12288 cbMsg -= 2;
12289
12290 /* Name of special enviornment variable requiring selective expansion. */
12291 if (cbMsg >= 1)
12292 {
12293 const char *pszSpecialEnv = pszMsg;
12294 cbTmp = kHlpStrLen(pszMsg);
12295 pszMsg += cbTmp + 1;
12296 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
12297
12298 /* Post command argument count (can be zero). */
12299 if (cbMsg >= sizeof(KU32))
12300 {
12301 KU32 cPostCmdArgs;
12302 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
12303 pszMsg += sizeof(cPostCmdArgs);
12304 cbMsg -= sizeof(cPostCmdArgs);
12305
12306 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
12307 {
12308 char const *apszPostCmdArgs[32+1];
12309 for (i = 0; i < cPostCmdArgs; i++)
12310 {
12311 apszPostCmdArgs[i] = pszMsg;
12312 cbTmp = kHlpStrLen(pszMsg) + 1;
12313 pszMsg += cbTmp;
12314 if ( cbTmp < cbMsg
12315 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
12316 cbMsg -= cbTmp;
12317 else
12318 {
12319 cbMsg = KSIZE_MAX;
12320 break;
12321 }
12322 }
12323 if (cbMsg == 0)
12324 {
12325 apszPostCmdArgs[cPostCmdArgs] = NULL;
12326
12327 /*
12328 * The next step.
12329 */
12330 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
12331 cArgs, papszArgs, fWatcomBrainDamange,
12332 cEnvVars, papszEnvVars, pszSpecialEnv,
12333 fNoPchCaching,
12334 cPostCmdArgs, apszPostCmdArgs);
12335 }
12336 else if (cbMsg == KSIZE_MAX)
12337 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
12338 else
12339 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
12340 }
12341 else
12342 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
12343 }
12344 else
12345 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
12346 }
12347 else
12348 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12349 }
12350 else
12351 kwErrPrintf("Detected bogus message unpacking flags!\n");
12352 kHlpFree((void *)papszEnvVars);
12353 }
12354 else
12355 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12356 }
12357 else
12358 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12359 }
12360 else
12361 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12362 kHlpFree((void *)papszArgs);
12363 }
12364 else
12365 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12366 }
12367 else
12368 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12369 }
12370 else
12371 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12372 }
12373 else
12374 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12375 return rcExit;
12376}
12377
12378
12379/**
12380 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12381 *
12382 * @retval 0 on success.
12383 * @retval -1 on error (fully bitched).
12384 *
12385 * @param hPipe The pipe handle.
12386 * @param pvBuf The buffer to write out out.
12387 * @param cbToWrite The number of bytes to write.
12388 */
12389static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12390{
12391 KU8 const *pbBuf = (KU8 const *)pvBuf;
12392 KU32 cbLeft = cbToWrite;
12393 while (g_rcCtrlC == 0)
12394 {
12395 DWORD cbActuallyWritten = 0;
12396 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12397 {
12398 cbLeft -= cbActuallyWritten;
12399 if (!cbLeft)
12400 return 0;
12401 pbBuf += cbActuallyWritten;
12402 }
12403 else
12404 {
12405 DWORD dwErr = GetLastError();
12406 if (cbLeft == cbToWrite)
12407 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12408 else
12409 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12410 return -1;
12411 }
12412 }
12413 return -1;
12414}
12415
12416
12417/**
12418 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12419 *
12420 * @retval 0 on success.
12421 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12422 * @retval -1 on error (fully bitched).
12423 * @param hPipe The pipe handle.
12424 * @param pvBuf The buffer to read into.
12425 * @param cbToRead The number of bytes to read.
12426 * @param fShutdownOkay Whether connection shutdown while reading the
12427 * first byte is okay or not.
12428 */
12429static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12430{
12431 KU8 *pbBuf = (KU8 *)pvBuf;
12432 KU32 cbLeft = cbToRead;
12433 while (g_rcCtrlC == 0)
12434 {
12435 DWORD cbActuallyRead = 0;
12436 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12437 {
12438 cbLeft -= cbActuallyRead;
12439 if (!cbLeft)
12440 return 0;
12441 pbBuf += cbActuallyRead;
12442 }
12443 else
12444 {
12445 DWORD dwErr = GetLastError();
12446 if (cbLeft == cbToRead)
12447 {
12448 if ( fMayShutdown
12449 && dwErr == ERROR_BROKEN_PIPE)
12450 return 1;
12451 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12452 }
12453 else
12454 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12455 return -1;
12456 }
12457 }
12458 return -1;
12459}
12460
12461
12462/**
12463 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12464 *
12465 * @returns pszBuf
12466 * @param pszBuf The buffer (sufficiently large).
12467 * @param uValue The value.
12468 */
12469static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12470{
12471 char szTmp[64];
12472 char *psz = &szTmp[63];
12473 int cch = 4;
12474
12475 *psz-- = '\0';
12476 do
12477 {
12478 if (--cch == 0)
12479 {
12480 *psz-- = ' ';
12481 cch = 3;
12482 }
12483 *psz-- = (uValue % 10) + '0';
12484 uValue /= 10;
12485 } while (uValue != 0);
12486
12487 return strcpy(pszBuf, psz + 1);
12488}
12489
12490
12491/**
12492 * Prints statistics.
12493 */
12494static void kwPrintStats(void)
12495{
12496 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12497 MEMORYSTATUSEX MemStatus;
12498 IO_COUNTERS IoCounters;
12499 DWORD cHandles;
12500 KSIZE cMisses;
12501 char szBuf[16*1024];
12502 int off = 0;
12503 char szPrf[24];
12504 char sz1[64];
12505 char sz2[64];
12506 char sz3[64];
12507 char sz4[64];
12508
12509 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12510
12511 szBuf[off++] = '\n';
12512
12513 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12514 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12515 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12516 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12517 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12518
12519 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12520 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12521
12522 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12523 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12524 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12525
12526 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12527 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12528 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12529
12530 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12531 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12532 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12533 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12534 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12535 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12536 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12537
12538 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12539 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12540 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12541 kwFmtU64(sz1, g_pFsCache->cObjects),
12542 kwFmtU64(sz2, g_pFsCache->cbObjects),
12543 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12544 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12545 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12546 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12547 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12548 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12549#ifdef KFSCACHE_CFG_UTF16
12550 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12551 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12552 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12553 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12554 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12555#endif
12556 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12557 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12558 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12559 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12560 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12561
12562 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12563 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12564 kwFmtU64(sz1, g_pFsCache->cLookups),
12565 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12566 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12567 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12568 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12569 kwFmtU64(sz4, cMisses),
12570 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12571
12572 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12573 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12574 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12575 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12576 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12577 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12578 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12579 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12580
12581
12582 /*
12583 * Process & Memory details.
12584 */
12585 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12586 cHandles = 0;
12587 MemInfo.cb = sizeof(MemInfo);
12588 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12589 memset(&MemInfo, 0, sizeof(MemInfo));
12590 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12591 kwFmtU64(sz1, cHandles),
12592 kwFmtU64(sz2, MemInfo.PageFaultCount),
12593 kwFmtU64(sz3, MemInfo.PagefileUsage),
12594 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12595 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12596 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12597 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12598 kwFmtU64(sz3, MemInfo.PrivateUsage));
12599 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12600 szPrf,
12601 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12602 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12603 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12604 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12605
12606 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12607 memset(&IoCounters, 0, sizeof(IoCounters));
12608 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12609 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12610 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12611 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12612 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12613 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12614 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12615 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12616 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12617
12618 MemStatus.dwLength = sizeof(MemStatus);
12619 if (!GlobalMemoryStatusEx(&MemStatus))
12620 memset(&MemStatus, 0, sizeof(MemStatus));
12621 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12622 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12623 MemStatus.ullAvailVirtual);
12624 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12625
12626 maybe_con_fwrite(szBuf, off, 1, stdout);
12627 fflush(stdout);
12628}
12629
12630
12631/**
12632 * Handles what comes after --test.
12633 *
12634 * @returns Exit code.
12635 * @param argc Number of arguments after --test.
12636 * @param argv Arguments after --test.
12637 */
12638static int kwTestRun(int argc, char **argv)
12639{
12640 int i;
12641 int j;
12642 int rcExit;
12643 int cRepeats;
12644 char szCwd[MAX_PATH];
12645 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12646 KU32 cEnvVars;
12647 char **papszEnvVars;
12648 const char *pszSpecialEnv = "";
12649 const char *pszSpecialEnvFull = NULL;
12650 KBOOL fWatcomBrainDamange = K_FALSE;
12651 KBOOL fNoPchCaching = K_FALSE;
12652
12653 /*
12654 * Parse arguments.
12655 */
12656 /* Repeat count. */
12657 i = 0;
12658 if (i >= argc)
12659 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12660 if (strcmp(argv[i], "--") != 0)
12661 {
12662 cRepeats = atoi(argv[i]);
12663 if (cRepeats <= 0)
12664 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12665 i++;
12666
12667 /* Optional directory change. */
12668 if ( i < argc
12669 && ( strcmp(argv[i], "--chdir") == 0
12670 || strcmp(argv[i], "-C") == 0 ) )
12671 {
12672 i++;
12673 if (i >= argc)
12674 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12675 pszCwd = argv[i++];
12676 }
12677
12678 /* Optional watcom flag directory change. */
12679 if ( i < argc
12680 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12681 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12682 {
12683 fWatcomBrainDamange = K_TRUE;
12684 i++;
12685 }
12686
12687 /* Optional watcom flag directory change. */
12688 if ( i < argc
12689 && strcmp(argv[i], "--no-pch-caching") == 0)
12690 {
12691 fNoPchCaching = K_TRUE;
12692 i++;
12693 }
12694
12695 /* Optional directory change. */
12696 if ( i < argc
12697 && ( strcmp(argv[i], "--set-special") == 0
12698 || strcmp(argv[i], "-s") == 0 ) )
12699 {
12700 i++;
12701 if (i >= argc)
12702 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12703 pszSpecialEnvFull = argv[i++];
12704 putenv(pszSpecialEnvFull);
12705 pszSpecialEnv = strdup(pszSpecialEnvFull);
12706 *strchr(pszSpecialEnv, '=') = '\0';
12707 }
12708
12709 /* Trigger breakpoint */
12710 if ( i < argc
12711 && strcmp(argv[i], "--breakpoint") == 0)
12712 {
12713 __debugbreak();
12714 i++;
12715 }
12716
12717 /* Check for '--'. */
12718 if (i >= argc)
12719 return kwErrPrintfRc(2, "Missing '--'\n");
12720 if (strcmp(argv[i], "--") != 0)
12721 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
12722 i++;
12723 }
12724 else
12725 {
12726 cRepeats = 1;
12727 i++;
12728 }
12729 if (i >= argc)
12730 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
12731
12732 /*
12733 * Duplicate the environment.
12734 */
12735 cEnvVars = 0;
12736 while (environ[cEnvVars] != NULL)
12737 cEnvVars++;
12738 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
12739
12740 /*
12741 * Do the job.
12742 */
12743 rcExit = 0;
12744 for (j = 0; j < cRepeats; j++)
12745 {
12746 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
12747 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
12748 argc - i, &argv[i], fWatcomBrainDamange,
12749 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
12750 0, NULL);
12751 KW_LOG(("rcExit=%d\n", rcExit));
12752 kwSandboxCleanupLate(&g_Sandbox);
12753 }
12754
12755 if (getenv("KWORKER_STATS") != NULL)
12756 kwPrintStats();
12757
12758# ifdef WITH_LOG_FILE
12759 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12760 CloseHandle(g_hLogFile);
12761# endif
12762 return rcExit;
12763}
12764
12765
12766/**
12767 * Reads @a pszFile into memory and chops it up into an argument vector.
12768 *
12769 * @returns Pointer to the argument vector on success, NULL on failure.
12770 * @param pszFile The file to load.
12771 * @param pcArgs Where to return the number of arguments.
12772 * @param ppszFileContent Where to return the allocation.
12773 */
12774static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
12775{
12776 char **papszArgs = NULL;
12777 FILE *pFile = fopen(pszFile, "r");
12778 if (pFile)
12779 {
12780 long cbFile;
12781 if ( fseek(pFile, 0, SEEK_END) == 0
12782 && (cbFile = ftell(pFile)) >= 0
12783 && fseek(pFile, 0, SEEK_SET) == 0)
12784 {
12785 char *pszFile = kHlpAllocZ(cbFile + 3);
12786 if (pszFile)
12787 {
12788 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
12789 if ( feof(pFile)
12790 && !ferror(pFile))
12791 {
12792 size_t off = 0;
12793 int cArgs = 0;
12794 int cAllocated = 0;
12795 char ch;
12796
12797 pszFile[cbRead] = '\0';
12798 pszFile[cbRead + 1] = '\0';
12799 pszFile[cbRead + 2] = '\0';
12800
12801 while ((ch = pszFile[off]) != '\0')
12802 {
12803 char *pszArg;
12804 switch (ch)
12805 {
12806 case ' ':
12807 case '\t':
12808 case '\n':
12809 case '\r':
12810 off++;
12811 continue;
12812
12813 case '\\':
12814 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
12815 {
12816 off += 2;
12817 continue;
12818 }
12819 /* fall thru */
12820 default:
12821 pszArg = &pszFile[off];
12822 do
12823 ch = pszFile[++off];
12824 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
12825 pszFile[off++] = '\0';
12826 break;
12827
12828 case '\'':
12829 pszArg = &pszFile[++off];
12830 while ((ch = pszFile[off]) != '\0' && ch != '\'')
12831 off++;
12832 pszFile[off++] = '\0';
12833 break;
12834
12835 case '\"': /** @todo escape sequences */
12836 pszArg = &pszFile[++off];
12837 while ((ch = pszFile[off]) != '\0' && ch != '"')
12838 off++;
12839 pszFile[off++] = '\0';
12840 break;
12841 }
12842 if (cArgs + 1 >= cAllocated)
12843 {
12844 void *pvNew;
12845 cAllocated = cAllocated ? cAllocated * 2 : 16;
12846 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
12847 if (pvNew)
12848 papszArgs = (char **)pvNew;
12849 else
12850 {
12851 kHlpFree(papszArgs);
12852 papszArgs = NULL;
12853 break;
12854 }
12855 }
12856 papszArgs[cArgs] = pszArg;
12857 papszArgs[++cArgs] = NULL;
12858 }
12859 *pcArgs = cArgs;
12860 }
12861 else
12862 kwErrPrintf("Error reading '%s'!\n", pszFile);
12863 }
12864 else
12865 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
12866 }
12867 else
12868 kwErrPrintf("Error seeking '%s'!\n", pszFile);
12869 fclose(pFile);
12870 }
12871 else
12872 kwErrPrintf("Error opening '%s'!\n", pszFile);
12873 return papszArgs;
12874}
12875
12876/**
12877 * Appends a string to an string vector (arguments or enviornment).
12878 *
12879 * @returns 0 on success, non-zero on failure (exit code).
12880 * @param ppapszVector Pointer to the string pointer array.
12881 * @param pcEntries Pointer to the array size.
12882 * @param pszAppend The string to append.
12883 */
12884static int kwFullTestVectorAppend(const char ***ppapszVector, KU32 *pcEntries, char const *pszAppend)
12885{
12886 KU32 cEntries = *pcEntries;
12887 if (!(cEntries & 15))
12888 {
12889 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
12890 if (pvNew)
12891 *ppapszVector = (const char **)pvNew;
12892 else
12893 return kwErrPrintfRc(2, "Out of memory!\n");
12894 }
12895 (*ppapszVector)[cEntries] = pszAppend;
12896 (*ppapszVector)[++cEntries] = NULL;
12897 *pcEntries = cEntries;
12898 return 0;
12899}
12900
12901
12902/**
12903 * Parses arguments for --full-test.
12904 *
12905 * @returns 0 on success, non-zero on failure (exit code).
12906 */
12907static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
12908 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
12909{
12910 PKWONETEST pCur = *ppHead;
12911 int i;
12912 for (i = 0; i < argc; i++)
12913 {
12914 int rc = 0;
12915 const char *pszArg = argv[i];
12916 if (*pszArg == 'k')
12917 {
12918 if (kHlpStrComp(pszArg, "kSubmit") == 0)
12919 {
12920 if (*piState != 0)
12921 {
12922 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
12923 if (!pCur)
12924 return kwErrPrintfRc(2, "Out of memory!\n");
12925 pCur->fVirgin = K_TRUE;
12926 pCur->pszCwd = pszDefaultCwd;
12927 pCur->cRuns = 1;
12928 pCur->pNext = *ppHead;
12929 *ppHead = pCur;
12930 *piState = 0;
12931 }
12932 else if (!pCur->fVirgin)
12933 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
12934 pCur->pszJobSrc = pszJobSrc;
12935 pCur->iJobSrc = i;
12936 continue; /* (to stay virgin) */
12937 }
12938
12939 /* Ignore "kWorker 378172/62:" sequences that kmk/kSubmit spews out on failure. */
12940 if ( kHlpStrComp(pszArg, "kWorker") == 0
12941 && i + 1 < argc
12942 && (unsigned)(argv[i + 1][0] - '0') <= 9)
12943 {
12944 i++;
12945 continue;
12946 }
12947 }
12948
12949 if ( *pszArg == '-'
12950 && ( *piState == 0
12951 || pszArg[1] == '@'))
12952 {
12953 const char *pszValue = NULL;
12954 char ch = *++pszArg;
12955 pszArg++;
12956 if (ch == '-')
12957 {
12958 ch = '\0';
12959 if (*pszArg == '\0') /* -- */
12960 *piState = 2;
12961 /* Translate or handle long options: */
12962 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
12963 ch = 'E';
12964 else if (kHlpStrComp(pszArg, "special-env") == 0)
12965 ch = 's';
12966 else if (kHlpStrComp(pszArg, "default-env") == 0)
12967 {
12968 unsigned i;
12969 pCur->cEnvVars = 0;
12970 for (i = 0; environ[i] && rc == 0; i++)
12971 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
12972 }
12973 else if (kHlpStrComp(pszArg, "chdir") == 0)
12974 ch = 'C';
12975 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
12976 ch = 'P';
12977 else if (kHlpStrComp(pszArg, "response-file") == 0)
12978 ch = '@';
12979 else if (kHlpStrComp(pszArg, "runs") == 0)
12980 ch = 'R';
12981 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
12982 pCur->fWatcomBrainDamange = K_TRUE;
12983 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
12984 pCur->fNoPchCaching = K_TRUE;
12985 else if (kHlpStrComp(pszArg, "executable") == 0)
12986 ch = 'e';
12987 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12988 {
12989 __debugbreak();
12990 continue; /* (to stay virgin) */
12991 }
12992 else
12993 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12994 pszArg = "";
12995 }
12996
12997 while (ch != '\0' && rc == 0)
12998 {
12999 /* Fetch value if needed: */
13000 switch (ch)
13001 {
13002 case '@':
13003 case 'e':
13004 case 'E':
13005 case 's':
13006 case 'C':
13007 case 'R':
13008 if (*pszArg == ':' || *pszArg == '=')
13009 pszValue = &pszArg[1];
13010 else if (*pszArg)
13011 pszValue = pszArg;
13012 else if (i + 1 < argc)
13013 pszValue = argv[++i];
13014 else
13015 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
13016 pszArg = "";
13017 break;
13018 }
13019
13020 /* Handle the option: */
13021 switch (ch)
13022 {
13023 case 'E':
13024 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
13025 break;
13026 case 'C':
13027 pCur->pszCwd = pszValue;
13028 break;
13029 case 's':
13030 pCur->pszSpecialEnv = pszValue;
13031 break;
13032 case 'e':
13033 pCur->pszExecutable = pszValue;
13034 break;
13035 case 'P':
13036 *piState = 1;
13037 if (*pszArg)
13038 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
13039 break;
13040 case 'R':
13041 pCur->cRuns = atoi(pszValue);
13042 if ((int)pCur->cRuns < 0)
13043 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
13044 break;
13045 case '@':
13046 if (cRecursions < 5)
13047 {
13048 char *pszLeaked = NULL;
13049 int cArgs = 0;
13050 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
13051 if (papszArgsLeaked)
13052 {
13053 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
13054 cRecursions + 1, pszValue);
13055 pCur = *ppHead;
13056 }
13057 else
13058 return 2;
13059 }
13060 else
13061 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
13062 break;
13063 }
13064
13065 /* next */
13066 ch = *pszArg++;
13067 }
13068 }
13069 else if (*piState == 2)
13070 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
13071 else if (*piState == 1)
13072 {
13073 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
13074 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
13075 else
13076 *piState = 2;
13077 }
13078 else
13079 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
13080 if (rc)
13081 return rc;
13082 pCur->fVirgin = K_FALSE;
13083 }
13084 return 0;
13085}
13086
13087
13088/**
13089 * Handles what comes after --full-test.
13090 *
13091 * @returns Exit code.
13092 * @param argc Number of arguments after --full-test.
13093 * @param argv Arguments after --full-test.
13094 */
13095static int kwFullTestRun(int argc, char **argv)
13096{
13097 char szDefaultCwd[MAX_PATH];
13098 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
13099 KWONETEST FirstTest;
13100 PKWONETEST pHead = &FirstTest;
13101 PKWONETEST pCur;
13102 int iState = 0;
13103 int rcExit;
13104
13105 /*
13106 * Parse arguments.
13107 */
13108 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
13109 FirstTest.pszJobSrc = "command-line";
13110 FirstTest.iJobSrc = 1;
13111 FirstTest.fVirgin = K_TRUE;
13112 FirstTest.pszCwd = pszDefaultCwd;
13113 FirstTest.cRuns = 1;
13114
13115 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
13116 if (rcExit)
13117 return rcExit;
13118
13119 /*
13120 * Do the job. LIFO ordering (see kSubmit).
13121 */
13122 for (pCur = pHead; pCur; pCur = pCur->pNext)
13123 {
13124 if (!pCur->pszExecutable && pCur->papszArgs)
13125 pCur->pszExecutable = pCur->papszArgs[0];
13126 if ( pCur->pszExecutable
13127 && pCur->cArgs > 0
13128 && pCur->cEnvVars > 0)
13129 {
13130 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
13131 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
13132 unsigned iRun;
13133
13134 for (iRun = 0; iRun < pCur->cRuns; iRun++)
13135 {
13136 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
13137 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
13138 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
13139 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
13140
13141 KW_LOG(("rcExit=%d\n", rcExit));
13142 kwSandboxCleanupLate(&g_Sandbox);
13143
13144 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
13145 }
13146 kHlpFree(papszEnvVarsCopy);
13147 }
13148 else
13149 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
13150 pCur->pszExecutable ? "" : " No executable!",
13151 pCur->cArgs < 1 ? " No arguments!" : "",
13152 pCur->cEnvVars < 1 ? " No environment!" : "",
13153 pCur->iJobSrc, pCur->pszJobSrc);
13154 }
13155
13156 if (getenv("KWORKER_STATS") != NULL)
13157 kwPrintStats();
13158
13159# ifdef WITH_LOG_FILE
13160 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13161 CloseHandle(g_hLogFile);
13162# endif
13163 return rcExit;
13164}
13165
13166
13167/**
13168 * Helper for main() argument handling that sets the processor group if
13169 * possible.
13170 */
13171static void kwSetProcessorGroup(unsigned long uGroup)
13172{
13173 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
13174 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
13175 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
13176
13177 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
13178 if (pfnSetThreadGroupAffinity)
13179 {
13180 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
13181 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
13182 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
13183 if (NewAff.Mask && (WORD)uGroup == uGroup)
13184 {
13185 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
13186 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
13187 }
13188 else if (GetLastError() == NO_ERROR)
13189 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
13190 else
13191 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
13192 uGroup, GetLastError());
13193 }
13194 else
13195 {
13196 OSVERSIONINFOA VerInfo = {0};
13197 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
13198 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
13199 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
13200 }
13201}
13202
13203
13204int main(int argc, char **argv)
13205{
13206 KSIZE cbMsgBuf = 0;
13207 KU8 *pbMsgBuf = NULL;
13208 int i;
13209 HANDLE hPipe = INVALID_HANDLE_VALUE;
13210 const char *pszTmp;
13211 KFSLOOKUPERROR enmIgnored;
13212 DWORD dwType;
13213#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
13214 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
13215 kwSandboxVecXcptEmulateChained);
13216#endif
13217#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13218 HANDLE hCurProc = GetCurrentProcess();
13219 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
13220 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
13221#endif
13222
13223
13224#ifdef WITH_FIXED_VIRTUAL_ALLOCS
13225 /*
13226 * Reserve memory for cl.exe
13227 */
13228 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
13229 {
13230 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
13231 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
13232 g_aFixedVirtualAllocs[i].cbFixed,
13233 MEM_RESERVE, PAGE_READWRITE);
13234 if ( !g_aFixedVirtualAllocs[i].pvReserved
13235 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
13236 {
13237 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
13238 GetLastError());
13239 if (g_aFixedVirtualAllocs[i].pvReserved)
13240 {
13241 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
13242 g_aFixedVirtualAllocs[i].pvReserved = NULL;
13243 }
13244 }
13245 }
13246#endif
13247
13248 /*
13249 * Register our Control-C and Control-Break handlers.
13250 */
13251 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
13252 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
13253
13254 /*
13255 * Create the cache and mark the temporary directory as using the custom revision.
13256 */
13257 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
13258 if (!g_pFsCache)
13259 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
13260
13261 pszTmp = getenv("TEMP");
13262 if (pszTmp && *pszTmp != '\0')
13263 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13264 pszTmp = getenv("TMP");
13265 if (pszTmp && *pszTmp != '\0')
13266 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13267 pszTmp = getenv("TMPDIR");
13268 if (pszTmp && *pszTmp != '\0')
13269 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13270
13271 /*
13272 * Make g_abDefLdBuf executable.
13273 */
13274 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
13275 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
13276 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
13277 InitializeCriticalSection(&g_Sandbox.HandlesLock);
13278 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
13279
13280#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13281 /*
13282 * Get and duplicate the console handles.
13283 */
13284 /* Standard output. */
13285 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
13286 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
13287 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13288 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
13289 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
13290 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
13291 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13292 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13293 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
13294 g_Sandbox.HandleStdOut.cRefs = 0x10001;
13295 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
13296 g_Sandbox.HandleStdOut.tidOwner = KU32_MAX;
13297 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
13298 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
13299 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
13300 {
13301 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
13302 g_Sandbox.cFixedHandles++;
13303 else
13304 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
13305 }
13306 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13307 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
13308
13309 /* Standard error. */
13310 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
13311 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
13312 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13313 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
13314 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
13315 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
13316 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13317 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13318 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
13319 g_Sandbox.HandleStdErr.cRefs = 0x10001;
13320 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
13321 g_Sandbox.HandleStdErr.tidOwner = KU32_MAX;
13322 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
13323 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
13324 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
13325 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
13326 {
13327 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
13328 g_Sandbox.cFixedHandles++;
13329 else
13330 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
13331 }
13332 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13333 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
13334
13335 /* Combined console buffer. */
13336 if (g_Sandbox.StdErr.fIsConsole)
13337 {
13338 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
13339 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13340 }
13341 else if (g_Sandbox.StdOut.fIsConsole)
13342 {
13343 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
13344 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13345 }
13346 else
13347 {
13348 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
13349 g_Sandbox.Combined.uCodepage = CP_ACP;
13350 }
13351 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
13352#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
13353
13354
13355 /*
13356 * Parse arguments.
13357 */
13358 for (i = 1; i < argc; i++)
13359 {
13360 if (strcmp(argv[i], "--pipe") == 0)
13361 {
13362 i++;
13363 if (i < argc)
13364 {
13365 char *pszEnd = NULL;
13366 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13367 if ( *argv[i]
13368 && pszEnd != NULL
13369 && *pszEnd == '\0'
13370 && u64Value != 0
13371 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13372 && (uintptr_t)u64Value == u64Value)
13373 hPipe = (HANDLE)(uintptr_t)u64Value;
13374 else
13375 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13376 }
13377 else
13378 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13379 }
13380 else if (strcmp(argv[i], "--volatile") == 0)
13381 {
13382 i++;
13383 if (i < argc)
13384 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13385 else
13386 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13387 }
13388 else if (strcmp(argv[i], "--test") == 0)
13389 return kwTestRun(argc - i - 1, &argv[i + 1]);
13390 else if (strcmp(argv[i], "--full-test") == 0)
13391 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13392 else if (strcmp(argv[i], "--priority") == 0)
13393 {
13394 i++;
13395 if (i < argc)
13396 {
13397 char *pszEnd = NULL;
13398 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13399 if ( *argv[i]
13400 && pszEnd != NULL
13401 && *pszEnd == '\0'
13402 && uValue >= 1
13403 && uValue <= 5)
13404 {
13405 DWORD dwClass;
13406 int dwPriority;
13407 switch (uValue)
13408 {
13409 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13410 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13411 default:
13412 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13413 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13414 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13415 }
13416 SetPriorityClass(GetCurrentProcess(), dwClass);
13417 if (dwPriority != INT_MAX)
13418 SetThreadPriority(GetCurrentThread(), dwPriority);
13419 }
13420 else
13421 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13422 }
13423 else
13424 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13425 }
13426 else if (strcmp(argv[i], "--group") == 0)
13427 {
13428 i++;
13429 if (i < argc)
13430 {
13431 char *pszEnd = NULL;
13432 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13433 if ( *argv[i]
13434 && pszEnd != NULL
13435 && *pszEnd == '\0'
13436 && uValue == (WORD)uValue)
13437 kwSetProcessorGroup(uValue);
13438 else
13439 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13440 }
13441 else
13442 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13443 }
13444 else if ( strcmp(argv[i], "--verbose") == 0
13445 || strcmp(argv[i], "-v") == 0)
13446 g_cVerbose++;
13447 else if ( strcmp(argv[i], "--help") == 0
13448 || strcmp(argv[i], "-h") == 0
13449 || strcmp(argv[i], "-?") == 0)
13450 {
13451 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13452 "usage: kWorker <--help|-h>\n"
13453 "usage: kWorker <--version|-V>\n"
13454 "usage: kWorker [--volatile dir] --full-test kSubmit ...\n"
13455 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13456 "\n"
13457 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13458 return 0;
13459 }
13460 else if ( strcmp(argv[i], "--version") == 0
13461 || strcmp(argv[i], "-V") == 0)
13462 return kbuild_version(argv[0]);
13463 else
13464 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13465 }
13466
13467 /*
13468 * If no --pipe argument, then assume its standard input.
13469 * We need to carefully replace the CRT stdin with a handle to "nul".
13470 */
13471 if (hPipe == INVALID_HANDLE_VALUE)
13472 {
13473 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13474 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13475 {
13476 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13477 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13478 {
13479 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13480 if (fdNul >= 0)
13481 {
13482 if (_dup2(fdNul, 0) >= 0)
13483 {
13484 close(fdNul);
13485 hPipe = hDuplicate;
13486 }
13487 else
13488 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13489 }
13490 else
13491 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13492 }
13493 else
13494 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13495 }
13496 else
13497 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13498 GetFileType(hPipe), GetLastError());
13499 }
13500 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13501 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13502 GetFileType(hPipe), GetLastError());
13503 g_hPipe = hPipe;
13504
13505 /*
13506 * Serve the pipe.
13507 */
13508 for (;;)
13509 {
13510 KU32 cbMsg = 0;
13511 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13512 if (rc == 0)
13513 {
13514 /* Make sure the message length is within sane bounds. */
13515 if ( cbMsg > 4
13516 && cbMsg <= 256*1024*1024)
13517 {
13518 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13519 if (cbMsg + 4 <= cbMsgBuf)
13520 { /* likely */ }
13521 else
13522 {
13523 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13524 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13525 if (!pbMsgBuf)
13526 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13527 }
13528
13529 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13530 *(KU32 *)pbMsgBuf = cbMsg;
13531 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13532 if (rc == 0)
13533 {
13534 const char *psz;
13535
13536 pbMsgBuf[cbMsg] = '\0';
13537 pbMsgBuf[cbMsg + 1] = '\0';
13538 pbMsgBuf[cbMsg + 2] = '\0';
13539 pbMsgBuf[cbMsg + 3] = '\0';
13540
13541 /* The first string after the header is the command. */
13542 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13543 if ( strcmp(psz, "JOB") == 0
13544 && g_rcCtrlC == 0)
13545 {
13546 struct
13547 {
13548 KI32 rcExitCode;
13549 KU8 bExiting;
13550 KU8 abZero[3];
13551 } Reply;
13552 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13553 Reply.bExiting = g_fRestart;
13554 Reply.abZero[0] = 0;
13555 Reply.abZero[1] = 0;
13556 Reply.abZero[2] = 0;
13557 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13558 if ( rc == 0
13559 && !g_fRestart)
13560 {
13561 kwSandboxCleanupLate(&g_Sandbox);
13562 if (g_rcCtrlC == 0)
13563 continue;
13564 }
13565 }
13566 else
13567 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13568 }
13569 }
13570 else
13571 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13572 }
13573
13574 /*
13575 * If we're exitting because we're restarting, we need to delay till
13576 * kmk/kSubmit has read the result. Windows documentation says it
13577 * immediately discards pipe buffers once the pipe is broken by the
13578 * server (us). So, We flush the buffer and queues a 1 byte read
13579 * waiting for kSubmit to close the pipe when it receives the
13580 * bExiting = K_TRUE result.
13581 */
13582 if (g_fRestart)
13583 {
13584 DWORD cbIgnored = 1;
13585 KU8 b;
13586 FlushFileBuffers(hPipe);
13587 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13588 }
13589
13590 CloseHandle(hPipe);
13591#ifdef WITH_LOG_FILE
13592 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13593 CloseHandle(g_hLogFile);
13594#endif
13595 if (getenv("KWORKER_STATS") != NULL)
13596 kwPrintStats();
13597 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13598 }
13599}
13600
13601
13602/** @page pg_kWorker kSubmit / kWorker
13603 *
13604 * @section sec_kWorker_Motivation Motivation / Inspiration
13605 *
13606 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13607 * builds on machines "infested" by Anti Virus protection and disk encryption
13608 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13609 * got "infected".
13610 *
13611 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13612 * mainly a bunch of tiny assembly and C files being compiler a million times.
13613 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13614 * own toolchain from within the same process, saving a lot of process creation
13615 * and teardown overhead.
13616 *
13617 *
13618 * @section sec_kWorker_kSubmit About kSubmit
13619 *
13620 * When wanting to execute a job in a kWorker instance, it must be submitted
13621 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13622 * built into kmk and does not exist as an external program. The reason for
13623 * this is that it keep track of the kWorker instances.
13624 *
13625 * The kSubmit command has the --32-bit and --64-bit options for selecting
13626 * between 32-bit and 64-bit worker instance. We generally assume the user of
13627 * the command knows which bit count the executable has, so kSubmit is spared
13628 * the extra work of finding out.
13629 *
13630 * The kSubmit command shares a environment and current directory manipulation
13631 * with the kRedirect command, but not the file redirection. So long no file
13632 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13633 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13634 * and/or current directory changes to work.
13635 *
13636 * Unlike the kRedirect command, the kSubmit command can also specify an
13637 * internall post command to be executed after the main command succeeds.
13638 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13639 * information from Microsoft COFF object files and Watcom OMF object files and
13640 * is scheduled to replace kDepIDB.
13641 *
13642 *
13643 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13644 *
13645 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13646 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13647 * it. When the job is completed, kWorker writes a short reply with the exit
13648 * code and an internal status indicating whether it is going to restart.
13649 *
13650 * The kWorker intance will reply to kSubmit before completing all the internal
13651 * cleanup work, so as not to delay the next job execution unnecessarily. This
13652 * includes checking its own memory consumption and checking whether it needs
13653 * restarting. So, a decision to restart unfortunately have to wait till after
13654 * the next job has completed. This is a little bit unfortunate if the next job
13655 * requires a lot of memory and kWorker has already leaked/used a lot.
13656 *
13657 *
13658 * @section sec_kWorker_How_Works How kWorker Works
13659 *
13660 * kWorker will load the executable specified by kSubmit into memory and call
13661 * it's entrypoint in a lightly sandbox'ed environment.
13662 *
13663 *
13664 * @subsection ssec_kWorker_Loaing Image loading
13665 *
13666 * kWorker will manually load all the executable images into memory, fix them
13667 * up, and make a copy of the virgin image so it can be restored using memcpy
13668 * the next time it is used.
13669 *
13670 * Imported functions are monitored and replacements used for a few of them.
13671 * These replacements are serve the following purposes:
13672 * - Provide a different command line.
13673 * - Provide a different environment.
13674 * - Intercept process termination.
13675 * - Intercept thread creation (only linker is allowed to create threads).
13676 * - Intercept file reading for caching (header files, ++) as file system
13677 * access is made even slower by anti-virus software.
13678 * - Intercept crypto hash APIs to cache MD5 digests of header files
13679 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13680 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13681 * in memory as writing files grows expensive with encryption and
13682 * anti-virus software active.
13683 * - Intercept some file system queries to use the kFsCache instead of
13684 * going to the kernel and slowly worm thru the AV filter driver.
13685 * - Intercept standard output/error and console writes to aggressivly
13686 * buffer the output. The MS CRT does not buffer either when it goes to
13687 * the console, resulting in terrible performance and mixing up output
13688 * with other compile jobs.
13689 * This also allows us to filter out the annoying source file announcements
13690 * by cl.exe.
13691 * - Intercept VirtualAlloc and VirtualFree to prevent
13692 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13693 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13694 * the callbacks run after each job.
13695 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13696 * executables and tools using custom heaps (like the microsoft linker).
13697 * [exectuable images only]
13698 * - Intercept atexit and _onexit registration to be able run them after
13699 * each job instead of crashing as kWorker exits. This also helps avoid
13700 * some leaks. [executable image only]
13701 *
13702 * DLLs falls into two categories, system DLLs which we always load using the
13703 * native loader, and tool DLLs which can be handled like the executable or
13704 * optionally using the native loader. We maintain a hardcoded white listing of
13705 * tool DLLs we trust to load using the native loader.
13706 *
13707 * Imports of natively loaded DLLs are processed too, but we only replace a
13708 * subset of the functions compared to natively loaded excutable and DLL images.
13709 *
13710 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13711 * This is to speed up job execution.
13712 *
13713 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13714 * for each job run, but so far this hasn't been necessary.
13715 *
13716 *
13717 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
13718 *
13719 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
13720 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
13721 * intermediate representation between the first (c1/c1xx.dll) and second pass
13722 * (c2.dll).
13723 *
13724 * kWorker helps the compiler as best as it can. Given a little knowledge about
13725 * stable and volatile file system areas, it can do a lot of caching that a
13726 * normal compiler driver cannot easily do when given a single file.
13727 *
13728 *
13729 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
13730 *
13731 * The preprocessor part will open and process header files exactly as they are
13732 * encountered in the source files. If string.h is included by the main source
13733 * and five other header files, it will be searched for (include path), opened,
13734 * read, MD5-summed, and pre-processed six times. The last five times is just a
13735 * waste of time because of the guards or \#pragma once. A smart compiler would
13736 * make a little extra effort and realize this.
13737 *
13738 * kWorker will cache help the preprocessor by remembering places where the
13739 * header was not found with help of kFsCache, and cache the file in memory when
13740 * found. The first part is taken care of by intercepting GetFileAttributesW,
13741 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
13742 * cached, the file is kept open and the CreateFileW call returns a duplicate of
13743 * that handle. An internal handle table is used by ReadFile and CloseFile to
13744 * keep track of intercepted handles (also used for temporary file, temporary
13745 * file mappings, console buffering, and standard out/err buffering).
13746 *
13747 * PS. The header search optimization also comes in handy when cl.exe goes on
13748 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
13749 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
13750 * optionally compile the three pass DLLs as executables during development
13751 * and problem analysis.
13752 *
13753 *
13754 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
13755 *
13756 * The issues of the temporary files is pretty severe on the Dell machine used
13757 * for benchmarking with full AV and encryption. The synthetic benchmark
13758 * improved by 30% when kWorker implemented measures to keep them entirely in
13759 * memory.
13760 *
13761 * kWorker implement these by recognizing the filename pattern in CreateFileW
13762 * and creating/opening the given file as needed. The handle returned is a
13763 * duplicate of the current process, thus giving us a good chance of catching
13764 * API calls we're not intercepting.
13765 *
13766 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
13767 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
13768 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
13769 * UnmapViewOfFile.
13770 *
13771 *
13772 * @section sec_kWorker_Numbers Some measurements.
13773 *
13774 * - r2881 building src/VBox/Runtime:
13775 * - without: 2m01.016388s = 120.016388 s
13776 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
13777 * - r2884 building vbox/debug (r110512):
13778 * - without: 11m14.446609s = 674.446609 s
13779 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
13780 * - r2896 building vbox/debug (r110577):
13781 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
13782 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
13783 * MS Defender as AV):
13784 * - without: 10m24.990389s = 624.990389s
13785 * - with: 08m04.738184s = 484.738184s
13786 * - delta: 624.99s - 484.74s = 140.25s
13787 * - saved: 140.25/624.99 = 22% faster
13788 *
13789 *
13790 * @subsection subsec_kWorker_Early_Numbers Early Experiments
13791 *
13792 * These are some early experiments doing 1024 compilations of
13793 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
13794 * main function:
13795 *
13796 * Skylake (W10/amd64, only stdandard MS defender):
13797 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
13798 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
13799 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
13800 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
13801 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
13802 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
13803 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
13804 *
13805 * Dell (W7/amd64, infected by mcafee):
13806 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
13807 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
13808 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
13809 *
13810 * The command line:
13811 * @code{.cpp}
13812 "\"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"
13813 * @endcode
13814 */
13815
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