VirtualBox

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

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

kWorker: Adjustments for VC++ 14.2. Fixed bug in GetFileAttributesExW/A where we'd forget to set the return value on success. Reduced the size of the Tls DLLs, but having to add more as the re-load-same-dll hack doesn't work anymore.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 507.6 KB
Line 
1/* $Id: kWorker.c 3361 2020-06-08 19:27:14Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <[email protected]>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33//#undef NDEBUG
34//#define K_STRICT 1
35//#define KW_LOG_ENABLED
36
37#define PSAPI_VERSION 1
38#include <k/kHlp.h>
39#include <k/kLdr.h>
40
41#include <stdio.h>
42#include <intrin.h>
43#include <setjmp.h>
44#include <ctype.h>
45#include <errno.h>
46#include <process.h>
47
48#include "nt/ntstat.h"
49#include "kbuild_version.h"
50
51#include "nt/ntstuff.h"
52#include "nt/nthlp.h"
53#include <psapi.h>
54
55#include "nt/kFsCache.h"
56#include "nt_fullpath.h"
57#include "win_get_processor_group_active_mask.h"
58#include "quote_argv.h"
59#include "md5.h"
60#include "console.h"
61
62#include "../kmk/kmkbuiltin.h"
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68/** @def WITH_TEMP_MEMORY_FILES
69 * Enables temporary memory files for cl.exe. */
70#define WITH_TEMP_MEMORY_FILES
71
72/** @def WITH_HASH_MD5_CACHE
73 * Enables caching of MD5 sums for cl.exe.
74 * This prevents wasting time on rehashing common headers each time
75 * they are included. */
76#define WITH_HASH_MD5_CACHE
77
78/** @def WITH_CRYPT_CTX_REUSE
79 * Enables reusing crypt contexts. The Visual C++ compiler always creates a
80 * context which is only used for MD5 and maybe some random bytes (VS 2010).
81 * So, only create it once and add a reference to it instead of creating new
82 * ones. Saves registry access among other things. */
83#define WITH_CRYPT_CTX_REUSE
84
85/** @def WITH_CONSOLE_OUTPUT_BUFFERING
86 * Enables buffering of all console output as well as removal of annoying
87 * source file echo by cl.exe. */
88#define WITH_CONSOLE_OUTPUT_BUFFERING
89
90/** @def WITH_STD_OUT_ERR_BUFFERING
91 * Enables buffering of standard output and standard error buffer as well as
92 * removal of annoying source file echo by cl.exe. */
93#define WITH_STD_OUT_ERR_BUFFERING
94
95/** @def WITH_LOG_FILE
96 * Log to file instead of stderr. */
97#define WITH_LOG_FILE
98
99/** @def WITH_HISTORY
100 * Keep history of the last jobs. For debugging. */
101#define WITH_HISTORY
102
103/** @def WITH_FIXED_VIRTUAL_ALLOCS
104 * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
105 * there is only one, but an important one, from cl.exe).
106 */
107#if K_ARCH == K_ARCH_X86_32
108# define WITH_FIXED_VIRTUAL_ALLOCS
109#endif
110
111/** @def WITH_PCH_CACHING
112 * Enables read caching of precompiled header files. */
113#if K_ARCH_BITS >= 64
114# define WITH_PCH_CACHING
115#endif
116
117
118#ifndef NDEBUG
119# define KW_LOG_ENABLED
120#endif
121
122/** @def KW_LOG
123 * Generic logging.
124 * @param a Argument list for kwDbgPrintf */
125#ifdef KW_LOG_ENABLED
126# define KW_LOG(a) kwDbgPrintf a
127#else
128# define KW_LOG(a) do { } while (0)
129#endif
130
131/** @def KWLDR_LOG
132 * Loader related logging.
133 * @param a Argument list for kwDbgPrintf */
134#ifdef KW_LOG_ENABLED
135# define KWLDR_LOG(a) kwDbgPrintf a
136#else
137# define KWLDR_LOG(a) do { } while (0)
138#endif
139
140
141/** @def KWFS_LOG
142 * FS cache logging.
143 * @param a Argument list for kwDbgPrintf */
144#ifdef KW_LOG_ENABLED
145# define KWFS_LOG(a) kwDbgPrintf a
146#else
147# define KWFS_LOG(a) do { } while (0)
148#endif
149
150/** @def KWOUT_LOG
151 * Output related logging.
152 * @param a Argument list for kwDbgPrintf */
153#ifdef KW_LOG_ENABLED
154# define KWOUT_LOG(a) kwDbgPrintf a
155#else
156# define KWOUT_LOG(a) do { } while (0)
157#endif
158
159/** @def KWCRYPT_LOG
160 * FS cache logging.
161 * @param a Argument list for kwDbgPrintf */
162#ifdef KW_LOG_ENABLED
163# define KWCRYPT_LOG(a) kwDbgPrintf a
164#else
165# define KWCRYPT_LOG(a) do { } while (0)
166#endif
167
168/** Converts a windows handle to a handle table index.
169 * @note We currently just mask off the 31th bit, and do no shifting or anything
170 * else to create an index of the handle.
171 * @todo consider shifting by 2 or 3. */
172#define KW_HANDLE_TO_INDEX(a_hHandle) ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
173/** Maximum handle value we can deal with. */
174#define KW_HANDLE_MAX 0x20000
175
176/** Max temporary file size (memory backed). */
177#if K_ARCH_BITS >= 64
178# define KWFS_TEMP_FILE_MAX (256*1024*1024)
179#else
180# define KWFS_TEMP_FILE_MAX (64*1024*1024)
181#endif
182
183/** Marks unfinished code. */
184#if 1
185# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
186#else
187# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
188#endif
189
190/** User data key for tools. */
191#define KW_DATA_KEY_TOOL (~(KUPTR)16381)
192/** User data key for a cached file. */
193#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
194
195/** String constant comma length. */
196#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
197
198
199/**
200 * Generate CRT slot wrapper functions.
201 */
202#define CRT_SLOT_FUNCTION_WRAPPER(a_RetTypeAndCallConv, a_FnName, a_aArgsDecl, a_aArgCall) \
203 static a_RetTypeAndCallConv a_FnName##00 a_aArgsDecl { const unsigned iCrtSlot = 0; return a_FnName##_wrapped a_aArgCall; } \
204 static a_RetTypeAndCallConv a_FnName##01 a_aArgsDecl { const unsigned iCrtSlot = 1; return a_FnName##_wrapped a_aArgCall; } \
205 static a_RetTypeAndCallConv a_FnName##02 a_aArgsDecl { const unsigned iCrtSlot = 2; return a_FnName##_wrapped a_aArgCall; } \
206 static a_RetTypeAndCallConv a_FnName##03 a_aArgsDecl { const unsigned iCrtSlot = 3; return a_FnName##_wrapped a_aArgCall; } \
207 static a_RetTypeAndCallConv a_FnName##04 a_aArgsDecl { const unsigned iCrtSlot = 4; return a_FnName##_wrapped a_aArgCall; } \
208 static a_RetTypeAndCallConv a_FnName##05 a_aArgsDecl { const unsigned iCrtSlot = 5; return a_FnName##_wrapped a_aArgCall; } \
209 static a_RetTypeAndCallConv a_FnName##06 a_aArgsDecl { const unsigned iCrtSlot = 6; return a_FnName##_wrapped a_aArgCall; } \
210 static a_RetTypeAndCallConv a_FnName##07 a_aArgsDecl { const unsigned iCrtSlot = 7; return a_FnName##_wrapped a_aArgCall; } \
211 static a_RetTypeAndCallConv a_FnName##08 a_aArgsDecl { const unsigned iCrtSlot = 8; return a_FnName##_wrapped a_aArgCall; } \
212 static a_RetTypeAndCallConv a_FnName##09 a_aArgsDecl { const unsigned iCrtSlot = 9; return a_FnName##_wrapped a_aArgCall; } \
213 static a_RetTypeAndCallConv a_FnName##10 a_aArgsDecl { const unsigned iCrtSlot = 10; return a_FnName##_wrapped a_aArgCall; } \
214 static a_RetTypeAndCallConv a_FnName##11 a_aArgsDecl { const unsigned iCrtSlot = 11; return a_FnName##_wrapped a_aArgCall; } \
215 static a_RetTypeAndCallConv a_FnName##12 a_aArgsDecl { const unsigned iCrtSlot = 12; return a_FnName##_wrapped a_aArgCall; } \
216 static a_RetTypeAndCallConv a_FnName##13 a_aArgsDecl { const unsigned iCrtSlot = 13; return a_FnName##_wrapped a_aArgCall; } \
217 static a_RetTypeAndCallConv a_FnName##14 a_aArgsDecl { const unsigned iCrtSlot = 14; return a_FnName##_wrapped a_aArgCall; } \
218 static a_RetTypeAndCallConv a_FnName##15 a_aArgsDecl { const unsigned iCrtSlot = 15; return a_FnName##_wrapped a_aArgCall; } \
219 static a_RetTypeAndCallConv a_FnName##16 a_aArgsDecl { const unsigned iCrtSlot = 16; return a_FnName##_wrapped a_aArgCall; } \
220 static a_RetTypeAndCallConv a_FnName##17 a_aArgsDecl { const unsigned iCrtSlot = 17; return a_FnName##_wrapped a_aArgCall; } \
221 static a_RetTypeAndCallConv a_FnName##18 a_aArgsDecl { const unsigned iCrtSlot = 18; return a_FnName##_wrapped a_aArgCall; } \
222 static a_RetTypeAndCallConv a_FnName##19 a_aArgsDecl { const unsigned iCrtSlot = 19; return a_FnName##_wrapped a_aArgCall; } \
223 static a_RetTypeAndCallConv a_FnName##20 a_aArgsDecl { const unsigned iCrtSlot = 20; return a_FnName##_wrapped a_aArgCall; } \
224 static a_RetTypeAndCallConv a_FnName##21 a_aArgsDecl { const unsigned iCrtSlot = 21; return a_FnName##_wrapped a_aArgCall; } \
225 static a_RetTypeAndCallConv a_FnName##22 a_aArgsDecl { const unsigned iCrtSlot = 22; return a_FnName##_wrapped a_aArgCall; } \
226 static a_RetTypeAndCallConv a_FnName##23 a_aArgsDecl { const unsigned iCrtSlot = 23; return a_FnName##_wrapped a_aArgCall; } \
227 static a_RetTypeAndCallConv a_FnName##24 a_aArgsDecl { const unsigned iCrtSlot = 24; return a_FnName##_wrapped a_aArgCall; } \
228 static a_RetTypeAndCallConv a_FnName##25 a_aArgsDecl { const unsigned iCrtSlot = 25; return a_FnName##_wrapped a_aArgCall; } \
229 static a_RetTypeAndCallConv a_FnName##26 a_aArgsDecl { const unsigned iCrtSlot = 26; return a_FnName##_wrapped a_aArgCall; } \
230 static a_RetTypeAndCallConv a_FnName##27 a_aArgsDecl { const unsigned iCrtSlot = 27; return a_FnName##_wrapped a_aArgCall; } \
231 static a_RetTypeAndCallConv a_FnName##28 a_aArgsDecl { const unsigned iCrtSlot = 28; return a_FnName##_wrapped a_aArgCall; } \
232 static a_RetTypeAndCallConv a_FnName##29 a_aArgsDecl { const unsigned iCrtSlot = 29; return a_FnName##_wrapped a_aArgCall; } \
233 static a_RetTypeAndCallConv a_FnName##30 a_aArgsDecl { const unsigned iCrtSlot = 30; return a_FnName##_wrapped a_aArgCall; } \
234 static a_RetTypeAndCallConv a_FnName##31 a_aArgsDecl { const unsigned iCrtSlot = 31; return a_FnName##_wrapped a_aArgCall; } \
235 static const KUPTR a_FnName[] = \
236 { \
237 (KUPTR)a_FnName##00, \
238 (KUPTR)a_FnName##01, \
239 (KUPTR)a_FnName##02, \
240 (KUPTR)a_FnName##03, \
241 (KUPTR)a_FnName##04, \
242 (KUPTR)a_FnName##05, \
243 (KUPTR)a_FnName##06, \
244 (KUPTR)a_FnName##07, \
245 (KUPTR)a_FnName##08, \
246 (KUPTR)a_FnName##09, \
247 (KUPTR)a_FnName##10, \
248 (KUPTR)a_FnName##11, \
249 (KUPTR)a_FnName##12, \
250 (KUPTR)a_FnName##13, \
251 (KUPTR)a_FnName##14, \
252 (KUPTR)a_FnName##15, \
253 (KUPTR)a_FnName##16, \
254 (KUPTR)a_FnName##17, \
255 (KUPTR)a_FnName##18, \
256 (KUPTR)a_FnName##19, \
257 (KUPTR)a_FnName##20, \
258 (KUPTR)a_FnName##21, \
259 (KUPTR)a_FnName##22, \
260 (KUPTR)a_FnName##23, \
261 (KUPTR)a_FnName##24, \
262 (KUPTR)a_FnName##25, \
263 (KUPTR)a_FnName##26, \
264 (KUPTR)a_FnName##27, \
265 (KUPTR)a_FnName##28, \
266 (KUPTR)a_FnName##29, \
267 (KUPTR)a_FnName##30, \
268 (KUPTR)a_FnName##31, \
269 }
270
271
272/*********************************************************************************************************************************
273* Structures and Typedefs *
274*********************************************************************************************************************************/
275typedef enum KWLOCATION
276{
277 KWLOCATION_INVALID = 0,
278 KWLOCATION_EXE_DIR,
279 KWLOCATION_IMPORTER_DIR,
280 KWLOCATION_SYSTEM32,
281 KWLOCATION_UNKNOWN_NATIVE,
282 KWLOCATION_UNKNOWN,
283} KWLOCATION;
284
285typedef enum KWMODSTATE
286{
287 KWMODSTATE_INVALID = 0,
288 KWMODSTATE_NEEDS_BITS,
289 KWMODSTATE_NEEDS_INIT,
290 KWMODSTATE_BEING_INITED,
291 KWMODSTATE_INIT_FAILED,
292 KWMODSTATE_READY,
293} KWMODSTATE;
294
295typedef struct KWMODULE *PKWMODULE;
296typedef struct KWMODULE
297{
298 /** Pointer to the next image withe the same hash. */
299 PKWMODULE pNextHash;
300 /** Pointer to the next image in the global list. */
301 PKWMODULE pNextList;
302 /** The normalized path to the image. */
303 const char *pszPath;
304 /** The hash of the program path. */
305 KU32 uHashPath;
306 /** Number of references. */
307 KU32 cRefs;
308 /** UTF-16 version of pszPath. */
309 const wchar_t *pwszPath;
310 /** The offset of the filename in pszPath. */
311 KU16 offFilename;
312 /** The offset of the filename in pwszPath. */
313 KU16 offFilenameW;
314 /** Set if executable. */
315 KBOOL fExe;
316 /** Set if native module entry. */
317 KBOOL fNative;
318 /** Loader module handle. */
319 PKLDRMOD pLdrMod;
320 /** The windows module handle. */
321 HMODULE hOurMod;
322 /** Parent (real) module if this is a virtual API module (api-ms-*.dll or
323 * ext-ms-*.dll). Referenced. */
324 PKWMODULE pVirtualApiMod;
325 /** The of the loaded image bits. */
326 KSIZE cbImage;
327 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
328 KU8 iCrtSlot;
329 /** Loop prevention when working the tree. */
330 KBOOL fVisited;
331 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
332 KBOOL fNeedReInit;
333 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
334 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
335 KU8 fReInitOnMsPdbSrvEndpointChange;
336 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
337 char *pszMsPdbSrvEndpoint;
338
339 union
340 {
341 /** Data for a manually loaded image. */
342 struct
343 {
344 /** Where we load the image. */
345 KU8 *pbLoad;
346 /** Virgin copy of the image. */
347 KU8 *pbCopy;
348 /** Ldr pvBits argument. This is NULL till we've successfully resolved
349 * the imports. */
350 void *pvBits;
351 /** The state. */
352 KWMODSTATE enmState;
353 /** The re-init state. */
354 KWMODSTATE enmReInitState;
355#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
356 /** The number of entries in the table. */
357 KU32 cFunctions;
358 /** The function table address (in the copy). */
359 PRUNTIME_FUNCTION paFunctions;
360 /** Set if we've already registered a function table already. */
361 KBOOL fRegisteredFunctionTable;
362#endif
363 /** Set if we share memory with other executables. */
364 KBOOL fUseLdBuf;
365 /** Set after the first whole image copy is done. */
366 KBOOL fCanDoQuick;
367 /** Number of quick copy chunks. */
368 KU8 cQuickCopyChunks;
369 /** Number of quick zero chunks. */
370 KU8 cQuickZeroChunks;
371 /** Quicker image copy instructions that skips non-writable parts when
372 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
373 * image. */
374 struct
375 {
376 /** The copy destination. */
377 KU8 *pbDst;
378 /** The copy source. */
379 KU8 const *pbSrc;
380 /** How much to copy. */
381 KSIZE cbToCopy;
382 } aQuickCopyChunks[3];
383 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
384 struct
385 {
386 /** Where to start zeroing. */
387 KU8 *pbDst;
388 /** How much to zero. */
389 KSIZE cbToZero;
390 } aQuickZeroChunks[3];
391
392 /** TLS index if one was allocated, otherwise KU32_MAX. */
393 KU32 idxTls;
394 /** Offset (RVA) of the TLS initialization data. */
395 KU32 offTlsInitData;
396 /** Number of bytes of TLS initialization data. */
397 KU32 cbTlsInitData;
398 /** Number of allocated bytes for TLS. */
399 KU32 cbTlsAlloc;
400 /** Number of TLS callbacks. */
401 KU32 cTlsCallbacks;
402 /** Offset (RVA) of the TLS callback table. */
403 KU32 offTlsCallbacks;
404
405 /** Number of imported modules. */
406 KSIZE cImpMods;
407 /** Import array (variable size). */
408 PKWMODULE apImpMods[1];
409 } Manual;
410 } u;
411} KWMODULE;
412
413
414typedef struct KWDYNLOAD *PKWDYNLOAD;
415typedef struct KWDYNLOAD
416{
417 /** Pointer to the next in the list. */
418 PKWDYNLOAD pNext;
419
420 /** The module handle we present to the application.
421 * This is the LoadLibraryEx return value for special modules and the
422 * KWMODULE.hOurMod value for the others. */
423 HMODULE hmod;
424
425 /** The module for non-special resource stuff, NULL if special. */
426 PKWMODULE pMod;
427
428 /** The length of the LoadLibary filename. */
429 KSIZE cchRequest;
430 /** The LoadLibrary filename. */
431 char szRequest[1];
432} KWDYNLOAD;
433
434
435/**
436 * GetModuleHandle cache for system modules frequently queried.
437 */
438typedef struct KWGETMODULEHANDLECACHE
439{
440 const char *pszName;
441 const wchar_t *pwszName;
442 KU8 cchName;
443 KU8 cwcName;
444 KBOOL fAlwaysPresent;
445 HANDLE hmod;
446} KWGETMODULEHANDLECACHE;
447typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
448
449
450/** One TLS DLL. */
451typedef struct KWTLSDLL
452{
453 const wchar_t *pwszName; /**< The DLL name. */
454 KBOOL fUsed; /**< Set if used, clear if not. */
455} KWTLSDLL;
456typedef KWTLSDLL *PKWTLSDLL;
457
458/**
459 * TLS DLL tracker.
460 */
461typedef struct KWTLSDLLENTRY
462{
463 KU32 cbTls; /**< Max TLS size. */
464 KU32 cDlls; /**< Number of DLLs we ship (paDlls). */
465 PKWTLSDLL paDlls; /**< Array of DLLs we ship. */
466} KWTLSDLLENTRY;
467typedef KWTLSDLLENTRY *PKWTLSDLLENTRY;
468
469
470/**
471 * A cached file.
472 */
473typedef struct KFSWCACHEDFILE
474{
475 /** The user data core. */
476 KFSUSERDATA Core;
477
478 /** Cached file handle. */
479 HANDLE hCached;
480 /** Cached file section handle. */
481 HANDLE hSection;
482 /** Cached file content. */
483 KU8 *pbCached;
484 /** The file size. */
485 KU32 cbCached;
486#ifdef WITH_HASH_MD5_CACHE
487 /** Set if we've got a valid MD5 hash in abMd5Digest. */
488 KBOOL fValidMd5;
489 /** The MD5 digest if fValidMd5 is set. */
490 KU8 abMd5Digest[16];
491#endif
492
493 /** Circular self reference. Prevents the object from ever going away and
494 * keeps it handy for debugging. */
495 PKFSOBJ pFsObj;
496 /** The file path (for debugging). */
497 char szPath[1];
498} KFSWCACHEDFILE;
499/** Pointer to a cached filed. */
500typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
501
502#ifdef WITH_HASH_MD5_CACHE
503
504/** Pointer to a MD5 hash instance. */
505typedef struct KWHASHMD5 *PKWHASHMD5;
506/**
507 * A MD5 hash instance.
508 */
509typedef struct KWHASHMD5
510{
511 /** The magic value. */
512 KUPTR uMagic;
513 /** Pointer to the next hash handle. */
514 PKWHASHMD5 pNext;
515 /** The cached file we've associated this handle with. */
516 PKFSWCACHEDFILE pCachedFile;
517 /** The number of bytes we've hashed. */
518 KU32 cbHashed;
519 /** Set if this has gone wrong. */
520 KBOOL fGoneBad;
521 /** Set if we're in fallback mode (file not cached). */
522 KBOOL fFallbackMode;
523 /** Set if we've already finalized the digest. */
524 KBOOL fFinal;
525 /** The MD5 fallback context. */
526 struct MD5Context Md5Ctx;
527 /** The finalized digest. */
528 KU8 abDigest[16];
529
530} KWHASHMD5;
531/** Magic value for KWHASHMD5::uMagic (Les McCann). */
532# define KWHASHMD5_MAGIC KUPTR_C(0x19350923)
533
534#endif /* WITH_HASH_MD5_CACHE */
535#ifdef WITH_TEMP_MEMORY_FILES
536
537typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
538typedef struct KWFSTEMPFILESEG
539{
540 /** File offset of data. */
541 KU32 offData;
542 /** The size of the buffer pbData points to. */
543 KU32 cbDataAlloc;
544 /** The segment data. */
545 KU8 *pbData;
546} KWFSTEMPFILESEG;
547
548typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
549typedef struct KWFSTEMPFILE
550{
551 /** Pointer to the next temporary file for this run. */
552 PKWFSTEMPFILE pNext;
553 /** The UTF-16 path. (Allocated after this structure.) */
554 const wchar_t *pwszPath;
555 /** The path length. */
556 KU16 cwcPath;
557 /** Number of active handles using this file/mapping (<= 2). */
558 KU8 cActiveHandles;
559 /** Number of active mappings (mapped views) (0 or 1). */
560 KU8 cMappings;
561 /** The amount of space allocated in the segments. */
562 KU32 cbFileAllocated;
563 /** The current file size. */
564 KU32 cbFile;
565 /** The number of segments. */
566 KU32 cSegs;
567 /** Segments making up the file. */
568 PKWFSTEMPFILESEG paSegs;
569} KWFSTEMPFILE;
570
571#endif /* WITH_TEMP_MEMORY_FILES */
572#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
573
574/**
575 * Console line buffer or output full buffer.
576 */
577typedef struct KWOUTPUTSTREAMBUF
578{
579 /** The main output handle. */
580 HANDLE hOutput;
581 /** Our backup handle. */
582 HANDLE hBackup;
583 /** Set if this is a console handle and we're in line buffered mode.
584 * When clear, we may buffer multiple lines, though try flush on line
585 * boundraries when ever possible. */
586 KBOOL fIsConsole;
587 /** Compressed GetFileType result. */
588 KU8 fFileType;
589 KU8 abPadding[2];
590 union
591 {
592 /** Line buffer mode (fIsConsole == K_TRUE). */
593 struct
594 {
595 /** Amount of pending console output in wchar_t's. */
596 KU32 cwcBuf;
597 /** The allocated buffer size. */
598 KU32 cwcBufAlloc;
599 /** Pending console output. */
600 wchar_t *pwcBuf;
601 } Con;
602 /** Fully buffered mode (fIsConsole == K_FALSE). */
603 struct
604 {
605 /** Amount of pending output (in chars). */
606 KU32 cchBuf;
607#ifdef WITH_STD_OUT_ERR_BUFFERING
608 /** The allocated buffer size (in chars). */
609 KU32 cchBufAlloc;
610 /** Pending output. */
611 char *pchBuf;
612#endif
613 } Fully;
614 } u;
615} KWOUTPUTSTREAMBUF;
616/** Pointer to a console line buffer. */
617typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
618
619/**
620 * Combined console buffer of complete lines.
621 */
622typedef struct KWCONSOLEOUTPUT
623{
624 /** The console output handle.
625 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
626 * combined output buffering. */
627 HANDLE hOutput;
628 /** The current code page for the console. */
629 KU32 uCodepage;
630 /** Amount of pending console output in wchar_t's. */
631 KU32 cwcBuf;
632 /** Number of times we've flushed it in any way (for cl.exe hack). */
633 KU32 cFlushes;
634 /** Pending console output. */
635 wchar_t wszBuf[8192];
636} KWCONSOLEOUTPUT;
637/** Pointer to a combined console buffer. */
638typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
639
640#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
641
642/** Handle type. */
643typedef enum KWHANDLETYPE
644{
645 KWHANDLETYPE_INVALID = 0,
646 KWHANDLETYPE_FSOBJ_READ_CACHE,
647 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
648 KWHANDLETYPE_TEMP_FILE,
649 KWHANDLETYPE_TEMP_FILE_MAPPING,
650 KWHANDLETYPE_OUTPUT_BUF
651} KWHANDLETYPE;
652
653/** Handle data. */
654typedef struct KWHANDLE
655{
656 KWHANDLETYPE enmType;
657 /** Number of references */
658 KU32 cRefs;
659 /** The current file offset. */
660 KU32 offFile;
661 /** Handle access. */
662 KU32 dwDesiredAccess;
663 /** The handle. */
664 HANDLE hHandle;
665
666 /** Type specific data. */
667 union
668 {
669 /** The file system object. */
670 PKFSWCACHEDFILE pCachedFile;
671 /** Temporary file handle or mapping handle. */
672 PKWFSTEMPFILE pTempFile;
673#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
674 /** Buffered output stream. */
675 PKWOUTPUTSTREAMBUF pOutBuf;
676#endif
677 } u;
678} KWHANDLE;
679typedef KWHANDLE *PKWHANDLE;
680
681/**
682 * Tracking one of our memory mappings.
683 */
684typedef struct KWMEMMAPPING
685{
686 /** Number of references. */
687 KU32 cRefs;
688 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
689 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
690 KWHANDLETYPE enmType;
691 /** The mapping address. */
692 PVOID pvMapping;
693 /** Type specific data. */
694 union
695 {
696 /** The file system object. */
697 PKFSWCACHEDFILE pCachedFile;
698 /** Temporary file handle or mapping handle. */
699 PKWFSTEMPFILE pTempFile;
700 } u;
701} KWMEMMAPPING;
702/** Pointer to a memory mapping tracker. */
703typedef KWMEMMAPPING *PKWMEMMAPPING;
704
705
706/** Pointer to a VirtualAlloc tracker entry. */
707typedef struct KWVIRTALLOC *PKWVIRTALLOC;
708/**
709 * Tracking an VirtualAlloc allocation.
710 */
711typedef struct KWVIRTALLOC
712{
713 PKWVIRTALLOC pNext;
714 void *pvAlloc;
715 KSIZE cbAlloc;
716 /** This is KU32_MAX if not a preallocated chunk. */
717 KU32 idxPreAllocated;
718} KWVIRTALLOC;
719
720
721/** Pointer to a heap (HeapCreate) tracker entry. */
722typedef struct KWHEAP *PKWHEAP;
723/**
724 * Tracking an heap (HeapCreate)
725 */
726typedef struct KWHEAP
727{
728 PKWHEAP pNext;
729 HANDLE hHeap;
730} KWHEAP;
731
732
733/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
734typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
735/**
736 * Tracking an FlsAlloc/TlsAlloc index.
737 */
738typedef struct KWLOCALSTORAGE
739{
740 PKWLOCALSTORAGE pNext;
741 KU32 idx;
742} KWLOCALSTORAGE;
743
744
745/** Pointer to an at exit callback record */
746typedef struct KWEXITCALLACK *PKWEXITCALLACK;
747/**
748 * At exit callback record.
749 */
750typedef struct KWEXITCALLACK
751{
752 PKWEXITCALLACK pNext;
753 _onexit_t pfnCallback;
754 /** At exit doesn't have an exit code. */
755 KBOOL fAtExit;
756} KWEXITCALLACK;
757
758
759typedef enum KWTOOLTYPE
760{
761 KWTOOLTYPE_INVALID = 0,
762 KWTOOLTYPE_SANDBOXED,
763 KWTOOLTYPE_WATCOM,
764 KWTOOLTYPE_EXEC,
765 KWTOOLTYPE_END
766} KWTOOLTYPE;
767
768typedef enum KWTOOLHINT
769{
770 KWTOOLHINT_INVALID = 0,
771 KWTOOLHINT_NONE,
772 KWTOOLHINT_VISUAL_CPP_CL,
773 KWTOOLHINT_VISUAL_CPP_LINK,
774 KWTOOLHINT_END
775} KWTOOLHINT;
776
777
778/**
779 * A kWorker tool.
780 */
781typedef struct KWTOOL
782{
783 /** The user data core structure. */
784 KFSUSERDATA Core;
785
786 /** The normalized path to the program. */
787 const char *pszPath;
788 /** UTF-16 version of pszPath. */
789 wchar_t const *pwszPath;
790 /** The kind of tool. */
791 KWTOOLTYPE enmType;
792
793 union
794 {
795 struct
796 {
797 /** The main entry point. */
798 KUPTR uMainAddr;
799 /** The executable. */
800 PKWMODULE pExe;
801 /** List of dynamically loaded modules.
802 * These will be kept loaded till the tool is destroyed (if we ever do that). */
803 PKWDYNLOAD pDynLoadHead;
804 /** Module array sorted by hOurMod. */
805 PKWMODULE *papModules;
806 /** Number of entries in papModules. */
807 KU32 cModules;
808
809 /** Tool hint (for hacks and such). */
810 KWTOOLHINT enmHint;
811 } Sandboxed;
812 } u;
813} KWTOOL;
814/** Pointer to a tool. */
815typedef struct KWTOOL *PKWTOOL;
816
817
818typedef struct KWSANDBOX *PKWSANDBOX;
819typedef struct KWSANDBOX
820{
821 /** Jump buffer (first for alignment reasons). */
822 jmp_buf JmpBuf;
823 /** The tool currently running in the sandbox. */
824 PKWTOOL pTool;
825 /** The thread ID of the main thread (owner of JmpBuf). */
826 DWORD idMainThread;
827 /** Copy of the NT TIB of the main thread. */
828 NT_TIB TibMainThread;
829 /** The NT_TIB::ExceptionList value inside the try case.
830 * We restore this prior to the longjmp. */
831 void *pOutXcptListHead;
832 /** The exit code in case of longjmp. */
833 int rcExitCode;
834 /** Set if we're running. */
835 KBOOL fRunning;
836 /** Whether to disable caching of ".pch" files. */
837 KBOOL fNoPchCaching;
838
839 /** The command line. */
840 char *pszCmdLine;
841 /** The UTF-16 command line. */
842 wchar_t *pwszCmdLine;
843 /** Number of arguments in papszArgs. */
844 int cArgs;
845 /** The argument vector. */
846 char **papszArgs;
847 /** The argument vector. */
848 wchar_t **papwszArgs;
849
850 /** The _pgmptr msvcrt variable. */
851 char *pgmptr;
852 /** The _wpgmptr msvcrt variable. */
853 wchar_t *wpgmptr;
854
855 /** The _initenv msvcrt variable. */
856 char **initenv;
857 /** The _winitenv msvcrt variable. */
858 wchar_t **winitenv;
859
860 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
861 KSIZE cEnvVarsAllocated;
862 /** The _environ msvcrt variable. */
863 char **environ;
864 /** The _wenviron msvcrt variable. */
865 wchar_t **wenviron;
866 /** The shadow _environ msvcrt variable. */
867 char **papszEnvVars;
868 /** The shadow _wenviron msvcrt variable. */
869 wchar_t **papwszEnvVars;
870
871
872 /** Handle table. */
873 PKWHANDLE *papHandles;
874 /** Size of the handle table. */
875 KU32 cHandles;
876 /** Number of active handles in the table. */
877 KU32 cActiveHandles;
878 /** Number of handles in the handle table that will not be freed. */
879 KU32 cFixedHandles;
880 /** Total number of leaked handles. */
881 KU32 cLeakedHandles;
882
883 /** Number of active memory mappings in paMemMappings. */
884 KU32 cMemMappings;
885 /** The allocated size of paMemMappings. */
886 KU32 cMemMappingsAlloc;
887 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
888 PKWMEMMAPPING paMemMappings;
889
890 /** Head of the list of temporary file. */
891 PKWFSTEMPFILE pTempFileHead;
892
893 /** Critical section protecting pVirtualAllocHead. */
894 CRITICAL_SECTION VirtualAllocLock;
895 /** Head of the virtual alloc allocations. */
896 PKWVIRTALLOC pVirtualAllocHead;
897 /** Head of the heap list (HeapCreate).
898 * This is only done from images we forcibly restore. */
899 PKWHEAP pHeapHead;
900 /** Head of the FlsAlloc indexes. */
901 PKWLOCALSTORAGE pFlsAllocHead;
902 /** Head of the TlsAlloc indexes. */
903 PKWLOCALSTORAGE pTlsAllocHead;
904
905 /** The at exit callback head.
906 * This is only done from images we forcibly restore. */
907 PKWEXITCALLACK pExitCallbackHead;
908
909 MY_UNICODE_STRING SavedCommandLine;
910
911#ifdef WITH_HASH_MD5_CACHE
912 /** The special MD5 hash instance. */
913 PKWHASHMD5 pHashHead;
914 /** ReadFile sets these while CryptHashData claims and clears them.
915 *
916 * This is part of the heuristics we use for MD5 caching for header files. The
917 * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
918 * header, then passes the same buffer and read byte count to CryptHashData.
919 */
920 struct
921 {
922 /** The cached file last read from. */
923 PKFSWCACHEDFILE pCachedFile;
924 /** The file offset of the last cached read. */
925 KU32 offRead;
926 /** The number of bytes read last. */
927 KU32 cbRead;
928 /** The buffer pointer of the last read. */
929 void *pvRead;
930 } LastHashRead;
931#endif
932
933#ifdef WITH_CRYPT_CTX_REUSE
934 /** Reusable crypt contexts. */
935 struct
936 {
937 /** The creation provider type. */
938 KU32 dwProvType;
939 /** The creation flags. */
940 KU32 dwFlags;
941 /** The length of the container name. */
942 KU32 cwcContainer;
943 /** The length of the provider name. */
944 KU32 cwcProvider;
945 /** The container name string. */
946 wchar_t *pwszContainer;
947 /** The provider name string. */
948 wchar_t *pwszProvider;
949 /** The context handle. */
950 HCRYPTPROV hProv;
951 } aCryptCtxs[4];
952 /** Number of reusable crypt conexts in aCryptCtxs. */
953 KU32 cCryptCtxs;
954#endif
955
956
957#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
958 /** The internal standard output handle. */
959 KWHANDLE HandleStdOut;
960 /** The internal standard error handle. */
961 KWHANDLE HandleStdErr;
962 /** Standard output (and whatever else) buffer. */
963 KWOUTPUTSTREAMBUF StdOut;
964 /** Standard error buffer. */
965 KWOUTPUTSTREAMBUF StdErr;
966 /** Combined buffer of completed lines. */
967 KWCONSOLEOUTPUT Combined;
968#endif
969} KWSANDBOX;
970
971
972/** A CRT slot. */
973typedef struct KWCRTSLOT
974{
975 KU32 iSlot;
976
977 /** The CRT module data. */
978 PKWMODULE pModule;
979 /** Pointer to the malloc function. */
980 void * (__cdecl *pfnMalloc)(size_t);
981 /** Pointer to the beginthreadex function. */
982 uintptr_t (__cdecl *pfnBeginThreadEx)(void *, unsigned, unsigned (__stdcall *)(void *), void *, unsigned, unsigned *);
983
984} KWCRTSLOT;
985typedef KWCRTSLOT *PKWCRTSLOT;
986
987
988/** Replacement function entry. */
989typedef struct KWREPLACEMENTFUNCTION
990{
991 /** The function name. */
992 const char *pszFunction;
993 /** The length of the function name. */
994 KSIZE cchFunction;
995 /** The module name (optional). */
996 const char *pszModule;
997 /** The replacement function, data address or CRT slot function array. */
998 KUPTR pfnReplacement;
999 /** Only replace in the executable.
1000 * @todo fix the reinitialization of non-native DLLs! */
1001 KBOOL fOnlyExe;
1002 /** Set if pfnReplacement points to a CRT slot function array. */
1003 KBOOL fCrtSlotArray;
1004} KWREPLACEMENTFUNCTION;
1005typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
1006
1007#if 0
1008/** Replacement function entry. */
1009typedef struct KWREPLACEMENTDATA
1010{
1011 /** The function name. */
1012 const char *pszFunction;
1013 /** The length of the function name. */
1014 KSIZE cchFunction;
1015 /** The module name (optional). */
1016 const char *pszModule;
1017 /** Function providing the replacement. */
1018 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
1019} KWREPLACEMENTDATA;
1020typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
1021#endif
1022
1023/**
1024 * One test job (--full-test).
1025 */
1026typedef struct KWONETEST
1027{
1028 /** Where this job originated. */
1029 const char *pszJobSrc;
1030 /** The argument number it started with. */
1031 unsigned iJobSrc;
1032 /** Set if virgin, clear if modified. */
1033 KBOOL fVirgin;
1034
1035 /** Number of runs to give it. */
1036 unsigned cRuns;
1037
1038 /** @name kSubmitHandleJobUnpacked arguments
1039 * @{ */
1040 const char *pszExecutable;
1041 const char *pszCwd;
1042 KU32 cArgs;
1043 const char **papszArgs;
1044 KU32 cEnvVars;
1045 const char **papszEnvVars;
1046 const char *pszSpecialEnv;
1047 KBOOL fWatcomBrainDamange;
1048 KBOOL fNoPchCaching;
1049 KU32 cPostCmdArgs;
1050 const char **papszPostCmdArgs;
1051 /** @} */
1052
1053 /** Pointer to the next one. */
1054 struct KWONETEST *pNext;
1055} KWONETEST;
1056/** Pointer to one test job. */
1057typedef KWONETEST *PKWONETEST;
1058
1059
1060/*********************************************************************************************************************************
1061* Global Variables *
1062*********************************************************************************************************************************/
1063/** The sandbox data. */
1064static KWSANDBOX g_Sandbox;
1065
1066/** The module currently occupying g_abDefLdBuf. */
1067static PKWMODULE g_pModInLdBuf = NULL;
1068
1069/** The module that previuosly occupied g_abDefLdBuf. */
1070static PKWMODULE g_pModPrevInLdBuf = NULL;
1071
1072/** Module list head. */
1073static PKWMODULE g_pModuleHead = NULL;
1074/** Where to insert the next module. */
1075static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1076
1077/** Module hash table. */
1078static PKWMODULE g_apModules[127];
1079
1080/** GetModuleHandle cache. */
1081static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1082{
1083#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1084 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1085#if 1
1086 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1087 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1088#endif
1089 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1090};
1091
1092/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1093static PKWMODULE g_pModPendingTlsAlloc = NULL;
1094
1095/** The 1KB TLS DLLs. */
1096static KWTLSDLL g_aTls1KDlls[] =
1097{
1098 { L"kWorkerTls1K.dll", K_FALSE },
1099 { L"kWorkerTls1K01.dll", K_FALSE },
1100 { L"kWorkerTls1K02.dll", K_FALSE },
1101 { L"kWorkerTls1K03.dll", K_FALSE },
1102 { L"kWorkerTls1K04.dll", K_FALSE },
1103 { L"kWorkerTls1K05.dll", K_FALSE },
1104 { L"kWorkerTls1K06.dll", K_FALSE },
1105 { L"kWorkerTls1K07.dll", K_FALSE },
1106 { L"kWorkerTls1K08.dll", K_FALSE },
1107 { L"kWorkerTls1K09.dll", K_FALSE },
1108 { L"kWorkerTls1K10.dll", K_FALSE },
1109 { L"kWorkerTls1K11.dll", K_FALSE },
1110 { L"kWorkerTls1K12.dll", K_FALSE },
1111 { L"kWorkerTls1K13.dll", K_FALSE },
1112 { L"kWorkerTls1K14.dll", K_FALSE },
1113 { L"kWorkerTls1K15.dll", K_FALSE },
1114};
1115
1116/** The 64KB TLS DLLs. */
1117static KWTLSDLL g_aTls64KDlls[] =
1118{
1119 { L"kWorkerTls64K.dll", K_FALSE },
1120};
1121
1122/** The 512KB TLS DLLs. */
1123static KWTLSDLL g_aTls512KDlls[] =
1124{
1125 { L"kWorkerTls512K.dll", K_FALSE },
1126};
1127
1128/** The TLS DLLs grouped by size. */
1129static KWTLSDLLENTRY const g_aTlsDlls[] =
1130{
1131 { 1024, K_ELEMENTS(g_aTls1KDlls), g_aTls1KDlls },
1132 { 64*1024, K_ELEMENTS(g_aTls64KDlls), g_aTls64KDlls },
1133 { 512*1024, K_ELEMENTS(g_aTls512KDlls), g_aTls512KDlls },
1134};
1135
1136/** CRT slots.
1137 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1138static KWCRTSLOT g_aCrtSlots[32];
1139
1140/** windbg .reload statements. vs */
1141char g_szReloads[4096];
1142/** Current offset into g_szReloads. */
1143KU32 volatile g_cchReloads;
1144
1145/** The file system cache. */
1146static PKFSCACHE g_pFsCache;
1147/** The current directory (referenced). */
1148static PKFSOBJ g_pCurDirObj = NULL;
1149#ifdef KBUILD_OS_WINDOWS
1150/** The windows system32 directory (referenced). */
1151static PKFSDIR g_pWinSys32 = NULL;
1152#endif
1153
1154/** Verbosity level. */
1155static int g_cVerbose = 2;
1156
1157/** Whether we should restart the worker. */
1158static KBOOL g_fRestart = K_FALSE;
1159
1160/** The process group this worker is tied to (--group option), -1 if none. */
1161static KI32 g_iProcessGroup = -1;
1162
1163/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1164static int volatile g_rcCtrlC = 0;
1165
1166/** The communication pipe handle. We break this when we see Ctrl-C such. */
1167#ifdef KBUILD_OS_WINDOWS
1168static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1169#else
1170static int g_hPipe = -1;
1171#endif
1172
1173
1174/* Further down. */
1175extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1176extern KU32 const g_cSandboxReplacements;
1177
1178extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1179extern KU32 const g_cSandboxNativeReplacements;
1180
1181extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1182extern KU32 const g_cSandboxGetProcReplacements;
1183
1184
1185/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1186 * cover the default executable link address of 0x400000.
1187 * @remarks Early main() makes it read+write+executable. Attempts as having
1188 * it as a separate section failed because the linker insists on
1189 * writing out every zero in the uninitialized section, resulting in
1190 * really big binaries. */
1191__declspec(align(0x1000))
1192static KU8 g_abDefLdBuf[16*1024*1024];
1193
1194#ifdef WITH_LOG_FILE
1195/** Log file handle. */
1196static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1197#endif
1198
1199
1200#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1201/** Virtual address space reserved for CL.EXE heap manager.
1202 *
1203 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1204 * address. It's among other things used for precompiled headers, which
1205 * seemingly have addresses hardcoded into them and won't work if mapped
1206 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1207 * for it. (The /Zm option may affect this allocation.)
1208 */
1209static struct
1210{
1211 /** The memory address we need. */
1212 KUPTR const uFixed;
1213 /** How much we need to fix. */
1214 KSIZE const cbFixed;
1215 /** What we actually got, NULL if given back. */
1216 void *pvReserved;
1217 /** Whether it is in use or not. */
1218 KBOOL fInUse;
1219} g_aFixedVirtualAllocs[] =
1220{
1221# if K_ARCH == K_ARCH_X86_32
1222 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1223 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1224 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1225# else
1226 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1227# endif
1228};
1229#endif
1230
1231
1232#ifdef WITH_HISTORY
1233/** The job history. */
1234static char *g_apszHistory[32];
1235/** Index of the next history entry. */
1236static unsigned g_iHistoryNext = 0;
1237#endif
1238
1239
1240/** Number of jobs executed. */
1241static KU32 g_cJobs;
1242/** Number of tools. */
1243static KU32 g_cTools;
1244/** Number of modules. */
1245static KU32 g_cModules;
1246/** Number of non-native modules. */
1247static KU32 g_cNonNativeModules;
1248/** Number of read-cached files. */
1249static KU32 g_cReadCachedFiles;
1250/** Total size of read-cached files. */
1251static KSIZE g_cbReadCachedFiles;
1252
1253/** Total number of ReadFile calls. */
1254static KSIZE g_cReadFileCalls;
1255/** Total bytes read via ReadFile. */
1256static KSIZE g_cbReadFileTotal;
1257/** Total number of read from read-cached files. */
1258static KSIZE g_cReadFileFromReadCached;
1259/** Total bytes read from read-cached files. */
1260static KSIZE g_cbReadFileFromReadCached;
1261/** Total number of read from in-memory temporary files. */
1262static KSIZE g_cReadFileFromInMemTemp;
1263/** Total bytes read from in-memory temporary files. */
1264static KSIZE g_cbReadFileFromInMemTemp;
1265
1266/** Total number of WriteFile calls. */
1267static KSIZE g_cWriteFileCalls;
1268/** Total bytes written via WriteFile. */
1269static KSIZE g_cbWriteFileTotal;
1270/** Total number of written to from in-memory temporary files. */
1271static KSIZE g_cWriteFileToInMemTemp;
1272/** Total bytes written to in-memory temporary files. */
1273static KSIZE g_cbWriteFileToInMemTemp;
1274
1275
1276/*********************************************************************************************************************************
1277* Internal Functions *
1278*********************************************************************************************************************************/
1279static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1280static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1281 const char *pszSearchPath, PKWMODULE *ppMod);
1282static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1283static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName);
1284static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule);
1285static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod);
1286static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1287static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1288#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1289static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1290#endif
1291static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1292
1293
1294
1295
1296/**
1297 * Debug printing.
1298 * @param pszFormat Debug format string.
1299 * @param ... Format argument.
1300 */
1301static void kwDbgPrintfV(const char *pszFormat, va_list va)
1302{
1303 if (g_cVerbose >= 2)
1304 {
1305 DWORD const dwSavedErr = GetLastError();
1306#ifdef WITH_LOG_FILE
1307 DWORD dwIgnored;
1308 char szTmp[2048];
1309 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1310 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1311 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1312 cch += cchPrefix;
1313 else
1314 {
1315 cch = sizeof(szTmp) - 1;
1316 szTmp[cch] = '\0';
1317 }
1318
1319 if (g_hLogFile == INVALID_HANDLE_VALUE)
1320 {
1321 wchar_t wszFilename[128];
1322 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1323 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1324 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1325 }
1326
1327 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1328#else
1329 fprintf(stderr, "debug: ");
1330 vfprintf(stderr, pszFormat, va);
1331#endif
1332
1333 SetLastError(dwSavedErr);
1334 }
1335}
1336
1337
1338/**
1339 * Debug printing.
1340 * @param pszFormat Debug format string.
1341 * @param ... Format argument.
1342 */
1343static void kwDbgPrintf(const char *pszFormat, ...)
1344{
1345 if (g_cVerbose >= 2)
1346 {
1347 va_list va;
1348 va_start(va, pszFormat);
1349 kwDbgPrintfV(pszFormat, va);
1350 va_end(va);
1351 }
1352}
1353
1354
1355/**
1356 * Debugger printing.
1357 * @param pszFormat Debug format string.
1358 * @param ... Format argument.
1359 */
1360static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1361{
1362 if (IsDebuggerPresent())
1363 {
1364 DWORD const dwSavedErr = GetLastError();
1365 char szTmp[2048];
1366
1367 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1368 OutputDebugStringA(szTmp);
1369
1370 SetLastError(dwSavedErr);
1371 }
1372}
1373
1374
1375/**
1376 * Debugger printing.
1377 * @param pszFormat Debug format string.
1378 * @param ... Format argument.
1379 */
1380static void kwDebuggerPrintf(const char *pszFormat, ...)
1381{
1382 va_list va;
1383 va_start(va, pszFormat);
1384 kwDebuggerPrintfV(pszFormat, va);
1385 va_end(va);
1386}
1387
1388
1389
1390/**
1391 * Error printing.
1392 * @param pszFormat Message format string.
1393 * @param ... Format argument.
1394 */
1395static void kwErrPrintfV(const char *pszFormat, va_list va)
1396{
1397 DWORD const dwSavedErr = GetLastError();
1398
1399 fprintf(stderr, "kWorker: error: ");
1400 vfprintf(stderr, pszFormat, va);
1401 fflush(stderr); /* In case it's a pipe. */
1402
1403 SetLastError(dwSavedErr);
1404}
1405
1406
1407/**
1408 * Error printing.
1409 * @param pszFormat Message format string.
1410 * @param ... Format argument.
1411 */
1412static void kwErrPrintf(const char *pszFormat, ...)
1413{
1414 va_list va;
1415 va_start(va, pszFormat);
1416 kwErrPrintfV(pszFormat, va);
1417 va_end(va);
1418}
1419
1420
1421/**
1422 * Error printing.
1423 * @return rc;
1424 * @param rc Return value
1425 * @param pszFormat Message format string.
1426 * @param ... Format argument.
1427 */
1428static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1429{
1430 va_list va;
1431 va_start(va, pszFormat);
1432 kwErrPrintfV(pszFormat, va);
1433 va_end(va);
1434 return rc;
1435}
1436
1437
1438#ifdef K_STRICT
1439
1440KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1441{
1442 DWORD const dwSavedErr = GetLastError();
1443
1444 fprintf(stderr,
1445 "\n"
1446 "!!Assertion failed!!\n"
1447 "Expression: %s\n"
1448 "Function : %s\n"
1449 "File: %s\n"
1450 "Line: %d\n"
1451 , pszExpr, pszFunction, pszFile, iLine);
1452
1453 SetLastError(dwSavedErr);
1454}
1455
1456
1457KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1458{
1459 DWORD const dwSavedErr = GetLastError();
1460 va_list va;
1461
1462 va_start(va, pszFormat);
1463 fprintf(stderr, pszFormat, va);
1464 va_end(va);
1465
1466 SetLastError(dwSavedErr);
1467}
1468
1469#endif /* K_STRICT */
1470
1471
1472/**
1473 * Hashes a string.
1474 *
1475 * @returns 32-bit string hash.
1476 * @param pszString String to hash.
1477 */
1478static KU32 kwStrHash(const char *pszString)
1479{
1480 /* This algorithm was created for sdbm (a public-domain reimplementation of
1481 ndbm) database library. it was found to do well in scrambling bits,
1482 causing better distribution of the keys and fewer splits. it also happens
1483 to be a good general hashing function with good distribution. the actual
1484 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1485 is the faster version used in gawk. [there is even a faster, duff-device
1486 version] the magic constant 65599 was picked out of thin air while
1487 experimenting with different constants, and turns out to be a prime.
1488 this is one of the algorithms used in berkeley db (see sleepycat) and
1489 elsewhere. */
1490 KU32 uHash = 0;
1491 KU32 uChar;
1492 while ((uChar = (unsigned char)*pszString++) != 0)
1493 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1494 return uHash;
1495}
1496
1497
1498/**
1499 * Hashes a string.
1500 *
1501 * @returns The string length.
1502 * @param pszString String to hash.
1503 * @param puHash Where to return the 32-bit string hash.
1504 */
1505static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1506{
1507 const char * const pszStart = pszString;
1508 KU32 uHash = 0;
1509 KU32 uChar;
1510 while ((uChar = (unsigned char)*pszString) != 0)
1511 {
1512 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1513 pszString++;
1514 }
1515 *puHash = uHash;
1516 return pszString - pszStart;
1517}
1518
1519
1520/**
1521 * Hashes a string.
1522 *
1523 * @returns The string length in wchar_t units.
1524 * @param pwszString String to hash.
1525 * @param puHash Where to return the 32-bit string hash.
1526 */
1527static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1528{
1529 const wchar_t * const pwszStart = pwszString;
1530 KU32 uHash = 0;
1531 KU32 uChar;
1532 while ((uChar = *pwszString) != 0)
1533 {
1534 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1535 pwszString++;
1536 }
1537 *puHash = uHash;
1538 return pwszString - pwszStart;
1539}
1540
1541
1542/**
1543 * Converts the given string to unicode.
1544 *
1545 * @returns Length of the resulting string in wchar_t's.
1546 * @param pszSrc The source string.
1547 * @param pwszDst The destination buffer.
1548 * @param cwcDst The size of the destination buffer in wchar_t's.
1549 */
1550static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1551{
1552 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1553 KSIZE offDst = 0;
1554 while (offDst < cwcDst)
1555 {
1556 char ch = *pszSrc++;
1557 pwszDst[offDst++] = ch;
1558 if (!ch)
1559 return offDst - 1;
1560 kHlpAssert((unsigned)ch < 127);
1561 }
1562
1563 pwszDst[offDst - 1] = '\0';
1564 return offDst;
1565}
1566
1567
1568/**
1569 * Converts the given string to UTF-16, allocating the buffer.
1570 *
1571 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1572 * the source string.
1573 * @param pchSrc The source string.
1574 * @param cchSrc The length of the source string.
1575 */
1576static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1577{
1578 DWORD const dwErrSaved = GetLastError();
1579 KSIZE cwcBuf = cchSrc + 1;
1580 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1581 if (pwszBuf)
1582 {
1583 if (cchSrc > 0)
1584 {
1585 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1586 if (cwcRet > 0)
1587 {
1588 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1589 pwszBuf[cwcRet] = '\0';
1590 }
1591 else
1592 {
1593 kHlpFree(pwszBuf);
1594
1595 /* Figure the length and allocate the right buffer size. */
1596 SetLastError(NO_ERROR);
1597 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1598 if (cwcRet)
1599 {
1600 cwcBuf = cwcRet + 2;
1601 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1602 if (pwszBuf)
1603 {
1604 SetLastError(NO_ERROR);
1605 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1606 if (cwcRet)
1607 {
1608 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1609 pwszBuf[cwcRet] = '\0';
1610 }
1611 else
1612 {
1613 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1614 kHlpFree(pwszBuf);
1615 pwszBuf = NULL;
1616 }
1617 }
1618 }
1619 else
1620 {
1621 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1622 pwszBuf = NULL;
1623 }
1624 }
1625 }
1626 else
1627 pwszBuf[0] = '\0';
1628 }
1629 SetLastError(dwErrSaved);
1630 return pwszBuf;
1631}
1632
1633
1634/**
1635 * Converts the given UTF-16 to a normal string.
1636 *
1637 * @returns Length of the resulting string.
1638 * @param pwszSrc The source UTF-16 string.
1639 * @param pszDst The destination buffer.
1640 * @param cbDst The size of the destination buffer in bytes.
1641 */
1642static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1643{
1644 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1645 KSIZE offDst = 0;
1646 while (offDst < cbDst)
1647 {
1648 wchar_t wc = *pwszSrc++;
1649 pszDst[offDst++] = (char)wc;
1650 if (!wc)
1651 return offDst - 1;
1652 kHlpAssert((unsigned)wc < 127);
1653 }
1654
1655 pszDst[offDst - 1] = '\0';
1656 return offDst;
1657}
1658
1659
1660/**
1661 * Converts the given UTF-16 to ASSI, allocating the buffer.
1662 *
1663 * @returns Pointer to the new heap allocation containing the ANSI version of
1664 * the source string.
1665 * @param pwcSrc The source string.
1666 * @param cwcSrc The length of the source string.
1667 */
1668static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1669{
1670 DWORD const dwErrSaved = GetLastError();
1671 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1672 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1673 if (pszBuf)
1674 {
1675 if (cwcSrc > 0)
1676 {
1677 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1678 if (cchRet > 0)
1679 {
1680 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1681 pszBuf[cchRet] = '\0';
1682 }
1683 else
1684 {
1685 kHlpFree(pszBuf);
1686
1687 /* Figure the length and allocate the right buffer size. */
1688 SetLastError(NO_ERROR);
1689 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1690 if (cchRet)
1691 {
1692 cbBuf = cchRet + 2;
1693 pszBuf = (char *)kHlpAlloc(cbBuf);
1694 if (pszBuf)
1695 {
1696 SetLastError(NO_ERROR);
1697 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1698 if (cchRet)
1699 {
1700 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1701 pszBuf[cchRet] = '\0';
1702 }
1703 else
1704 {
1705 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1706 kHlpFree(pszBuf);
1707 pszBuf = NULL;
1708 }
1709 }
1710 }
1711 else
1712 {
1713 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1714 pszBuf = NULL;
1715 }
1716 }
1717 }
1718 else
1719 pszBuf[0] = '\0';
1720 }
1721 SetLastError(dwErrSaved);
1722 return pszBuf;
1723}
1724
1725
1726
1727/** UTF-16 string length. */
1728static KSIZE kwUtf16Len(wchar_t const *pwsz)
1729{
1730 KSIZE cwc = 0;
1731 while (*pwsz != '\0')
1732 cwc++, pwsz++;
1733 return cwc;
1734}
1735
1736/**
1737 * Copy out the UTF-16 string following the convension of GetModuleFileName
1738 */
1739static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1740{
1741 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1742 if (cwcSrc + 1 <= cwcDst)
1743 {
1744 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1745 return (DWORD)cwcSrc;
1746 }
1747 if (cwcDst > 0)
1748 {
1749 KSIZE cwcDstTmp = cwcDst - 1;
1750 pwszDst[cwcDstTmp] = '\0';
1751 if (cwcDstTmp > 0)
1752 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1753 }
1754 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1755 return (DWORD)cwcDst;
1756}
1757
1758
1759/**
1760 * Copy out the ANSI string following the convension of GetModuleFileName
1761 */
1762static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1763{
1764 KSIZE cchSrc = kHlpStrLen(pszSrc);
1765 if (cchSrc + 1 <= cbDst)
1766 {
1767 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1768 return (DWORD)cchSrc;
1769 }
1770 if (cbDst > 0)
1771 {
1772 KSIZE cbDstTmp = cbDst - 1;
1773 pszDst[cbDstTmp] = '\0';
1774 if (cbDstTmp > 0)
1775 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1776 }
1777 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1778 return (DWORD)cbDst;
1779}
1780
1781
1782/**
1783 * Normalizes the path so we get a consistent hash.
1784 *
1785 * @returns status code.
1786 * @param pszPath The path.
1787 * @param pszNormPath The output buffer.
1788 * @param cbNormPath The size of the output buffer.
1789 */
1790static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1791{
1792 KFSLOOKUPERROR enmError;
1793 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1794 if (pFsObj)
1795 {
1796 KBOOL fRc;
1797 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1798 kFsCacheObjRelease(g_pFsCache, pFsObj);
1799 if (fRc)
1800 return 0;
1801 return KERR_BUFFER_OVERFLOW;
1802 }
1803 return KERR_FILE_NOT_FOUND;
1804}
1805
1806
1807/**
1808 * Get the pointer to the filename part of the path.
1809 *
1810 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1811 * @returns Pointer to the terminator char if no filename.
1812 * @param pszPath The path to parse.
1813 */
1814static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1815{
1816 const wchar_t *pwszLast = NULL;
1817 for (;;)
1818 {
1819 wchar_t wc = *pwszPath;
1820#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1821 if (wc == '/' || wc == '\\' || wc == ':')
1822 {
1823 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1824 /* nothing */;
1825 pwszLast = pwszPath;
1826 }
1827#else
1828 if (wc == '/')
1829 {
1830 while ((wc = *++pszFilename) == '/')
1831 /* betsuni */;
1832 pwszLast = pwszPath;
1833 }
1834#endif
1835 if (!wc)
1836 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1837 pwszPath++;
1838 }
1839}
1840
1841
1842
1843/**
1844 * Retains a new reference to the given module
1845 * @returns pMod
1846 * @param pMod The module to retain.
1847 */
1848static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1849{
1850 kHlpAssert(pMod->cRefs > 0);
1851 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1852 pMod->cRefs++;
1853 return pMod;
1854}
1855
1856
1857/**
1858 * Releases a module reference.
1859 *
1860 * @param pMod The module to release.
1861 */
1862static void kwLdrModuleRelease(PKWMODULE pMod)
1863{
1864 if (--pMod->cRefs == 0)
1865 {
1866 /* Unlink it from the hash table. */
1867 if (!pMod->fExe)
1868 {
1869 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1870 if (g_apModules[idx] == pMod)
1871 g_apModules[idx] = pMod->pNextHash;
1872 else
1873 {
1874 PKWMODULE pPrev = g_apModules[idx];
1875 kHlpAssert(pPrev != NULL);
1876 while (pPrev->pNextHash != pMod)
1877 {
1878 pPrev = pPrev->pNextHash;
1879 kHlpAssert(pPrev != NULL);
1880 }
1881 pPrev->pNextHash = pMod->pNextHash;
1882 }
1883 }
1884
1885 /* Unlink it from the list. */
1886 if (pMod != g_pModuleHead)
1887 {
1888 PKWMODULE pPrev = g_pModuleHead;
1889 while (pPrev)
1890 {
1891 if (pPrev->pNextList == pMod)
1892 {
1893 pPrev->pNextList = pMod->pNextList;
1894 if (!pMod->pNextList)
1895 g_ppModuleNext = &pPrev->pNextList;
1896 break;
1897 }
1898 pPrev = pPrev->pNextList;
1899 }
1900 kHlpAssert(pPrev != NULL);
1901 }
1902 else
1903 {
1904 g_pModuleHead = pMod->pNextList;
1905 if (!pMod->pNextList)
1906 g_ppModuleNext = &g_pModuleHead;
1907 }
1908
1909 /* Release import modules. */
1910 if (!pMod->fNative)
1911 {
1912 KSIZE idx = pMod->u.Manual.cImpMods;
1913 while (idx-- > 0)
1914 if (pMod->u.Manual.apImpMods[idx])
1915 {
1916 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1917 pMod->u.Manual.apImpMods[idx] = NULL;
1918 }
1919 }
1920
1921 /* Free our resources. */
1922 kLdrModClose(pMod->pLdrMod);
1923 pMod->pLdrMod = NULL;
1924
1925 if (!pMod->fNative)
1926 {
1927 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1928 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1929 }
1930
1931 if (pMod->iCrtSlot != KU8_MAX)
1932 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
1933
1934 if (pMod->pszMsPdbSrvEndpoint)
1935 {
1936 kHlpFree(pMod->pszMsPdbSrvEndpoint);
1937 pMod->pszMsPdbSrvEndpoint = NULL;
1938 }
1939
1940 kHlpFree(pMod);
1941 }
1942 else
1943 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1944}
1945
1946
1947/**
1948 * Links the module into the module hash table.
1949 *
1950 * @returns pMod
1951 * @param pMod The module to link.
1952 */
1953static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
1954{
1955 if (!pMod->fExe)
1956 {
1957 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1958 pMod->pNextHash = g_apModules[idx];
1959 g_apModules[idx] = pMod;
1960 }
1961
1962 pMod->pNextList = NULL;
1963 *g_ppModuleNext = pMod;
1964 g_ppModuleNext = &pMod->pNextList;
1965
1966 return pMod;
1967}
1968
1969
1970/**
1971 * Replaces imports for this module according to g_aSandboxNativeReplacements.
1972 *
1973 * @param pMod The natively loaded module to process.
1974 */
1975static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
1976{
1977 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
1978 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
1979 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
1980 IMAGE_NT_HEADERS const *pNtHdrs;
1981 IMAGE_DATA_DIRECTORY const *pDirEnt;
1982
1983 kHlpAssert(pMod->fNative);
1984
1985 /*
1986 * Locate the export descriptors.
1987 */
1988 /* MZ header. */
1989 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
1990 {
1991 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
1992 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
1993 }
1994 else
1995 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
1996
1997 /* Check PE header. */
1998 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
1999 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
2000
2001 /* Locate the import descriptor array. */
2002 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2003 if ( pDirEnt->Size > 0
2004 && pDirEnt->VirtualAddress != 0)
2005 {
2006 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
2007 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
2008 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
2009 KU8 *pbProtRange = NULL;
2010 SIZE_T cbProtRange = 0;
2011 DWORD fOldProt = 0;
2012 KU32 const cbPage = 0x1000;
2013 BOOL fRc;
2014
2015
2016 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
2017 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
2018 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
2019
2020 /*
2021 * Walk the import descriptor array.
2022 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
2023 */
2024 while ( cLeft-- > 0
2025 && pImpDesc->Name > 0
2026 && pImpDesc->FirstThunk > 0)
2027 {
2028 KU32 iThunk;
2029 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
2030 PKWMODULE pImportMod = NULL;
2031 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
2032 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
2033 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
2034 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
2035 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
2036 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
2037 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
2038
2039 /* Iterate the thunks. */
2040 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
2041 {
2042 KUPTR const off = paOrgThunks[iThunk].u1.Function;
2043 kHlpAssertReturnVoid(off < cbImage);
2044 if (!IMAGE_SNAP_BY_ORDINAL(off))
2045 {
2046 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
2047 KSIZE const cchSymbol = kHlpStrLen((const char *)&pName->Name[0]);
2048 KU32 i = g_cSandboxNativeReplacements;
2049 while (i-- > 0)
2050 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
2051 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
2052 {
2053 if ( !g_aSandboxNativeReplacements[i].pszModule
2054 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
2055 {
2056 KWLDR_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
2057
2058 /* The .rdata section is normally read-only, so we need to make it writable first. */
2059 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
2060 {
2061 /* Restore previous .rdata page. */
2062 if (fOldProt)
2063 {
2064 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
2065 kHlpAssert(fRc);
2066 fOldProt = 0;
2067 }
2068
2069 /* Query attributes for the current .rdata page. */
2070 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
2071 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
2072 kHlpAssert(cbProtRange);
2073 if (cbProtRange)
2074 {
2075 switch (ProtInfo.Protect)
2076 {
2077 case PAGE_READWRITE:
2078 case PAGE_WRITECOPY:
2079 case PAGE_EXECUTE_READWRITE:
2080 case PAGE_EXECUTE_WRITECOPY:
2081 /* Already writable, nothing to do. */
2082 fRc = TRUE;
2083 break;
2084
2085 default:
2086 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2087 case PAGE_READONLY:
2088 cbProtRange = cbPage;
2089 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2090 break;
2091
2092 case PAGE_EXECUTE:
2093 case PAGE_EXECUTE_READ:
2094 cbProtRange = cbPage;
2095 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2096 break;
2097 }
2098 kHlpAssertStmt(fRc, fOldProt = 0);
2099 }
2100 }
2101
2102 /*
2103 * Unslotted replacements are simple.
2104 */
2105 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2106 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2107 else
2108 {
2109 /*
2110 * Must find our module entry for this module, possibly creating one.
2111 */
2112 if (!pImportMod)
2113 {
2114 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2115 K_TRUE /*fAlwaysPresent*/);
2116 if (!pImportMod)
2117 {
2118 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2119 pszImport, pMod->pszPath);
2120 break;
2121 }
2122 }
2123 paThunks[iThunk].u1.AddressOfData
2124 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2125 }
2126 break;
2127 }
2128 }
2129 }
2130 }
2131
2132
2133 /* Next import descriptor. */
2134 pImpDesc++;
2135 }
2136
2137
2138 if (fOldProt)
2139 {
2140 DWORD fIgnore = 0;
2141 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2142 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2143 }
2144 }
2145
2146}
2147
2148
2149/**
2150 * Creates a module from a native kLdr module handle.
2151 *
2152 * @returns Module w/ 1 reference on success, NULL on failure.
2153 * @param pLdrMod The native kLdr module.
2154 * @param pszPath The normalized path to the module.
2155 * @param cbPath The module path length with terminator.
2156 * @param uHashPath The module path hash.
2157 * @param fDoReplacements Whether to do import replacements on this
2158 * module.
2159 */
2160static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2161 KBOOL fDoReplacements, PKWMODULE pVirtualApiMod)
2162{
2163 /*
2164 * Create the entry.
2165 */
2166 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2167 if (pMod)
2168 {
2169 pMod->pwszPath = (wchar_t *)(pMod + 1);
2170 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2171 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2172 pMod->uHashPath = uHashPath;
2173 pMod->cRefs = 1;
2174 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2175 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2176 pMod->fExe = K_FALSE;
2177 pMod->fNative = K_TRUE;
2178 pMod->pLdrMod = pLdrMod;
2179 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2180 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2181 pMod->iCrtSlot = KU8_MAX;
2182 pMod->fNeedReInit = K_FALSE;
2183 pMod->pszMsPdbSrvEndpoint = NULL;
2184 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2185 pMod->pVirtualApiMod = pVirtualApiMod;
2186 if (pVirtualApiMod)
2187 kwLdrModuleRetain(pVirtualApiMod);
2188
2189 if (fDoReplacements)
2190 {
2191 DWORD const dwSavedErr = GetLastError();
2192 kwLdrModuleDoNativeImportReplacements(pMod);
2193 SetLastError(dwSavedErr);
2194 }
2195
2196 KWLDR_LOG(("New module: %p LB %#010x %s (native%s%s)\n",
2197 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath,
2198 pVirtualApiMod ? ", virtual api => " : "", pVirtualApiMod ? pVirtualApiMod->pszPath : ""));
2199 g_cModules++;
2200 return kwLdrModuleLink(pMod);
2201 }
2202 return NULL;
2203}
2204
2205
2206
2207/**
2208 * Creates a module using the native loader.
2209 *
2210 * @returns Module w/ 1 reference on success, NULL on failure.
2211 * @param pszPath The normalized path to the module.
2212 * @param uHashPath The module path hash.
2213 * @param fDoReplacements Whether to do import replacements on this
2214 * module.
2215 */
2216static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2217{
2218 PKLDRMOD pLdrMod;
2219 int rc;
2220
2221 /*
2222 * HACK ALERT! Make sure the application path is searched when looking for
2223 * imports in the module we're loading.
2224 */
2225 /** @todo improve on this hack! */
2226 PKWMODULE pExe = g_Sandbox.pTool ? g_Sandbox.pTool->u.Sandboxed.pExe : NULL;
2227 if (pExe)
2228 {
2229 /* HACK ALERT! */
2230 wchar_t *pwzFilename = (wchar_t *)&pExe->pwszPath[pExe->offFilenameW];
2231 wchar_t wcSaved = pExe->pwszPath[pExe->offFilenameW];
2232 *pwzFilename = '\0';
2233 if (!SetDllDirectoryW(pExe->pwszPath))
2234 kwErrPrintf("SetDllDirectoryW failed: %u\n", GetLastError());
2235 *pwzFilename = wcSaved;
2236 }
2237
2238 /*
2239 * Load the library and create a module structure for it.
2240 */
2241 rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2242 if (rc == 0)
2243 {
2244 KSIZE cchPath = kHlpStrLen(pszPath);
2245 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, cchPath + 1, uHashPath,
2246 fDoReplacements, NULL /*pVirtualApiMod*/);
2247 if (pMod)
2248 return pMod;
2249 kLdrModClose(pLdrMod);
2250 }
2251 return NULL;
2252}
2253
2254
2255/**
2256 * Checks if the given name could be a virtual API module or not.
2257 */
2258static KBOOL kwLdrIsVirtualApiModule(const char *pszName, KSIZE cchName)
2259{
2260 if (cchName <= 7)
2261 return K_FALSE;
2262 switch (*pszName)
2263 {
2264 default:
2265 return K_FALSE;
2266 case 'a':
2267 case 'A':
2268 if (pszName[1] != 'p' && pszName[1] != 'P')
2269 return K_FALSE;
2270 if (pszName[2] != 'i' && pszName[2] != 'I')
2271 return K_FALSE;
2272 break;
2273 case 'e':
2274 case 'E':
2275 if (pszName[1] != 'x' && pszName[1] != 'X')
2276 return K_FALSE;
2277 if (pszName[2] != 't' && pszName[2] != 'T')
2278 return K_FALSE;
2279 break;
2280 }
2281 if (pszName[3] != '-')
2282 return K_FALSE;
2283 if (pszName[4] != 'm' && pszName[4] != 'M')
2284 return K_FALSE;
2285 if (pszName[5] != 's' && pszName[5] != 'S')
2286 return K_FALSE;
2287 if (pszName[6] != '-')
2288 return K_FALSE;
2289 return K_TRUE;
2290}
2291
2292
2293/**
2294 * Try load what seems to be a virtual API DLL.
2295 *
2296 * This is a worker for kwLdrModuleResolveAndLookup and
2297 * kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule.
2298 *
2299 * @returns Pointer to module on success, NULL on failure.
2300 * @param pszName The name of the module. This must be
2301 * normalized already!
2302 * @param cchName The length of the name.
2303 */
2304static PKWMODULE kwLdrModuleTryLoadVirtualDll(const char *pszName, KSIZE cchName)
2305{
2306 HMODULE hModule;
2307
2308 /*
2309 * Look it up in the hash table.
2310 */
2311 KU32 const uHashPath = kwStrHash(pszName);
2312 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2313 PKWMODULE pMod = g_apModules[idxHash];
2314 if (pMod)
2315 {
2316 do
2317 {
2318 if ( pMod->uHashPath == uHashPath
2319 && kHlpStrComp(pMod->pszPath, pszName) == 0)
2320 return kwLdrModuleRetain(pMod);
2321 pMod = pMod->pNextHash;
2322 } while (pMod);
2323 }
2324
2325 /*
2326 * Not found. Try load it.
2327 */
2328 hModule = LoadLibraryA(pszName);
2329 if (!hModule)
2330 {
2331 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s failed (%u)\n", pszName, GetLastError()));
2332 return NULL;
2333 }
2334
2335 /*
2336 * Loaded successfully. Create a module for the real module.
2337 */
2338 pMod = kwLdrModuleForLoadedNativeByHandle(hModule, K_FALSE /*fEnsureCrtSlot*/, pszName);
2339 if (pMod)
2340 {
2341 /* Create a module for the virtual API name too, unless it is actually a real DLL. */
2342 if (stricmp(&pMod->pszPath[pMod->offFilename], pszName) != 0)
2343 {
2344 PKLDRMOD pLdrMod;
2345 int rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2346 if (rc == 0)
2347 {
2348 PKWMODULE pVirtMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszName, cchName + 1, kwStrHash(pszName),
2349 K_FALSE /*fDoReplacements*/, pMod /*pVirtualApiMod*/);
2350 if (pVirtMod)
2351 {
2352 kwLdrModuleRelease(pMod);
2353 pMod = pVirtMod;
2354 }
2355 else
2356 {
2357 kLdrModClose(pLdrMod);
2358 kwErrPrintf("out of memory\n");
2359 }
2360 }
2361 else
2362 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
2363 }
2364 else
2365 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s -> %s - A real DLL!\n", pszName, pMod->pszPath));
2366 }
2367
2368 return pMod;
2369}
2370
2371
2372/**
2373 * Sets up the quick zero & copy tables for the non-native module.
2374 *
2375 * This is a worker for kwLdrModuleCreateNonNative.
2376 *
2377 * @param pMod The module.
2378 */
2379static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2380{
2381 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2382 KU32 cSegs = pMod->pLdrMod->cSegments;
2383 KU32 iSeg;
2384
2385 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2386 pMod->u.Manual.cQuickCopyChunks = 0;
2387 pMod->u.Manual.cQuickZeroChunks = 0;
2388
2389 for (iSeg = 0; iSeg < cSegs; iSeg++)
2390 switch (paSegs[iSeg].enmProt)
2391 {
2392 case KPROT_READWRITE:
2393 case KPROT_WRITECOPY:
2394 case KPROT_EXECUTE_READWRITE:
2395 case KPROT_EXECUTE_WRITECOPY:
2396 if (paSegs[iSeg].cbMapped)
2397 {
2398 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2399 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2400 {
2401 /*
2402 * Check for trailing zero words.
2403 */
2404 KSIZE cbTrailingZeros;
2405 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2406 && (paSegs[iSeg].cbMapped & 7) == 0
2407 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2408 {
2409 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2410 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2411 KSIZE idxFirstZero = cNatural;
2412 while (idxFirstZero > 0)
2413 if (pauNatural[--idxFirstZero] == 0)
2414 { /* likely */ }
2415 else
2416 {
2417 idxFirstZero++;
2418 break;
2419 }
2420 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2421 if (cbTrailingZeros < 128)
2422 cbTrailingZeros = 0;
2423 }
2424 else
2425 cbTrailingZeros = 0;
2426
2427 /*
2428 * Add quick copy entry.
2429 */
2430 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2431 {
2432 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2433 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2434 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2435 pMod->u.Manual.cQuickCopyChunks = (KU8)(iChunk + 1);
2436 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2437 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2438 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2439 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2440 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2441 }
2442
2443 /*
2444 * Add quick zero entry.
2445 */
2446 if (cbTrailingZeros)
2447 {
2448 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2449 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2450 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2451 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2452 pMod->u.Manual.cQuickZeroChunks = (KU8)(iZero + 1);
2453 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2454 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2455 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2456 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2457 }
2458 }
2459 else
2460 {
2461 /*
2462 * We're out of quick copy table entries, so just copy the whole darn thing.
2463 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2464 */
2465 kHlpAssertFailed();
2466 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2467 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2468 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2469 pMod->u.Manual.cQuickCopyChunks = 1;
2470 KWLDR_LOG(("Quick copy not possible!\n"));
2471 return;
2472 }
2473 }
2474 break;
2475
2476 default:
2477 break;
2478 }
2479}
2480
2481
2482/**
2483 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2484 *
2485 * @param hDll The DLL handle.
2486 * @param idxTls The allocated TLS index.
2487 * @param ppfnTlsCallback Pointer to the TLS callback table entry.
2488 */
2489__declspec(dllexport) void kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, PIMAGE_TLS_CALLBACK *ppfnTlsCallback)
2490{
2491 /*
2492 * Do the module initialization thing first.
2493 */
2494 PKWMODULE pMod = g_pModPendingTlsAlloc;
2495 if (pMod)
2496 {
2497 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2498 LIST_ENTRY *pHead;
2499 LIST_ENTRY *pCur;
2500
2501 pMod->u.Manual.idxTls = idxTls;
2502 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2503
2504 /*
2505 * Try sabotage the DLL name so we can load this module again.
2506 */
2507/** @todo this doesn't work W10 18363 */
2508 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2509 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2510 {
2511 LDR_DATA_TABLE_ENTRY *pMte;
2512 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2513 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2514 {
2515 PUNICODE_STRING pStr = &pMte->FullDllName;
2516 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2517 pStr->Buffer[--off]++;
2518 pStr->Buffer[--off]++;
2519 pStr->Buffer[--off]++;
2520 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2521 break;
2522 }
2523 }
2524 }
2525}
2526
2527
2528/**
2529 * Allocates and initializes TLS variables.
2530 *
2531 * @returns 0 on success, non-zero failure.
2532 * @param pMod The module.
2533 */
2534static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2535{
2536 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2537 IMAGE_NT_HEADERS const *pNtHdrs;
2538 IMAGE_DATA_DIRECTORY const *pTlsDir;
2539
2540 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2541 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2542 else
2543 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2544 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2545
2546 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2547 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2548 {
2549 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2550 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2551 KU32 iEntry;
2552 KU32 iTlsDll;
2553 KU32 iTlsDllSub;
2554 KUPTR offIndex;
2555 KUPTR offCallbacks;
2556 KUPTR const *puCallbacks;
2557 KSIZE cbData;
2558 const wchar_t *pwszTlsDll;
2559 HMODULE hmodTlsDll;
2560
2561 /*
2562 * Check and log.
2563 */
2564 for (iEntry = 0; iEntry < cEntries; iEntry++)
2565 {
2566 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2567 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2568 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2569 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2570 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2571 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2572 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2573
2574 if (offIndex >= pMod->cbImage)
2575 {
2576 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2577 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2578 return -1;
2579 }
2580 if (offCallbacks >= pMod->cbImage)
2581 {
2582 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2583 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2584 return -1;
2585 }
2586 while (*puCallbacks != 0)
2587 {
2588 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2589 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2590 puCallbacks++;
2591 }
2592 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2593 {
2594 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2595 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2596 return -1;
2597 }
2598 }
2599
2600 if (cEntries > 1)
2601 {
2602 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2603 return -1;
2604 }
2605
2606 /*
2607 * Make the allocation by loading a new instance of one of the TLS dlls.
2608 * The DLL will make a call to kwLdrTlsAllocationHook.
2609 */
2610 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2611 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2612 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2613 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2614
2615 /** @todo find better strategy here. Like temporary copy or whatever when
2616 * there is more than a single user. */
2617 for (iTlsDll = 0; cbData > g_aTlsDlls[iTlsDll].cbTls;)
2618 if (++iTlsDll >= K_ELEMENTS(g_aTlsDlls))
2619 {
2620 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2621 return -1;
2622 }
2623 for (iTlsDllSub = 0; g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed;)
2624 if (++iTlsDllSub >= g_aTlsDlls[iTlsDll].cDlls)
2625 {
2626 kwErrPrintf("No unused TLS DLLs for %s of size %u!\n", pMod->pszPath, (unsigned)cbData);
2627 return -1;
2628 }
2629
2630 g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed = K_TRUE;
2631 pwszTlsDll = g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].pwszName;
2632
2633 pMod->u.Manual.idxTls = KU32_MAX;
2634 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2635 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2636 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2637 pMod->u.Manual.cTlsCallbacks = 0;
2638 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2639 pMod->u.Manual.cTlsCallbacks++;
2640 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2641
2642 g_pModPendingTlsAlloc = pMod;
2643 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2644 g_pModPendingTlsAlloc = NULL;
2645 if (hmodTlsDll == NULL)
2646 {
2647 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2648 return -1;
2649 }
2650 if (pMod->u.Manual.idxTls == KU32_MAX)
2651 {
2652 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2653 return -1;
2654 }
2655
2656 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2657 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x\n",
2658 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData));
2659 }
2660 return 0;
2661}
2662
2663
2664/**
2665 * Creates a module using the our own loader.
2666 *
2667 * @returns Module w/ 1 reference on success, NULL on failure.
2668 * @param pszPath The normalized path to the module.
2669 * @param uHashPath The module path hash.
2670 * @param fExe K_TRUE if this is an executable image, K_FALSE
2671 * if not. Executable images does not get entered
2672 * into the global module table.
2673 * @param pExeMod The executable module of the process (for
2674 * resolving imports). NULL if fExe is set.
2675 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2676 */
2677static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2678 PKWMODULE pExeMod, const char *pszSearchPath)
2679{
2680 /*
2681 * Open the module and check the type.
2682 */
2683 PKLDRMOD pLdrMod;
2684 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2685 if (rc == 0)
2686 {
2687 switch (pLdrMod->enmType)
2688 {
2689 case KLDRTYPE_EXECUTABLE_FIXED:
2690 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2691 case KLDRTYPE_EXECUTABLE_PIC:
2692 if (!fExe)
2693 rc = KERR_GENERAL_FAILURE;
2694 break;
2695
2696 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2697 case KLDRTYPE_SHARED_LIBRARY_PIC:
2698 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2699 if (fExe)
2700 rc = KERR_GENERAL_FAILURE;
2701 break;
2702
2703 default:
2704 rc = KERR_GENERAL_FAILURE;
2705 break;
2706 }
2707 if (rc == 0)
2708 {
2709 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2710 if (cImports >= 0)
2711 {
2712 /*
2713 * Create the entry.
2714 */
2715 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2716 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2717 + sizeof(pMod) * cImports
2718 + cbPath
2719 + cbPath * 2 * sizeof(wchar_t));
2720 if (pMod)
2721 {
2722 KBOOL fFixed;
2723
2724 pMod->cRefs = 1;
2725 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2726 pMod->uHashPath = uHashPath;
2727 pMod->fExe = fExe;
2728 pMod->fNative = K_FALSE;
2729 pMod->pLdrMod = pLdrMod;
2730 pMod->iCrtSlot = KU8_MAX;
2731 pMod->fNeedReInit = K_FALSE;
2732 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2733 pMod->pszMsPdbSrvEndpoint = NULL;
2734 pMod->u.Manual.cImpMods = (KU32)cImports;
2735#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2736 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2737#endif
2738 pMod->u.Manual.fUseLdBuf = K_FALSE;
2739 pMod->u.Manual.fCanDoQuick = K_FALSE;
2740 pMod->u.Manual.cQuickZeroChunks = 0;
2741 pMod->u.Manual.cQuickCopyChunks = 0;
2742 pMod->u.Manual.idxTls = KU32_MAX;
2743 pMod->u.Manual.offTlsInitData = KU32_MAX;
2744 pMod->u.Manual.cbTlsInitData = 0;
2745 pMod->u.Manual.cbTlsAlloc = 0;
2746 pMod->u.Manual.cTlsCallbacks = 0;
2747 pMod->u.Manual.offTlsCallbacks = 0;
2748 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2749 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2750 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2751 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2752
2753 /*
2754 * Figure out where to load it and get memory there.
2755 */
2756 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2757 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2758 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2759 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2760 if ( !fFixed
2761 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2762 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2763 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2764 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2765 else
2766 pMod->u.Manual.fUseLdBuf = K_TRUE;
2767 if (rc == 0)
2768 {
2769 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2770 if (rc == 0)
2771 {
2772 KI32 iImp;
2773 KU32 cchReloads;
2774
2775 /*
2776 * Link the module (unless it's an executable image) and process the imports.
2777 */
2778 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2779 kwLdrModuleLink(pMod);
2780 KWLDR_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2781 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2782 KWLDR_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2783 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2784 cchReloads = g_cchReloads;
2785 if (cchReloads + 80 < sizeof(g_szReloads))
2786 {
2787 cchReloads += _snprintf(&g_szReloads[cchReloads], sizeof(g_szReloads) - cchReloads,
2788 "%s.reload /f %s=%p\n", cchReloads ? "; " : "",
2789 pMod->pszPath, pMod->u.Manual.pbLoad);
2790 g_cchReloads = cchReloads;
2791 }
2792
2793 for (iImp = 0; iImp < cImports; iImp++)
2794 {
2795 char szName[1024];
2796 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2797 if (rc == 0)
2798 {
2799 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2800 &pMod->u.Manual.apImpMods[iImp]);
2801 if (rc == 0)
2802 continue;
2803 }
2804 break;
2805 }
2806
2807 if (rc == 0)
2808 {
2809 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2810 kwLdrModuleGetImportCallback, pMod);
2811 if (rc == 0)
2812 {
2813#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2814 /*
2815 * Find the function table. No validation here because the
2816 * loader did that already, right...
2817 */
2818 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2819 IMAGE_NT_HEADERS const *pNtHdrs;
2820 IMAGE_DATA_DIRECTORY const *pXcptDir;
2821 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2822 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2823 else
2824 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2825 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2826 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2827 if (pXcptDir->Size > 0)
2828 {
2829 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2830 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2831 == pXcptDir->Size);
2832 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2833 }
2834 else
2835 {
2836 pMod->u.Manual.cFunctions = 0;
2837 pMod->u.Manual.paFunctions = NULL;
2838 }
2839#endif
2840
2841 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2842
2843 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2844 if (rc == 0)
2845 {
2846 /*
2847 * Final finish.
2848 */
2849 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2850 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2851 pMod->u.Manual.enmReInitState = KWMODSTATE_NEEDS_BITS;
2852 if ( g_Sandbox.pTool
2853 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
2854 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
2855 && !pMod->fExe)
2856 pMod->u.Manual.enmReInitState = KWMODSTATE_READY;
2857 g_cModules++;
2858 g_cNonNativeModules++;
2859 return pMod;
2860 }
2861 }
2862 else
2863 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
2864 }
2865
2866 kwLdrModuleRelease(pMod);
2867 return NULL;
2868 }
2869
2870 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2871 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2872 }
2873 else if (fFixed)
2874 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
2875 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
2876 else
2877 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2878 }
2879 }
2880 }
2881 kLdrModClose(pLdrMod);
2882 }
2883 else
2884 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
2885 return NULL;
2886}
2887
2888
2889/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
2890static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
2891 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
2892{
2893 PKWMODULE pCurMod = (PKWMODULE)pvUser;
2894 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
2895 int rc;
2896 K_NOREF(pMod);
2897
2898 if (pImpMod->fNative)
2899 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
2900 iSymbol, pchSymbol, cchSymbol, pszVersion,
2901 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2902 puValue, pfKind);
2903 else
2904 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
2905 iSymbol, pchSymbol, cchSymbol, pszVersion,
2906 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2907 puValue, pfKind);
2908 if (rc == 0)
2909 {
2910 KU32 i = g_cSandboxReplacements;
2911 while (i-- > 0)
2912 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
2913 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
2914 {
2915 if ( !g_aSandboxReplacements[i].pszModule
2916 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
2917 {
2918 if ( pCurMod->fExe
2919 || !g_aSandboxReplacements[i].fOnlyExe)
2920 {
2921 KWLDR_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
2922 if (!g_aSandboxReplacements[i].fCrtSlotArray)
2923 *puValue = g_aSandboxReplacements[i].pfnReplacement;
2924 else
2925 {
2926 if (pImpMod->iCrtSlot == KU8_MAX)
2927 {
2928 rc = kwLdrModuleCreateCrtSlot(pImpMod);
2929 if (rc)
2930 KWLDR_LOG(("kwLdrModuleGetImportCallback: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
2931 }
2932 *puValue = ((KUPTR *)g_aSandboxReplacements[i].pfnReplacement)[pImpMod->iCrtSlot];
2933 }
2934 }
2935 break;
2936 }
2937 }
2938 }
2939
2940 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
2941 KWLDR_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
2942 return rc;
2943
2944}
2945
2946
2947/**
2948 * Gets the main entrypoint for a module.
2949 *
2950 * @returns 0 on success, KERR on failure
2951 * @param pMod The module.
2952 * @param puAddrMain Where to return the address.
2953 */
2954static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
2955{
2956 KLDRADDR uLdrAddrMain;
2957 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
2958 if (rc == 0)
2959 {
2960 *puAddrMain = (KUPTR)uLdrAddrMain;
2961 return 0;
2962 }
2963 return rc;
2964}
2965
2966
2967/**
2968 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
2969 *
2970 * @returns K_TRUE/K_FALSE.
2971 * @param pszFilename The filename (no path).
2972 * @param enmLocation The location.
2973 */
2974static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
2975{
2976 if (enmLocation != KWLOCATION_SYSTEM32)
2977 return K_TRUE;
2978 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2979 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2980 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
2981 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
2982 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
2983 || kHlpStrNICompAscii(pszFilename, TUPLE("api-ms-win-crt-")) == 0
2984 ;
2985}
2986
2987
2988/**
2989 * Lazily initializes the g_pWinSys32 variable.
2990 */
2991static PKFSDIR kwLdrResolveWinSys32(void)
2992{
2993 KFSLOOKUPERROR enmError;
2994 PKFSDIR pWinSys32;
2995
2996 /* Get the path first. */
2997 char szSystem32[MAX_PATH];
2998 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
2999 {
3000 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
3001 strcpy(szSystem32, "C:\\Windows\\System32");
3002 }
3003
3004 /* Look it up and verify it. */
3005 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
3006 if (pWinSys32)
3007 {
3008 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
3009 {
3010 g_pWinSys32 = pWinSys32;
3011 return pWinSys32;
3012 }
3013
3014 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
3015 }
3016 else
3017 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
3018 return NULL;
3019}
3020
3021
3022/**
3023 * Whether we can load this DLL natively or not.
3024 *
3025 * @returns K_TRUE/K_FALSE.
3026 * @param pszFilename The filename (no path).
3027 * @param enmLocation The location.
3028 * @param pszFullPath The full filename and path.
3029 */
3030static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
3031{
3032 if (enmLocation == KWLOCATION_SYSTEM32)
3033 return K_TRUE;
3034 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
3035 return K_TRUE;
3036 if ( enmLocation == KWLOCATION_UNKNOWN
3037 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3038 return K_TRUE;
3039
3040 /* If the location is unknown, we must check if it's some dynamic loading
3041 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
3042 if (enmLocation == KWLOCATION_UNKNOWN)
3043 {
3044 PKFSDIR pWinSys32 = g_pWinSys32;
3045 if (!pWinSys32)
3046 pWinSys32 = kwLdrResolveWinSys32();
3047 if (pWinSys32)
3048 {
3049 KFSLOOKUPERROR enmError;
3050 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
3051 if (pFsObj)
3052 {
3053 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
3054 kFsCacheObjRelease(g_pFsCache, pFsObj);
3055 if (fInWinSys32)
3056 return K_TRUE;
3057 }
3058 }
3059 }
3060
3061 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3062 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3063 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3064 || kHlpStrNICompAscii(pszFilename, TUPLE("tbbmalloc")) == 0
3065 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3066 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3067 || ( enmLocation != KWLOCATION_UNKNOWN
3068 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)));
3069}
3070
3071
3072/**
3073 * Check if the path leads to a regular file (that exists).
3074 *
3075 * @returns K_TRUE / K_FALSE
3076 * @param pszPath Path to the file to check out.
3077 */
3078static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
3079{
3080 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
3081 KSIZE cchPath = kHlpStrLen(pszPath);
3082 if ( cchPath > 3
3083 && pszPath[cchPath - 4] == '.'
3084 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
3085 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
3086 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
3087 {
3088 KFSLOOKUPERROR enmError;
3089 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3090 if (pFsObj)
3091 {
3092 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
3093 kFsCacheObjRelease(g_pFsCache, pFsObj);
3094 return fRc;
3095 }
3096 }
3097 else
3098 {
3099 BirdStat_T Stat;
3100 int rc = birdStatFollowLink(pszPath, &Stat);
3101 if (rc == 0)
3102 {
3103 if (S_ISREG(Stat.st_mode))
3104 return K_TRUE;
3105 }
3106 }
3107 return K_FALSE;
3108}
3109
3110
3111/**
3112 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
3113 *
3114 * If the file exists, we consult the module hash table before trying to load it
3115 * off the disk.
3116 *
3117 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
3118 * failure.
3119 * @param pszPath The name of the import module.
3120 * @param enmLocation The location we're searching. This is used in
3121 * the heuristics for determining if we can use the
3122 * native loader or need to sandbox the DLL.
3123 * @param pExe The executable (optional).
3124 * @param pszSearchPath The PATH to search (optional).
3125 */
3126static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
3127{
3128 /*
3129 * Does the file exists and is it a regular file?
3130 */
3131 if (kwLdrModuleIsRegularFile(pszPath))
3132 {
3133 /*
3134 * Yes! Normalize it and look it up in the hash table.
3135 */
3136 char szNormPath[1024];
3137 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
3138 if (rc == 0)
3139 {
3140 const char *pszName;
3141 KU32 const uHashPath = kwStrHash(szNormPath);
3142 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3143 PKWMODULE pMod = g_apModules[idxHash];
3144 if (pMod)
3145 {
3146 do
3147 {
3148 if ( pMod->uHashPath == uHashPath
3149 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3150 return kwLdrModuleRetain(pMod);
3151 pMod = pMod->pNextHash;
3152 } while (pMod);
3153 }
3154
3155 /*
3156 * Not in the hash table, so we have to load it from scratch.
3157 */
3158 pszName = kHlpGetFilename(szNormPath);
3159 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
3160 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
3161 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
3162 else
3163 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
3164 if (pMod)
3165 return pMod;
3166 return (PKWMODULE)~(KUPTR)0;
3167 }
3168 }
3169 return NULL;
3170}
3171
3172
3173/**
3174 * Gets a reference to the module by the given name.
3175 *
3176 * We must do the search path thing, as our hash table may multiple DLLs with
3177 * the same base name due to different tools version and similar. We'll use a
3178 * modified search sequence, though. No point in searching the current
3179 * directory for instance.
3180 *
3181 * @returns 0 on success, KERR on failure.
3182 * @param pszName The name of the import module.
3183 * @param pExe The executable (optional).
3184 * @param pImporter The module doing the importing (optional).
3185 * @param pszSearchPath The PATH to search (optional).
3186 * @param ppMod Where to return the module pointer w/ reference.
3187 */
3188static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
3189 const char *pszSearchPath, PKWMODULE *ppMod)
3190{
3191 KSIZE const cchName = kHlpStrLen(pszName);
3192 char szPath[1024];
3193 char *psz;
3194 PKWMODULE pMod = NULL;
3195 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
3196 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
3197
3198 /* Virtual API module. Normalize and try load it. */
3199 if (pMod == NULL && cchName > 7 && kwLdrIsVirtualApiModule(pszName, cchName))
3200 {
3201 if (cchName + cchSuffix >= sizeof(szPath))
3202 return KERR_BUFFER_OVERFLOW;
3203 kHlpMemCopy(szPath, pszName, cchName);
3204 if (fNeedSuffix)
3205 kHlpMemCopy(&szPath[cchName], ".dll", sizeof(".dll"));
3206 szPath[cchName + cchSuffix] = '\0';
3207 _strlwr(szPath);
3208 pMod = kwLdrModuleTryLoadVirtualDll(szPath, cchName + cchSuffix);
3209 }
3210
3211 /* The import path. */
3212 if (pMod == NULL && pImporter != NULL)
3213 {
3214 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
3215 return KERR_BUFFER_OVERFLOW;
3216
3217 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
3218 if (fNeedSuffix)
3219 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3220 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
3221 }
3222
3223 /* Application directory first. */
3224 if (pMod == NULL && pExe != NULL && pExe != pImporter)
3225 {
3226 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
3227 return KERR_BUFFER_OVERFLOW;
3228 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
3229 if (fNeedSuffix)
3230 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3231 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
3232 }
3233
3234 /* The windows directory. */
3235 if (pMod == NULL)
3236 {
3237 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
3238 if ( cchDir <= 2
3239 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
3240 return KERR_BUFFER_OVERFLOW;
3241 szPath[cchDir++] = '\\';
3242 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
3243 if (fNeedSuffix)
3244 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3245 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3246 }
3247
3248 /* The path. */
3249 if ( pMod == NULL
3250 && pszSearchPath)
3251 {
3252 const char *pszCur = pszSearchPath;
3253 while (*pszCur != '\0')
3254 {
3255 /* Find the end of the component */
3256 KSIZE cch = 0;
3257 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
3258 cch++;
3259
3260 if ( cch > 0 /* wrong, but whatever */
3261 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
3262 {
3263 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
3264 if ( szPath[cch - 1] != ':'
3265 && szPath[cch - 1] != '/'
3266 && szPath[cch - 1] != '\\')
3267 *pszDst++ = '\\';
3268 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
3269 if (fNeedSuffix)
3270 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
3271 *pszDst = '\0';
3272
3273 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3274 if (pMod)
3275 break;
3276 }
3277
3278 /* Advance */
3279 pszCur += cch;
3280 while (*pszCur == ';')
3281 pszCur++;
3282 }
3283 }
3284
3285 /* Return. */
3286 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3287 {
3288 *ppMod = pMod;
3289 return 0;
3290 }
3291 *ppMod = NULL;
3292 return KERR_GENERAL_FAILURE;
3293}
3294
3295
3296/**
3297 * Creates a CRT slot for the given module.
3298 *
3299 * @returns 0 on success, non-zero on failure.
3300 * @param pModule The module.
3301 */
3302static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3303{
3304 KSIZE iSlot;
3305 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3306 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3307 if (g_aCrtSlots[iSlot].pModule == NULL)
3308 {
3309 KLDRADDR uAddr;
3310 int rc;
3311
3312 /* Do the linking: */
3313 g_aCrtSlots[iSlot].pModule = pModule;
3314 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3315 pModule->iCrtSlot = (KU8)iSlot;
3316
3317 /* resolve symbols: */
3318 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3319 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3320 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3321 if (rc != 0)
3322 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3323
3324 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "_beginthreadex", 14,
3325 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3326 *(KUPTR *)&g_aCrtSlots[iSlot].pfnBeginThreadEx = rc == 0 ? (KUPTR)uAddr : 0;
3327 //if (rc != 0)
3328 // kwErrPrintf("Failed to resolved '_beginthreadex' in '%s': %d\n", pModule->pszPath, rc);
3329
3330 return 0;
3331 }
3332 kwErrPrintf("Out of CRT slots!\n");
3333 return KERR_NO_MEMORY;
3334}
3335
3336
3337/**
3338 * Locates the module structure for an already loaded native module.
3339 *
3340 * This will create a module structure if needed.
3341 *
3342 * @returns Pointer to the module structure on success, NULL on failure.
3343 * @param hModule The native module handle.
3344 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3345 * @param pszLogName The name to use for logging/errors.
3346 */
3347static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName)
3348{
3349 /*
3350 * Get a normalized path for it.
3351 */
3352 char szModPath[1024];
3353 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3354 {
3355 char szNormPath[1024];
3356 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3357 if (rc == 0)
3358 {
3359 /*
3360 * Hash the path and look it up.
3361 */
3362 KU32 uHashPath;
3363 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3364 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3365 PKWMODULE pMod = g_apModules[idxHash];
3366 if (pMod)
3367 {
3368 do
3369 {
3370 if ( pMod->uHashPath == uHashPath
3371 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3372 {
3373 kwLdrModuleRetain(pMod);
3374 break;
3375 }
3376 pMod = pMod->pNextHash;
3377 } while (pMod);
3378 }
3379
3380 /*
3381 * If not in the hash table, so create a module entry.
3382 */
3383 if (!pMod)
3384 {
3385 PKLDRMOD pLdrMod;
3386 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3387 if (rc == 0)
3388 {
3389 /** @todo more accurately determine location */
3390 const char *pszFilename = kHlpGetFilename(szNormPath);
3391 KBOOL fDoReplacements = kwLdrModuleShouldDoNativeReplacements(pszFilename, KWLOCATION_SYSTEM32);
3392 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3393 fDoReplacements, NULL /*pVirtualApiMod*/);
3394 if (!pMod)
3395 {
3396 kLdrModClose(pLdrMod);
3397 kwErrPrintf("out of memory\n");
3398 }
3399 }
3400 else
3401 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszLogName, rc);
3402 }
3403 if (pMod)
3404 {
3405 /*
3406 * Create a CRT slot for the module if necessary.
3407 */
3408 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3409 return pMod;
3410 rc = kwLdrModuleCreateCrtSlot(pMod);
3411 if (rc == 0)
3412 return pMod;
3413 kwLdrModuleRelease(pMod);
3414 }
3415 }
3416 else
3417 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszLogName, GetLastError());
3418 }
3419 else
3420 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszLogName, GetLastError());
3421 return NULL;
3422}
3423
3424
3425/**
3426 * Locates the module structure for an already loaded native module.
3427 *
3428 * This will create a module structure if needed.
3429 *
3430 * @returns Pointer to the module structure on success, NULL on failure.
3431 * @param pszName The name of the module.
3432 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3433 * @param fAlwaysPresent Whether the module is expected to always be present,
3434 * or not. If not, complain less.
3435 */
3436static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3437{
3438 /*
3439 * Locate the module handle and pass it to kwLdrModuleForLoadedNativeByHandle.
3440 */
3441 HANDLE hModule = GetModuleHandleA(pszName);
3442 if (hModule)
3443 return kwLdrModuleForLoadedNativeByHandle(hModule, fEnsureCrtSlot, pszName);
3444 if (fAlwaysPresent)
3445 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3446 return NULL;
3447}
3448
3449
3450/**
3451 * Does the TLS memory initialization for a module on the current thread.
3452 *
3453 * @returns 0 on success, error on failure.
3454 * @param pMod The module.
3455 */
3456static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3457{
3458 if (pMod->u.Manual.idxTls != KU32_MAX)
3459 {
3460 PTEB pTeb = NtCurrentTeb();
3461 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3462 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3463 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3464 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3465 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3466 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3467 if (pMod->u.Manual.cbTlsInitData)
3468 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3469 }
3470 return 0;
3471}
3472
3473
3474/**
3475 * Does the TLS callbacks for a module.
3476 *
3477 * @param pMod The module.
3478 * @param dwReason The callback reason.
3479 */
3480static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3481{
3482 if (pMod->u.Manual.cTlsCallbacks)
3483 {
3484 PIMAGE_TLS_CALLBACK *pCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3485 do
3486 {
3487 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *pCallback, pMod->hOurMod, dwReason));
3488 (*pCallback)(pMod->hOurMod, dwReason, 0);
3489 } while (*++pCallback);
3490 }
3491}
3492
3493
3494/**
3495 * Does module initialization starting at @a pMod.
3496 *
3497 * This is initially used on the executable. Later it is used by the
3498 * LoadLibrary interceptor.
3499 *
3500 * @returns 0 on success, error on failure.
3501 * @param pMod The module to initialize.
3502 */
3503static int kwLdrModuleInitTree(PKWMODULE pMod)
3504{
3505 int rc = 0;
3506 if (!pMod->fNative)
3507 {
3508 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3509 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3510
3511 /*
3512 * Need to copy bits?
3513 */
3514 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3515 {
3516 if (pMod->u.Manual.fUseLdBuf)
3517 {
3518#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3519 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3520 {
3521 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3522 kHlpAssert(fRc); K_NOREF(fRc);
3523 }
3524#endif
3525 g_pModPrevInLdBuf = g_pModInLdBuf;
3526 g_pModInLdBuf = pMod;
3527 }
3528
3529 /* Do quick zeroing and copying when we can. */
3530 pMod->u.Manual.fCanDoQuick = K_FALSE;
3531 if ( pMod->u.Manual.fCanDoQuick
3532 && ( !pMod->u.Manual.fUseLdBuf
3533 || g_pModPrevInLdBuf == pMod))
3534 {
3535 /* Zero first. */
3536 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3537 switch (pMod->u.Manual.cQuickZeroChunks)
3538 {
3539 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3540 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3541 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3542 case 0: break;
3543 }
3544
3545 /* Then copy. */
3546 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3547 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3548 switch (pMod->u.Manual.cQuickCopyChunks)
3549 {
3550 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3551 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3552 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3553 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3554 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3555 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3556 case 0: break;
3557 }
3558 }
3559 /* Must copy the whole image. */
3560 else
3561 {
3562 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3563 pMod->u.Manual.fCanDoQuick = K_TRUE;
3564 }
3565 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3566 }
3567
3568#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3569 /*
3570 * Need to register function table?
3571 */
3572 if ( !pMod->u.Manual.fRegisteredFunctionTable
3573 && pMod->u.Manual.cFunctions > 0)
3574 {
3575 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3576 pMod->u.Manual.cFunctions,
3577 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3578 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3579 }
3580#endif
3581
3582
3583 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3584 {
3585 /*
3586 * Must do imports first, but mark our module as being initialized to avoid
3587 * endless recursion should there be a dependency loop.
3588 */
3589 KSIZE iImp;
3590 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3591
3592 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3593 {
3594 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3595 if (rc != 0)
3596 return rc;
3597 }
3598
3599 /* Do TLS allocations for module init? */
3600 rc = kwLdrCallTlsAllocateAndInit(pMod);
3601 if (rc != 0)
3602 return rc;
3603 if (pMod->u.Manual.cTlsCallbacks > 0)
3604 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3605
3606 /* Finally call the entry point. */
3607 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3608 if (rc == 0)
3609 pMod->u.Manual.enmState = KWMODSTATE_READY;
3610 else
3611 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3612 }
3613 }
3614 /*
3615 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3616 * _MSPDBSRV_ENDPOINT_ changes value.
3617 */
3618 else if (pMod->fNeedReInit)
3619 {
3620 int rc2;
3621 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3622 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3623 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3624 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3625 if (!rc && !rc2)
3626 { /* likely */ }
3627 else
3628 {
3629 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3630 if (rc2 && !rc)
3631 rc = rc2;
3632 }
3633 pMod->fNeedReInit = K_FALSE;
3634 }
3635 return rc;
3636}
3637
3638
3639/**
3640 * Looks up a module handle for a tool.
3641 *
3642 * @returns Referenced loader module on success, NULL on if not found.
3643 * @param pTool The tool.
3644 * @param hmod The module handle.
3645 */
3646static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3647{
3648 KUPTR const uHMod = (KUPTR)hmod;
3649 PKWMODULE *papMods;
3650 KU32 iEnd;
3651 KU32 i;
3652 PKWDYNLOAD pDynLoad;
3653
3654 if (pTool)
3655 { /* likely */ }
3656 else
3657 return NULL;
3658
3659 /* The executable. */
3660 if ( hmod == NULL
3661 || (pTool->u.Sandboxed.pExe && pTool->u.Sandboxed.pExe->hOurMod == hmod))
3662 {
3663 if (pTool->u.Sandboxed.pExe)
3664 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3665 return NULL;
3666 }
3667
3668 /*
3669 * Binary lookup using the module table.
3670 */
3671 papMods = pTool->u.Sandboxed.papModules;
3672 iEnd = pTool->u.Sandboxed.cModules;
3673 if (iEnd)
3674 {
3675 KU32 iStart = 0;
3676 i = iEnd / 2;
3677 for (;;)
3678 {
3679 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
3680 if (uHMod < uHModCur)
3681 {
3682 iEnd = i--;
3683 if (iStart <= i)
3684 { }
3685 else
3686 break;
3687 }
3688 else if (uHMod != uHModCur)
3689 {
3690 iStart = ++i;
3691 if (i < iEnd)
3692 { }
3693 else
3694 break;
3695 }
3696 /* We've got a match. Always return the non-virtual module (first) when there is one. */
3697 else if (!papMods[i]->pVirtualApiMod)
3698 return kwLdrModuleRetain(papMods[i]);
3699 else
3700 {
3701 while (i > 0 && papMods[i - 1]->pVirtualApiMod && papMods[i - 1]->hOurMod == hmod)
3702 i--;
3703 return kwLdrModuleRetain(papMods[i]);
3704 }
3705
3706 i = iStart + (iEnd - iStart) / 2;
3707 }
3708
3709#ifndef NDEBUG
3710 iStart = pTool->u.Sandboxed.cModules;
3711 while (--iStart > 0)
3712 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3713 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3714#endif
3715 }
3716
3717 /*
3718 * Dynamically loaded images.
3719 */
3720 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3721 if (pDynLoad->hmod == hmod)
3722 {
3723 if (pDynLoad->pMod)
3724 return kwLdrModuleRetain(pDynLoad->pMod);
3725 KWFS_TODO();
3726 return NULL;
3727 }
3728
3729 return NULL;
3730}
3731
3732/**
3733 * Adds the given module to the tool import table.
3734 *
3735 * @returns 0 on success, non-zero on failure.
3736 * @param pTool The tool.
3737 * @param pMod The module.
3738 */
3739static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3740{
3741 /*
3742 * Binary lookup. Locating the right slot for it, return if already there.
3743 */
3744 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3745 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3746 KU32 iEnd = pTool->u.Sandboxed.cModules;
3747 KU32 i;
3748 if (iEnd)
3749 {
3750 KU32 iStart = 0;
3751 i = iEnd / 2;
3752 for (;;)
3753 {
3754 PKWMODULE pCurMod = papMods[i];
3755 KUPTR const uHModCur = (KUPTR)pCurMod->hOurMod;
3756 if (uHMod < uHModCur)
3757 {
3758 iEnd = i;
3759 if (iStart < i)
3760 { }
3761 else
3762 break;
3763 }
3764 else if (uHMod != uHModCur)
3765 {
3766 iStart = ++i;
3767 if (i < iEnd)
3768 { }
3769 else
3770 break;
3771 }
3772 else
3773 {
3774 /* Already there in the table. The non-virtual module must be the first
3775 entry if we've got duplicate hmod values because of virtual modules. */
3776 if (pMod != pCurMod)
3777 {
3778 /* Skip to the last module with the same hmod. */
3779 while (i + 1 < iEnd && (KUPTR)(pCurMod = papMods[i + 1])->hOurMod == uHMod)
3780 {
3781 if (pMod == pCurMod)
3782 return 0;
3783 i++;
3784 }
3785
3786 /* Then scan backwards till the first one. */
3787 while (i > 0 && (KUPTR)(pCurMod = papMods[i - 1])->hOurMod == uHMod)
3788 {
3789 if (pMod == pCurMod)
3790 return 0;
3791 i--;
3792 }
3793 pCurMod = papMods[i];
3794 if (pMod != pCurMod)
3795 {
3796 if (pMod->pVirtualApiMod && !pCurMod->pVirtualApiMod)
3797 i++;
3798 break;
3799 }
3800 }
3801 return 0;
3802 }
3803
3804 i = iStart + (iEnd - iStart) / 2;
3805 }
3806#ifndef NDEBUG
3807 iStart = pTool->u.Sandboxed.cModules;
3808 while (--iStart > 0)
3809 {
3810 kHlpAssert(papMods[iStart] != pMod);
3811 kHlpAssert( (KUPTR)papMods[iStart]->hOurMod != uHMod
3812 || pMod->pVirtualApiMod
3813 || papMods[iStart]->pVirtualApiMod);
3814 }
3815 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod <= uHMod);
3816 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod >= uHMod);
3817#endif
3818 }
3819 else
3820 i = 0;
3821
3822 /*
3823 * Grow the table?
3824 */
3825 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3826 {
3827 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3828 if (!pvNew)
3829 return KERR_NO_MEMORY;
3830 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3831 }
3832
3833 /* Insert it. */
3834 if (i != pTool->u.Sandboxed.cModules)
3835 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3836 papMods[i] = kwLdrModuleRetain(pMod);
3837 pTool->u.Sandboxed.cModules++;
3838 KWLDR_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3839 return 0;
3840}
3841
3842
3843/**
3844 * Adds the given module and all its imports to the
3845 *
3846 * @returns 0 on success, non-zero on failure.
3847 * @param pTool The tool.
3848 * @param pMod The module.
3849 */
3850static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
3851{
3852 int rc = kwToolAddModule(pTool, pMod);
3853 if (pMod->pVirtualApiMod && rc == 0)
3854 rc = kwToolAddModule(pTool, pMod->pVirtualApiMod);
3855 if (!pMod->fNative && rc == 0)
3856 {
3857 KSIZE iImp = pMod->u.Manual.cImpMods;
3858 while (iImp-- > 0)
3859 {
3860 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
3861 if (rc == 0)
3862 { }
3863 else
3864 break;
3865 }
3866 }
3867
3868 return 0;
3869}
3870
3871
3872/**
3873 * Creates a tool entry and inserts it.
3874 *
3875 * @returns Pointer to the tool entry. NULL on failure.
3876 * @param pToolFsObj The file object of the tool. The created tool
3877 * will be associated with it.
3878 *
3879 * A reference is donated by the caller and must be
3880 * released.
3881 * @param pszSearchPath The PATH environment variable value, or NULL.
3882 */
3883static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
3884{
3885 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
3886 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
3887 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
3888 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
3889 if (pTool)
3890 {
3891 KBOOL fRc;
3892 pTool->pwszPath = (wchar_t const *)(pTool + 1);
3893 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
3894 kHlpAssert(fRc); K_NOREF(fRc);
3895
3896 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
3897 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
3898 kHlpAssert(fRc);
3899
3900 pTool->enmType = KWTOOLTYPE_SANDBOXED;
3901 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
3902 NULL /*pEexeMod*/, pszSearchPath);
3903 if (pTool->u.Sandboxed.pExe)
3904 {
3905 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
3906 if (rc == 0)
3907 {
3908 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
3909 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
3910 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
3911 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
3912 else
3913 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
3914 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
3915 }
3916 else
3917 {
3918 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
3919 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
3920 pTool->u.Sandboxed.pExe = NULL;
3921 pTool->enmType = KWTOOLTYPE_EXEC;
3922 }
3923 }
3924 else
3925 pTool->enmType = KWTOOLTYPE_EXEC;
3926
3927 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3928 g_cTools++;
3929 return pTool;
3930 }
3931 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3932 return NULL;
3933}
3934
3935
3936/**
3937 * Looks up the given tool, creating a new tool table entry if necessary.
3938 *
3939 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
3940 * @param pszExe The executable for the tool (not normalized).
3941 * @param cEnvVars Number of environment varibles.
3942 * @param papszEnvVars Environment variables. For getting the PATH.
3943 */
3944static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
3945{
3946 /*
3947 * We associate the tools instances with the file system objects.
3948 *
3949 * We'd like to do the lookup without invaliding the volatile parts of the
3950 * cache, thus the double lookup here. The cache gets invalidate later on.
3951 */
3952 KFSLOOKUPERROR enmError;
3953 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3954 if ( !pToolFsObj
3955 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
3956 {
3957 kFsCacheInvalidateCustomBoth(g_pFsCache);
3958 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3959 }
3960 if (pToolFsObj)
3961 {
3962 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
3963 {
3964 const char *pszSearchPath;
3965 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
3966 if (pTool)
3967 {
3968 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3969 return pTool;
3970 }
3971
3972 /*
3973 * Need to create a new tool.
3974 */
3975 pszSearchPath = NULL;
3976 while (cEnvVars-- > 0)
3977 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
3978 {
3979 pszSearchPath = &papszEnvVars[cEnvVars][5];
3980 break;
3981 }
3982
3983 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
3984 if (pTool)
3985 return pTool;
3986
3987 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
3988 }
3989 else
3990 {
3991 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3992 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
3993 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
3994 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
3995 }
3996 }
3997 else
3998 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
3999 return NULL;
4000}
4001
4002
4003
4004/*
4005 *
4006 * File system cache.
4007 * File system cache.
4008 * File system cache.
4009 *
4010 */
4011
4012
4013/**
4014 * This is for kDep.
4015 */
4016int kwFsPathExists(const char *pszPath)
4017{
4018 BirdTimeSpec_T TsIgnored;
4019 KFSLOOKUPERROR enmError;
4020 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
4021 if (pFsObj)
4022 {
4023 kFsCacheObjRelease(g_pFsCache, pFsObj);
4024 return 1;
4025 }
4026 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
4027}
4028
4029
4030/* duplicated in dir-nt-bird.c */
4031void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
4032{
4033 KFSLOOKUPERROR enmError;
4034 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
4035 if (pPathObj)
4036 {
4037 KSIZE off = pPathObj->cchParent;
4038 if (off > 0)
4039 {
4040 KSIZE offEnd = off + pPathObj->cchName;
4041 if (offEnd < cbFull)
4042 {
4043 PKFSDIR pAncestor;
4044
4045 pszFull[off + pPathObj->cchName] = '\0';
4046 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
4047
4048 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4049 {
4050 kHlpAssert(off > 1);
4051 kHlpAssert(pAncestor != NULL);
4052 kHlpAssert(pAncestor->Obj.cchName > 0);
4053 pszFull[--off] = '/';
4054 off -= pAncestor->Obj.cchName;
4055 kHlpAssert(pAncestor->Obj.cchParent == off);
4056 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4057 }
4058 kFsCacheObjRelease(g_pFsCache, pPathObj);
4059 return;
4060 }
4061 }
4062 else
4063 {
4064 if ((size_t)pPathObj->cchName + 1 < cbFull)
4065 {
4066 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
4067 pszFull[pPathObj->cchName] = '/';
4068 pszFull[pPathObj->cchName + 1] = '\0';
4069
4070 kFsCacheObjRelease(g_pFsCache, pPathObj);
4071 return;
4072 }
4073 }
4074
4075 /* do fallback. */
4076 kHlpAssertFailed();
4077 kFsCacheObjRelease(g_pFsCache, pPathObj);
4078 }
4079
4080 nt_fullpath(pszPath, pszFull, cbFull);
4081}
4082
4083
4084/**
4085 * Helper for getting the extension of a UTF-16 path.
4086 *
4087 * @returns Pointer to the extension or the terminator.
4088 * @param pwszPath The path.
4089 * @param pcwcExt Where to return the length of the extension.
4090 */
4091static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
4092{
4093 wchar_t const *pwszName = pwszPath;
4094 wchar_t const *pwszExt = NULL;
4095 for (;;)
4096 {
4097 wchar_t const wc = *pwszPath++;
4098 if (wc == '.')
4099 pwszExt = pwszPath;
4100 else if (wc == '/' || wc == '\\' || wc == ':')
4101 {
4102 pwszName = pwszPath;
4103 pwszExt = NULL;
4104 }
4105 else if (wc == '\0')
4106 {
4107 if (pwszExt)
4108 {
4109 *pcwcExt = pwszPath - pwszExt - 1;
4110 return pwszExt;
4111 }
4112 *pcwcExt = 0;
4113 return pwszPath - 1;
4114 }
4115 }
4116}
4117
4118
4119
4120/**
4121 * Parses the argument string passed in as pszSrc.
4122 *
4123 * @returns size of the processed arguments.
4124 * @param pszSrc Pointer to the commandline that's to be parsed.
4125 * @param pcArgs Where to return the number of arguments.
4126 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
4127 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
4128 *
4129 * @remarks Lifted from startuphacks-win.c
4130 */
4131static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
4132{
4133 int bs;
4134 char chQuote;
4135 char *pfFlags;
4136 int cbArgs;
4137 int cArgs;
4138
4139#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
4140#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
4141#define WHITE(c) ((c) == ' ' || (c) == '\t')
4142
4143#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
4144#define _ARG_RESPONSE 0x02 /* Argument read from response file */
4145#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
4146#define _ARG_ENV 0x08 /* Argument from environment */
4147#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
4148
4149 cArgs = 0;
4150 cbArgs = 0;
4151
4152#if 0
4153 /* argv[0] */
4154 PUTC((char)_ARG_NONZERO);
4155 PUTV;
4156 for (;;)
4157 {
4158 PUTC(*pszSrc);
4159 if (*pszSrc == 0)
4160 break;
4161 ++pszSrc;
4162 }
4163 ++pszSrc;
4164#endif
4165
4166 for (;;)
4167 {
4168 while (WHITE(*pszSrc))
4169 ++pszSrc;
4170 if (*pszSrc == 0)
4171 break;
4172 pfFlags = pchPool;
4173 PUTC((unsigned char)_ARG_NONZERO);
4174 PUTV;
4175 bs = 0; chQuote = 0;
4176 for (;;)
4177 {
4178 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
4179 {
4180 while (bs >= 2)
4181 {
4182 PUTC('\\');
4183 bs -= 2;
4184 }
4185 if (bs & 1)
4186 PUTC(*pszSrc);
4187 else
4188 {
4189 chQuote = chQuote ? 0 : *pszSrc;
4190 if (pfFlags != NULL)
4191 *pfFlags |= _ARG_DQUOTE;
4192 }
4193 bs = 0;
4194 }
4195 else if (*pszSrc == '\\')
4196 ++bs;
4197 else
4198 {
4199 while (bs != 0)
4200 {
4201 PUTC('\\');
4202 --bs;
4203 }
4204 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
4205 break;
4206 PUTC(*pszSrc);
4207 }
4208 ++pszSrc;
4209 }
4210 PUTC(0);
4211 }
4212
4213 *pcArgs = cArgs;
4214 return cbArgs;
4215}
4216
4217
4218
4219
4220/*
4221 *
4222 * Process and thread related APIs.
4223 * Process and thread related APIs.
4224 * Process and thread related APIs.
4225 *
4226 */
4227
4228/** Common worker for ExitProcess(), exit() and friends. */
4229static void WINAPI kwSandboxDoExit(int uExitCode)
4230{
4231 if (g_Sandbox.idMainThread == GetCurrentThreadId())
4232 {
4233 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
4234
4235 g_Sandbox.rcExitCode = (int)uExitCode;
4236
4237 /* Before we jump, restore the TIB as we're not interested in any
4238 exception chain stuff installed by the sandboxed executable. */
4239 *pTib = g_Sandbox.TibMainThread;
4240 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
4241
4242 longjmp(g_Sandbox.JmpBuf, 1);
4243 }
4244 KWFS_TODO();
4245}
4246
4247
4248/** ExitProcess replacement. */
4249static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
4250{
4251 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
4252 kwSandboxDoExit((int)uExitCode);
4253}
4254
4255
4256/** ExitProcess replacement. */
4257static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
4258{
4259 if (hProcess == GetCurrentProcess())
4260 kwSandboxDoExit(uExitCode);
4261 KWFS_TODO();
4262 return TerminateProcess(hProcess, uExitCode);
4263}
4264
4265
4266/** Normal CRT exit(). */
4267static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
4268{
4269 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
4270 kwSandboxDoExit(rcExitCode);
4271}
4272
4273
4274/** Quick CRT _exit(). */
4275static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
4276{
4277 /* Quick. */
4278 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
4279 kwSandboxDoExit(rcExitCode);
4280}
4281
4282
4283/** Return to caller CRT _cexit(). */
4284static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
4285{
4286 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
4287 kwSandboxDoExit(rcExitCode);
4288}
4289
4290
4291/** Quick return to caller CRT _c_exit(). */
4292static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
4293{
4294 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
4295 kwSandboxDoExit(rcExitCode);
4296}
4297
4298
4299/** Runtime error and exit _amsg_exit(). */
4300static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
4301{
4302 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
4303 kwSandboxDoExit(255);
4304}
4305
4306
4307/** CRT - terminate(). */
4308static void __cdecl kwSandbox_msvcrt_terminate(void)
4309{
4310 KW_LOG(("\nRuntime - terminate!\n"));
4311 kwSandboxDoExit(254);
4312}
4313
4314
4315/** CRT - _onexit */
4316static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
4317{
4318 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4319 {
4320 PKWEXITCALLACK pCallback;
4321 KW_LOG(("_onexit(%p)\n", pfnFunc));
4322 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4323
4324 pCallback = kHlpAlloc(sizeof(*pCallback));
4325 if (pCallback)
4326 {
4327 pCallback->pfnCallback = pfnFunc;
4328 pCallback->fAtExit = K_FALSE;
4329 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4330 g_Sandbox.pExitCallbackHead = pCallback;
4331 return pfnFunc;
4332 }
4333 return NULL;
4334 }
4335 //KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
4336 //return pfnFunc;
4337}
4338
4339
4340/** CRT - atexit */
4341static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
4342{
4343 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4344 {
4345 PKWEXITCALLACK pCallback;
4346 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4347 KW_LOG(("atexit(%p)\n", pfnFunc));
4348
4349 pCallback = kHlpAlloc(sizeof(*pCallback));
4350 if (pCallback)
4351 {
4352 pCallback->pfnCallback = (_onexit_t)pfnFunc;
4353 pCallback->fAtExit = K_TRUE;
4354 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4355 g_Sandbox.pExitCallbackHead = pCallback;
4356 return 0;
4357 }
4358 return -1;
4359 }
4360 //KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4361 //return 0;
4362}
4363
4364
4365/** Kernel32 - SetConsoleCtrlHandler(). */
4366static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4367{
4368 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4369 return TRUE;
4370}
4371
4372
4373/** The CRT internal __getmainargs() API. */
4374static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4375 int dowildcard, int const *piNewMode)
4376{
4377 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4378 *pargc = g_Sandbox.cArgs;
4379 *pargv = g_Sandbox.papszArgs;
4380 *penvp = g_Sandbox.environ;
4381
4382 /** @todo startinfo points at a newmode (setmode) value. */
4383 return 0;
4384}
4385
4386
4387/** The CRT internal __wgetmainargs() API. */
4388static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4389 int dowildcard, int const *piNewMode)
4390{
4391 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4392 *pargc = g_Sandbox.cArgs;
4393 *pargv = g_Sandbox.papwszArgs;
4394 *penvp = g_Sandbox.wenviron;
4395
4396 /** @todo startinfo points at a newmode (setmode) value. */
4397 return 0;
4398}
4399
4400
4401
4402/** Kernel32 - GetCommandLineA() */
4403static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4404{
4405 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4406 return g_Sandbox.pszCmdLine;
4407}
4408
4409
4410/** Kernel32 - GetCommandLineW() */
4411static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4412{
4413 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4414 return g_Sandbox.pwszCmdLine;
4415}
4416
4417
4418/** Kernel32 - GetStartupInfoA() */
4419static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4420{
4421 KW_LOG(("GetStartupInfoA\n"));
4422 GetStartupInfoA(pStartupInfo);
4423 pStartupInfo->lpReserved = NULL;
4424 pStartupInfo->lpTitle = NULL;
4425 pStartupInfo->lpReserved2 = NULL;
4426 pStartupInfo->cbReserved2 = 0;
4427}
4428
4429
4430/** Kernel32 - GetStartupInfoW() */
4431static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4432{
4433 KW_LOG(("GetStartupInfoW\n"));
4434 GetStartupInfoW(pStartupInfo);
4435 pStartupInfo->lpReserved = NULL;
4436 pStartupInfo->lpTitle = NULL;
4437 pStartupInfo->lpReserved2 = NULL;
4438 pStartupInfo->cbReserved2 = 0;
4439}
4440
4441
4442/** CRT - __p___argc(). */
4443static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4444{
4445 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4446 return &g_Sandbox.cArgs;
4447}
4448
4449
4450/** CRT - __p___argv(). */
4451static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4452{
4453 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4454 return &g_Sandbox.papszArgs;
4455}
4456
4457
4458/** CRT - __p___sargv(). */
4459static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4460{
4461 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4462 return &g_Sandbox.papwszArgs;
4463}
4464
4465
4466/** CRT - __p__acmdln(). */
4467static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4468{
4469 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4470 return (char **)&g_Sandbox.pszCmdLine;
4471}
4472
4473
4474/** CRT - __p__acmdln(). */
4475static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4476{
4477 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4478 return &g_Sandbox.pwszCmdLine;
4479}
4480
4481
4482/** CRT - __p__pgmptr(). */
4483static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4484{
4485 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4486 return &g_Sandbox.pgmptr;
4487}
4488
4489
4490/** CRT - __p__wpgmptr(). */
4491static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4492{
4493 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4494 return &g_Sandbox.wpgmptr;
4495}
4496
4497
4498/** CRT - _get_pgmptr(). */
4499static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4500{
4501 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4502 *ppszValue = g_Sandbox.pgmptr;
4503 return 0;
4504}
4505
4506
4507/** CRT - _get_wpgmptr(). */
4508static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4509{
4510 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4511 *ppwszValue = g_Sandbox.wpgmptr;
4512 return 0;
4513}
4514
4515/** Just in case. */
4516static void kwSandbox_msvcrt__wincmdln(void)
4517{
4518 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4519 KWFS_TODO();
4520}
4521
4522
4523/** Just in case. */
4524static void kwSandbox_msvcrt__wwincmdln(void)
4525{
4526 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4527 KWFS_TODO();
4528}
4529
4530/** CreateThread interceptor. */
4531static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4532 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4533 DWORD fFlags, PDWORD pidThread)
4534{
4535 HANDLE hThread = NULL;
4536 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4537 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4538 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4539 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4540 {
4541 /* Allow link::DbgThread. */
4542 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4543 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4544 }
4545 else
4546 KWFS_TODO();
4547 return hThread;
4548}
4549
4550
4551/** _beginthread - create a new thread. */
4552static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4553{
4554 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4555 KWFS_TODO();
4556 return 0;
4557}
4558
4559
4560/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4561static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4562 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4563 unsigned fCreate, unsigned *pidThread)
4564{
4565 /*
4566 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4567 * whatever it needs to.
4568 */
4569 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4570 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4571 pfnThreadProc, pvUser, fCreate, pidThread));
4572 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4573 {
4574 uintptr_t rcRet;
4575 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4576 if (!s_pfnReal)
4577 {
4578 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4579 if (!s_pfnReal)
4580 {
4581 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4582 __debugbreak();
4583 }
4584 }
4585 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4586 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4587 return rcRet;
4588 }
4589
4590 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4591 KWFS_TODO();
4592 return 0;
4593}
4594
4595
4596/** _beginthreadex - create a new thread. */
4597static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex_wrapped(void *pvSecAttr, unsigned cbStack,
4598 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4599 unsigned fCreate, unsigned *pidThread, PKWCRTSLOT pSlot)
4600{
4601 /*
4602 * Since the VC++ 12 (VS 2013) compiler, the 2nd pass is now threaded.
4603 * Let it do whatever it needs to.
4604 */
4605 KW_LOG(("kwSandbox_msvcrt__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4606 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4607 pfnThreadProc, pvUser, fCreate, pidThread));
4608 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4609 && pSlot->pfnBeginThreadEx)
4610 {
4611 uintptr_t rcRet = pSlot->pfnBeginThreadEx(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4612 KW_LOG(("kwSandbox_msvcrt__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4613 return rcRet;
4614 }
4615
4616 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4617 KWFS_TODO();
4618 return 0;
4619}
4620
4621CRT_SLOT_FUNCTION_WRAPPER(uintptr_t __cdecl, kwSandbox_msvcrt__beginthreadex,
4622 (void *pvSecAttr, unsigned cbStack, unsigned (__stdcall *pfnThreadProc)(void *),
4623 void *pvUser, unsigned fCreate, unsigned *pidThread),
4624 (pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread, &g_aCrtSlots[iCrtSlot]));
4625
4626
4627
4628/*
4629 *
4630 * Environment related APIs.
4631 * Environment related APIs.
4632 * Environment related APIs.
4633 *
4634 */
4635
4636/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4637static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4638{
4639 char *pszzEnv;
4640 char *pszCur;
4641 KSIZE cbNeeded = 1;
4642 KSIZE iVar = 0;
4643
4644 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4645
4646 /* Figure how space much we need first. */
4647 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4648 cbNeeded += kHlpStrLen(pszCur) + 1;
4649
4650 /* Allocate it. */
4651 pszzEnv = kHlpAlloc(cbNeeded);
4652 if (pszzEnv)
4653 {
4654 char *psz = pszzEnv;
4655 iVar = 0;
4656 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4657 {
4658 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4659 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4660 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4661 }
4662 *psz++ = '\0';
4663 kHlpAssert((KUPTR)(psz - pszzEnv) == cbNeeded);
4664 }
4665
4666 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4667#if 0
4668 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4669 pszCur = pszzEnv;
4670 iVar = 0;
4671 while (*pszCur)
4672 {
4673 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4674 iVar++;
4675 pszCur += kHlpStrLen(pszCur) + 1;
4676 }
4677 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4678 pszCur++;
4679 fprintf(stderr, "ended at %p, after %u bytes (expected %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4680#endif
4681 return pszzEnv;
4682}
4683
4684
4685/** Kernel32 - GetEnvironmentStrings */
4686static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4687{
4688 KW_LOG(("GetEnvironmentStrings!\n"));
4689 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4690}
4691
4692
4693/** Kernel32 - GetEnvironmentStringsW */
4694static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4695{
4696 wchar_t *pwszzEnv;
4697 wchar_t *pwszCur;
4698 KSIZE cwcNeeded = 1;
4699 KSIZE iVar = 0;
4700
4701 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4702
4703 /* Figure how space much we need first. */
4704 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4705 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4706
4707 /* Allocate it. */
4708 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4709 if (pwszzEnv)
4710 {
4711 wchar_t *pwsz = pwszzEnv;
4712 iVar = 0;
4713 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4714 {
4715 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4716 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4717 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4718 }
4719 *pwsz++ = '\0';
4720 kHlpAssert((KUPTR)(pwsz - pwszzEnv) == cwcNeeded);
4721 }
4722
4723 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4724 return pwszzEnv;
4725}
4726
4727
4728/** Kernel32 - FreeEnvironmentStringsA */
4729static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4730{
4731 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4732 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4733 kHlpFree(pszzEnv);
4734 return TRUE;
4735}
4736
4737
4738/** Kernel32 - FreeEnvironmentStringsW */
4739static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4740{
4741 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4742 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4743 kHlpFree(pwszzEnv);
4744 return TRUE;
4745}
4746
4747
4748/**
4749 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4750 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4751 *
4752 * @returns 0 on success, non-zero on failure.
4753 * @param pSandbox The sandbox.
4754 * @param cMin Minimum size, including terminator.
4755 */
4756static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4757{
4758 void *pvNew;
4759 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4760 KSIZE cNew = cOld + 256;
4761 while (cNew < cMin)
4762 cNew += 256;
4763
4764 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4765 if (pvNew)
4766 {
4767 pSandbox->environ = (char **)pvNew;
4768 pSandbox->environ[cOld] = NULL;
4769
4770 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4771 if (pvNew)
4772 {
4773 pSandbox->papszEnvVars = (char **)pvNew;
4774 pSandbox->papszEnvVars[cOld] = NULL;
4775
4776 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4777 if (pvNew)
4778 {
4779 pSandbox->wenviron = (wchar_t **)pvNew;
4780 pSandbox->wenviron[cOld] = NULL;
4781
4782 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4783 if (pvNew)
4784 {
4785 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4786 pSandbox->papwszEnvVars[cOld] = NULL;
4787
4788 pSandbox->cEnvVarsAllocated = cNew;
4789 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4790 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4791 return 0;
4792 }
4793 }
4794 }
4795 }
4796 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4797 return KERR_NO_MEMORY;
4798}
4799
4800
4801/**
4802 * Sets an environment variable, ANSI style.
4803 *
4804 * @returns 0 on success, non-zero on failure.
4805 * @param pSandbox The sandbox.
4806 * @param pchVar The variable name.
4807 * @param cchVar The length of the name.
4808 * @param pszValue The value.
4809 */
4810static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4811{
4812 /* Allocate and construct the new strings. */
4813 KSIZE cchTmp = kHlpStrLen(pszValue);
4814 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4815 if (pszNew)
4816 {
4817 wchar_t *pwszNew;
4818 kHlpMemCopy(pszNew, pchVar, cchVar);
4819 pszNew[cchVar] = '=';
4820 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4821 cchTmp += cchVar + 1;
4822 pszNew[cchTmp] = '\0';
4823
4824 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4825 if (pwszNew)
4826 {
4827 /* Look it up. */
4828 KSIZE iVar = 0;
4829 char *pszEnv;
4830 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4831 {
4832 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4833 && pszEnv[cchVar] == '=')
4834 {
4835 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4836 " iVar=%d: %p='%s' and %p='%ls'\n",
4837 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4838 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4839 iVar, pszNew, pszNew, pwszNew, pwszNew));
4840
4841 kHlpFree(pSandbox->papszEnvVars[iVar]);
4842 pSandbox->papszEnvVars[iVar] = pszNew;
4843 pSandbox->environ[iVar] = pszNew;
4844
4845 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4846 pSandbox->papwszEnvVars[iVar] = pwszNew;
4847 pSandbox->wenviron[iVar] = pwszNew;
4848 return 0;
4849 }
4850 iVar++;
4851 }
4852
4853 /* Not found, do we need to grow the table first? */
4854 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4855 kwSandboxGrowEnv(pSandbox, iVar + 2);
4856 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4857 {
4858 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4859
4860 pSandbox->papszEnvVars[iVar + 1] = NULL;
4861 pSandbox->papszEnvVars[iVar] = pszNew;
4862 pSandbox->environ[iVar + 1] = NULL;
4863 pSandbox->environ[iVar] = pszNew;
4864
4865 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4866 pSandbox->papwszEnvVars[iVar] = pwszNew;
4867 pSandbox->wenviron[iVar + 1] = NULL;
4868 pSandbox->wenviron[iVar] = pwszNew;
4869 return 0;
4870 }
4871
4872 kHlpFree(pwszNew);
4873 }
4874 kHlpFree(pszNew);
4875 }
4876 KW_LOG(("Out of memory!\n"));
4877 return 0;
4878}
4879
4880
4881/**
4882 * Sets an environment variable, UTF-16 style.
4883 *
4884 * @returns 0 on success, non-zero on failure.
4885 * @param pSandbox The sandbox.
4886 * @param pwcVar The variable name.
4887 * @param cwcVar The length of the name.
4888 * @param pwszValue The value.
4889 */
4890static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4891{
4892 /* Allocate and construct the new strings. */
4893 KSIZE cwcTmp = kwUtf16Len(pwszValue);
4894 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4895 if (pwszNew)
4896 {
4897 char *pszNew;
4898 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4899 pwszNew[cwcVar] = '=';
4900 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4901 cwcTmp += cwcVar + 1;
4902 pwszNew[cwcVar] = '\0';
4903
4904 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4905 if (pszNew)
4906 {
4907 /* Look it up. */
4908 KSIZE iVar = 0;
4909 wchar_t *pwszEnv;
4910 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4911 {
4912 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4913 && pwszEnv[cwcVar] == '=')
4914 {
4915 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4916 " iVar=%d: %p='%s' and %p='%ls'\n",
4917 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4918 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4919 iVar, pszNew, pszNew, pwszNew, pwszNew));
4920
4921 kHlpFree(pSandbox->papszEnvVars[iVar]);
4922 pSandbox->papszEnvVars[iVar] = pszNew;
4923 pSandbox->environ[iVar] = pszNew;
4924
4925 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4926 pSandbox->papwszEnvVars[iVar] = pwszNew;
4927 pSandbox->wenviron[iVar] = pwszNew;
4928 return 0;
4929 }
4930 iVar++;
4931 }
4932
4933 /* Not found, do we need to grow the table first? */
4934 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4935 kwSandboxGrowEnv(pSandbox, iVar + 2);
4936 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4937 {
4938 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4939
4940 pSandbox->papszEnvVars[iVar + 1] = NULL;
4941 pSandbox->papszEnvVars[iVar] = pszNew;
4942 pSandbox->environ[iVar + 1] = NULL;
4943 pSandbox->environ[iVar] = pszNew;
4944
4945 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4946 pSandbox->papwszEnvVars[iVar] = pwszNew;
4947 pSandbox->wenviron[iVar + 1] = NULL;
4948 pSandbox->wenviron[iVar] = pwszNew;
4949 return 0;
4950 }
4951
4952 kHlpFree(pwszNew);
4953 }
4954 kHlpFree(pszNew);
4955 }
4956 KW_LOG(("Out of memory!\n"));
4957 return 0;
4958}
4959
4960
4961/** ANSI unsetenv worker. */
4962static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4963{
4964 KSIZE iVar = 0;
4965 char *pszEnv;
4966 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4967 {
4968 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4969 && pszEnv[cchVar] == '=')
4970 {
4971 KSIZE cVars = iVar;
4972 while (pSandbox->papszEnvVars[cVars])
4973 cVars++;
4974 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4975 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4976
4977 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4978 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4979 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4980
4981 kHlpFree(pSandbox->papszEnvVars[iVar]);
4982 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4983 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4984 pSandbox->papszEnvVars[cVars] = NULL;
4985 pSandbox->environ[cVars] = NULL;
4986
4987 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4988 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4989 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4990 pSandbox->papwszEnvVars[cVars] = NULL;
4991 pSandbox->wenviron[cVars] = NULL;
4992 return 0;
4993 }
4994 iVar++;
4995 }
4996 return KERR_ENVVAR_NOT_FOUND;
4997}
4998
4999
5000/** UTF-16 unsetenv worker. */
5001static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5002{
5003 KSIZE iVar = 0;
5004 wchar_t *pwszEnv;
5005 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5006 {
5007 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5008 && pwszEnv[cwcVar] == '=')
5009 {
5010 KSIZE cVars = iVar;
5011 while (pSandbox->papwszEnvVars[cVars])
5012 cVars++;
5013 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
5014 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
5015
5016 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5017 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5018 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5019
5020 kHlpFree(pSandbox->papszEnvVars[iVar]);
5021 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5022 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5023 pSandbox->papszEnvVars[cVars] = NULL;
5024 pSandbox->environ[cVars] = NULL;
5025
5026 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5027 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5028 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5029 pSandbox->papwszEnvVars[cVars] = NULL;
5030 pSandbox->wenviron[cVars] = NULL;
5031 return 0;
5032 }
5033 iVar++;
5034 }
5035 return KERR_ENVVAR_NOT_FOUND;
5036}
5037
5038
5039
5040/** ANSI getenv worker. */
5041static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5042{
5043 KSIZE iVar = 0;
5044 char *pszEnv;
5045 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
5046 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5047 && pszEnv[cchVar] == '=')
5048 return &pszEnv[cchVar + 1];
5049 return NULL;
5050}
5051
5052
5053/** UTF-16 getenv worker. */
5054static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5055{
5056 KSIZE iVar = 0;
5057 wchar_t *pwszEnv;
5058 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
5059 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5060 && pwszEnv[cwcVar] == '=')
5061 return &pwszEnv[cwcVar + 1];
5062 return NULL;
5063}
5064
5065
5066/** Kernel32 - GetEnvironmentVariableA() */
5067static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
5068{
5069 char *pszFoundValue;
5070 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5071
5072 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5073 if (pszFoundValue)
5074 {
5075 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
5076 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
5077 return cchRet;
5078 }
5079 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
5080 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5081 return 0;
5082}
5083
5084
5085/** Kernel32 - GetEnvironmentVariableW() */
5086static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
5087{
5088 wchar_t *pwszFoundValue;
5089 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5090
5091 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5092 if (pwszFoundValue)
5093 {
5094 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
5095 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
5096 return cchRet;
5097 }
5098 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
5099 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5100 return 0;
5101}
5102
5103
5104/** Kernel32 - SetEnvironmentVariableA() */
5105static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
5106{
5107 int rc;
5108 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5109
5110 if (pszValue)
5111 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5112 else
5113 {
5114 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5115 rc = 0; //??
5116 }
5117 if (rc == 0)
5118 {
5119 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
5120 return TRUE;
5121 }
5122 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5123 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
5124 return FALSE;
5125}
5126
5127
5128/** Kernel32 - SetEnvironmentVariableW() */
5129static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
5130{
5131 int rc;
5132 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5133
5134 if (pwszValue)
5135 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5136 else
5137 {
5138 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5139 rc = 0; //??
5140 }
5141 if (rc == 0)
5142 {
5143 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
5144 return TRUE;
5145 }
5146 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5147 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
5148 return FALSE;
5149}
5150
5151
5152/** Kernel32 - ExpandEnvironmentStringsA() */
5153static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
5154{
5155 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5156 KWFS_TODO();
5157 return 0;
5158}
5159
5160
5161/** Kernel32 - ExpandEnvironmentStringsW() */
5162static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
5163{
5164 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5165 KWFS_TODO();
5166 return 0;
5167}
5168
5169
5170/** CRT - _putenv(). */
5171static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
5172{
5173 int rc;
5174 char const *pszEqual;
5175 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5176
5177 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
5178 if (pszEqual)
5179 {
5180 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
5181 if (rc == 0)
5182 { }
5183 else
5184 rc = -1;
5185 }
5186 else
5187 {
5188 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
5189 rc = 0;
5190 }
5191 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
5192 return rc;
5193}
5194
5195
5196/** CRT - _wputenv(). */
5197static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
5198{
5199 int rc;
5200 wchar_t const *pwszEqual;
5201 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5202
5203 pwszEqual = wcschr(pwszVarEqualValue, '=');
5204 if (pwszEqual)
5205 {
5206 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
5207 if (rc == 0)
5208 { }
5209 else
5210 rc = -1;
5211 }
5212 else
5213 {
5214 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
5215 rc = 0;
5216 }
5217 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
5218 return rc;
5219}
5220
5221
5222/** CRT - _putenv_s(). */
5223static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
5224{
5225 char const *pszEqual;
5226 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5227
5228 pszEqual = kHlpStrChr(pszVar, '=');
5229 if (pszEqual == NULL)
5230 {
5231 if (pszValue)
5232 {
5233 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5234 if (rc == 0)
5235 {
5236 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
5237 return 0;
5238 }
5239 }
5240 else
5241 {
5242 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5243 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
5244 return 0;
5245 }
5246 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
5247 return ENOMEM;
5248 }
5249 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
5250 return EINVAL;
5251}
5252
5253
5254/** CRT - _wputenv_s(). */
5255static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
5256{
5257 wchar_t const *pwszEqual;
5258 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5259
5260 pwszEqual = wcschr(pwszVar, '=');
5261 if (pwszEqual == NULL)
5262 {
5263 if (pwszValue)
5264 {
5265 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5266 if (rc == 0)
5267 {
5268 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
5269 return 0;
5270 }
5271 }
5272 else
5273 {
5274 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5275 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
5276 return 0;
5277 }
5278 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
5279 return ENOMEM;
5280 }
5281 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
5282 return EINVAL;
5283}
5284
5285
5286/** CRT - get pointer to the __initenv variable (initial environment). */
5287static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
5288{
5289 KW_LOG(("__p___initenv\n"));
5290 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5291 KWFS_TODO();
5292 return &g_Sandbox.initenv;
5293}
5294
5295
5296/** CRT - get pointer to the __winitenv variable (initial environment). */
5297static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
5298{
5299 KW_LOG(("__p___winitenv\n"));
5300 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5301 KWFS_TODO();
5302 return &g_Sandbox.winitenv;
5303}
5304
5305
5306/** CRT - get pointer to the _environ variable (current environment). */
5307static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
5308{
5309 KW_LOG(("__p__environ\n"));
5310 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5311 return &g_Sandbox.environ;
5312}
5313
5314
5315/** CRT - get pointer to the _wenviron variable (current environment). */
5316static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
5317{
5318 KW_LOG(("__p__wenviron\n"));
5319 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5320 return &g_Sandbox.wenviron;
5321}
5322
5323
5324/** CRT - get the _environ variable (current environment).
5325 * @remarks Not documented or prototyped? */
5326static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
5327{
5328 KWFS_TODO(); /** @todo check the callers expectations! */
5329 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5330 *ppapszEnviron = g_Sandbox.environ;
5331 return 0;
5332}
5333
5334
5335/** CRT - get the _wenviron variable (current environment).
5336 * @remarks Not documented or prototyped? */
5337static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
5338{
5339 KWFS_TODO(); /** @todo check the callers expectations! */
5340 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5341 *ppapwszEnviron = g_Sandbox.wenviron;
5342 return 0;
5343}
5344
5345
5346/** CRT - _wdupenv_s() (see _tdupenv_s(). */
5347static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
5348 PKWCRTSLOT pSlot)
5349{
5350 errno_t rc;
5351 wchar_t *pwszValue;
5352 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5353
5354 if (ppwszValue)
5355 {
5356 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
5357 if (pwszValue)
5358 {
5359 size_t cwcValue = wcslen(pwszValue);
5360 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
5361 if (pwszDst)
5362 {
5363 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
5364 pwszDst[cwcValue] = '\0';
5365 *ppwszValue = pwszDst;
5366 if (pcwcValue)
5367 *pcwcValue = cwcValue;
5368 rc = 0;
5369 }
5370 else
5371 {
5372 *ppwszValue = NULL;
5373 if (pcwcValue)
5374 *pcwcValue = 0;
5375 rc = ENOMEM;
5376 }
5377 }
5378 else
5379 {
5380 *ppwszValue = NULL;
5381 if (pcwcValue)
5382 *pcwcValue = 0;
5383 rc = 0;
5384 }
5385 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5386 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5387 }
5388 else
5389 {
5390 /*
5391 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5392 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5393 * was set to a value.
5394 */
5395 if (pcwcValue)
5396 *pcwcValue = 0;
5397 rc = EINVAL;
5398 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5399 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5400 }
5401 return rc;
5402}
5403CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5404 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5405 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5406
5407
5408
5409/*
5410 *
5411 * Loader related APIs
5412 * Loader related APIs
5413 * Loader related APIs
5414 *
5415 */
5416
5417/**
5418 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5419 */
5420static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5421{
5422 /* Load it first. */
5423 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5424 if (hmod)
5425 {
5426 pDynLoad->hmod = hmod;
5427 pDynLoad->pMod = NULL; /* indicates special */
5428
5429 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5430 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5431 KWLDR_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5432 }
5433 else
5434 kHlpFree(pDynLoad);
5435 return hmod;
5436}
5437
5438
5439/**
5440 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5441 */
5442static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5443{
5444 static const char s_szDll[] = ".dll";
5445 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5446 PKWMODULE pMod;
5447 char szNormPath[256];
5448
5449 /*
5450 * Lower case it and make sure it ends with .dll.
5451 */
5452 if (cbFilename > sizeof(szNormPath))
5453 {
5454 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5455 return NULL;
5456 }
5457 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5458 _strlwr(szNormPath);
5459 kHlpAssert(cbFilename > 7 /* api-ms- */ );
5460 if (strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5461 {
5462 if (cbFilename + sizeof(s_szDll) - 1 > sizeof(szNormPath))
5463 {
5464 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5465 return NULL;
5466 }
5467
5468 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5469 cbFilename += sizeof(s_szDll) - 1;
5470 }
5471
5472 /*
5473 * Try load it.
5474 */
5475 pMod = kwLdrModuleTryLoadVirtualDll(szNormPath, cbFilename - 1);
5476 if (pMod)
5477 {
5478 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5479
5480 pDynLoad->pMod = pMod;
5481 pDynLoad->hmod = pMod->hOurMod;
5482
5483 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5484 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5485 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5486 return pDynLoad->hmod;
5487 }
5488 kHlpFree(pDynLoad);
5489 return NULL;
5490}
5491
5492
5493/** Kernel32 - LoadLibraryExA() */
5494static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5495{
5496 KSIZE cchFilename = kHlpStrLen(pszFilename);
5497 const char *pszSearchPath;
5498 PKWDYNLOAD pDynLoad;
5499 PKWMODULE pMod;
5500 int rc;
5501 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5502 //fprintf(stderr, "LoadLibraryExA: %s, %#x\n", pszFilename, fFlags);
5503
5504 /*
5505 * Deal with a couple of extremely unlikely special cases right away.
5506 */
5507 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5508 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5509 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5510 { /* likely */ }
5511 else
5512 {
5513 KWFS_TODO();
5514 return LoadLibraryExA(pszFilename, hFile, fFlags);
5515 }
5516
5517 /*
5518 * Check if we've already got a dynload entry for this one.
5519 */
5520 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5521 if ( pDynLoad->cchRequest == cchFilename
5522 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5523 {
5524 if (pDynLoad->pMod)
5525 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5526 else
5527 rc = 0;
5528 if (rc == 0)
5529 {
5530 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5531 return pDynLoad->hmod;
5532 }
5533 SetLastError(ERROR_DLL_INIT_FAILED);
5534 return NULL;
5535 }
5536
5537 /*
5538 * Allocate a dynload entry for the request.
5539 */
5540 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5541 if (pDynLoad)
5542 {
5543 pDynLoad->cchRequest = cchFilename;
5544 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5545 }
5546 else
5547 {
5548 KWLDR_LOG(("LoadLibraryExA: Out of memory!\n"));
5549 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5550 return NULL;
5551 }
5552
5553 /*
5554 * Deal with resource / data DLLs.
5555 */
5556 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5557 | LOAD_LIBRARY_AS_DATAFILE
5558 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5559 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5560 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5561
5562 /*
5563 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5564 */
5565 if ( kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5566 && kHlpIsFilenameOnly(pszFilename))
5567 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5568
5569 /*
5570 * Normal library loading.
5571 * We start by being very lazy and reusing the code for resolving imports.
5572 */
5573 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5574 if (!kHlpIsFilenameOnly(pszFilename))
5575 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5576 else
5577 {
5578 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5579 if (rc != 0)
5580 pMod = NULL;
5581 }
5582 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5583 {
5584 /* Enter it into the tool module table and dynamic link request cache. */
5585 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5586
5587 pDynLoad->pMod = pMod;
5588 pDynLoad->hmod = pMod->hOurMod;
5589
5590 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5591 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5592
5593 /*
5594 * Make sure it's initialized (need to link it first since DllMain may
5595 * use loader APIs).
5596 */
5597 rc = kwLdrModuleInitTree(pMod);
5598 if (rc == 0)
5599 {
5600 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5601 return pDynLoad->hmod;
5602 }
5603
5604 SetLastError(ERROR_DLL_INIT_FAILED);
5605 }
5606 else
5607 {
5608 KWFS_TODO();
5609 kHlpFree(pDynLoad);
5610 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5611 }
5612 return NULL;
5613}
5614
5615
5616/** Kernel32 - LoadLibraryExA() for native overloads */
5617static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5618{
5619 char szPath[1024];
5620 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5621
5622 /*
5623 * We may have to help resolved unqualified DLLs living in the executable directory.
5624 */
5625 if (kHlpIsFilenameOnly(pszFilename))
5626 {
5627 KSIZE cchFilename = kHlpStrLen(pszFilename);
5628 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5629 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5630 {
5631 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5632 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5633 if (kwFsPathExists(szPath))
5634 {
5635 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5636 pszFilename = szPath;
5637 }
5638 }
5639
5640 if (pszFilename != szPath)
5641 {
5642 KSIZE cchSuffix = 0;
5643 KBOOL fNeedSuffix = K_FALSE;
5644 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5645 while (*pszCur != '\0')
5646 {
5647 /* Find the end of the component */
5648 KSIZE cch = 0;
5649 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5650 cch++;
5651
5652 if ( cch > 0 /* wrong, but whatever */
5653 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5654 {
5655 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5656 if ( szPath[cch - 1] != ':'
5657 && szPath[cch - 1] != '/'
5658 && szPath[cch - 1] != '\\')
5659 *pszDst++ = '\\';
5660 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5661 if (fNeedSuffix)
5662 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5663 *pszDst = '\0';
5664
5665 if (kwFsPathExists(szPath))
5666 {
5667 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5668 pszFilename = szPath;
5669 break;
5670 }
5671 }
5672
5673 /* Advance */
5674 pszCur += cch;
5675 while (*pszCur == ';')
5676 pszCur++;
5677 }
5678 }
5679 }
5680
5681 return LoadLibraryExA(pszFilename, hFile, fFlags);
5682}
5683
5684
5685/** Kernel32 - LoadLibraryExW() */
5686static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5687{
5688 char szTmp[4096];
5689 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5690 if (cchTmp < sizeof(szTmp))
5691 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5692
5693 KWFS_TODO();
5694 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5695 return NULL;
5696}
5697
5698/** Kernel32 - LoadLibraryA() */
5699static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5700{
5701 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5702}
5703
5704
5705/** Kernel32 - LoadLibraryW() */
5706static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5707{
5708 char szTmp[4096];
5709 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5710 if (cchTmp < sizeof(szTmp))
5711 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5712 KWFS_TODO();
5713 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5714 return NULL;
5715}
5716
5717
5718/** Kernel32 - FreeLibrary() */
5719static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5720{
5721 /* Ignored, we like to keep everything loaded. */
5722 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5723 return TRUE;
5724}
5725
5726
5727/** Worker for GetModuleHandleA/W for handling cached modules. */
5728static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5729{
5730 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5731 if (hmod)
5732 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5733 hmod, g_aGetModuleHandleCache[i].pszName));
5734 else
5735 {
5736 /*
5737 * The first time around we have to make sure we have a module table
5738 * entry for it, if not we add one. We need to add it to the tools
5739 * module list to for it to work.
5740 */
5741 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
5742 g_aGetModuleHandleCache[i].fAlwaysPresent);
5743 if (pMod)
5744 {
5745 hmod = pMod->hOurMod;
5746 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5747 {
5748 kwToolAddModule(g_Sandbox.pTool, pMod);
5749 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5750 hmod, g_aGetModuleHandleCache[i].pszName));
5751 }
5752 else
5753 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5754 hmod, g_aGetModuleHandleCache[i].pszName));
5755
5756 }
5757 }
5758 return hmod;
5759}
5760
5761
5762/** Kernel32 - GetModuleHandleA() */
5763static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5764{
5765 KSIZE i;
5766 KSIZE cchModule;
5767 PKWDYNLOAD pDynLoad;
5768 KSIZE cchSuffix;
5769 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5770 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5771
5772 /*
5773 * The executable.
5774 */
5775 if (pszModule == NULL)
5776 {
5777 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5778 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5779 }
5780
5781 /*
5782 * If no path of suffix, pretend it ends with .DLL.
5783 */
5784 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5785
5786 /*
5787 * Cache of system modules we've seen queried.
5788 */
5789 cchModule = kHlpStrLen(pszModule);
5790 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5791 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5792 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5793 || ( cchSuffix > 0
5794 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5795 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5796 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5797 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5798
5799 /*
5800 * Modules we've dynamically loaded.
5801 */
5802 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5803 if (pDynLoad->pMod)
5804 {
5805 const char *pszPath = pDynLoad->pMod->pszPath;
5806 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
5807 if ( stricmp(pszPath, pszModule) == 0
5808 || stricmp(pszName, pszModule) == 0
5809 || ( cchSuffix > 0
5810 && strnicmp(pszName, pszModule, cchModule) == 0
5811 && stricmp(&pszName[cchModule], ".dll") == 0))
5812 {
5813 if ( pDynLoad->pMod->fNative
5814 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5815 {
5816 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
5817 return pDynLoad->hmod;
5818 }
5819 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
5820 SetLastError(ERROR_MOD_NOT_FOUND);
5821 return NULL;
5822 }
5823 }
5824
5825 /*
5826 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5827 * to and go via the g_aGetModuleHandleCache cache.
5828 */
5829/** @todo virtual api DLLs */
5830 if (kHlpStrNICompAscii(pszModule, "api-ms-win-", 11) == 0)
5831 {
5832 HMODULE hmod = GetModuleHandleA(pszModule);
5833 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
5834 if (hmod)
5835 {
5836 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5837 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
5838 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5839 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
5840 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5841 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
5842 if (hmod == GetModuleHandleW(L"UCRTBASE.DLL"))
5843 return kwSandbox_Kernel32_GetModuleHandleA("UCRTBASE.DLL");
5844 }
5845 else
5846 dwErr = GetLastError();
5847 }
5848
5849 kwErrPrintf("pszModule=%s\n", pszModule);
5850 KWFS_TODO();
5851 SetLastError(ERROR_MOD_NOT_FOUND);
5852 return NULL;
5853}
5854
5855
5856/** Kernel32 - GetModuleHandleW() */
5857static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
5858{
5859 KSIZE i;
5860 KSIZE cwcModule;
5861 PKWDYNLOAD pDynLoad;
5862 KSIZE cwcSuffix;
5863 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5864 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5865
5866 /*
5867 * The executable.
5868 */
5869 if (pwszModule == NULL)
5870 {
5871 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5872 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5873 }
5874
5875 /*
5876 * If no path of suffix, pretend it ends with .DLL.
5877 */
5878 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
5879
5880 /*
5881 * Cache of system modules we've seen queried.
5882 */
5883 cwcModule = kwUtf16Len(pwszModule);
5884 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5885 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
5886 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5887 || ( cwcSuffix > 0
5888 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
5889 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
5890 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
5891 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5892
5893 /*
5894 * Modules we've dynamically loaded.
5895 */
5896 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5897 if (pDynLoad->pMod)
5898 {
5899 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
5900 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
5901 if ( _wcsicmp(pwszPath, pwszModule) == 0
5902 || _wcsicmp(pwszName, pwszModule) == 0
5903 || ( cwcSuffix
5904 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
5905 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
5906 {
5907 if ( pDynLoad->pMod->fNative
5908 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5909 {
5910 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5911 return pDynLoad->hmod;
5912 }
5913 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
5914 SetLastError(ERROR_MOD_NOT_FOUND);
5915 return NULL;
5916 }
5917 }
5918
5919 /*
5920 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5921 * to and go via the g_aGetModuleHandleCache cache.
5922 */
5923 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
5924 {
5925 HMODULE hmod = GetModuleHandleW(pwszModule);
5926 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
5927 if (hmod)
5928 {
5929 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5930 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
5931 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5932 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
5933 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5934 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
5935 }
5936 else
5937 dwErr = GetLastError();
5938 }
5939
5940 kwErrPrintf("pwszModule=%ls\n", pwszModule);
5941 KWFS_TODO();
5942 SetLastError(dwErr);
5943 return NULL;
5944}
5945
5946
5947/** Used to debug dynamically resolved procedures. */
5948static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5949{
5950#ifdef _MSC_VER
5951 __debugbreak();
5952#else
5953 KWFS_TODO();
5954#endif
5955 return ~(UINT)0;
5956}
5957
5958
5959#ifndef NDEBUG
5960/*
5961 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5962 */
5963# if K_ARCH == K_ARCH_X86_32
5964static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5965# else
5966static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5967# endif
5968typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5969typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5970typedef struct KWCXINTERCEPTORENTRY
5971{
5972 PFNINVOKECOMPILERPASSW pfnOrg;
5973 PKWMODULE pModule;
5974 PFNINVOKECOMPILERPASSW pfnWrap;
5975} KWCXINTERCEPTORENTRY;
5976
5977static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5978 KWCXINTERCEPTORENTRY *pEntry)
5979{
5980 int i;
5981 KIPTR rcExit;
5982 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5983 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5984 for (i = 0; i < cArgs; i++)
5985 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5986
5987 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5988
5989 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5990 return rcExit;
5991}
5992
5993static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
5994static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
5995static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
5996
5997static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
5998{
5999 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
6000 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
6001 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
6002};
6003
6004static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6005{
6006 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
6007}
6008
6009static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6010{
6011 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
6012}
6013
6014static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6015{
6016 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
6017}
6018
6019#endif /* !NDEBUG */
6020
6021
6022/** Kernel32 - GetProcAddress() */
6023static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6024{
6025 KSIZE i;
6026 PKWMODULE pMod;
6027 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6028
6029 /*
6030 * Try locate the module.
6031 */
6032 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6033 if (pMod)
6034 {
6035 KLDRADDR uValue;
6036 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
6037 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
6038 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
6039 KU32_MAX /*iSymbol*/,
6040 pszProc,
6041 kHlpStrLen(pszProc),
6042 NULL /*pszVersion*/,
6043 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
6044 &uValue,
6045 NULL /*pfKind*/);
6046 if (rc == 0)
6047 {
6048 //static int s_cDbgGets = 0;
6049 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
6050 KU32 i = g_cSandboxGetProcReplacements;
6051 while (i-- > 0)
6052 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
6053 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
6054 {
6055 if ( !g_aSandboxGetProcReplacements[i].pszModule
6056 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
6057 {
6058 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
6059 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
6060 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
6061 {
6062 if (!g_aSandboxReplacements[i].fCrtSlotArray)
6063 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
6064 else
6065 {
6066 if (pMod->iCrtSlot == KU8_MAX)
6067 {
6068 int rc = kwLdrModuleCreateCrtSlot(pMod);
6069 if (rc)
6070 {
6071 KW_LOG(("GetProcAddress: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
6072 SetLastError(ERROR_INTERNAL_ERROR);
6073 return NULL;
6074 }
6075 }
6076 uValue = ((KUPTR *)g_aSandboxGetProcReplacements[i].pfnReplacement)[pMod->iCrtSlot];
6077 }
6078
6079 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6080 }
6081 kwLdrModuleRelease(pMod);
6082 return (FARPROC)(KUPTR)uValue;
6083 }
6084 }
6085
6086#ifndef NDEBUG
6087 /* Intercept the compiler pass method, dumping arguments. */
6088 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
6089 {
6090 KU32 i;
6091 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6092 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6093 {
6094 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6095 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6096 break;
6097 }
6098 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6099 while (i-- > 0)
6100 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6101 {
6102 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6103 g_aCxInterceptorEntries[i].pModule = pMod;
6104 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6105 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6106 break;
6107 }
6108 }
6109#endif
6110 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6111 kwLdrModuleRelease(pMod);
6112 //s_cDbgGets++;
6113 //if (s_cGets >= 3)
6114 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6115 return (FARPROC)(KUPTR)uValue;
6116 }
6117
6118 KWFS_TODO();
6119 SetLastError(ERROR_PROC_NOT_FOUND);
6120 kwLdrModuleRelease(pMod);
6121 return NULL;
6122 }
6123
6124 /*
6125 * Hmm... could be a cached module-by-name.
6126 */
6127 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6128 if (g_aGetModuleHandleCache[i].hmod == hmod)
6129 return GetProcAddress(hmod, pszProc);
6130
6131 KWFS_TODO();
6132 return GetProcAddress(hmod, pszProc);
6133}
6134
6135
6136/** Kernel32 - GetModuleFileNameA() */
6137static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6138{
6139 PKWMODULE pMod;
6140 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6141
6142 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6143 if (pMod != NULL)
6144 {
6145 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6146 kwLdrModuleRelease(pMod);
6147 return cbRet;
6148 }
6149 KWFS_TODO();
6150 return 0;
6151}
6152
6153
6154/** Kernel32 - GetModuleFileNameW() */
6155static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6156{
6157 PKWMODULE pMod;
6158 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6159
6160 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6161 if (pMod)
6162 {
6163 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6164 kwLdrModuleRelease(pMod);
6165 return cwcRet;
6166 }
6167
6168 KWFS_TODO();
6169 return 0;
6170}
6171
6172
6173/** NtDll - RtlPcToFileHeader
6174 * This is necessary for msvcr100.dll!CxxThrowException. */
6175static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6176{
6177 PVOID pvRet;
6178
6179 /*
6180 * Do a binary lookup of the module table for the current tool.
6181 * This will give us a
6182 */
6183 if (g_Sandbox.fRunning)
6184 {
6185 KUPTR const uPC = (KUPTR)pvPC;
6186 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6187 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6188 KU32 i;
6189 if (iEnd)
6190 {
6191 KU32 iStart = 0;
6192 i = iEnd / 2;
6193 for (;;)
6194 {
6195 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6196 if (uPC < uHModCur)
6197 {
6198 iEnd = i;
6199 if (iStart < i)
6200 { }
6201 else
6202 break;
6203 }
6204 else if (uPC != uHModCur)
6205 {
6206 iStart = ++i;
6207 if (i < iEnd)
6208 { }
6209 else
6210 break;
6211 }
6212 else
6213 {
6214 /* This isn't supposed to happen. */
6215 break;
6216 }
6217
6218 i = iStart + (iEnd - iStart) / 2;
6219 }
6220
6221 /* For reasons of simplicity (= copy & paste), we end up with the
6222 module after the one we're interested in here. */
6223 i--;
6224 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6225 && papMods[i]->pLdrMod)
6226 {
6227 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6228 if (uRvaPC < papMods[i]->cbImage)
6229 {
6230 *ppvImageBase = papMods[i]->hOurMod;
6231 pvRet = papMods[i]->hOurMod;
6232 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6233 return pvRet;
6234 }
6235 }
6236 }
6237 else
6238 i = 0;
6239 }
6240
6241 /*
6242 * Call the regular API.
6243 */
6244 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6245 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6246 return pvRet;
6247}
6248
6249
6250/*
6251 *
6252 * File access APIs (for speeding them up).
6253 * File access APIs (for speeding them up).
6254 * File access APIs (for speeding them up).
6255 *
6256 */
6257
6258
6259/**
6260 * Converts a lookup error to a windows error code.
6261 *
6262 * @returns The windows error code.
6263 * @param enmError The lookup error.
6264 */
6265static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6266{
6267 switch (enmError)
6268 {
6269 case KFSLOOKUPERROR_NOT_FOUND:
6270 case KFSLOOKUPERROR_NOT_DIR:
6271 return ERROR_FILE_NOT_FOUND;
6272
6273 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6274 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6275 return ERROR_PATH_NOT_FOUND;
6276
6277 case KFSLOOKUPERROR_PATH_TOO_LONG:
6278 return ERROR_FILENAME_EXCED_RANGE;
6279
6280 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6281 return ERROR_NOT_ENOUGH_MEMORY;
6282
6283 default:
6284 return ERROR_PATH_NOT_FOUND;
6285 }
6286}
6287
6288#ifdef WITH_TEMP_MEMORY_FILES
6289
6290/**
6291 * Checks for a cl.exe temporary file.
6292 *
6293 * There are quite a bunch of these. They seems to be passing data between the
6294 * first and second compiler pass. Since they're on disk, they get subjected to
6295 * AV software screening and normal file consistency rules. So, not necessarily
6296 * a very efficient way of handling reasonably small amounts of data.
6297 *
6298 * We make the files live in virtual memory by intercepting their opening,
6299 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6300 *
6301 * @returns K_TRUE / K_FALSE
6302 * @param pwszFilename The file name being accessed.
6303 */
6304static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6305{
6306 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6307 if (pwszName)
6308 {
6309 /* The name starts with _CL_... */
6310 if ( pwszName[0] == '_'
6311 && pwszName[1] == 'C'
6312 && pwszName[2] == 'L'
6313 && pwszName[3] == '_' )
6314 {
6315 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6316 this check by just checking that it's alpha numerical ascii from here on. */
6317 wchar_t wc;
6318 pwszName += 4;
6319 while ((wc = *pwszName++) != '\0')
6320 {
6321 if (wc < 127 && iswalnum(wc))
6322 { /* likely */ }
6323 else
6324 return K_FALSE;
6325 }
6326 return K_TRUE;
6327 }
6328
6329 /* In VC2019 there is also one {UUID} file in temp: */
6330 if (pwszName[0] == '{')
6331 {
6332 KSIZE cwcName = kwUtf16Len(pwszName);
6333 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6334 && pwszName[37] == '}'
6335 && iswalnum(pwszName[1]) // 4
6336 && iswalnum(pwszName[2]) // 4
6337 && iswalnum(pwszName[3]) // 6
6338 && iswalnum(pwszName[4]) // 5
6339 && iswalnum(pwszName[5]) // d
6340 && iswalnum(pwszName[6]) // d
6341 && iswalnum(pwszName[7]) // d
6342 && iswalnum(pwszName[8]) // 9
6343 && pwszName[9] == '-' // -
6344 && iswalnum(pwszName[10]) // e
6345 && iswalnum(pwszName[11]) // 4
6346 && iswalnum(pwszName[12]) // 9
6347 && iswalnum(pwszName[13]) // 4
6348 && pwszName[14] == '-' // -
6349 && iswalnum(pwszName[15]) // 4
6350 && iswalnum(pwszName[16]) // 7
6351 && iswalnum(pwszName[17]) // 1
6352 && iswalnum(pwszName[18]) // b
6353 && pwszName[19] == '-' // -
6354 && iswalnum(pwszName[20]) // 9
6355 && iswalnum(pwszName[21]) // 9
6356 && iswalnum(pwszName[22]) // 6
6357 && iswalnum(pwszName[23]) // b
6358 && pwszName[24] == '-' // -
6359 && iswalnum(pwszName[25]) // 9
6360 && iswalnum(pwszName[26]) // b
6361 && iswalnum(pwszName[27]) // 5
6362 && iswalnum(pwszName[28]) // 5
6363 && iswalnum(pwszName[29]) // 6
6364 && iswalnum(pwszName[30]) // e
6365 && iswalnum(pwszName[31]) // 2
6366 && iswalnum(pwszName[32]) // 5
6367 && iswalnum(pwszName[33]) // a
6368 && iswalnum(pwszName[34]) // 3
6369 && iswalnum(pwszName[35]) // f
6370 && iswalnum(pwszName[36])) // 8
6371 return K_TRUE;
6372 }
6373 }
6374 return K_FALSE;
6375}
6376
6377
6378/**
6379 * Creates a handle to a temporary file.
6380 *
6381 * @returns The handle on success.
6382 * INVALID_HANDLE_VALUE and SetLastError on failure.
6383 * @param pTempFile The temporary file.
6384 * @param dwDesiredAccess The desired access to the handle.
6385 * @param fMapping Whether this is a mapping (K_TRUE) or file
6386 * (K_FALSE) handle type.
6387 */
6388static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6389{
6390 /*
6391 * Create a handle to the temporary file.
6392 */
6393 HANDLE hFile = INVALID_HANDLE_VALUE;
6394 HANDLE hProcSelf = GetCurrentProcess();
6395 if (DuplicateHandle(hProcSelf, hProcSelf,
6396 hProcSelf, &hFile,
6397 SYNCHRONIZE, FALSE,
6398 0 /*dwOptions*/))
6399 {
6400 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6401 if (pHandle)
6402 {
6403 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6404 pHandle->cRefs = 1;
6405 pHandle->offFile = 0;
6406 pHandle->hHandle = hFile;
6407 pHandle->dwDesiredAccess = dwDesiredAccess;
6408 pHandle->u.pTempFile = pTempFile;
6409 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6410 {
6411 pTempFile->cActiveHandles++;
6412 kHlpAssert(pTempFile->cActiveHandles >= 1);
6413 kHlpAssert(pTempFile->cActiveHandles <= 2);
6414 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6415 return hFile;
6416 }
6417
6418 kHlpFree(pHandle);
6419 }
6420 else
6421 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6422 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6423 }
6424 else
6425 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6426 return INVALID_HANDLE_VALUE;
6427}
6428
6429
6430static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6431 KBOOL *pfFallback)
6432{
6433 HANDLE hFile;
6434 DWORD dwErr;
6435
6436 /*
6437 * Check if we've got an existing temp file.
6438 * ASSUME exact same path for now.
6439 */
6440 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6441 PKWFSTEMPFILE pTempFile;
6442 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6443 {
6444 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6445 if ( pTempFile->cwcPath == cwcFilename
6446 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6447 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6448 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6449 break;
6450 }
6451
6452 /*
6453 * Create a new temporary file instance if not found.
6454 */
6455 *pfFallback = K_FALSE;
6456 if (pTempFile == NULL)
6457 {
6458 KSIZE cbFilename;
6459
6460 switch (dwCreationDisposition)
6461 {
6462 case CREATE_ALWAYS:
6463 case OPEN_ALWAYS:
6464 case CREATE_NEW:
6465 dwErr = NO_ERROR;
6466 break;
6467
6468 case OPEN_EXISTING:
6469 case TRUNCATE_EXISTING:
6470 *pfFallback = K_TRUE;
6471 kHlpAssertFailed();
6472 SetLastError(ERROR_FILE_NOT_FOUND);
6473 return INVALID_HANDLE_VALUE;
6474
6475 default:
6476 kHlpAssertFailed();
6477 SetLastError(ERROR_INVALID_PARAMETER);
6478 return INVALID_HANDLE_VALUE;
6479 }
6480
6481 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6482 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6483 if (pTempFile)
6484 {
6485 pTempFile->cwcPath = (KU16)cwcFilename;
6486 pTempFile->cbFile = 0;
6487 pTempFile->cbFileAllocated = 0;
6488 pTempFile->cActiveHandles = 0;
6489 pTempFile->cMappings = 0;
6490 pTempFile->cSegs = 0;
6491 pTempFile->paSegs = NULL;
6492 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6493
6494 pTempFile->pNext = g_Sandbox.pTempFileHead;
6495 g_Sandbox.pTempFileHead = pTempFile;
6496 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6497 }
6498 else
6499 {
6500 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6501 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6502 return INVALID_HANDLE_VALUE;
6503 }
6504 }
6505 else
6506 {
6507 switch (dwCreationDisposition)
6508 {
6509 case OPEN_EXISTING:
6510 dwErr = NO_ERROR;
6511 break;
6512 case OPEN_ALWAYS:
6513 dwErr = ERROR_ALREADY_EXISTS;
6514 break;
6515
6516 case TRUNCATE_EXISTING:
6517 case CREATE_ALWAYS:
6518 kHlpAssertFailed();
6519 pTempFile->cbFile = 0;
6520 dwErr = ERROR_ALREADY_EXISTS;
6521 break;
6522
6523 case CREATE_NEW:
6524 kHlpAssertFailed();
6525 SetLastError(ERROR_FILE_EXISTS);
6526 return INVALID_HANDLE_VALUE;
6527
6528 default:
6529 kHlpAssertFailed();
6530 SetLastError(ERROR_INVALID_PARAMETER);
6531 return INVALID_HANDLE_VALUE;
6532 }
6533 }
6534
6535 /*
6536 * Create a handle to the temporary file.
6537 */
6538 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6539 if (hFile != INVALID_HANDLE_VALUE)
6540 SetLastError(dwErr);
6541 return hFile;
6542}
6543
6544#endif /* WITH_TEMP_MEMORY_FILES */
6545
6546/**
6547 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6548 *
6549 * @returns K_TRUE if cacheable, K_FALSE if not.
6550 * @param wcFirst The first extension character.
6551 * @param wcSecond The second extension character.
6552 * @param wcThird The third extension character.
6553 * @param fAttrQuery Set if it's for an attribute query, clear if for
6554 * file creation.
6555 */
6556static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6557{
6558 /* C++ header without an extension or a directory. */
6559 if (wcFirst == '\0')
6560 {
6561 /** @todo exclude temporary files... */
6562 return K_TRUE;
6563 }
6564
6565 /* C Header: .h */
6566 if (wcFirst == 'h' || wcFirst == 'H')
6567 {
6568 if (wcSecond == '\0')
6569 return K_TRUE;
6570
6571 /* C++ Header: .hpp, .hxx */
6572 if ( (wcSecond == 'p' || wcSecond == 'P')
6573 && (wcThird == 'p' || wcThird == 'P'))
6574 return K_TRUE;
6575 if ( (wcSecond == 'x' || wcSecond == 'X')
6576 && (wcThird == 'x' || wcThird == 'X'))
6577 return K_TRUE;
6578 }
6579 /* Misc starting with i. */
6580 else if (wcFirst == 'i' || wcFirst == 'I')
6581 {
6582 if (wcSecond != '\0')
6583 {
6584 if (wcSecond == 'n' || wcSecond == 'N')
6585 {
6586 /* C++ inline header: .inl */
6587 if (wcThird == 'l' || wcThird == 'L')
6588 return K_TRUE;
6589
6590 /* Assembly include file: .inc */
6591 if (wcThird == 'c' || wcThird == 'C')
6592 return K_TRUE;
6593 }
6594 }
6595 }
6596 /* Assembly header: .mac */
6597 else if (wcFirst == 'm' || wcFirst == 'M')
6598 {
6599 if (wcSecond == 'a' || wcSecond == 'A')
6600 {
6601 if (wcThird == 'c' || wcThird == 'C')
6602 return K_TRUE;
6603 }
6604 }
6605#ifdef WITH_PCH_CACHING
6606 /* Precompiled header: .pch */
6607 else if (wcFirst == 'p' || wcFirst == 'P')
6608 {
6609 if (wcSecond == 'c' || wcSecond == 'C')
6610 {
6611 if (wcThird == 'h' || wcThird == 'H')
6612 return !g_Sandbox.fNoPchCaching;
6613 }
6614 }
6615#endif
6616#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6617 /* Linker - Object file: .obj */
6618 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6619 {
6620 if (wcSecond == 'b' || wcSecond == 'B')
6621 {
6622 if (wcThird == 'j' || wcThird == 'J')
6623 return K_TRUE;
6624 }
6625 }
6626#endif
6627 else if (fAttrQuery)
6628 {
6629 /* Dynamic link library: .dll */
6630 if (wcFirst == 'd' || wcFirst == 'D')
6631 {
6632 if (wcSecond == 'l' || wcSecond == 'L')
6633 {
6634 if (wcThird == 'l' || wcThird == 'L')
6635 return K_TRUE;
6636 }
6637 }
6638 /* Executable file: .exe */
6639 else if (wcFirst == 'e' || wcFirst == 'E')
6640 {
6641 if (wcSecond == 'x' || wcSecond == 'X')
6642 {
6643 if (wcThird == 'e' || wcThird == 'E')
6644 return K_TRUE;
6645 }
6646 }
6647 /* Response file: .rsp */
6648 else if (wcFirst == 'r' || wcFirst == 'R')
6649 {
6650 if (wcSecond == 's' || wcSecond == 'S')
6651 {
6652 if (wcThird == 'p' || wcThird == 'P')
6653 return !g_Sandbox.fNoPchCaching;
6654 }
6655 }
6656 /* Linker: */
6657 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6658 {
6659 /* Object file: .obj */
6660 if (wcFirst == 'o' || wcFirst == 'O')
6661 {
6662 if (wcSecond == 'b' || wcSecond == 'B')
6663 {
6664 if (wcThird == 'j' || wcThird == 'J')
6665 return K_TRUE;
6666 }
6667 }
6668 /* Library file: .lib */
6669 else if (wcFirst == 'l' || wcFirst == 'L')
6670 {
6671 if (wcSecond == 'i' || wcSecond == 'I')
6672 {
6673 if (wcThird == 'b' || wcThird == 'B')
6674 return K_TRUE;
6675 }
6676 }
6677 /* Linker definition file: .def */
6678 else if (wcFirst == 'd' || wcFirst == 'D')
6679 {
6680 if (wcSecond == 'e' || wcSecond == 'E')
6681 {
6682 if (wcThird == 'f' || wcThird == 'F')
6683 return K_TRUE;
6684 }
6685 }
6686 }
6687 }
6688
6689 return K_FALSE;
6690}
6691
6692
6693/**
6694 * Checks if the file extension indicates that the file/dir is something we
6695 * ought to cache.
6696 *
6697 * @returns K_TRUE if cachable, K_FALSE if not.
6698 * @param pszExt The kHlpGetExt result.
6699 * @param fAttrQuery Set if it's for an attribute query, clear if for
6700 * file creation.
6701 */
6702static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6703{
6704 wchar_t const wcFirst = *pszExt;
6705 if (wcFirst)
6706 {
6707 wchar_t const wcSecond = pszExt[1];
6708 if (wcSecond)
6709 {
6710 wchar_t const wcThird = pszExt[2];
6711 if (pszExt[3] == '\0')
6712 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6713 return K_FALSE;
6714 }
6715 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6716 }
6717 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6718}
6719
6720
6721/**
6722 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6723 * should be cached.
6724 *
6725 * @returns K_TRUE if cachable, K_FALSE if not.
6726 * @param pwszPath The UTF-16 path to examine.
6727 * @param fAttrQuery Set if it's for an attribute query, clear if for
6728 * file creation.
6729 */
6730static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6731{
6732 KSIZE cwcExt;
6733 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6734 switch (cwcExt)
6735 {
6736 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6737 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6738 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6739 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6740 }
6741 return K_FALSE;
6742}
6743
6744
6745
6746/**
6747 * Creates a new
6748 *
6749 * @returns
6750 * @param pFsObj .
6751 * @param pwszFilename .
6752 */
6753static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6754{
6755 HANDLE hFile;
6756 MY_IO_STATUS_BLOCK Ios;
6757 MY_OBJECT_ATTRIBUTES ObjAttr;
6758 MY_UNICODE_STRING UniStr;
6759 MY_NTSTATUS rcNt;
6760 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6761
6762 /*
6763 * Open the file relative to the parent directory.
6764 */
6765 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6766 kHlpAssert(pFsObj->pParent);
6767 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6768
6769 Ios.Information = ~(ULONG_PTR)0;
6770 Ios.u.Status = -1;
6771
6772 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6773 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6774 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6775
6776 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6777
6778 rcNt = g_pfnNtCreateFile(&hFile,
6779 GENERIC_READ | SYNCHRONIZE,
6780 &ObjAttr,
6781 &Ios,
6782 NULL, /*cbFileInitialAlloc */
6783 FILE_ATTRIBUTE_NORMAL,
6784 FILE_SHARE_READ,
6785 FILE_OPEN,
6786 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6787 NULL, /*pEaBuffer*/
6788 0); /*cbEaBuffer*/
6789 if (MY_NT_SUCCESS(rcNt))
6790 {
6791 /*
6792 * Read the whole file into memory.
6793 */
6794 LARGE_INTEGER cbFile;
6795 if (GetFileSizeEx(hFile, &cbFile))
6796 {
6797 if ( cbFile.QuadPart >= 0
6798#ifdef WITH_PCH_CACHING
6799 && ( cbFile.QuadPart < 16*1024*1024
6800 || ( cbFile.QuadPart < 96*1024*1024
6801 && pFsObj->cchName > 4
6802 && !g_Sandbox.fNoPchCaching
6803 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
6804#endif
6805 )
6806 {
6807 KU32 cbCache = (KU32)cbFile.QuadPart;
6808 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
6809 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
6810 if (hMapping != NULL)
6811 {
6812 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
6813 if (pbCache)
6814 {
6815 /*
6816 * Create the cached file object.
6817 */
6818 PKFSWCACHEDFILE pCachedFile;
6819 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
6820 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
6821 sizeof(*pCachedFile) + cbPath);
6822 if (pCachedFile)
6823 {
6824 pCachedFile->hCached = hFile;
6825 pCachedFile->hSection = hMapping;
6826 pCachedFile->cbCached = cbCache;
6827 pCachedFile->pbCached = pbCache;
6828 pCachedFile->pFsObj = pFsObj;
6829 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
6830 kFsCacheObjRetain(pFsObj);
6831
6832 g_cReadCachedFiles++;
6833 g_cbReadCachedFiles += cbCache;
6834
6835 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
6836 return pCachedFile;
6837 }
6838
6839 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
6840 }
6841 else
6842 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
6843 CloseHandle(hMapping);
6844 }
6845 else
6846 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
6847 }
6848 else
6849 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
6850 }
6851 else
6852 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
6853 g_pfnNtClose(hFile);
6854 }
6855 else
6856 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
6857 return NULL;
6858}
6859
6860
6861/**
6862 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
6863 */
6864static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
6865 KBOOL fIsFileHandle, HANDLE *phFile)
6866{
6867 HANDLE hProcSelf = GetCurrentProcess();
6868 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
6869 hProcSelf, phFile,
6870 dwDesiredAccess, fInheritHandle,
6871 0 /*dwOptions*/))
6872 {
6873 /*
6874 * Create handle table entry for the duplicate handle.
6875 */
6876 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6877 if (pHandle)
6878 {
6879 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
6880 pHandle->cRefs = 1;
6881 pHandle->offFile = 0;
6882 pHandle->hHandle = *phFile;
6883 pHandle->dwDesiredAccess = dwDesiredAccess;
6884 pHandle->u.pCachedFile = pCachedFile;
6885 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
6886 return K_TRUE;
6887
6888 kHlpFree(pHandle);
6889 }
6890 else
6891 KWFS_LOG(("Out of memory for handle!\n"));
6892
6893 CloseHandle(*phFile);
6894 *phFile = INVALID_HANDLE_VALUE;
6895 }
6896 else
6897 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
6898 return K_FALSE;
6899}
6900
6901
6902/**
6903 * Kernel32 - Common code for CreateFileW and CreateFileA.
6904 */
6905static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
6906{
6907 *phFile = INVALID_HANDLE_VALUE;
6908
6909 /*
6910 * At the moment we only handle existing files.
6911 */
6912 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
6913 {
6914 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
6915 kHlpAssert(pFsObj->fHaveStats);
6916 if ( pCachedFile != NULL
6917 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
6918 {
6919 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
6920 return K_TRUE;
6921 }
6922 }
6923 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
6924
6925 /* Do fallback, please. */
6926 return K_FALSE;
6927}
6928
6929
6930/** Kernel32 - CreateFileA */
6931static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6932 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6933 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6934{
6935 HANDLE hFile;
6936
6937 /*
6938 * Check for include files and similar that we do read-only caching of.
6939 */
6940 if (dwCreationDisposition == FILE_OPEN_IF)
6941 {
6942 if ( dwDesiredAccess == GENERIC_READ
6943 || dwDesiredAccess == FILE_GENERIC_READ)
6944 {
6945 if (dwShareMode & FILE_SHARE_READ)
6946 {
6947 if ( !pSecAttrs
6948 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6949 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6950 {
6951 const char *pszExt = kHlpGetExt(pszFilename);
6952 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
6953 {
6954 KFSLOOKUPERROR enmError;
6955 PKFSOBJ pFsObj;
6956 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6957
6958 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
6959 if (pFsObj)
6960 {
6961 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6962 &hFile);
6963 kFsCacheObjRelease(g_pFsCache, pFsObj);
6964 if (fRc)
6965 {
6966 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6967 return hFile;
6968 }
6969 }
6970 /* These are for nasm and yasm header searching. Cache will already
6971 have checked the directories for the file, no need to call
6972 CreateFile to do it again. */
6973 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6974 {
6975 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6976 return INVALID_HANDLE_VALUE;
6977 }
6978 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6979 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6980 {
6981 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6982 return INVALID_HANDLE_VALUE;
6983 }
6984
6985 /* fallback */
6986 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6987 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6988 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
6989 return hFile;
6990 }
6991 }
6992 }
6993 }
6994 }
6995
6996 /*
6997 * Okay, normal.
6998 */
6999 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7000 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7001 if (hFile != INVALID_HANDLE_VALUE)
7002 {
7003 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
7004 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
7005 }
7006 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7007 return hFile;
7008}
7009
7010
7011/** Kernel32 - CreateFileW */
7012static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7013 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7014 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7015{
7016 HANDLE hFile;
7017
7018#ifdef WITH_TEMP_MEMORY_FILES
7019 /*
7020 * Check for temporary files (cl.exe only).
7021 */
7022 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7023 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7024 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7025 && kwFsIsClTempFileW(pwszFilename))
7026 {
7027 KBOOL fFallback = K_FALSE;
7028 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7029 if (!fFallback)
7030 {
7031 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7032 return hFile;
7033 }
7034 }
7035#endif
7036
7037 /*
7038 * Check for include files and similar that we do read-only caching of.
7039 */
7040 if (dwCreationDisposition == FILE_OPEN_IF)
7041 {
7042 if ( dwDesiredAccess == GENERIC_READ
7043 || dwDesiredAccess == FILE_GENERIC_READ)
7044 {
7045 if (dwShareMode & FILE_SHARE_READ)
7046 {
7047 if ( !pSecAttrs
7048 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7049 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7050 {
7051 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7052 {
7053 KFSLOOKUPERROR enmError;
7054 PKFSOBJ pFsObj;
7055 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7056
7057 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
7058 if (pFsObj)
7059 {
7060 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
7061 &hFile);
7062 kFsCacheObjRelease(g_pFsCache, pFsObj);
7063 if (fRc)
7064 {
7065 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7066 return hFile;
7067 }
7068 }
7069 /* These are for nasm and yasm style header searching. Cache will
7070 already have checked the directories for the file, no need to call
7071 CreateFile to do it again. */
7072 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7073 {
7074 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7075 return INVALID_HANDLE_VALUE;
7076 }
7077 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7078 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7079 {
7080 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7081 return INVALID_HANDLE_VALUE;
7082 }
7083
7084 /* fallback */
7085 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7086 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7087 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7088 return hFile;
7089 }
7090 }
7091 else
7092 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7093 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7094 }
7095 else
7096 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7097 }
7098 else
7099 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7100 }
7101 else
7102 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7103
7104 /*
7105 * Okay, normal.
7106 */
7107 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7108 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7109 if (hFile != INVALID_HANDLE_VALUE)
7110 {
7111 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
7112 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
7113 }
7114 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7115 return hFile;
7116}
7117
7118
7119/** Kernel32 - SetFilePointer */
7120static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7121{
7122 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7123 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7124 if (idxHandle < g_Sandbox.cHandles)
7125 {
7126 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7127 if (pHandle != NULL)
7128 {
7129 KU32 cbFile;
7130 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7131 switch (pHandle->enmType)
7132 {
7133 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7134 cbFile = pHandle->u.pCachedFile->cbCached;
7135 break;
7136#ifdef WITH_TEMP_MEMORY_FILES
7137 case KWHANDLETYPE_TEMP_FILE:
7138 cbFile = pHandle->u.pTempFile->cbFile;
7139 break;
7140#endif
7141 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7142 case KWHANDLETYPE_OUTPUT_BUF:
7143 default:
7144 kHlpAssertFailed();
7145 SetLastError(ERROR_INVALID_FUNCTION);
7146 return INVALID_SET_FILE_POINTER;
7147 }
7148
7149 switch (dwMoveMethod)
7150 {
7151 case FILE_BEGIN:
7152 break;
7153 case FILE_CURRENT:
7154 offMove += pHandle->offFile;
7155 break;
7156 case FILE_END:
7157 offMove += cbFile;
7158 break;
7159 default:
7160 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7161 SetLastError(ERROR_INVALID_PARAMETER);
7162 return INVALID_SET_FILE_POINTER;
7163 }
7164 if (offMove >= 0)
7165 {
7166 if (offMove >= (KSSIZE)cbFile)
7167 {
7168 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7169 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7170 offMove = (KSSIZE)cbFile;
7171 /* For writable files, seeking beyond the end is fine, but check that we've got
7172 the type range for the request. */
7173 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7174 {
7175 kHlpAssertMsgFailed(("%#llx\n", offMove));
7176 SetLastError(ERROR_SEEK);
7177 return INVALID_SET_FILE_POINTER;
7178 }
7179 }
7180 pHandle->offFile = (KU32)offMove;
7181 }
7182 else
7183 {
7184 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7185 SetLastError(ERROR_NEGATIVE_SEEK);
7186 return INVALID_SET_FILE_POINTER;
7187 }
7188 if (pcbMoveHi)
7189 *pcbMoveHi = (KU64)offMove >> 32;
7190 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7191 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7192 SetLastError(NO_ERROR);
7193 return (KU32)offMove;
7194 }
7195 }
7196 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7197 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7198}
7199
7200
7201/** Kernel32 - SetFilePointerEx */
7202static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7203 DWORD dwMoveMethod)
7204{
7205 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7206 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7207 if (idxHandle < g_Sandbox.cHandles)
7208 {
7209 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7210 if (pHandle != NULL)
7211 {
7212 KI64 offMyMove = offMove.QuadPart;
7213 KU32 cbFile;
7214 switch (pHandle->enmType)
7215 {
7216 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7217 cbFile = pHandle->u.pCachedFile->cbCached;
7218 break;
7219#ifdef WITH_TEMP_MEMORY_FILES
7220 case KWHANDLETYPE_TEMP_FILE:
7221 cbFile = pHandle->u.pTempFile->cbFile;
7222 break;
7223#endif
7224 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7225 case KWHANDLETYPE_OUTPUT_BUF:
7226 default:
7227 kHlpAssertFailed();
7228 SetLastError(ERROR_INVALID_FUNCTION);
7229 return INVALID_SET_FILE_POINTER;
7230 }
7231
7232 switch (dwMoveMethod)
7233 {
7234 case FILE_BEGIN:
7235 break;
7236 case FILE_CURRENT:
7237 offMyMove += pHandle->offFile;
7238 break;
7239 case FILE_END:
7240 offMyMove += cbFile;
7241 break;
7242 default:
7243 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7244 SetLastError(ERROR_INVALID_PARAMETER);
7245 return INVALID_SET_FILE_POINTER;
7246 }
7247 if (offMyMove >= 0)
7248 {
7249 if (offMyMove >= (KSSIZE)cbFile)
7250 {
7251 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7252 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7253 offMyMove = (KSSIZE)cbFile;
7254 /* For writable files, seeking beyond the end is fine, but check that we've got
7255 the type range for the request. */
7256 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7257 {
7258 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7259 SetLastError(ERROR_SEEK);
7260 return INVALID_SET_FILE_POINTER;
7261 }
7262 }
7263 pHandle->offFile = (KU32)offMyMove;
7264 }
7265 else
7266 {
7267 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7268 SetLastError(ERROR_NEGATIVE_SEEK);
7269 return INVALID_SET_FILE_POINTER;
7270 }
7271 if (poffNew)
7272 poffNew->QuadPart = offMyMove;
7273 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7274 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7275 return TRUE;
7276 }
7277 }
7278 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7279 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7280}
7281
7282
7283/** Kernel32 - ReadFile */
7284static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7285 LPOVERLAPPED pOverlapped)
7286{
7287 BOOL fRet;
7288 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7289 g_cReadFileCalls++;
7290 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7291 if (idxHandle < g_Sandbox.cHandles)
7292 {
7293 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7294 if (pHandle != NULL)
7295 {
7296 switch (pHandle->enmType)
7297 {
7298 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7299 {
7300 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7301 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7302 if (cbActually > cbToRead)
7303 cbActually = cbToRead;
7304
7305#ifdef WITH_HASH_MD5_CACHE
7306 if (g_Sandbox.pHashHead)
7307 {
7308 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7309 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7310 g_Sandbox.LastHashRead.cbRead = cbActually;
7311 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7312 }
7313#endif
7314
7315 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7316 pHandle->offFile += cbActually;
7317
7318 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7319 *pcbActuallyRead = cbActually;
7320
7321 g_cbReadFileFromReadCached += cbActually;
7322 g_cbReadFileTotal += cbActually;
7323 g_cReadFileFromReadCached++;
7324
7325 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7326 return TRUE;
7327 }
7328
7329#ifdef WITH_TEMP_MEMORY_FILES
7330 case KWHANDLETYPE_TEMP_FILE:
7331 {
7332 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7333 KU32 cbActually;
7334 if (pHandle->offFile < pTempFile->cbFile)
7335 {
7336 cbActually = pTempFile->cbFile - pHandle->offFile;
7337 if (cbActually > cbToRead)
7338 cbActually = cbToRead;
7339
7340 /* Copy the data. */
7341 if (cbActually > 0)
7342 {
7343 KU32 cbLeft;
7344 KU32 offSeg;
7345 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7346
7347 /* Locate the segment containing the byte at offFile. */
7348 KU32 iSeg = pTempFile->cSegs - 1;
7349 kHlpAssert(pTempFile->cSegs > 0);
7350 while (paSegs[iSeg].offData > pHandle->offFile)
7351 iSeg--;
7352
7353 /* Copy out the data. */
7354 cbLeft = cbActually;
7355 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7356 for (;;)
7357 {
7358 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7359 if (cbAvail >= cbLeft)
7360 {
7361 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7362 break;
7363 }
7364
7365 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7366 cbLeft -= cbAvail;
7367 offSeg = 0;
7368 iSeg++;
7369 kHlpAssert(iSeg < pTempFile->cSegs);
7370 }
7371
7372 /* Update the file offset. */
7373 pHandle->offFile += cbActually;
7374 }
7375 }
7376 /* Read does not commit file space, so return zero bytes. */
7377 else
7378 cbActually = 0;
7379
7380 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7381 *pcbActuallyRead = cbActually;
7382
7383 g_cbReadFileTotal += cbActually;
7384 g_cbReadFileFromInMemTemp += cbActually;
7385 g_cReadFileFromInMemTemp++;
7386
7387 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7388 return TRUE;
7389 }
7390#endif /* WITH_TEMP_MEMORY_FILES */
7391
7392 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7393 case KWHANDLETYPE_OUTPUT_BUF:
7394 default:
7395 kHlpAssertFailed();
7396 SetLastError(ERROR_INVALID_FUNCTION);
7397 *pcbActuallyRead = 0;
7398 return FALSE;
7399 }
7400 }
7401 }
7402
7403 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7404 if (fRet && pcbActuallyRead)
7405 g_cbReadFileTotal += *pcbActuallyRead;
7406 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7407 return fRet;
7408}
7409
7410
7411/** Kernel32 - ReadFileEx */
7412static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7413 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7414{
7415 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7416 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7417 if (idxHandle < g_Sandbox.cHandles)
7418 {
7419 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7420 if (pHandle != NULL)
7421 {
7422 kHlpAssertFailed();
7423 }
7424 }
7425
7426 KWFS_LOG(("ReadFile(%p)\n", hFile));
7427 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7428}
7429
7430#ifdef WITH_STD_OUT_ERR_BUFFERING
7431
7432/**
7433 * Write something to a handle, making sure everything is actually written.
7434 *
7435 * @param hHandle Where to write it to.
7436 * @param pchBuf What to write
7437 * @param cchToWrite How much to write.
7438 */
7439static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7440{
7441 if (cchToWrite > 0)
7442 {
7443 DWORD cchWritten = 0;
7444 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7445 {
7446 if (cchWritten == cchToWrite)
7447 { /* likely */ }
7448 else
7449 {
7450 do
7451 {
7452 pchBuf += cchWritten;
7453 cchToWrite -= cchWritten;
7454 cchWritten = 0;
7455 } while ( cchToWrite > 0
7456 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7457 }
7458 }
7459 else
7460 kHlpAssertFailed();
7461 }
7462}
7463
7464
7465/**
7466 * Worker for WriteFile when the output isn't going to the console.
7467 *
7468 * @param pSandbox The sandbox.
7469 * @param pOutBuf The output buffer.
7470 * @param pchBuffer What to write.
7471 * @param cchToWrite How much to write.
7472 */
7473static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7474{
7475 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7476 { /* likely */ }
7477 else
7478 {
7479 /* No realloc, max size is 64KB. */
7480 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7481 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7482 if (!pOutBuf->u.Fully.pchBuf)
7483 {
7484 while ( !pOutBuf->u.Fully.pchBuf
7485 && pOutBuf->u.Fully.cchBufAlloc > 64)
7486 {
7487 pOutBuf->u.Fully.cchBufAlloc /= 2;
7488 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7489 }
7490 if (!pOutBuf->u.Fully.pchBuf)
7491 {
7492 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7493 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7494 }
7495 }
7496 }
7497
7498 /*
7499 * Special case: Output ends with newline and fits in the buffer.
7500 */
7501 if ( cchToWrite > 1
7502 && pchBuffer[cchToWrite - 1] == '\n'
7503 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7504 {
7505 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7506 pOutBuf->u.Fully.cchBuf += cchToWrite;
7507 }
7508 else
7509 {
7510 /*
7511 * Work thru the text line by line, flushing the buffer when
7512 * appropriate. The buffer is not a line buffer here, it's a
7513 * full buffer.
7514 */
7515 while (cchToWrite > 0)
7516 {
7517 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7518 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7519 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7520 {
7521 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7522 pOutBuf->u.Fully.cchBuf += cchLine;
7523 }
7524 /*
7525 * Option one: Flush the buffer and the current line.
7526 *
7527 * We choose this one when the line won't ever fit, or when we have
7528 * an incomplete line in the buffer.
7529 */
7530 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7531 || pOutBuf->u.Fully.cchBuf == 0
7532 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7533 {
7534 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7535 if (pOutBuf->u.Fully.cchBuf > 0)
7536 {
7537 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7538 pOutBuf->u.Fully.cchBuf = 0;
7539 }
7540 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7541 }
7542 /*
7543 * Option two: Only flush the lines in the buffer.
7544 */
7545 else
7546 {
7547 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7548 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7549 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7550 pOutBuf->u.Fully.cchBuf = cchLine;
7551 }
7552
7553 /* advance */
7554 pchBuffer += cchLine;
7555 cchToWrite -= cchLine;
7556 }
7557 }
7558}
7559
7560#endif /* WITH_STD_OUT_ERR_BUFFERING */
7561
7562#ifdef WITH_TEMP_MEMORY_FILES
7563static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7564{
7565 KU32 cbMinFile = offFile + cbNeeded;
7566 if (cbMinFile >= offFile)
7567 {
7568 /* Calc how much space we've already allocated and */
7569 if (cbMinFile <= pTempFile->cbFileAllocated)
7570 return K_TRUE;
7571
7572 /* Grow the file. */
7573 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7574 {
7575 int rc;
7576 KU32 cSegs = pTempFile->cSegs;
7577 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7578 do
7579 {
7580 /* grow the segment array? */
7581 if ((cSegs % 16) == 0)
7582 {
7583 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7584 if (!pvNew)
7585 return K_FALSE;
7586 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7587 }
7588
7589 /* Use page alloc here to simplify mapping later. */
7590 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7591 if (rc == 0)
7592 { /* likely */ }
7593 else
7594 {
7595 cbNewSeg = 64*1024;
7596 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7597 if (rc != 0)
7598 {
7599 kHlpAssertFailed();
7600 return K_FALSE;
7601 }
7602 }
7603 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7604 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7605 pTempFile->cbFileAllocated += cbNewSeg;
7606 pTempFile->cSegs = ++cSegs;
7607
7608 } while (pTempFile->cbFileAllocated < cbMinFile);
7609
7610 return K_TRUE;
7611 }
7612 }
7613
7614 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7615 return K_FALSE;
7616}
7617#endif /* WITH_TEMP_MEMORY_FILES */
7618
7619
7620#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7621/** Kernel32 - WriteFile */
7622static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7623 LPOVERLAPPED pOverlapped)
7624{
7625 BOOL fRet;
7626 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7627 g_cWriteFileCalls++;
7628 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7629 if (idxHandle < g_Sandbox.cHandles)
7630 {
7631 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7632 if (pHandle != NULL)
7633 {
7634 switch (pHandle->enmType)
7635 {
7636# ifdef WITH_TEMP_MEMORY_FILES
7637 case KWHANDLETYPE_TEMP_FILE:
7638 {
7639 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7640
7641 kHlpAssert(!pOverlapped);
7642 kHlpAssert(pcbActuallyWritten);
7643
7644 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7645 {
7646 KU32 cbLeft;
7647 KU32 offSeg;
7648
7649 /* Locate the segment containing the byte at offFile. */
7650 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7651 KU32 iSeg = pTempFile->cSegs - 1;
7652 kHlpAssert(pTempFile->cSegs > 0);
7653 while (paSegs[iSeg].offData > pHandle->offFile)
7654 iSeg--;
7655
7656 /* Copy in the data. */
7657 cbLeft = cbToWrite;
7658 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7659 for (;;)
7660 {
7661 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7662 if (cbAvail >= cbLeft)
7663 {
7664 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7665 break;
7666 }
7667
7668 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7669 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7670 cbLeft -= cbAvail;
7671 offSeg = 0;
7672 iSeg++;
7673 kHlpAssert(iSeg < pTempFile->cSegs);
7674 }
7675
7676 /* Update the file offset. */
7677 pHandle->offFile += cbToWrite;
7678 if (pHandle->offFile > pTempFile->cbFile)
7679 pTempFile->cbFile = pHandle->offFile;
7680
7681 *pcbActuallyWritten = cbToWrite;
7682
7683 g_cbWriteFileTotal += cbToWrite;
7684 g_cbWriteFileToInMemTemp += cbToWrite;
7685 g_cWriteFileToInMemTemp++;
7686
7687 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7688 return TRUE;
7689 }
7690
7691 kHlpAssertFailed();
7692 *pcbActuallyWritten = 0;
7693 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7694 return FALSE;
7695 }
7696# endif
7697
7698 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7699 kHlpAssertFailed();
7700 SetLastError(ERROR_ACCESS_DENIED);
7701 *pcbActuallyWritten = 0;
7702 return FALSE;
7703
7704# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7705 /*
7706 * Standard output & error.
7707 */
7708 case KWHANDLETYPE_OUTPUT_BUF:
7709 {
7710 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7711 if (pOutBuf->fIsConsole)
7712 {
7713 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7714 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7715 }
7716 else
7717 {
7718# ifdef WITH_STD_OUT_ERR_BUFFERING
7719 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7720 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7721 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7722# else
7723 kHlpAssertFailed();
7724# endif
7725 }
7726 if (pcbActuallyWritten)
7727 *pcbActuallyWritten = cbToWrite;
7728 g_cbWriteFileTotal += cbToWrite;
7729 return TRUE;
7730 }
7731# endif
7732
7733 default:
7734 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7735 kHlpAssertFailed();
7736 SetLastError(ERROR_INVALID_FUNCTION);
7737 *pcbActuallyWritten = 0;
7738 return FALSE;
7739 }
7740 }
7741 }
7742
7743 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7744 if (fRet && pcbActuallyWritten)
7745 g_cbWriteFileTotal += *pcbActuallyWritten;
7746 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7747 return fRet;
7748}
7749
7750
7751/** Kernel32 - WriteFileEx */
7752static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7753 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7754{
7755 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7756 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7757 if (idxHandle < g_Sandbox.cHandles)
7758 {
7759 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7760 if (pHandle != NULL)
7761 {
7762 kHlpAssertFailed();
7763 }
7764 }
7765
7766 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
7767 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
7768}
7769
7770#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
7771
7772#ifdef WITH_TEMP_MEMORY_FILES
7773
7774/** Kernel32 - SetEndOfFile; */
7775static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
7776{
7777 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7778 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7779 if (idxHandle < g_Sandbox.cHandles)
7780 {
7781 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7782 if (pHandle != NULL)
7783 {
7784 switch (pHandle->enmType)
7785 {
7786 case KWHANDLETYPE_TEMP_FILE:
7787 {
7788 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7789 if ( pHandle->offFile > pTempFile->cbFile
7790 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
7791 {
7792 kHlpAssertFailed();
7793 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7794 return FALSE;
7795 }
7796
7797 pTempFile->cbFile = pHandle->offFile;
7798 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
7799 return TRUE;
7800 }
7801
7802 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7803 kHlpAssertFailed();
7804 SetLastError(ERROR_ACCESS_DENIED);
7805 return FALSE;
7806
7807 case KWHANDLETYPE_OUTPUT_BUF:
7808 kHlpAssertFailed();
7809 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
7810 return FALSE;
7811
7812 default:
7813 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7814 kHlpAssertFailed();
7815 SetLastError(ERROR_INVALID_FUNCTION);
7816 return FALSE;
7817 }
7818 }
7819 }
7820
7821 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
7822 return SetEndOfFile(hFile);
7823}
7824
7825
7826/** Kernel32 - GetFileType */
7827static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
7828{
7829 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7830 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7831 if (idxHandle < g_Sandbox.cHandles)
7832 {
7833 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7834 if (pHandle != NULL)
7835 {
7836 switch (pHandle->enmType)
7837 {
7838 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7839 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
7840 return FILE_TYPE_DISK;
7841
7842 case KWHANDLETYPE_TEMP_FILE:
7843 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
7844 return FILE_TYPE_DISK;
7845
7846 case KWHANDLETYPE_OUTPUT_BUF:
7847 {
7848 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7849 DWORD fRet;
7850 if (pOutBuf->fFileType != KU8_MAX)
7851 {
7852 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
7853 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
7854 }
7855 else
7856 {
7857 fRet = GetFileType(hFile);
7858 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
7859 }
7860 return fRet;
7861 }
7862
7863 }
7864 }
7865 }
7866
7867 KWFS_LOG(("GetFileType(%p)\n", hFile));
7868 return GetFileType(hFile);
7869}
7870
7871
7872/** Kernel32 - GetFileSize */
7873static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
7874{
7875 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7876 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7877 if (idxHandle < g_Sandbox.cHandles)
7878 {
7879 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7880 if (pHandle != NULL)
7881 {
7882 if (pcbHighDword)
7883 *pcbHighDword = 0;
7884 SetLastError(NO_ERROR);
7885 switch (pHandle->enmType)
7886 {
7887 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7888 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7889 return pHandle->u.pCachedFile->cbCached;
7890
7891 case KWHANDLETYPE_TEMP_FILE:
7892 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7893 return pHandle->u.pTempFile->cbFile;
7894
7895 case KWHANDLETYPE_OUTPUT_BUF:
7896 /* do default */
7897 break;
7898
7899 default:
7900 kHlpAssertFailed();
7901 SetLastError(ERROR_INVALID_FUNCTION);
7902 return INVALID_FILE_SIZE;
7903 }
7904 }
7905 }
7906
7907 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
7908 return GetFileSize(hFile, pcbHighDword);
7909}
7910
7911
7912/** Kernel32 - GetFileSizeEx */
7913static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
7914{
7915 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7916 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7917 if (idxHandle < g_Sandbox.cHandles)
7918 {
7919 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7920 if (pHandle != NULL)
7921 {
7922 switch (pHandle->enmType)
7923 {
7924 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7925 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7926 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
7927 return TRUE;
7928
7929 case KWHANDLETYPE_TEMP_FILE:
7930 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7931 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
7932 return TRUE;
7933
7934 case KWHANDLETYPE_OUTPUT_BUF:
7935 /* do default */
7936 break;
7937
7938 default:
7939 kHlpAssertFailed();
7940 SetLastError(ERROR_INVALID_FUNCTION);
7941 return INVALID_FILE_SIZE;
7942 }
7943 }
7944 }
7945
7946 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
7947 return GetFileSizeEx(hFile, pcbFile);
7948}
7949
7950
7951/** Kernel32 - CreateFileMappingW */
7952static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
7953 DWORD fProtect, DWORD dwMaximumSizeHigh,
7954 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
7955{
7956 HANDLE hMapping;
7957 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7958 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7959 if (idxHandle < g_Sandbox.cHandles)
7960 {
7961 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7962 if (pHandle != NULL)
7963 {
7964 switch (pHandle->enmType)
7965 {
7966 case KWHANDLETYPE_TEMP_FILE:
7967 {
7968 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7969 if ( ( fProtect == PAGE_READONLY
7970 || fProtect == PAGE_EXECUTE_READ)
7971 && dwMaximumSizeHigh == 0
7972 && ( dwMaximumSizeLow == 0
7973 || dwMaximumSizeLow == pTempFile->cbFile)
7974 && pwszName == NULL)
7975 {
7976 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
7977 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
7978 return hMapping;
7979 }
7980 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
7981 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7982 SetLastError(ERROR_ACCESS_DENIED);
7983 return INVALID_HANDLE_VALUE;
7984 }
7985
7986 /* moc.exe benefits from this. */
7987 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7988 {
7989 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7990 if ( ( fProtect == PAGE_READONLY
7991 || fProtect == PAGE_EXECUTE_READ)
7992 && dwMaximumSizeHigh == 0
7993 && ( dwMaximumSizeLow == 0
7994 || dwMaximumSizeLow == pCachedFile->cbCached)
7995 && pwszName == NULL)
7996 {
7997 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
7998 K_FALSE /*fIsFileHandle*/, &hMapping))
7999 { /* likely */ }
8000 else
8001 hMapping = NULL;
8002 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8003 return hMapping;
8004 }
8005
8006 /* Do fallback (for .pch). */
8007 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8008 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8009 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8010
8011 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8012 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8013 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8014 return hMapping;
8015 }
8016
8017 /** @todo read cached memory mapped files too for moc. */
8018 }
8019 }
8020 }
8021
8022 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8023 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8024 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8025 return hMapping;
8026}
8027
8028
8029/** Kernel32 - MapViewOfFile */
8030static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8031 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8032{
8033 PVOID pvRet;
8034 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
8035 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8036 if (idxHandle < g_Sandbox.cHandles)
8037 {
8038 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8039 if (pHandle != NULL)
8040 {
8041 KU32 idxMapping;
8042
8043 /*
8044 * Ensure one free entry in the mapping tracking table first,
8045 * since this is common to both temporary and cached files.
8046 */
8047 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8048 { /* likely */ }
8049 else
8050 {
8051 void *pvNew;
8052 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8053 if (cNew)
8054 cNew *= 2;
8055 else
8056 cNew = 32;
8057 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8058 if (pvNew)
8059 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8060 else
8061 {
8062 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8063 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8064 return NULL;
8065 }
8066 g_Sandbox.cMemMappingsAlloc = cNew;
8067 }
8068
8069 /*
8070 * Type specific work.
8071 */
8072 switch (pHandle->enmType)
8073 {
8074 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8075 case KWHANDLETYPE_TEMP_FILE:
8076 case KWHANDLETYPE_OUTPUT_BUF:
8077 default:
8078 kHlpAssertFailed();
8079 SetLastError(ERROR_INVALID_OPERATION);
8080 return NULL;
8081
8082 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8083 {
8084 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8085 if ( dwDesiredAccess == FILE_MAP_READ
8086 && offFileHigh == 0
8087 && offFileLow == 0
8088 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8089 {
8090 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8091 if (pTempFile->cSegs != 1)
8092 {
8093 KU32 iSeg;
8094 KU32 cbLeft;
8095 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8096 KU8 *pbAll = NULL;
8097 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8098 if (rc != 0)
8099 {
8100 kHlpAssertFailed();
8101 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8102 return NULL;
8103 }
8104
8105 cbLeft = pTempFile->cbFile;
8106 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8107 {
8108 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8109 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8110 cbLeft -= cbToCopy;
8111 }
8112
8113 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8114 {
8115 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8116 pTempFile->paSegs[iSeg].pbData = NULL;
8117 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8118 }
8119
8120 pTempFile->cSegs = 1;
8121 pTempFile->cbFileAllocated = cbAll;
8122 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8123 pTempFile->paSegs[0].pbData = pbAll;
8124 pTempFile->paSegs[0].offData = 0;
8125 }
8126
8127 pTempFile->cMappings++;
8128 kHlpAssert(pTempFile->cMappings == 1);
8129
8130 pvRet = pTempFile->paSegs[0].pbData;
8131 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8132 break;
8133 }
8134
8135 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8136 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8137 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8138 return NULL;
8139 }
8140
8141 /*
8142 * This is simple in comparison to the above temporary file code.
8143 */
8144 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8145 {
8146 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8147 if ( dwDesiredAccess == FILE_MAP_READ
8148 && offFileHigh == 0
8149 && offFileLow == 0
8150 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8151 {
8152 pvRet = pCachedFile->pbCached;
8153 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8154 break;
8155 }
8156 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8157 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8158 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8159 return NULL;
8160 }
8161 }
8162
8163 /*
8164 * Insert into the mapping tracking table. This is common
8165 * and we should only get here with a non-NULL pvRet.
8166 *
8167 * Note! We could look for duplicates and do ref counting, but it's
8168 * easier to just append for now.
8169 */
8170 kHlpAssert(pvRet != NULL);
8171 idxMapping = g_Sandbox.cMemMappings;
8172 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8173
8174 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8175 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8176 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8177 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8178 g_Sandbox.cMemMappings++;
8179
8180 return pvRet;
8181 }
8182 }
8183
8184 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8185 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8186 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8187 return pvRet;
8188}
8189
8190
8191/** Kernel32 - MapViewOfFileEx */
8192static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8193 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8194{
8195 PVOID pvRet;
8196 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
8197 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8198 if (idxHandle < g_Sandbox.cHandles)
8199 {
8200 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8201 if (pHandle != NULL)
8202 {
8203 switch (pHandle->enmType)
8204 {
8205 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8206 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8207 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8208 if (!pvMapAddr)
8209 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8210 kHlpAssertFailed();
8211 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8212 return NULL;
8213
8214 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8215 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8216 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8217 if (!pvMapAddr)
8218 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8219 /* We can use fallback here as the handle is an actual section handle. */
8220 break;
8221
8222 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8223 case KWHANDLETYPE_TEMP_FILE:
8224 case KWHANDLETYPE_OUTPUT_BUF:
8225 default:
8226 kHlpAssertFailed();
8227 SetLastError(ERROR_INVALID_OPERATION);
8228 return NULL;
8229 }
8230 }
8231 }
8232
8233 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8234 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8235 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8236 return pvRet;
8237
8238}
8239
8240/** Kernel32 - UnmapViewOfFile */
8241static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8242{
8243 /*
8244 * Consult the memory mapping tracker.
8245 */
8246 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8247 KU32 idxMapping = g_Sandbox.cMemMappings;
8248 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8249 while (idxMapping-- > 0)
8250 if (paMemMappings[idxMapping].pvMapping == pvBase)
8251 {
8252 /* Type specific stuff. */
8253 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8254 {
8255 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8256 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8257 }
8258 else
8259 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8260
8261 /* Deref and probably free it. */
8262 if (--paMemMappings[idxMapping].cRefs == 0)
8263 {
8264 g_Sandbox.cMemMappings--;
8265 if (idxMapping != g_Sandbox.cMemMappings)
8266 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8267 }
8268 return TRUE;
8269 }
8270
8271 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8272 return UnmapViewOfFile(pvBase);
8273}
8274
8275/** @todo UnmapViewOfFileEx */
8276
8277#endif /* WITH_TEMP_MEMORY_FILES */
8278
8279
8280/** Kernel32 - DuplicateHandle */
8281static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8282 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8283{
8284 BOOL fRet;
8285
8286 /*
8287 * We must catch our handles being duplicated.
8288 */
8289 if (hSrcProc == GetCurrentProcess())
8290 {
8291 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
8292 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8293 if (idxHandle < g_Sandbox.cHandles)
8294 {
8295 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8296 if (pHandle)
8297 {
8298 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8299 if (fRet)
8300 {
8301 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8302 {
8303 pHandle->cRefs++;
8304 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8305 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8306 pHandle->enmType, pHandle->cRefs));
8307 }
8308 else
8309 {
8310 fRet = FALSE;
8311 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8312 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8313 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8314 }
8315 }
8316 else
8317 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8318 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8319 return fRet;
8320 }
8321 }
8322 }
8323
8324 /*
8325 * Not one of ours, just do what the caller asks and log it.
8326 */
8327 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8328 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8329 fInheritHandle, dwOptions, fRet, *phNew));
8330 return fRet;
8331}
8332
8333
8334/** Kernel32 - CloseHandle */
8335static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8336{
8337 BOOL fRet;
8338 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8339 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
8340 if (idxHandle < g_Sandbox.cHandles)
8341 {
8342 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8343 if (pHandle)
8344 {
8345 /* Prevent the closing of the standard output and error handles. */
8346 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8347 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
8348 {
8349 fRet = CloseHandle(hObject);
8350 if (fRet)
8351 {
8352 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8353 g_Sandbox.papHandles[idxHandle] = NULL;
8354 g_Sandbox.cActiveHandles--;
8355 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8356 if (--pHandle->cRefs == 0)
8357 {
8358#ifdef WITH_TEMP_MEMORY_FILES
8359 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8360 {
8361 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8362 pHandle->u.pTempFile->cActiveHandles--;
8363 }
8364#endif
8365 kHlpFree(pHandle);
8366 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8367 }
8368 else
8369 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8370 }
8371 else
8372 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8373 }
8374 else
8375 {
8376 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8377 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8378 fRet = TRUE;
8379 }
8380 return fRet;
8381 }
8382 }
8383
8384 fRet = CloseHandle(hObject);
8385 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8386 return fRet;
8387}
8388
8389
8390/** Kernel32 - GetFileAttributesA. */
8391static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8392{
8393 DWORD fRet;
8394 const char *pszExt = kHlpGetExt(pszFilename);
8395 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8396 {
8397 KFSLOOKUPERROR enmError;
8398 PKFSOBJ pFsObj;
8399 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8400
8401 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8402 if (pFsObj)
8403 {
8404 kHlpAssert(pFsObj->fHaveStats);
8405 fRet = pFsObj->Stats.st_attribs;
8406 kFsCacheObjRelease(g_pFsCache, pFsObj);
8407 }
8408 else
8409 {
8410 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8411 fRet = INVALID_FILE_ATTRIBUTES;
8412 }
8413
8414 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8415 return fRet;
8416 }
8417
8418 fRet = GetFileAttributesA(pszFilename);
8419 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8420 return fRet;
8421}
8422
8423
8424/** Kernel32 - GetFileAttributesW. */
8425static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8426{
8427 DWORD fRet;
8428 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8429 {
8430 KFSLOOKUPERROR enmError;
8431 PKFSOBJ pFsObj;
8432 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8433
8434 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8435 if (pFsObj)
8436 {
8437 kHlpAssert(pFsObj->fHaveStats);
8438 fRet = pFsObj->Stats.st_attribs;
8439 kFsCacheObjRelease(g_pFsCache, pFsObj);
8440 }
8441 else
8442 {
8443 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8444 fRet = INVALID_FILE_ATTRIBUTES;
8445 }
8446#ifndef NDEBUG
8447 {
8448 DWORD fCheck = GetFileAttributesW(pwszFilename);
8449 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8450 }
8451#endif
8452 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8453 return fRet;
8454 }
8455
8456 fRet = GetFileAttributesW(pwszFilename);
8457 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8458 return fRet;
8459}
8460
8461
8462/** Kernel32 - GetFileAttributesExA. */
8463static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8464 WIN32_FILE_ATTRIBUTE_DATA *pData)
8465{
8466 BOOL fRet;
8467 const char *pszExt = kHlpGetExt(pszFilename);
8468 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8469 {
8470 KFSLOOKUPERROR enmError;
8471 PKFSOBJ pFsObj;
8472 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8473
8474 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8475 if (pFsObj)
8476 {
8477 kHlpAssert(pFsObj->fHaveStats);
8478 if (enmLevel == GetFileExInfoStandard)
8479 {
8480 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8481 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8482 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8483 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8484 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8485 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8486 kFsCacheObjRelease(g_pFsCache, pFsObj);
8487 fRet = TRUE;
8488 }
8489 else
8490 {
8491 kFsCacheObjRelease(g_pFsCache, pFsObj);
8492 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8493 }
8494 }
8495 else
8496 {
8497 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8498 fRet = FALSE;
8499 }
8500
8501#ifdef K_STRICT
8502 {
8503 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8504 DWORD const dwErrSaved = GetLastError();
8505 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8506 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8507 if (fRetCheck && fRet)
8508 {
8509# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8510 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8511 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8512 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8513 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8514 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8515 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8516 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8517 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8518 }
8519 else
8520 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8521 SetLastError(dwErrSaved);
8522 }
8523#endif
8524 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8525 return fRet;
8526 }
8527
8528 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8529 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8530 return fRet;
8531}
8532
8533
8534/** Kernel32 - GetFileAttributesExW. */
8535static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8536 WIN32_FILE_ATTRIBUTE_DATA *pData)
8537{
8538 BOOL fRet;
8539 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8540 {
8541 KFSLOOKUPERROR enmError;
8542 PKFSOBJ pFsObj;
8543 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8544
8545 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8546 if (pFsObj)
8547 {
8548 kHlpAssert(pFsObj->fHaveStats);
8549 if (enmLevel == GetFileExInfoStandard)
8550 {
8551 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8552 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8553 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8554 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8555 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8556 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8557 kFsCacheObjRelease(g_pFsCache, pFsObj);
8558 fRet = TRUE;
8559 }
8560 else
8561 {
8562 kFsCacheObjRelease(g_pFsCache, pFsObj);
8563 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8564 }
8565 }
8566 else
8567 {
8568 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8569 fRet = FALSE;
8570 }
8571
8572#ifdef K_STRICT
8573 {
8574 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8575 DWORD const dwErrSaved = GetLastError();
8576 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8577 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8578 if (fRetCheck && fRet)
8579 {
8580# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8581 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8582 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8583 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8584 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8585 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8586 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8587 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8588 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8589 }
8590 else
8591 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8592 SetLastError(dwErrSaved);
8593 }
8594#endif
8595 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8596 return fRet;
8597 }
8598
8599 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8600 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8601 return fRet;
8602}
8603
8604
8605/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8606 * directory containing each include file. We cache the result to speed
8607 * things up a little. */
8608static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8609{
8610 DWORD cwcRet;
8611 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8612 {
8613 KFSLOOKUPERROR enmError;
8614 PKFSOBJ pObj;
8615 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8616
8617 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8618 if (pObj)
8619 {
8620 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8621 {
8622 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8623 {
8624 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8625
8626 /* Should preserve trailing slash on directory paths. */
8627 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8628 {
8629 if ( cwcRet + 1 < cwcShortPath
8630 && pwszShortPath[cwcRet - 1] != '\\')
8631 {
8632 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8633 if ( cwcIn > 0
8634 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8635 {
8636 pwszShortPath[cwcRet++] = '\\';
8637 pwszShortPath[cwcRet] = '\0';
8638 }
8639 }
8640 }
8641
8642 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8643 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8644 kFsCacheObjRelease(g_pFsCache, pObj);
8645 return cwcRet;
8646 }
8647
8648 /* fall back for complicated cases. */
8649 }
8650 kFsCacheObjRelease(g_pFsCache, pObj);
8651 }
8652 }
8653 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8654 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8655 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8656 return cwcRet;
8657}
8658
8659
8660#ifdef WITH_TEMP_MEMORY_FILES
8661/** Kernel32 - DeleteFileW
8662 * Skip deleting the in-memory files. */
8663static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8664{
8665 BOOL fRc;
8666 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8667 && kwFsIsClTempFileW(pwszFilename))
8668 {
8669 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8670 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8671 fRc = TRUE;
8672 }
8673 else
8674 {
8675 fRc = DeleteFileW(pwszFilename);
8676 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8677 }
8678 return fRc;
8679}
8680#endif /* WITH_TEMP_MEMORY_FILES */
8681
8682
8683
8684#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8685
8686/*
8687 *
8688 * Console output buffering.
8689 * Console output buffering.
8690 * Console output buffering.
8691 *
8692 */
8693
8694
8695/**
8696 * Write a wide char string to the console.
8697 *
8698 * @param pSandbox The sandbox which output buffer to flush.
8699 */
8700static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8701{
8702 if (cwcToWrite > 0)
8703 {
8704 DWORD cwcWritten = 0;
8705 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8706 {
8707 if (cwcWritten == cwcToWrite)
8708 { /* likely */ }
8709 else
8710 {
8711 DWORD off = 0;
8712 do
8713 {
8714 off += cwcWritten;
8715 cwcWritten = 0;
8716 } while ( off < cwcToWrite
8717 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8718 kHlpAssert(off == cwcWritten);
8719 }
8720 }
8721 else
8722 kHlpAssertFailed();
8723 pSandbox->Combined.cFlushes++;
8724 }
8725}
8726
8727
8728/**
8729 * Flushes the combined console output buffer.
8730 *
8731 * @param pSandbox The sandbox which output buffer to flush.
8732 */
8733static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8734{
8735 if (pSandbox->Combined.cwcBuf > 0)
8736 {
8737 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8738 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8739 pSandbox->Combined.cwcBuf = 0;
8740 }
8741}
8742
8743
8744/**
8745 * For handling combined buffer overflow cases line by line.
8746 *
8747 * @param pSandbox The sandbox.
8748 * @param pwcBuf What to add to the combined buffer. Usually a
8749 * line, unless we're really low on buffer space.
8750 * @param cwcBuf The length of what to add.
8751 * @param fBrokenLine Whether this is a broken line.
8752 */
8753static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8754{
8755 if (fBrokenLine)
8756 kwSandboxConsoleFlushCombined(pSandbox);
8757 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8758 {
8759 kwSandboxConsoleFlushCombined(pSandbox);
8760 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
8761 }
8762 else
8763 {
8764 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
8765 pSandbox->Combined.cwcBuf += cwcBuf;
8766 }
8767}
8768
8769
8770/**
8771 * Called to final flush a line buffer via the combined buffer (if applicable).
8772 *
8773 * @param pSandbox The sandbox.
8774 * @param pLineBuf The line buffer.
8775 * @param pszName The line buffer name (for logging)
8776 */
8777static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
8778{
8779 if (pLineBuf->fIsConsole)
8780 {
8781 if (pLineBuf->u.Con.cwcBuf > 0)
8782 {
8783 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
8784
8785 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
8786 {
8787 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
8788 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
8789 }
8790 else
8791 {
8792 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
8793 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
8794 }
8795 pLineBuf->u.Con.cwcBuf = 0;
8796 }
8797 }
8798#ifdef WITH_STD_OUT_ERR_BUFFERING
8799 else if (pLineBuf->u.Fully.cchBuf > 0)
8800 {
8801 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
8802
8803 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
8804 pLineBuf->u.Fully.cchBuf = 0;
8805 }
8806#endif
8807}
8808
8809
8810/**
8811 * Called at the end of sandboxed execution to flush both stream buffers.
8812 *
8813 * @param pSandbox The sandbox.
8814 */
8815static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
8816{
8817 /*
8818 * First do the cl.exe source file supression trick, if applicable.
8819 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
8820 * handle.
8821 */
8822 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8823 && pSandbox->Combined.cFlushes == 0)
8824 {
8825 if ( pSandbox->StdOut.fIsConsole
8826 || pSandbox->StdErr.fIsConsole)
8827 {
8828 if ( pSandbox->Combined.cwcBuf >= 3
8829 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
8830 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
8831 {
8832 KI32 off = pSandbox->Combined.cwcBuf - 1;
8833 if (pSandbox->Combined.wszBuf[off] == '\n')
8834 {
8835 KBOOL fOk = K_TRUE;
8836 while (off-- > 0)
8837 {
8838 wchar_t const wc = pSandbox->Combined.wszBuf[off];
8839 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
8840 { /* likely */ }
8841 else
8842 {
8843 fOk = K_FALSE;
8844 break;
8845 }
8846 }
8847 if (fOk)
8848 {
8849 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
8850 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8851 pSandbox->Combined.cwcBuf = 0;
8852 return;
8853 }
8854 }
8855 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
8856 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8857 }
8858 }
8859#ifdef WITH_STD_OUT_ERR_BUFFERING
8860 /*
8861 * Otherwise, it goes to standard output (redirected).
8862 */
8863 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
8864 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
8865 {
8866 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
8867 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
8868 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
8869
8870 if (pchBuf[off] == '\n')
8871 {
8872 KBOOL fOk = K_TRUE;
8873 if (pchBuf[off - 1] == '\r')
8874 off--;
8875 while (off-- > 0)
8876 {
8877 char const ch = pchBuf[off];
8878 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
8879 { /* likely */ }
8880 else
8881 {
8882 fOk = K_FALSE;
8883 break;
8884 }
8885 }
8886 if (fOk)
8887 {
8888 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
8889 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8890 pSandbox->StdOut.u.Fully.cchBuf = 0;
8891 return;
8892 }
8893 }
8894 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
8895 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8896 }
8897#endif
8898 }
8899
8900 /*
8901 * Flush the two line buffer, then the combined buffer.
8902 */
8903 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
8904 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
8905 kwSandboxConsoleFlushCombined(pSandbox);
8906}
8907
8908
8909/**
8910 * Writes a string to the given output stream.
8911 *
8912 * @param pSandbox The sandbox.
8913 * @param pLineBuf The line buffer for the output stream.
8914 * @param pwcBuffer The buffer to write.
8915 * @param cwcToWrite The number of wchar_t's in the buffer.
8916 */
8917static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
8918{
8919 kHlpAssert(pLineBuf->fIsConsole);
8920 if (cwcToWrite > 0)
8921 {
8922 /*
8923 * First, find the start of the last incomplete line so we can figure
8924 * out how much line buffering we need to do.
8925 */
8926 KU32 cchLastIncompleteLine;
8927 KU32 offLastIncompleteLine = cwcToWrite;
8928 while ( offLastIncompleteLine > 0
8929 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
8930 offLastIncompleteLine--;
8931 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
8932
8933 /* Was there anything to line buffer? */
8934 if (offLastIncompleteLine < cwcToWrite)
8935 {
8936 /* Need to grow the line buffer? */
8937 KU32 cwcNeeded = offLastIncompleteLine == 0
8938 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
8939 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
8940 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
8941 {
8942 void *pvNew;
8943 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
8944 while (cwcNew < cwcNeeded)
8945 cwcNew *= 2;
8946 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
8947 if (pvNew)
8948 {
8949 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8950 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
8951 }
8952 else
8953 {
8954 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
8955 if (pvNew)
8956 {
8957 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8958 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
8959 }
8960 else
8961 {
8962 /* This isn't perfect, but it will have to do for now. */
8963 if (pLineBuf->u.Con.cwcBuf > 0)
8964 {
8965 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8966 K_TRUE /*fBrokenLine*/);
8967 pLineBuf->u.Con.cwcBuf = 0;
8968 }
8969 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
8970 return;
8971 }
8972 }
8973 }
8974
8975 /*
8976 * Handle the case where we only add to the line buffer.
8977 */
8978 if (offLastIncompleteLine == 0)
8979 {
8980 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
8981 pLineBuf->u.Con.cwcBuf += cwcToWrite;
8982 return;
8983 }
8984 }
8985
8986 /*
8987 * If there is sufficient combined buffer to handle this request, this is rather simple.
8988 */
8989 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
8990 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8991 {
8992 if (pLineBuf->u.Con.cwcBuf > 0)
8993 {
8994 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8995 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8996 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8997 pLineBuf->u.Con.cwcBuf = 0;
8998 }
8999
9000 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9001 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9002 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9003 }
9004 else
9005 {
9006 /*
9007 * Do line-by-line processing of the input, flusing the combined buffer
9008 * when it becomes necessary. We may have to write lines
9009 */
9010 KU32 off = 0;
9011 KU32 offNextLine = 0;
9012
9013 /* If there are buffered chars, we handle the first line outside the
9014 main loop. We must try our best outputting it as a complete line. */
9015 if (pLineBuf->u.Con.cwcBuf > 0)
9016 {
9017 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9018 offNextLine++;
9019 offNextLine++;
9020 kHlpAssert(offNextLine <= offLastIncompleteLine);
9021
9022 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9023 {
9024 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9025 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9026 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9027 pLineBuf->u.Con.cwcBuf = 0;
9028
9029 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9030 pSandbox->Combined.cwcBuf += offNextLine;
9031 }
9032 else
9033 {
9034 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9035 if (cwcLeft > 0)
9036 {
9037 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9038 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9039 pLineBuf->u.Con.cwcBuf += cwcCopy;
9040 off += cwcCopy;
9041 }
9042 if (pLineBuf->u.Con.cwcBuf > 0)
9043 {
9044 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9045 K_TRUE /*fBrokenLine*/);
9046 pLineBuf->u.Con.cwcBuf = 0;
9047 }
9048 if (off < offNextLine)
9049 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9050 }
9051 off = offNextLine;
9052 }
9053
9054 /* Deal with the remaining lines */
9055 while (off < offLastIncompleteLine)
9056 {
9057 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9058 offNextLine++;
9059 offNextLine++;
9060 kHlpAssert(offNextLine <= offLastIncompleteLine);
9061 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9062 off = offNextLine;
9063 }
9064 }
9065
9066 /*
9067 * Buffer any remaining incomplete line chars.
9068 */
9069 if (cchLastIncompleteLine)
9070 {
9071 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9072 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9073 }
9074 }
9075}
9076
9077
9078/**
9079 * Worker for WriteConsoleA and WriteFile.
9080 *
9081 * @param pSandbox The sandbox.
9082 * @param pLineBuf The line buffer.
9083 * @param pchBuffer What to write.
9084 * @param cchToWrite How much to write.
9085 */
9086static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9087{
9088 /*
9089 * Convert it to wide char and use the 'W' to do the work.
9090 */
9091 int cwcRet;
9092 KU32 cwcBuf = cchToWrite * 2 + 1;
9093 wchar_t *pwcBufFree = NULL;
9094 wchar_t *pwcBuf;
9095 kHlpAssert(pLineBuf->fIsConsole);
9096
9097 if (cwcBuf <= 4096)
9098 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9099 else
9100 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9101
9102 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9103 if (cwcRet > 0)
9104 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9105 else
9106 {
9107 DWORD cchWritten;
9108 kHlpAssertFailed();
9109
9110 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9111 if (pLineBuf->u.Con.cwcBuf > 0)
9112 {
9113 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9114 pLineBuf->u.Con.cwcBuf = 0;
9115 }
9116 kwSandboxConsoleFlushCombined(pSandbox);
9117
9118 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9119 {
9120 if (cchWritten >= cchToWrite)
9121 { /* likely */ }
9122 else
9123 {
9124 KU32 off = 0;
9125 do
9126 {
9127 off += cchWritten;
9128 cchWritten = 0;
9129 } while ( off < cchToWrite
9130 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9131 }
9132 }
9133 }
9134
9135 if (pwcBufFree)
9136 kHlpFree(pwcBufFree);
9137}
9138
9139
9140/** Kernel32 - WriteConsoleA */
9141BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9142 PVOID pvReserved)
9143{
9144 BOOL fRc;
9145 PKWOUTPUTSTREAMBUF pLineBuf;
9146 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9147
9148 if (hConOutput == g_Sandbox.StdErr.hOutput)
9149 pLineBuf = &g_Sandbox.StdErr;
9150 else
9151 pLineBuf = &g_Sandbox.StdOut;
9152 if (pLineBuf->fIsConsole)
9153 {
9154 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9155
9156 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9157 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9158 if (pcbWritten)
9159 *pcbWritten = cbToWrite;
9160 fRc = TRUE;
9161 }
9162 else
9163 {
9164 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9165 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9166 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9167 }
9168 return fRc;
9169}
9170
9171
9172/** Kernel32 - WriteConsoleW */
9173BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9174 PVOID pvReserved)
9175{
9176 BOOL fRc;
9177 PKWOUTPUTSTREAMBUF pLineBuf;
9178 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9179
9180 if (hConOutput == g_Sandbox.StdErr.hOutput)
9181 pLineBuf = &g_Sandbox.StdErr;
9182 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9183 pLineBuf = &g_Sandbox.StdOut;
9184 else
9185 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9186 if (pLineBuf->fIsConsole)
9187 {
9188 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9189
9190 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9191 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9192 if (pcwcWritten)
9193 *pcwcWritten = cwcToWrite;
9194 fRc = TRUE;
9195 }
9196 else
9197 {
9198 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9199 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9200 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9201 }
9202 return fRc;
9203}
9204
9205#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9206
9207
9208
9209/*
9210 *
9211 * Virtual memory leak prevension.
9212 * Virtual memory leak prevension.
9213 * Virtual memory leak prevension.
9214 *
9215 */
9216
9217#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9218
9219/** For debug logging. */
9220# ifndef NDEBUG
9221static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9222{
9223 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9224 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9225 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9226 if (cbMemInfo != 0)
9227 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9228 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9229 MemInfo.BaseAddress,
9230 MemInfo.AllocationBase,
9231 MemInfo.RegionSize,
9232 MemInfo.State,
9233 MemInfo.Protect,
9234 MemInfo.Type));
9235}
9236# else
9237# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9238# endif
9239
9240/**
9241 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9242 *
9243 * @param idxFixed The fixed allocation index to "free".
9244 */
9245static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9246{
9247 BOOL fRc;
9248 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9249 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9250 kHlpAssert(fRc); K_NOREF(fRc);
9251 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9252 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9253}
9254
9255#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9256
9257
9258/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9259 * location (~78MB in 32-bit 2010 compiler). */
9260static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9261{
9262 PVOID pvMem;
9263 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9264 {
9265 KU32 idxPreAllocated = KU32_MAX;
9266
9267#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9268 /*
9269 * Look for a pre-reserved CL.exe heap allocation.
9270 */
9271 pvMem = NULL;
9272 if ( pvAddr != 0
9273 && (fAllocType & MEM_RESERVE))
9274 {
9275 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9276 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9277 while (idxFixed-- > 0)
9278 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9279 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9280 {
9281 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9282 {
9283 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9284 {
9285 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9286 pvMem = pvAddr;
9287 idxPreAllocated = idxFixed;
9288 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9289 pvAddr, cb, fAllocType, fProt, pvMem));
9290 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9291 SetLastError(NO_ERROR);
9292 break;
9293 }
9294 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9295 }
9296 else
9297 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9298 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9299 }
9300 }
9301 if (!pvMem)
9302#endif
9303 {
9304 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9305 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9306 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9307 if (pvAddr && pvAddr != pvMem)
9308 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9309 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9310 }
9311
9312 if (pvMem)
9313 {
9314 /*
9315 * Track it.
9316 */
9317 PKWVIRTALLOC pTracker;
9318
9319 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9320 pTracker = g_Sandbox.pVirtualAllocHead;
9321 while ( pTracker
9322 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9323 pTracker = pTracker->pNext;
9324 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9325 if (!pTracker)
9326 {
9327 DWORD dwErr = GetLastError();
9328 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9329 if (pTracker)
9330 {
9331 pTracker->pvAlloc = pvMem;
9332 pTracker->cbAlloc = cb;
9333 pTracker->idxPreAllocated = idxPreAllocated;
9334 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9335 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9336 g_Sandbox.pVirtualAllocHead = pTracker;
9337 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9338 }
9339 SetLastError(dwErr);
9340 }
9341 }
9342 }
9343 else
9344 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9345 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9346 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9347 return pvMem;
9348}
9349
9350
9351/** Kernel32 - VirtualFree. */
9352static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9353{
9354 BOOL fRc;
9355 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9356 {
9357 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9358 if (dwFreeType & MEM_RELEASE)
9359 {
9360 PKWVIRTALLOC pTracker;
9361 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9362 pTracker = g_Sandbox.pVirtualAllocHead;
9363 if (pTracker)
9364 {
9365 if (pTracker->pvAlloc == pvAddr)
9366 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9367 else
9368 {
9369 PKWVIRTALLOC pPrev;
9370 do
9371 {
9372 pPrev = pTracker;
9373 pTracker = pTracker->pNext;
9374 } while (pTracker && pTracker->pvAlloc != pvAddr);
9375 if (pTracker)
9376 pPrev->pNext = pTracker->pNext;
9377 }
9378 if (pTracker)
9379 {
9380#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9381 if (pTracker->idxPreAllocated != KU32_MAX)
9382 {
9383 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9384 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9385 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9386 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9387 kHlpFree(pTracker);
9388 return TRUE;
9389 }
9390#endif
9391
9392 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9393 if (fRc)
9394 kHlpFree(pTracker);
9395 else
9396 {
9397 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9398 g_Sandbox.pVirtualAllocHead = pTracker;
9399 }
9400 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9401 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9402 return fRc;
9403 }
9404
9405 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9406 }
9407 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9408 }
9409 }
9410
9411#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9412 /*
9413 * Protect our fixed allocations (this isn't just paranoia, btw.).
9414 */
9415 if (dwFreeType & MEM_RELEASE)
9416 {
9417 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9418 while (idxFixed-- > 0)
9419 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9420 {
9421 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9422 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9423 return TRUE;
9424 }
9425 }
9426#endif
9427
9428 /*
9429 * Not tracker or not actually free the virtual range.
9430 */
9431 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9432 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9433 return fRc;
9434}
9435
9436
9437/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9438HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9439{
9440 HANDLE hHeap;
9441 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9442
9443 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9444 if (hHeap != NULL)
9445 {
9446 DWORD dwErr = GetLastError();
9447 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9448 if (pTracker)
9449 {
9450 pTracker->hHeap = hHeap;
9451 pTracker->pNext = g_Sandbox.pHeapHead;
9452 g_Sandbox.pHeapHead = pTracker;
9453 }
9454
9455 SetLastError(dwErr);
9456 }
9457 return hHeap;
9458
9459}
9460
9461
9462/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9463BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9464{
9465 BOOL fRc = HeapDestroy(hHeap);
9466 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9467 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9468 if (fRc)
9469 {
9470 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9471 if (pTracker)
9472 {
9473 if (pTracker->hHeap == hHeap)
9474 g_Sandbox.pHeapHead = pTracker->pNext;
9475 else
9476 {
9477 PKWHEAP pPrev;
9478 do
9479 {
9480 pPrev = pTracker;
9481 pTracker = pTracker->pNext;
9482 } while (pTracker && pTracker->hHeap == hHeap);
9483 if (pTracker)
9484 pPrev->pNext = pTracker->pNext;
9485 }
9486 if (pTracker)
9487 kHlpFree(pTracker);
9488 else
9489 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9490 }
9491 }
9492
9493 return fRc;
9494}
9495
9496
9497
9498/*
9499 *
9500 * Thread/Fiber local storage leak prevention.
9501 * Thread/Fiber local storage leak prevention.
9502 * Thread/Fiber local storage leak prevention.
9503 *
9504 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9505 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9506 * we're leaking these indexes, but more importantely we crash during
9507 * worker exit since the callback is triggered multiple times.
9508 */
9509
9510
9511/** Kernel32 - FlsAlloc */
9512DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9513{
9514 DWORD idxFls = FlsAlloc(pfnCallback);
9515 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9516 if (idxFls != FLS_OUT_OF_INDEXES)
9517 {
9518 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9519 if (pTracker)
9520 {
9521 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9522 pTracker->idx = idxFls;
9523 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9524 g_Sandbox.pFlsAllocHead = pTracker;
9525 }
9526 }
9527
9528 return idxFls;
9529}
9530
9531/** Kernel32 - FlsFree */
9532BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9533{
9534 BOOL fRc = FlsFree(idxFls);
9535 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9536 if (fRc)
9537 {
9538 PKWLOCALSTORAGE pTracker;
9539 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9540
9541 pTracker = g_Sandbox.pFlsAllocHead;
9542 if (pTracker)
9543 {
9544 if (pTracker->idx == idxFls)
9545 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9546 else
9547 {
9548 PKWLOCALSTORAGE pPrev;
9549 do
9550 {
9551 pPrev = pTracker;
9552 pTracker = pTracker->pNext;
9553 } while (pTracker && pTracker->idx != idxFls);
9554 if (pTracker)
9555 pPrev->pNext = pTracker->pNext;
9556 }
9557 if (pTracker)
9558 {
9559 pTracker->idx = FLS_OUT_OF_INDEXES;
9560 pTracker->pNext = NULL;
9561 kHlpFree(pTracker);
9562 }
9563 }
9564 }
9565 return fRc;
9566}
9567
9568
9569/** Kernel32 - TlsAlloc */
9570DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9571{
9572 DWORD idxTls = TlsAlloc();
9573 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9574 if (idxTls != TLS_OUT_OF_INDEXES)
9575 {
9576 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9577 if (pTracker)
9578 {
9579 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9580 pTracker->idx = idxTls;
9581 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9582 g_Sandbox.pTlsAllocHead = pTracker;
9583 }
9584 }
9585
9586 return idxTls;
9587}
9588
9589/** Kernel32 - TlsFree */
9590BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9591{
9592 BOOL fRc = TlsFree(idxTls);
9593 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9594 if (fRc)
9595 {
9596 PKWLOCALSTORAGE pTracker;
9597 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9598
9599 pTracker = g_Sandbox.pTlsAllocHead;
9600 if (pTracker)
9601 {
9602 if (pTracker->idx == idxTls)
9603 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9604 else
9605 {
9606 PKWLOCALSTORAGE pPrev;
9607 do
9608 {
9609 pPrev = pTracker;
9610 pTracker = pTracker->pNext;
9611 } while (pTracker && pTracker->idx != idxTls);
9612 if (pTracker)
9613 pPrev->pNext = pTracker->pNext;
9614 }
9615 if (pTracker)
9616 {
9617 pTracker->idx = TLS_OUT_OF_INDEXES;
9618 pTracker->pNext = NULL;
9619 kHlpFree(pTracker);
9620 }
9621 }
9622 }
9623 return fRc;
9624}
9625
9626
9627
9628/*
9629 *
9630 * Header file hashing.
9631 * Header file hashing.
9632 * Header file hashing.
9633 *
9634 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9635 * indicated that ~12% of the time was spent doing MD5 caluclation when
9636 * rebuiling openssl. The hashing it done right after reading the source
9637 * via ReadFile, same buffers and sizes.
9638 */
9639
9640#ifdef WITH_HASH_MD5_CACHE
9641
9642/** AdvApi32 - CryptCreateHash */
9643static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9644 HCRYPTHASH *phHash)
9645{
9646 BOOL fRc;
9647
9648 /*
9649 * Only do this for cl.exe when it request normal MD5.
9650 */
9651 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9652 {
9653 /** @todo do generic caching of hash results. Need SHA-1 and SHA-256 for
9654 * more recent compilers. */
9655 if (idAlg == CALG_MD5)
9656 {
9657 if (hKey == 0)
9658 {
9659 if (dwFlags == 0)
9660 {
9661 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9662 if (pHash)
9663 {
9664 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9665 pHash->uMagic = KWHASHMD5_MAGIC;
9666 pHash->cbHashed = 0;
9667 pHash->fGoneBad = K_FALSE;
9668 pHash->fFallbackMode = K_FALSE;
9669 pHash->fFinal = K_FALSE;
9670
9671 /* link it. */
9672 pHash->pNext = g_Sandbox.pHashHead;
9673 g_Sandbox.pHashHead = pHash;
9674
9675 *phHash = (KUPTR)pHash;
9676 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9677 hProv, *phHash, TRUE));
9678 return TRUE;
9679 }
9680
9681 kwErrPrintf("CryptCreateHash: out of memory!\n");
9682 }
9683 else
9684 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9685 }
9686 else
9687 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9688 }
9689 //else
9690 // kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9691 }
9692
9693 /*
9694 * Fallback.
9695 */
9696 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9697 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9698 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9699 return fRc;
9700}
9701
9702
9703/** AdvApi32 - CryptHashData */
9704static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9705{
9706 BOOL fRc;
9707 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9708 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9709 while (pHash && (KUPTR)pHash != hHash)
9710 pHash = pHash->pNext;
9711 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9712 hHash, pHash, pbData, cbData, dwFlags));
9713 if (pHash)
9714 {
9715 /*
9716 * Validate the state.
9717 */
9718 if ( pHash->uMagic == KWHASHMD5_MAGIC
9719 && !pHash->fFinal)
9720 {
9721 if (!pHash->fFallbackMode)
9722 {
9723 /*
9724 * Does this match the previous ReadFile call to a cached file?
9725 * If it doesn't, try falling back.
9726 */
9727 if ( g_Sandbox.LastHashRead.cbRead == cbData
9728 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9729 {
9730 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9731 if ( pCachedFile
9732 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9733 {
9734
9735 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9736 {
9737 if ( pHash->pCachedFile == NULL
9738 && pHash->cbHashed == 0)
9739 pHash->pCachedFile = pCachedFile;
9740 if (pHash->pCachedFile == pCachedFile)
9741 {
9742 pHash->cbHashed += cbData;
9743 g_Sandbox.LastHashRead.pCachedFile = NULL;
9744 g_Sandbox.LastHashRead.pvRead = NULL;
9745 g_Sandbox.LastHashRead.cbRead = 0;
9746 g_Sandbox.LastHashRead.offRead = 0;
9747 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9748 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9749 return TRUE;
9750 }
9751
9752 /* Note! it's possible to fall back here too, if necessary. */
9753 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
9754 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
9755 }
9756 else
9757 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
9758 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
9759 }
9760 else if (!pCachedFile)
9761 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
9762 else
9763 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
9764 }
9765 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
9766 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
9767 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
9768 if (pHash->cbHashed == 0)
9769 pHash->fFallbackMode = K_TRUE;
9770 if (pHash->fFallbackMode)
9771 {
9772 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
9773 pHash->fFallbackMode = K_TRUE;
9774 MD5Init(&pHash->Md5Ctx);
9775 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9776 pHash->cbHashed = cbData;
9777 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
9778 hHash, pbData, cbData, dwFlags));
9779 return TRUE;
9780 }
9781 pHash->fGoneBad = K_TRUE;
9782 SetLastError(ERROR_INVALID_PARAMETER);
9783 fRc = FALSE;
9784 }
9785 else
9786 {
9787 /* fallback. */
9788 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9789 pHash->cbHashed += cbData;
9790 fRc = TRUE;
9791 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
9792 hHash, pbData, cbData, dwFlags));
9793 }
9794 }
9795 /*
9796 * Bad handle state.
9797 */
9798 else
9799 {
9800 if (pHash->uMagic != KWHASHMD5_MAGIC)
9801 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
9802 else
9803 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
9804 SetLastError((DWORD)NTE_BAD_HASH);
9805 fRc = FALSE;
9806 }
9807 }
9808 else
9809 {
9810
9811 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
9812 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
9813 }
9814 return fRc;
9815}
9816
9817
9818/** AdvApi32 - CryptGetHashParam */
9819static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
9820 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
9821{
9822 BOOL fRc;
9823 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9824 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9825 while (pHash && (KUPTR)pHash != hHash)
9826 pHash = pHash->pNext;
9827 if (pHash)
9828 {
9829 if (pHash->uMagic == KWHASHMD5_MAGIC)
9830 {
9831 if (dwFlags == 0)
9832 {
9833 DWORD cbRet;
9834 void *pvRet;
9835 union
9836 {
9837 DWORD dw;
9838 } uBuf;
9839
9840 switch (dwParam)
9841 {
9842 case HP_HASHVAL:
9843 {
9844 /* Check the hash progress. */
9845 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
9846 if (pCachedFile)
9847 {
9848 if ( pCachedFile->cbCached == pHash->cbHashed
9849 && !pHash->fGoneBad)
9850 {
9851 if (pCachedFile->fValidMd5)
9852 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
9853 else
9854 {
9855 MD5Init(&pHash->Md5Ctx);
9856 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
9857 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
9858 pCachedFile->fValidMd5 = K_TRUE;
9859 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
9860 }
9861 pvRet = pCachedFile->abMd5Digest;
9862 }
9863 else
9864 {
9865 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
9866 from what I can tell, so just deal with it. */
9867 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
9868 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
9869 pHash, pCachedFile, pCachedFile->szPath));
9870 pHash->fFallbackMode = K_TRUE;
9871 pHash->pCachedFile = NULL;
9872 MD5Init(&pHash->Md5Ctx);
9873 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
9874 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9875 pvRet = pHash->abDigest;
9876 }
9877 pHash->fFinal = K_TRUE;
9878 cbRet = 16;
9879 break;
9880 }
9881 else if (pHash->fFallbackMode)
9882 {
9883 if (!pHash->fFinal)
9884 {
9885 pHash->fFinal = K_TRUE;
9886 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9887 }
9888 pvRet = pHash->abDigest;
9889 cbRet = 16;
9890 break;
9891 }
9892 else
9893 {
9894 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
9895 SetLastError(ERROR_INVALID_SERVER_STATE);
9896 }
9897 return FALSE;
9898 }
9899
9900 case HP_HASHSIZE:
9901 uBuf.dw = 16;
9902 pvRet = &uBuf;
9903 cbRet = sizeof(DWORD);
9904 break;
9905
9906 case HP_ALGID:
9907 uBuf.dw = CALG_MD5;
9908 pvRet = &uBuf;
9909 cbRet = sizeof(DWORD);
9910 break;
9911
9912 default:
9913 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
9914 SetLastError((DWORD)NTE_BAD_TYPE);
9915 return FALSE;
9916 }
9917
9918 /*
9919 * Copy out cbRet from pvRet.
9920 */
9921 if (pbData)
9922 {
9923 if (*pcbData >= cbRet)
9924 {
9925 *pcbData = cbRet;
9926 kHlpMemCopy(pbData, pvRet, cbRet);
9927 if (cbRet == 4)
9928 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
9929 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
9930 else if (cbRet == 16)
9931 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",
9932 dwParam, pHash, pHash->pCachedFile, cbRet,
9933 pbData[0], pbData[1], pbData[2], pbData[3],
9934 pbData[4], pbData[5], pbData[6], pbData[7],
9935 pbData[8], pbData[9], pbData[10], pbData[11],
9936 pbData[12], pbData[13], pbData[14], pbData[15]));
9937 else
9938 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
9939 dwParam, pHash, pHash->pCachedFile, cbRet));
9940 return TRUE;
9941 }
9942
9943 kHlpMemCopy(pbData, pvRet, *pcbData);
9944 }
9945 SetLastError(ERROR_MORE_DATA);
9946 *pcbData = cbRet;
9947 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
9948 }
9949 else
9950 {
9951 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
9952 SetLastError((DWORD)NTE_BAD_FLAGS);
9953 }
9954 }
9955 else
9956 {
9957 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
9958 SetLastError((DWORD)NTE_BAD_HASH);
9959 }
9960 fRc = FALSE;
9961 }
9962 /*
9963 * Regular handle.
9964 */
9965 else
9966 {
9967 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
9968 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
9969 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
9970 }
9971
9972 return fRc;
9973}
9974
9975
9976/** AdvApi32 - CryptDestroyHash */
9977static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
9978{
9979 BOOL fRc;
9980 PKWHASHMD5 pPrev = NULL;
9981 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9982 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9983 while (pHash && (KUPTR)pHash != hHash)
9984 {
9985 pPrev = pHash;
9986 pHash = pHash->pNext;
9987 }
9988 if (pHash)
9989 {
9990 if (pHash->uMagic == KWHASHMD5_MAGIC)
9991 {
9992 pHash->uMagic = 0;
9993 if (!pPrev)
9994 g_Sandbox.pHashHead = pHash->pNext;
9995 else
9996 pPrev->pNext = pHash->pNext;
9997 kHlpFree(pHash);
9998 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
9999 fRc = TRUE;
10000 }
10001 else
10002 {
10003 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10004 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10005 SetLastError(ERROR_INVALID_HANDLE);
10006 fRc = FALSE;
10007 }
10008 }
10009 /*
10010 * Regular handle.
10011 */
10012 else
10013 {
10014 fRc = CryptDestroyHash(hHash);
10015 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10016 }
10017 return fRc;
10018}
10019
10020#endif /* WITH_HASH_MD5_CACHE */
10021
10022
10023/*
10024 *
10025 * Reuse crypt context.
10026 * Reuse crypt context.
10027 * Reuse crypt context.
10028 *
10029 *
10030 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10031 *
10032 */
10033
10034#ifdef WITH_CRYPT_CTX_REUSE
10035
10036/** AdvApi32 - CryptAcquireContextW. */
10037static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10038 DWORD dwProvType, DWORD dwFlags)
10039{
10040 BOOL fRet;
10041
10042 /*
10043 * Lookup reusable context based on the input.
10044 */
10045 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10046 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10047 KU32 iCtx = g_Sandbox.cCryptCtxs;
10048 while (iCtx-- > 0)
10049 {
10050 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10051 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10052 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10053 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10054 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10055 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10056 {
10057 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10058 {
10059 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10060 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10061 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10062 return TRUE;
10063 }
10064 }
10065 }
10066
10067 /*
10068 * Create it and enter it into the reused array if possible.
10069 */
10070 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10071 if (fRet)
10072 {
10073 iCtx = g_Sandbox.cCryptCtxs;
10074 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10075 {
10076 /* Try duplicate the input strings. */
10077 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10078 (cwcContainer + 1) * sizeof(wchar_t));
10079 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10080 {
10081 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10082 (cwcProvider + 1) * sizeof(wchar_t));
10083 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10084 {
10085 /* Add a couple of references just to be on the safe side and all that. */
10086 HCRYPTPROV hProv = *phProv;
10087 if (CryptContextAddRef(hProv, NULL, 0))
10088 {
10089 if (CryptContextAddRef(hProv, NULL, 0))
10090 {
10091 /* Okay, finish the entry and return success */
10092 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10093 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10094 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10095 g_Sandbox.cCryptCtxs = iCtx + 1;
10096
10097 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10098 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10099 return TRUE;
10100 }
10101 CryptReleaseContext(hProv, 0);
10102 }
10103 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10104
10105 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10106 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10107 }
10108 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10109 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10110 }
10111 }
10112 else
10113 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10114 }
10115
10116 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10117 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10118 return fRet;
10119}
10120
10121
10122/** AdvApi32 - CryptReleaseContext */
10123static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10124{
10125 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10126 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10127 return fRet;
10128}
10129
10130
10131/** AdvApi32 - CryptContextAddRef */
10132static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10133{
10134 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10135 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10136 return fRet;
10137}
10138
10139#endif /* WITH_CRYPT_CTX_REUSE */
10140
10141/*
10142 *
10143 * Structured exception handling.
10144 * Structured exception handling.
10145 * Structured exception handling.
10146 *
10147 */
10148#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10149
10150# define EH_NONCONTINUABLE KU32_C(0x00000001)
10151# define EH_UNWINDING KU32_C(0x00000002)
10152# define EH_EXIT_UNWIND KU32_C(0x00000004)
10153# define EH_STACK_INVALID KU32_C(0x00000008)
10154# define EH_NESTED_CALL KU32_C(0x00000010)
10155
10156typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10157 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10158typedef struct _EXCEPTION_REGISTRATION_RECORD
10159{
10160 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10161 PFNXCPTHANDLER pfnXcptHandler;
10162};
10163
10164
10165/**
10166 * Calls @a pfnHandler.
10167 */
10168static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10169 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10170 PFNXCPTHANDLER pfnHandler)
10171{
10172# if 1
10173 /* This is a more robust version that isn't subject to calling
10174 convension cleanup disputes and such. */
10175 KU32 uSavedEdi;
10176 KU32 uSavedEsi;
10177 KU32 uSavedEbx;
10178 KU32 rcHandler;
10179
10180 __asm
10181 {
10182 mov [uSavedEdi], edi
10183 mov [uSavedEsi], esi
10184 mov [uSavedEbx], ebx
10185 mov esi, esp
10186 mov edi, esp
10187 mov edi, [pXcptRec]
10188 mov edx, [pRegRec]
10189 mov eax, [pXcptCtx]
10190 mov ebx, [ppRegRec]
10191 mov ecx, [pfnHandler]
10192 sub esp, 16
10193 and esp, 0fffffff0h
10194 mov [esp ], edi
10195 mov [esp + 4], edx
10196 mov [esp + 8], eax
10197 mov [esp + 12], ebx
10198 mov edi, esi
10199 call ecx
10200 mov esp, esi
10201 cmp esp, edi
10202 je stack_ok
10203 int 3
10204 stack_ok:
10205 mov edi, [uSavedEdi]
10206 mov esi, [uSavedEsi]
10207 mov ebx, [uSavedEbx]
10208 mov [rcHandler], eax
10209 }
10210 return rcHandler;
10211# else
10212 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10213# endif
10214}
10215
10216
10217/**
10218 * Vectored exception handler that emulates x86 chained exception handler.
10219 *
10220 * This is necessary because the RtlIsValidHandler check fails for self loaded
10221 * code and prevents cl.exe from working. (On AMD64 we can register function
10222 * tables, but on X86 cooking your own handling seems to be the only viabke
10223 * alternative.)
10224 *
10225 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10226 * @param pXcptPtrs The exception details.
10227 */
10228static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10229{
10230 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10231 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10232 if (g_Sandbox.fRunning)
10233 {
10234 HANDLE const hCurProc = GetCurrentProcess();
10235 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10236 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10237 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10238 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10239 {
10240 /* Read the exception record in a safe manner. */
10241 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10242 DWORD cbActuallyRead = 0;
10243 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10244 && cbActuallyRead == sizeof(RegRec))
10245 {
10246 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10247 KU32 rcHandler;
10248 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10249 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10250 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10251 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10252 if (rcHandler == ExceptionContinueExecution)
10253 {
10254 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10255 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10256 return EXCEPTION_CONTINUE_EXECUTION;
10257 }
10258
10259 if (rcHandler == ExceptionContinueSearch)
10260 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10261 else if (rcHandler == ExceptionNestedException)
10262 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10263 else
10264 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10265 }
10266 else
10267 {
10268 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10269 break;
10270 }
10271
10272 /*
10273 * Next.
10274 */
10275 pRegRec = RegRec.pPrevRegRec;
10276 }
10277 }
10278 return EXCEPTION_CONTINUE_SEARCH;
10279}
10280
10281
10282/** NtDll,Kernel32 - RtlUnwind */
10283static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10284 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10285{
10286 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10287 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10288 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10289 if (g_Sandbox.fRunning)
10290 {
10291 HANDLE const hCurProc = GetCurrentProcess();
10292 PCONTEXT pXcptCtx = NULL;
10293 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10294
10295 /*
10296 * Update / create an exception record.
10297 */
10298 if (pXcptRec)
10299 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10300 else
10301 {
10302 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10303 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10304 pXcptRec->ExceptionCode = STATUS_UNWIND;
10305 pXcptRec->ExceptionFlags = EH_UNWINDING;
10306 }
10307 if (!pStopXcptRec)
10308 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10309
10310 /*
10311 * Walk the chain till we find pStopXctpRec.
10312 */
10313 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10314 && pRegRec != NULL
10315 && pRegRec != pStopXcptRec)
10316 {
10317 /* Read the exception record in a safe manner. */
10318 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10319 DWORD cbActuallyRead = 0;
10320 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10321 && cbActuallyRead == sizeof(RegRec))
10322 {
10323 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10324 KU32 rcHandler;
10325 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10326 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10327 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10328 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10329
10330 if (rcHandler == ExceptionContinueSearch)
10331 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10332 else if (rcHandler == ExceptionCollidedUnwind)
10333 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10334 else
10335 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10336 }
10337 else
10338 {
10339 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10340 break;
10341 }
10342
10343 /*
10344 * Pop next.
10345 */
10346 pTib->ExceptionList = RegRec.pPrevRegRec;
10347 pRegRec = RegRec.pPrevRegRec;
10348 }
10349 return;
10350 }
10351
10352 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10353}
10354
10355#endif /* WINDOWS + X86 */
10356
10357
10358/*
10359 *
10360 * Misc function only intercepted while debugging.
10361 * Misc function only intercepted while debugging.
10362 * Misc function only intercepted while debugging.
10363 *
10364 */
10365
10366#ifndef NDEBUG
10367
10368/** CRT - memcpy */
10369static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10370{
10371 KU8 const *pbSrc = (KU8 const *)pvSrc;
10372 KU8 *pbDst = (KU8 *)pvDst;
10373 KSIZE cbLeft = cb;
10374 while (cbLeft-- > 0)
10375 *pbDst++ = *pbSrc++;
10376 return pvDst;
10377}
10378
10379
10380/** CRT - memset */
10381static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10382{
10383 KU8 *pbDst = (KU8 *)pvDst;
10384 KSIZE cbLeft = cb;
10385 while (cbLeft-- > 0)
10386 *pbDst++ = (KU8)bFiller;
10387 return pvDst;
10388}
10389
10390#endif /* NDEBUG */
10391
10392
10393
10394/**
10395 * Functions that needs replacing for sandboxed execution.
10396 */
10397KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10398{
10399 /*
10400 * Kernel32.dll and friends.
10401 */
10402 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10403 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10404
10405 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10406 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10407 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10408 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10409 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10410 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10411 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10412 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10413 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10414 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10415 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10416
10417 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10418 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10419 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10420 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10421
10422 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10423
10424 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10425 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10426 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10427 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10428 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10429 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10430 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10431 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10432 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10433 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10434 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10435
10436 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10437 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10438 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10439 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10440#ifdef WITH_TEMP_MEMORY_FILES
10441 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10442 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10443 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10444 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10445 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10446 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10447 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10448 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10449 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10450 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10451#endif
10452 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10453 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10454 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10455 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10456 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10457 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10458 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10459 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10460 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10461#ifdef WITH_TEMP_MEMORY_FILES
10462 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10463#endif
10464
10465 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10466 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10467
10468 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10469 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10470
10471 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10472 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10473
10474 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10475 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10476 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10477 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10478
10479 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10480
10481#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10482 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10483#endif
10484
10485#ifdef WITH_HASH_MD5_CACHE
10486 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10487 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10488 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10489 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10490#endif
10491
10492#ifdef WITH_CRYPT_CTX_REUSE
10493 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10494 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10495 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10496#endif
10497
10498 /*
10499 * MS Visual C++ CRTs.
10500 */
10501 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10502 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10503 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10504 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10505 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10506 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10507
10508 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10509 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10510 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10511
10512 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10513 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10514 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
10515
10516 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
10517 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
10518 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
10519 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
10520 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
10521 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
10522 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
10523 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
10524 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
10525 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
10526 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
10527 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
10528 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
10529 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
10530 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
10531 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
10532 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
10533 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
10534 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
10535 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
10536
10537 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
10538 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
10539 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
10540 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
10541 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
10542 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
10543 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
10544 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
10545 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
10546 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
10547 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
10548 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
10549 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
10550 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
10551
10552#ifndef NDEBUG
10553 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
10554 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
10555#endif
10556};
10557/** Number of entries in g_aReplacements. */
10558KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
10559
10560
10561/**
10562 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
10563 * execution.
10564 */
10565KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
10566{
10567 /*
10568 * Kernel32.dll and friends.
10569 */
10570 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10571 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10572
10573#if 0
10574 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10575#endif
10576
10577 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10578 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10579 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10580 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10581#ifdef WITH_TEMP_MEMORY_FILES
10582 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10583 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10584 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10585 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10586 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10587 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10588 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10589 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10590 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10591 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10592#endif
10593 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10594 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10595 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10596 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10597 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10598 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10599 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10600 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10601 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10602#ifdef WITH_TEMP_MEMORY_FILES
10603 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10604#endif
10605 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10606 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10607
10608 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10609 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10610
10611#ifdef WITH_HASH_MD5_CACHE
10612 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10613 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10614 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10615 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10616#endif
10617
10618 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10619
10620#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10621 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10622#endif
10623
10624 /*
10625 * MS Visual C++ CRTs.
10626 */
10627 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10628 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10629 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10630 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10631 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10632 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10633 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10634
10635#if 0 /* used by mspdbXXX.dll */
10636 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10637 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10638#endif
10639};
10640/** Number of entries in g_aSandboxNativeReplacements. */
10641KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10642
10643
10644/**
10645 * Functions that needs replacing when queried by GetProcAddress.
10646 */
10647KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10648{
10649 /*
10650 * Kernel32.dll and friends.
10651 */
10652 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10653 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10654 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10655 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10656};
10657/** Number of entries in g_aSandboxGetProcReplacements. */
10658KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10659
10660
10661/**
10662 * Control handler.
10663 *
10664 * @returns TRUE if handled, FALSE if not.
10665 * @param dwCtrlType The signal.
10666 */
10667static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10668{
10669 DWORD cbIgn;
10670 int volatile rc; /* volatile for debugging */
10671 int volatile rcPrev;
10672 const char *pszMsg;
10673 switch (dwCtrlType)
10674 {
10675 case CTRL_C_EVENT:
10676 rc = 9;
10677 pszMsg = "kWorker: Ctrl-C\r\n";
10678 break;
10679
10680 case CTRL_BREAK_EVENT:
10681 rc = 10;
10682 pszMsg = "kWorker: Ctrl-Break\r\n";
10683 break;
10684
10685 case CTRL_CLOSE_EVENT:
10686 rc = 11;
10687 pszMsg = "kWorker: console closed\r\n";
10688 break;
10689
10690 case CTRL_LOGOFF_EVENT:
10691 rc = 11;
10692 pszMsg = "kWorker: logoff event\r\n";
10693 break;
10694
10695 case CTRL_SHUTDOWN_EVENT:
10696 rc = 11;
10697 pszMsg = "kWorker: shutdown event\r\n";
10698 break;
10699
10700 default:
10701 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10702 return TRUE;
10703 }
10704
10705 /*
10706 * Terminate the process after 5 seconds.
10707 * If we get here a second time we just terminate the process ourselves.
10708 *
10709 * Note! We do no try call exit() here as it turned out to deadlock a lot
10710 * flusing file descriptors (stderr back when we first wrote to it).
10711 */
10712 rcPrev = g_rcCtrlC;
10713 g_rcCtrlC = rc;
10714 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10715 if (rcPrev == 0)
10716 {
10717 int i;
10718 for (i = 0; i < 10; i++)
10719 {
10720 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10721 Sleep(500);
10722 }
10723 }
10724 TerminateProcess(GetCurrentProcess(), rc);
10725 return TRUE;
10726}
10727
10728
10729#if 0
10730/**
10731 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10732 */
10733static void kwSandboxResetModuleVisited(void)
10734{
10735 PKWMODULE pMod = g_pModuleHead;
10736 while (pMod)
10737 {
10738 pMod->fVisited = K_FALSE;
10739 pMod = pMod->pNextList;
10740 }
10741}
10742
10743
10744/**
10745 * Used by kwSandboxExec to reset the state of the module tree.
10746 *
10747 * This is done recursively.
10748 *
10749 * @param pMod The root of the tree to consider.
10750 */
10751static void kwSandboxResetModuleState(PKWMODULE pMod)
10752{
10753 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
10754 if (!pMod->fNative)
10755 {
10756 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
10757 if (!pMod->fVisited) /* Avoid loops. */
10758 {
10759 KSIZE iImp;
10760 pMod->fVisited = K_TRUE;
10761 iImp = pMod->u.Manual.cImpMods;
10762 while (iImp-- > 0)
10763 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
10764 }
10765 }
10766 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10767 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
10768 {
10769 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10770 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10771 {
10772 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10773 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10774 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10775 pMod->pszPath, pszValue ? pszValue : "<null>"));
10776 }
10777 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10778 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10779 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10780 pMod->pszPath, pszValue ? pszValue : "<null>"));
10781 else
10782 {
10783 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10784 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10785 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10786 if (pszValue != NULL)
10787 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10788 else
10789 pMod->pszMsPdbSrvEndpoint = NULL;
10790 pMod->fNeedReInit = K_TRUE;
10791 }
10792 }
10793}
10794#else
10795/**
10796 * Used by kwSandboxExec to reset the state of the module tree.
10797 */
10798static void kwSandboxResetModuleState(void)
10799{
10800 PKWMODULE pMod = g_pModuleHead;
10801 while (pMod)
10802 {
10803 if (!pMod->fNative)
10804 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
10805 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10806 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
10807 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
10808 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
10809 {
10810 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10811 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10812 {
10813 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10814 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10815 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10816 pMod->pszPath, pszValue ? pszValue : "<null>"));
10817 }
10818 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10819 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10820 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10821 pMod->pszPath, pszValue ? pszValue : "<null>"));
10822 else
10823 {
10824 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10825 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10826 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10827 if (pszValue != NULL)
10828 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10829 else
10830 pMod->pszMsPdbSrvEndpoint = NULL;
10831 pMod->fNeedReInit = K_TRUE;
10832 }
10833 }
10834
10835 pMod = pMod->pNextList;
10836 }
10837}
10838#endif
10839
10840static PPEB kwSandboxGetProcessEnvironmentBlock(void)
10841{
10842#if K_ARCH == K_ARCH_X86_32
10843 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
10844#elif K_ARCH == K_ARCH_AMD64
10845 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
10846#else
10847# error "Port me!"
10848#endif
10849}
10850
10851
10852/**
10853 * Enters the given handle into the handle table.
10854 *
10855 * @returns K_TRUE on success, K_FALSE on failure.
10856 * @param pSandbox The sandbox.
10857 * @param pHandle The handle.
10858 * @param hHandle The handle value to enter it under (for the
10859 * duplicate handle API).
10860 */
10861static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
10862{
10863 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
10864 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
10865
10866 /*
10867 * Grow handle table.
10868 */
10869 if (idxHandle >= pSandbox->cHandles)
10870 {
10871 void *pvNew;
10872 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
10873 while (cHandles <= idxHandle)
10874 cHandles *= 2;
10875 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
10876 if (!pvNew)
10877 {
10878 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
10879 return K_FALSE;
10880 }
10881 pSandbox->papHandles = (PKWHANDLE *)pvNew;
10882 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
10883 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
10884 pSandbox->cHandles = cHandles;
10885 }
10886
10887 /*
10888 * Check that the entry is unused then insert it.
10889 */
10890 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
10891 pSandbox->papHandles[idxHandle] = pHandle;
10892 pSandbox->cActiveHandles++;
10893 return K_TRUE;
10894}
10895
10896
10897/**
10898 * Creates a correctly quoted ANSI command line string from the given argv.
10899 *
10900 * @returns Pointer to the command line.
10901 * @param cArgs Number of arguments.
10902 * @param papszArgs The argument vector.
10903 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10904 * @param pcbCmdLine Where to return the command line length,
10905 * including one terminator.
10906 */
10907static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
10908{
10909 KU32 i;
10910 KSIZE cbCmdLine;
10911 char *pszCmdLine;
10912
10913 /* Make a copy of the argument vector that we'll be quoting. */
10914 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
10915 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
10916
10917 /* Quote the arguments that need it. */
10918 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
10919
10920 /* figure out cmd line length. */
10921 cbCmdLine = 0;
10922 for (i = 0; i < cArgs; i++)
10923 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
10924 *pcbCmdLine = cbCmdLine;
10925
10926 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
10927 if (pszCmdLine)
10928 {
10929 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
10930 if (papszQuotedArgs[0] != papszArgs[0])
10931 free(papszQuotedArgs[0]);
10932
10933 for (i = 1; i < cArgs; i++)
10934 {
10935 *psz++ = ' ';
10936 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
10937 if (papszQuotedArgs[i] != papszArgs[i])
10938 free(papszQuotedArgs[i]);
10939 }
10940 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
10941
10942 *psz++ = '\0';
10943 *psz++ = '\0';
10944 }
10945
10946 return pszCmdLine;
10947}
10948
10949
10950
10951static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
10952 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10953 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10954{
10955 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10956 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10957 wchar_t *pwcPool;
10958 KSIZE cbStrings;
10959 KSIZE cwc;
10960 KSIZE cbCmdLine;
10961 KU32 i;
10962
10963 /* Simple stuff. */
10964 pSandbox->rcExitCode = 256;
10965 pSandbox->pTool = pTool;
10966 pSandbox->idMainThread = GetCurrentThreadId();
10967 pSandbox->pgmptr = (char *)pTool->pszPath;
10968 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
10969#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10970 if (pSandbox->StdOut.fIsConsole)
10971 pSandbox->StdOut.u.Con.cwcBuf = 0;
10972 else
10973 pSandbox->StdOut.u.Fully.cchBuf = 0;
10974 if (pSandbox->StdErr.fIsConsole)
10975 pSandbox->StdErr.u.Con.cwcBuf = 0;
10976 else
10977 pSandbox->StdErr.u.Fully.cchBuf = 0;
10978 pSandbox->Combined.cwcBuf = 0;
10979 pSandbox->Combined.cFlushes = 0;
10980#endif
10981 pSandbox->fNoPchCaching = fNoPchCaching;
10982 pSandbox->cArgs = cArgs;
10983 pSandbox->papszArgs = (char **)papszArgs;
10984 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
10985 if (!pSandbox->pszCmdLine)
10986 return KERR_NO_MEMORY;
10987
10988 /*
10989 * Convert command line and argv to UTF-16.
10990 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
10991 */
10992 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
10993 if (!pSandbox->papwszArgs)
10994 return KERR_NO_MEMORY;
10995 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
10996 for (i = 0; i < cArgs; i++)
10997 {
10998 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
10999 pSandbox->papwszArgs[i] = pwcPool;
11000 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11001 pwcPool++;
11002 }
11003 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11004 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11005
11006 /*
11007 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11008 */
11009 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11010 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11011 if (!pSandbox->pwszCmdLine)
11012 return KERR_NO_MEMORY;
11013 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11014
11015 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11016 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11017 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11018
11019 /*
11020 * Setup the environment.
11021 */
11022 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11023 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11024 {
11025 KU32 iDst = 0;
11026 for (i = 0; i < cEnvVars; i++)
11027 {
11028 const char *pszVar = papszEnvVars[i];
11029 KSIZE cchVar = kHlpStrLen(pszVar);
11030 const char *pszEqual;
11031 if ( cchVar > 0
11032 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11033 {
11034 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11035 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11036 if (pszCopy && pwszCopy)
11037 {
11038 pSandbox->papszEnvVars[iDst] = pszCopy;
11039 pSandbox->environ[iDst] = pszCopy;
11040 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11041 pSandbox->wenviron[iDst] = pwszCopy;
11042
11043 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11044 if ( (pszEqual - pszVar) == 4
11045 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11046 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11047 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11048 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11049 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11050 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11051
11052 iDst++;
11053 }
11054 else
11055 {
11056 kHlpFree(pszCopy);
11057 kHlpFree(pwszCopy);
11058 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11059 }
11060 }
11061 else
11062 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11063 }
11064 pSandbox->papszEnvVars[iDst] = NULL;
11065 pSandbox->environ[iDst] = NULL;
11066 pSandbox->papwszEnvVars[iDst] = NULL;
11067 pSandbox->wenviron[iDst] = NULL;
11068 }
11069 else
11070 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11071
11072 /*
11073 * Invalidate the volatile parts of cache (kBuild output directory,
11074 * temporary directory, whatever).
11075 */
11076 kFsCacheInvalidateCustomBoth(g_pFsCache);
11077
11078#ifdef WITH_HISTORY
11079 /*
11080 * Record command line in debug history.
11081 */
11082 kHlpFree(g_apszHistory[g_iHistoryNext]);
11083 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11084 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11085#endif
11086
11087 return 0;
11088}
11089
11090
11091/**
11092 * Does sandbox cleanup between jobs.
11093 *
11094 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11095 * influence the result, so that kmk can get on with things ASAP.
11096 *
11097 * @param pSandbox The sandbox.
11098 */
11099static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11100{
11101 PROCESS_MEMORY_COUNTERS MemInfo;
11102 PKWVIRTALLOC pTracker;
11103 PKWHEAP pHeap;
11104 PKWLOCALSTORAGE pLocalStorage;
11105#ifdef WITH_HASH_MD5_CACHE
11106 PKWHASHMD5 pHash;
11107#endif
11108#ifdef WITH_TEMP_MEMORY_FILES
11109 PKWFSTEMPFILE pTempFile;
11110#endif
11111 PKWEXITCALLACK pExitCallback;
11112
11113 /*
11114 * First stuff that may cause code to run.
11115 */
11116
11117 /* Do exit callback first. */
11118 pExitCallback = g_Sandbox.pExitCallbackHead;
11119 g_Sandbox.pExitCallbackHead = NULL;
11120 while (pExitCallback)
11121 {
11122 PKWEXITCALLACK pNext = pExitCallback->pNext;
11123 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11124 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11125 __try
11126 {
11127 pExitCallback->pfnCallback();
11128 }
11129 __except (EXCEPTION_EXECUTE_HANDLER)
11130 {
11131 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11132 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11133 kHlpAssertFailed();
11134 }
11135 kHlpFree(pExitCallback);
11136 pExitCallback = pNext;
11137 }
11138
11139 /* Free left behind FlsAlloc leaks. */
11140 pLocalStorage = g_Sandbox.pFlsAllocHead;
11141 g_Sandbox.pFlsAllocHead = NULL;
11142 while (pLocalStorage)
11143 {
11144 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11145 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11146 FlsFree(pLocalStorage->idx);
11147 kHlpFree(pLocalStorage);
11148 pLocalStorage = pNext;
11149 }
11150
11151 /* Free left behind TlsAlloc leaks. */
11152 pLocalStorage = g_Sandbox.pTlsAllocHead;
11153 g_Sandbox.pTlsAllocHead = NULL;
11154 while (pLocalStorage)
11155 {
11156 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11157 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11158 TlsFree(pLocalStorage->idx);
11159 kHlpFree(pLocalStorage);
11160 pLocalStorage = pNext;
11161 }
11162
11163
11164 /*
11165 * Then free resources associated with the sandbox run.
11166 */
11167
11168 /* Open handles, except fixed handles (stdout and stderr). */
11169 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11170 {
11171 KU32 idxHandle = pSandbox->cHandles;
11172 while (idxHandle-- > 0)
11173 if (pSandbox->papHandles[idxHandle] == NULL)
11174 { /* likely */ }
11175 else
11176 {
11177 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11178 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11179 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11180 {
11181 pSandbox->papHandles[idxHandle] = NULL;
11182 pSandbox->cLeakedHandles++;
11183
11184 switch (pHandle->enmType)
11185 {
11186 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11187 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11188 idxHandle, pHandle->hHandle, pHandle->cRefs));
11189 break;
11190 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11191 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11192 idxHandle, pHandle->hHandle, pHandle->cRefs));
11193 break;
11194 case KWHANDLETYPE_OUTPUT_BUF:
11195 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11196 idxHandle, pHandle->hHandle, pHandle->cRefs));
11197 break;
11198 case KWHANDLETYPE_TEMP_FILE:
11199 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11200 idxHandle, pHandle->hHandle, pHandle->cRefs));
11201 pHandle->u.pTempFile->cActiveHandles--;
11202 break;
11203 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11204 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11205 idxHandle, pHandle->hHandle, pHandle->cRefs));
11206 pHandle->u.pTempFile->cActiveHandles--;
11207 break;
11208 default:
11209 kHlpAssertFailed();
11210 }
11211 if (--pHandle->cRefs == 0)
11212 kHlpFree(pHandle);
11213 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11214 break;
11215 }
11216 }
11217 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11218 }
11219
11220 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11221 g_Sandbox.cMemMappings = 0;
11222
11223#ifdef WITH_TEMP_MEMORY_FILES
11224 /* The temporary files aren't externally visible, they're all in memory. */
11225 pTempFile = pSandbox->pTempFileHead;
11226 pSandbox->pTempFileHead = NULL;
11227 while (pTempFile)
11228 {
11229 PKWFSTEMPFILE pNext = pTempFile->pNext;
11230 KU32 iSeg = pTempFile->cSegs;
11231 while (iSeg-- > 0)
11232 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11233 kHlpFree(pTempFile->paSegs);
11234 pTempFile->pNext = NULL;
11235 kHlpFree(pTempFile);
11236
11237 pTempFile = pNext;
11238 }
11239#endif
11240
11241 /* Free left behind HeapCreate leaks. */
11242 pHeap = g_Sandbox.pHeapHead;
11243 g_Sandbox.pHeapHead = NULL;
11244 while (pHeap != NULL)
11245 {
11246 PKWHEAP pNext = pHeap->pNext;
11247 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11248 HeapDestroy(pHeap->hHeap);
11249 pHeap = pNext;
11250 }
11251
11252 /* Free left behind VirtualAlloc leaks. */
11253 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11254 pTracker = g_Sandbox.pVirtualAllocHead;
11255 g_Sandbox.pVirtualAllocHead = NULL;
11256 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11257 while (pTracker)
11258 {
11259 PKWVIRTALLOC pNext = pTracker->pNext;
11260 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11261
11262#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11263 if (pTracker->idxPreAllocated != KU32_MAX)
11264 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11265 else
11266#endif
11267 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11268 kHlpFree(pTracker);
11269 pTracker = pNext;
11270 }
11271
11272 /* Free the environment. */
11273 if (pSandbox->papszEnvVars)
11274 {
11275 KU32 i;
11276 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11277 kHlpFree(pSandbox->papszEnvVars[i]);
11278 pSandbox->environ[0] = NULL;
11279 pSandbox->papszEnvVars[0] = NULL;
11280
11281 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11282 kHlpFree(pSandbox->papwszEnvVars[i]);
11283 pSandbox->wenviron[0] = NULL;
11284 pSandbox->papwszEnvVars[0] = NULL;
11285 }
11286
11287#ifdef WITH_HASH_MD5_CACHE
11288 /*
11289 * Hash handles.
11290 */
11291 pHash = pSandbox->pHashHead;
11292 pSandbox->pHashHead = NULL;
11293 while (pHash)
11294 {
11295 PKWHASHMD5 pNext = pHash->pNext;
11296 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11297 kHlpFree(pHash);
11298 pHash = pNext;
11299 }
11300#endif
11301
11302 /*
11303 * Check the memory usage. If it's getting high, trigger a respawn
11304 * after the next job.
11305 */
11306 MemInfo.WorkingSetSize = 0;
11307 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11308 {
11309 /* The first time thru, we figure out approximately when to restart
11310 based on installed RAM and CPU threads. */
11311 static KU64 s_cbMaxWorkingSet = 0;
11312 if (s_cbMaxWorkingSet != 0)
11313 { /* likely */ }
11314 else
11315 {
11316 SYSTEM_INFO SysInfo;
11317 MEMORYSTATUSEX GlobalMemInfo;
11318 const char *pszValue;
11319
11320 /* Calculate a reasonable estimate. */
11321 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11322 GetNativeSystemInfo(&SysInfo);
11323
11324 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11325 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11326 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11327#if K_ARCH_BITS >= 64
11328 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11329#else
11330 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11331#endif
11332 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11333 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11334
11335 /* User limit. */
11336 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11337 if (pszValue != NULL)
11338 {
11339 char *pszNext;
11340 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11341 if (*pszNext == '\0' || *pszNext == 'M')
11342 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11343 else if (*pszNext == 'K')
11344 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11345 else if (*pszNext == 'G')
11346 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11347 else
11348 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11349 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11350 }
11351
11352 /* Clamp it a little. */
11353 if (s_cbMaxWorkingSet < 168*1024*1024)
11354 s_cbMaxWorkingSet = 168*1024*1024;
11355#if K_ARCH_BITS < 64
11356 else
11357 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11358 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11359 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11360 : 1536*1024*1024 /* got 4GB VA */);
11361#endif
11362 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11363 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11364 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11365 }
11366
11367 /* Finally the check. */
11368 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11369 {
11370 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11371 g_fRestart = K_TRUE;
11372 }
11373 }
11374
11375 /*
11376 * The CRT has a max of 8192 handles, so we better restart after a while if
11377 * someone is leaking handles or we risk running out of descriptors.
11378 *
11379 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11380 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11381 */
11382 if (pSandbox->cLeakedHandles > 6000)
11383 {
11384 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11385 g_fRestart = K_TRUE;
11386 }
11387}
11388
11389
11390/**
11391 * Does essential cleanups and restoring, anything externally visible.
11392 *
11393 * All cleanups that aren't externally visible are postponed till after we've
11394 * informed kmk of the result, so it can be done in the dead time between jobs.
11395 *
11396 * @param pSandbox The sandbox.
11397 */
11398static void kwSandboxCleanup(PKWSANDBOX pSandbox)
11399{
11400 /*
11401 * Restore the parent command line string.
11402 */
11403 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11404 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11405 pProcParams->CommandLine = pSandbox->SavedCommandLine;
11406 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
11407 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
11408}
11409
11410
11411static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11412 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11413{
11414 int rcExit = 42;
11415 int rc;
11416
11417 /*
11418 * Initialize the sandbox environment.
11419 */
11420 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
11421 if (rc == 0)
11422 {
11423 /*
11424 * Do module initialization.
11425 */
11426#if 0
11427 //kwSandboxResetModuleVisited();
11428 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
11429#else
11430 kwSandboxResetModuleState();
11431#endif
11432 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
11433 if (rc == 0)
11434 {
11435 /*
11436 * Call the main function.
11437 */
11438#if K_ARCH == K_ARCH_AMD64
11439 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
11440#elif K_ARCH == K_ARCH_X86_32
11441 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
11442#else
11443# error "Port me!"
11444#endif
11445
11446 /* Save the NT TIB first (should do that here, not in some other function). */
11447 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
11448 pSandbox->TibMainThread = *pTib;
11449
11450 /* Make the call in a guarded fashion. */
11451#if K_ARCH == K_ARCH_AMD64
11452 /* AMD64 */
11453 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
11454 __try
11455 {
11456 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11457 if (setjmp(pSandbox->JmpBuf) == 0)
11458 {
11459 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11460 pSandbox->fRunning = K_TRUE;
11461 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
11462 pSandbox->fRunning = K_FALSE;
11463 }
11464 else
11465 rcExit = pSandbox->rcExitCode;
11466 }
11467#elif K_ARCH == K_ARCH_X86_32
11468 /* x86 (see _tmainCRTStartup) */
11469 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
11470 __try
11471 {
11472 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11473 if (setjmp(pSandbox->JmpBuf) == 0)
11474 {
11475 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11476 pSandbox->fRunning = K_TRUE;
11477 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
11478 pSandbox->fRunning = K_FALSE;
11479 }
11480 else
11481 rcExit = pSandbox->rcExitCode;
11482 }
11483#endif
11484 __except (EXCEPTION_EXECUTE_HANDLER)
11485 {
11486 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
11487#ifdef WITH_HISTORY
11488 {
11489 KU32 cPrinted = 0;
11490 while (cPrinted++ < 5)
11491 {
11492 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
11493 if (g_apszHistory[idx])
11494 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
11495 }
11496 }
11497#endif
11498 rcExit = 512;
11499 }
11500 pSandbox->fRunning = K_FALSE;
11501
11502 /* Now, restore the NT TIB. */
11503 *pTib = pSandbox->TibMainThread;
11504 }
11505 else
11506 rcExit = 42 + 4;
11507
11508 /*
11509 * Flush and clean up the essential bits only, postpone whatever we
11510 * can till after we've replied to kmk.
11511 */
11512#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11513 kwSandboxConsoleFlushAll(&g_Sandbox);
11514#endif
11515 kwSandboxCleanup(&g_Sandbox);
11516 /** @todo Flush sandboxed native CRTs too. */
11517 }
11518 else
11519 rcExit = 42 + 3;
11520
11521 return rcExit;
11522}
11523
11524
11525/**
11526 * Does the post command part of a job (optional).
11527 *
11528 * @returns The exit code of the job.
11529 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11530 * @param papszPostCmdArgs The post command and its argument.
11531 */
11532static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11533{
11534 const char *pszCmd = papszPostCmdArgs[0];
11535
11536 /* Allow the kmk builtin prefix. */
11537 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
11538 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
11539 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
11540
11541 /* Command switch. */
11542 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
11543 {
11544 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
11545 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
11546 }
11547
11548 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
11549}
11550
11551
11552/**
11553 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11554 */
11555static unsigned kwGetCurrentProcessorGroup(void)
11556{
11557 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
11558 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
11559 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
11560 if (pfnGetter)
11561 {
11562 GROUP_AFFINITY GroupAffinity;
11563 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
11564 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
11565 return GroupAffinity.Group;
11566 }
11567 return 0;
11568}
11569
11570
11571/**
11572 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11573 */
11574static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
11575{
11576 KSIZE cchRet = 0;
11577 HANDLE hToken;
11578 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
11579 {
11580 DWORD cbRet;
11581 TOKEN_STATISTICS TokenStats;
11582 memset(&TokenStats, 0, sizeof(TokenStats));
11583 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
11584 cchRet = sprintf(pszValue, "%" KX64_PRI,
11585 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
11586 else
11587 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
11588 CloseHandle(hToken);
11589 }
11590 else
11591 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
11592 return cchRet;
11593}
11594
11595
11596/**
11597 * Look for and expand the special environment variable.
11598 *
11599 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
11600 * couldn't accuratly determine. Currently the following variables are
11601 * implemented:
11602 * - "@@PROCESSOR_GROUP@@" - The processor group number.
11603 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
11604 * - "@@PID@@" - The kWorker process ID.
11605 * - "@@@@" - Escaped "@@".
11606 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
11607 */
11608static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
11609{
11610 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
11611 KU32 i = cEnvVars;
11612 while (i-- > 0)
11613 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
11614 && papszEnvVars[i][cchSpecialEnv] == '=')
11615 {
11616 /* We will expand stuff like @@NAME@@ */
11617 const char *pszValue = papszEnvVars[i];
11618 KSIZE offDst = 0;
11619 char szTmp[1024];
11620 for (;;)
11621 {
11622 const char *pszAt = kHlpStrChr(pszValue, '@');
11623 while (pszAt && pszAt[1] != '@')
11624 pszAt = kHlpStrChr(pszAt + 1, '@');
11625 if (pszAt)
11626 {
11627 KSIZE cchSrc = pszAt - pszValue;
11628 if (offDst + cchSrc < sizeof(szTmp))
11629 {
11630 char szSrc[64];
11631
11632 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11633 offDst += cchSrc;
11634 pszValue = pszAt + 2;
11635
11636 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11637 {
11638 pszValue += 15;
11639 if (g_iProcessGroup == -1)
11640 g_iProcessGroup = kwGetCurrentProcessorGroup();
11641 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11642 }
11643 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11644 {
11645 pszValue += 19;
11646 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11647 }
11648 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11649 {
11650 pszValue += 5;
11651 cchSrc = sprintf(szSrc, "%d", getpid());
11652 }
11653 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
11654 {
11655 pszValue += 2;
11656 szSrc[0] = '@';
11657 szSrc[1] = '@';
11658 szSrc[2] = '\0';
11659 cchSrc = 2;
11660 }
11661 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
11662 {
11663 static unsigned int s_iCounter = 0;
11664 pszValue += 15;
11665 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
11666 }
11667 else
11668 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
11669 pszValue - 2);
11670 if (offDst + cchSrc < sizeof(szTmp))
11671 {
11672 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
11673 offDst += cchSrc;
11674 continue;
11675 }
11676 }
11677 }
11678 else
11679 {
11680 KSIZE cchSrc = kHlpStrLen(pszValue);
11681 if (offDst + cchSrc < sizeof(szTmp))
11682 {
11683 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11684 offDst += cchSrc;
11685 break;
11686 }
11687 }
11688 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
11689 }
11690 szTmp[offDst] = '\0';
11691
11692 /* Return a copy of it: */
11693 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
11694 if (papszEnvVars[i])
11695 {
11696 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
11697 return 0;
11698 }
11699 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
11700 }
11701
11702 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
11703}
11704
11705
11706/**
11707 * Part 2 of the "JOB" command handler.
11708 *
11709 * @returns The exit code of the job.
11710 * @param pszExecutable The executable to execute.
11711 * @param pszCwd The current working directory of the job.
11712 * @param cArgs The number of arguments.
11713 * @param papszArgs The argument vector.
11714 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11715 * @param cEnvVars The number of environment variables.
11716 * @param papszEnvVars The environment vector.
11717 * @param pszSpecialEnv Name of special environment variable that
11718 * requires selective expansion here.
11719 * @param fNoPchCaching Whether to disable precompiled header file
11720 * caching. Avoid trouble when creating them.
11721 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11722 * @param papszPostCmdArgs The post command and its argument.
11723 */
11724static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
11725 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11726 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
11727 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11728{
11729 int rcExit;
11730 PKWTOOL pTool;
11731 char *pszSpecialEnvFree = NULL;
11732
11733 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
11734 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
11735#ifdef KW_LOG_ENABLED
11736 {
11737 KU32 i;
11738 for (i = 0; i < cArgs; i++)
11739 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
11740 for (i = 0; i < cPostCmdArgs; i++)
11741 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
11742 }
11743#endif
11744 g_cJobs++;
11745
11746 /*
11747 * Expand pszSpecialEnv if present.
11748 */
11749 if (*pszSpecialEnv)
11750 {
11751 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
11752 if (!rcExit)
11753 { /* likely */ }
11754 else
11755 return rcExit;
11756 }
11757
11758 /*
11759 * Lookup the tool.
11760 */
11761 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
11762 if (pTool)
11763 {
11764 /*
11765 * Change the directory if we're going to execute the job inside
11766 * this process. Then invoke the tool type specific handler.
11767 */
11768 switch (pTool->enmType)
11769 {
11770 case KWTOOLTYPE_SANDBOXED:
11771 case KWTOOLTYPE_WATCOM:
11772 {
11773 /* Change dir. */
11774 KFSLOOKUPERROR enmError;
11775 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
11776 if ( pNewCurDir == g_pCurDirObj
11777 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
11778 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11779 else if (SetCurrentDirectoryA(pszCwd))
11780 {
11781 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
11782 g_pCurDirObj = pNewCurDir;
11783 }
11784 else
11785 {
11786 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
11787 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11788 rcExit = 42 + 1;
11789 break;
11790 }
11791
11792 /* Call specific handler. */
11793 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
11794 {
11795 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
11796 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
11797 cEnvVars, papszEnvVars, fNoPchCaching);
11798 }
11799 else
11800 {
11801 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
11802 rcExit = 42 + 2;
11803 }
11804 break;
11805 }
11806
11807 case KWTOOLTYPE_EXEC:
11808 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
11809 rcExit = 42 + 2;
11810 break;
11811
11812 default:
11813 kHlpAssertFailed();
11814 kwErrPrintf("Internal tool type corruption!!\n");
11815 rcExit = 42 + 2;
11816 g_fRestart = K_TRUE;
11817 break;
11818 }
11819
11820 /*
11821 * Do the post command, if present.
11822 */
11823 if (cPostCmdArgs && rcExit == 0)
11824 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
11825 }
11826 else
11827 rcExit = 42 + 1;
11828 if (pszSpecialEnvFree)
11829 {
11830 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
11831 kHlpFree(pszSpecialEnvFree);
11832 }
11833 return rcExit;
11834}
11835
11836
11837/**
11838 * Handles a "JOB" command.
11839 *
11840 * @returns The exit code of the job.
11841 * @param pszMsg Points to the "JOB" command part of the message.
11842 * @param cbMsg Number of message bytes at @a pszMsg. There are
11843 * 4 more zero bytes after the message body to
11844 * simplify parsing.
11845 */
11846static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
11847{
11848 int rcExit = 42;
11849
11850 /*
11851 * Unpack the message.
11852 */
11853 const char *pszExecutable;
11854 KSIZE cbTmp;
11855
11856 pszMsg += sizeof("JOB");
11857 cbMsg -= sizeof("JOB");
11858
11859 /* Executable name. */
11860 pszExecutable = pszMsg;
11861 cbTmp = kHlpStrLen(pszMsg) + 1;
11862 pszMsg += cbTmp;
11863 if ( cbTmp < cbMsg
11864 && cbTmp > 2)
11865 {
11866 const char *pszCwd;
11867 cbMsg -= cbTmp;
11868
11869 /* Current working directory. */
11870 pszCwd = pszMsg;
11871 cbTmp = kHlpStrLen(pszMsg) + 1;
11872 pszMsg += cbTmp;
11873 if ( cbTmp + sizeof(KU32) < cbMsg
11874 && cbTmp >= 2)
11875 {
11876 KU32 cArgs;
11877 cbMsg -= cbTmp;
11878
11879 /* Argument count. */
11880 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
11881 pszMsg += sizeof(cArgs);
11882 cbMsg -= sizeof(cArgs);
11883
11884 if (cArgs > 0 && cArgs < 4096)
11885 {
11886 /* The argument vector. */
11887 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
11888 if (papszArgs)
11889 {
11890 KU32 i;
11891 for (i = 0; i < cArgs; i++)
11892 {
11893 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
11894 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
11895 pszMsg += cbTmp;
11896 if (cbTmp < cbMsg)
11897 cbMsg -= cbTmp;
11898 else
11899 {
11900 cbMsg = 0;
11901 break;
11902 }
11903
11904 }
11905 papszArgs[cArgs] = 0;
11906
11907 /* Environment variable count. */
11908 if (cbMsg > sizeof(KU32))
11909 {
11910 KU32 cEnvVars;
11911 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
11912 pszMsg += sizeof(cEnvVars);
11913 cbMsg -= sizeof(cEnvVars);
11914
11915 if (cEnvVars >= 0 && cEnvVars < 4096)
11916 {
11917 /* The argument vector. */
11918 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
11919 if (papszEnvVars)
11920 {
11921 for (i = 0; i < cEnvVars; i++)
11922 {
11923 papszEnvVars[i] = pszMsg;
11924 cbTmp = kHlpStrLen(pszMsg) + 1;
11925 pszMsg += cbTmp;
11926 if (cbTmp < cbMsg)
11927 cbMsg -= cbTmp;
11928 else
11929 {
11930 cbMsg = 0;
11931 break;
11932 }
11933 }
11934 papszEnvVars[cEnvVars] = 0;
11935
11936 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
11937 if (cbMsg >= sizeof(KU8) * 2)
11938 {
11939 KBOOL fWatcomBrainDamange = *pszMsg++;
11940 KBOOL fNoPchCaching = *pszMsg++;
11941 cbMsg -= 2;
11942
11943 /* Name of special enviornment variable requiring selective expansion. */
11944 if (cbMsg >= 1)
11945 {
11946 const char *pszSpecialEnv = pszMsg;
11947 cbTmp = kHlpStrLen(pszMsg);
11948 pszMsg += cbTmp + 1;
11949 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
11950
11951 /* Post command argument count (can be zero). */
11952 if (cbMsg >= sizeof(KU32))
11953 {
11954 KU32 cPostCmdArgs;
11955 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
11956 pszMsg += sizeof(cPostCmdArgs);
11957 cbMsg -= sizeof(cPostCmdArgs);
11958
11959 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
11960 {
11961 char const *apszPostCmdArgs[32+1];
11962 for (i = 0; i < cPostCmdArgs; i++)
11963 {
11964 apszPostCmdArgs[i] = pszMsg;
11965 cbTmp = kHlpStrLen(pszMsg) + 1;
11966 pszMsg += cbTmp;
11967 if ( cbTmp < cbMsg
11968 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
11969 cbMsg -= cbTmp;
11970 else
11971 {
11972 cbMsg = KSIZE_MAX;
11973 break;
11974 }
11975 }
11976 if (cbMsg == 0)
11977 {
11978 apszPostCmdArgs[cPostCmdArgs] = NULL;
11979
11980 /*
11981 * The next step.
11982 */
11983 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
11984 cArgs, papszArgs, fWatcomBrainDamange,
11985 cEnvVars, papszEnvVars, pszSpecialEnv,
11986 fNoPchCaching,
11987 cPostCmdArgs, apszPostCmdArgs);
11988 }
11989 else if (cbMsg == KSIZE_MAX)
11990 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
11991 else
11992 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
11993 }
11994 else
11995 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
11996 }
11997 else
11998 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
11999 }
12000 else
12001 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12002 }
12003 else
12004 kwErrPrintf("Detected bogus message unpacking flags!\n");
12005 kHlpFree((void *)papszEnvVars);
12006 }
12007 else
12008 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12009 }
12010 else
12011 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12012 }
12013 else
12014 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12015 kHlpFree((void *)papszArgs);
12016 }
12017 else
12018 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12019 }
12020 else
12021 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12022 }
12023 else
12024 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12025 }
12026 else
12027 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12028 return rcExit;
12029}
12030
12031
12032/**
12033 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12034 *
12035 * @retval 0 on success.
12036 * @retval -1 on error (fully bitched).
12037 *
12038 * @param hPipe The pipe handle.
12039 * @param pvBuf The buffer to write out out.
12040 * @param cbToWrite The number of bytes to write.
12041 */
12042static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12043{
12044 KU8 const *pbBuf = (KU8 const *)pvBuf;
12045 KU32 cbLeft = cbToWrite;
12046 while (g_rcCtrlC == 0)
12047 {
12048 DWORD cbActuallyWritten = 0;
12049 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12050 {
12051 cbLeft -= cbActuallyWritten;
12052 if (!cbLeft)
12053 return 0;
12054 pbBuf += cbActuallyWritten;
12055 }
12056 else
12057 {
12058 DWORD dwErr = GetLastError();
12059 if (cbLeft == cbToWrite)
12060 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12061 else
12062 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12063 return -1;
12064 }
12065 }
12066 return -1;
12067}
12068
12069
12070/**
12071 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12072 *
12073 * @retval 0 on success.
12074 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12075 * @retval -1 on error (fully bitched).
12076 * @param hPipe The pipe handle.
12077 * @param pvBuf The buffer to read into.
12078 * @param cbToRead The number of bytes to read.
12079 * @param fShutdownOkay Whether connection shutdown while reading the
12080 * first byte is okay or not.
12081 */
12082static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12083{
12084 KU8 *pbBuf = (KU8 *)pvBuf;
12085 KU32 cbLeft = cbToRead;
12086 while (g_rcCtrlC == 0)
12087 {
12088 DWORD cbActuallyRead = 0;
12089 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12090 {
12091 cbLeft -= cbActuallyRead;
12092 if (!cbLeft)
12093 return 0;
12094 pbBuf += cbActuallyRead;
12095 }
12096 else
12097 {
12098 DWORD dwErr = GetLastError();
12099 if (cbLeft == cbToRead)
12100 {
12101 if ( fMayShutdown
12102 && dwErr == ERROR_BROKEN_PIPE)
12103 return 1;
12104 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12105 }
12106 else
12107 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12108 return -1;
12109 }
12110 }
12111 return -1;
12112}
12113
12114
12115/**
12116 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12117 *
12118 * @returns pszBuf
12119 * @param pszBuf The buffer (sufficiently large).
12120 * @param uValue The value.
12121 */
12122static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12123{
12124 char szTmp[64];
12125 char *psz = &szTmp[63];
12126 int cch = 4;
12127
12128 *psz-- = '\0';
12129 do
12130 {
12131 if (--cch == 0)
12132 {
12133 *psz-- = ' ';
12134 cch = 3;
12135 }
12136 *psz-- = (uValue % 10) + '0';
12137 uValue /= 10;
12138 } while (uValue != 0);
12139
12140 return strcpy(pszBuf, psz + 1);
12141}
12142
12143
12144/**
12145 * Prints statistics.
12146 */
12147static void kwPrintStats(void)
12148{
12149 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12150 MEMORYSTATUSEX MemStatus;
12151 IO_COUNTERS IoCounters;
12152 DWORD cHandles;
12153 KSIZE cMisses;
12154 char szBuf[16*1024];
12155 int off = 0;
12156 char szPrf[24];
12157 char sz1[64];
12158 char sz2[64];
12159 char sz3[64];
12160 char sz4[64];
12161
12162 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12163
12164 szBuf[off++] = '\n';
12165
12166 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12167 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12168 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12169 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12170 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12171
12172 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12173 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12174
12175 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12176 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12177 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12178
12179 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12180 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12181 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12182
12183 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12184 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12185 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12186 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12187 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12188 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12189 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12190
12191 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12192 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12193 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12194 kwFmtU64(sz1, g_pFsCache->cObjects),
12195 kwFmtU64(sz2, g_pFsCache->cbObjects),
12196 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12197 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12198 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12199 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12200 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12201 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12202#ifdef KFSCACHE_CFG_UTF16
12203 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12204 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12205 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12206 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12207 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12208#endif
12209 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12210 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12211 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12212 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12213 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12214
12215 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12216 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12217 kwFmtU64(sz1, g_pFsCache->cLookups),
12218 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12219 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12220 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12221 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12222 kwFmtU64(sz4, cMisses),
12223 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12224
12225 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12226 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12227 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12228 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12229 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12230 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12231 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12232 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12233
12234
12235 /*
12236 * Process & Memory details.
12237 */
12238 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12239 cHandles = 0;
12240 MemInfo.cb = sizeof(MemInfo);
12241 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12242 memset(&MemInfo, 0, sizeof(MemInfo));
12243 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12244 kwFmtU64(sz1, cHandles),
12245 kwFmtU64(sz2, MemInfo.PageFaultCount),
12246 kwFmtU64(sz3, MemInfo.PagefileUsage),
12247 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12248 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12249 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12250 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12251 kwFmtU64(sz3, MemInfo.PrivateUsage));
12252 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12253 szPrf,
12254 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12255 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12256 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12257 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12258
12259 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12260 memset(&IoCounters, 0, sizeof(IoCounters));
12261 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12262 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12263 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12264 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12265 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12266 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12267 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12268 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12269 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12270
12271 MemStatus.dwLength = sizeof(MemStatus);
12272 if (!GlobalMemoryStatusEx(&MemStatus))
12273 memset(&MemStatus, 0, sizeof(MemStatus));
12274 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12275 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12276 MemStatus.ullAvailVirtual);
12277 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12278
12279 maybe_con_fwrite(szBuf, off, 1, stdout);
12280 fflush(stdout);
12281}
12282
12283
12284/**
12285 * Handles what comes after --test.
12286 *
12287 * @returns Exit code.
12288 * @param argc Number of arguments after --test.
12289 * @param argv Arguments after --test.
12290 */
12291static int kwTestRun(int argc, char **argv)
12292{
12293 int i;
12294 int j;
12295 int rcExit;
12296 int cRepeats;
12297 char szCwd[MAX_PATH];
12298 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12299 KU32 cEnvVars;
12300 char **papszEnvVars;
12301 const char *pszSpecialEnv = "";
12302 const char *pszSpecialEnvFull = NULL;
12303 KBOOL fWatcomBrainDamange = K_FALSE;
12304 KBOOL fNoPchCaching = K_FALSE;
12305
12306 /*
12307 * Parse arguments.
12308 */
12309 /* Repeat count. */
12310 i = 0;
12311 if (i >= argc)
12312 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12313 if (strcmp(argv[i], "--") != 0)
12314 {
12315 cRepeats = atoi(argv[i]);
12316 if (cRepeats <= 0)
12317 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12318 i++;
12319
12320 /* Optional directory change. */
12321 if ( i < argc
12322 && ( strcmp(argv[i], "--chdir") == 0
12323 || strcmp(argv[i], "-C") == 0 ) )
12324 {
12325 i++;
12326 if (i >= argc)
12327 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12328 pszCwd = argv[i++];
12329 }
12330
12331 /* Optional watcom flag directory change. */
12332 if ( i < argc
12333 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12334 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12335 {
12336 fWatcomBrainDamange = K_TRUE;
12337 i++;
12338 }
12339
12340 /* Optional watcom flag directory change. */
12341 if ( i < argc
12342 && strcmp(argv[i], "--no-pch-caching") == 0)
12343 {
12344 fNoPchCaching = K_TRUE;
12345 i++;
12346 }
12347
12348 /* Optional directory change. */
12349 if ( i < argc
12350 && ( strcmp(argv[i], "--set-special") == 0
12351 || strcmp(argv[i], "-s") == 0 ) )
12352 {
12353 i++;
12354 if (i >= argc)
12355 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12356 pszSpecialEnvFull = argv[i++];
12357 putenv(pszSpecialEnvFull);
12358 pszSpecialEnv = strdup(pszSpecialEnvFull);
12359 *strchr(pszSpecialEnv, '=') = '\0';
12360 }
12361
12362 /* Trigger breakpoint */
12363 if ( i < argc
12364 && strcmp(argv[i], "--breakpoint") == 0)
12365 {
12366 __debugbreak();
12367 i++;
12368 }
12369
12370 /* Check for '--'. */
12371 if (i >= argc)
12372 return kwErrPrintfRc(2, "Missing '--'\n");
12373 if (strcmp(argv[i], "--") != 0)
12374 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
12375 i++;
12376 }
12377 else
12378 {
12379 cRepeats = 1;
12380 i++;
12381 }
12382 if (i >= argc)
12383 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
12384
12385 /*
12386 * Duplicate the environment.
12387 */
12388 cEnvVars = 0;
12389 while (environ[cEnvVars] != NULL)
12390 cEnvVars++;
12391 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
12392
12393 /*
12394 * Do the job.
12395 */
12396 rcExit = 0;
12397 for (j = 0; j < cRepeats; j++)
12398 {
12399 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
12400 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
12401 argc - i, &argv[i], fWatcomBrainDamange,
12402 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
12403 0, NULL);
12404 KW_LOG(("rcExit=%d\n", rcExit));
12405 kwSandboxCleanupLate(&g_Sandbox);
12406 }
12407
12408 if (getenv("KWORKER_STATS") != NULL)
12409 kwPrintStats();
12410
12411# ifdef WITH_LOG_FILE
12412 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12413 CloseHandle(g_hLogFile);
12414# endif
12415 return rcExit;
12416}
12417
12418
12419/**
12420 * Reads @a pszFile into memory and chops it up into an argument vector.
12421 *
12422 * @returns Pointer to the argument vector on success, NULL on failure.
12423 * @param pszFile The file to load.
12424 * @param pcArgs Where to return the number of arguments.
12425 * @param ppszFileContent Where to return the allocation.
12426 */
12427static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
12428{
12429 char **papszArgs = NULL;
12430 FILE *pFile = fopen(pszFile, "r");
12431 if (pFile)
12432 {
12433 long cbFile;
12434 if ( fseek(pFile, 0, SEEK_END) == 0
12435 && (cbFile = ftell(pFile)) >= 0
12436 && fseek(pFile, 0, SEEK_SET) == 0)
12437 {
12438 char *pszFile = kHlpAllocZ(cbFile + 3);
12439 if (pszFile)
12440 {
12441 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
12442 if ( feof(pFile)
12443 && !ferror(pFile))
12444 {
12445 size_t off = 0;
12446 int cArgs = 0;
12447 int cAllocated = 0;
12448 char ch;
12449
12450 pszFile[cbRead] = '\0';
12451 pszFile[cbRead + 1] = '\0';
12452 pszFile[cbRead + 2] = '\0';
12453
12454 while ((ch = pszFile[off]) != '\0')
12455 {
12456 char *pszArg;
12457 switch (ch)
12458 {
12459 case ' ':
12460 case '\t':
12461 case '\n':
12462 case '\r':
12463 off++;
12464 continue;
12465
12466 case '\\':
12467 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
12468 {
12469 off += 2;
12470 continue;
12471 }
12472 /* fall thru */
12473 default:
12474 pszArg = &pszFile[off];
12475 do
12476 ch = pszFile[++off];
12477 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
12478 pszFile[off++] = '\0';
12479 break;
12480
12481 case '\'':
12482 pszArg = &pszFile[++off];
12483 while ((ch = pszFile[off]) != '\0' && ch != '\'')
12484 off++;
12485 pszFile[off++] = '\0';
12486 break;
12487
12488 case '\"': /** @todo escape sequences */
12489 pszArg = &pszFile[++off];
12490 while ((ch = pszFile[off]) != '\0' && ch != '"')
12491 off++;
12492 pszFile[off++] = '\0';
12493 break;
12494 }
12495 if (cArgs + 1 >= cAllocated)
12496 {
12497 void *pvNew;
12498 cAllocated = cAllocated ? cAllocated * 2 : 16;
12499 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
12500 if (pvNew)
12501 papszArgs = (char **)pvNew;
12502 else
12503 {
12504 kHlpFree(papszArgs);
12505 papszArgs = NULL;
12506 break;
12507 }
12508 }
12509 papszArgs[cArgs] = pszArg;
12510 papszArgs[++cArgs] = NULL;
12511 }
12512 *pcArgs = cArgs;
12513 }
12514 else
12515 kwErrPrintf("Error reading '%s'!\n", pszFile);
12516 }
12517 else
12518 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
12519 }
12520 else
12521 kwErrPrintf("Error seeking '%s'!\n", pszFile);
12522 fclose(pFile);
12523 }
12524 else
12525 kwErrPrintf("Error opening '%s'!\n", pszFile);
12526 return papszArgs;
12527}
12528
12529/**
12530 * Appends a string to an string vector (arguments or enviornment).
12531 *
12532 * @returns 0 on success, non-zero on failure (exit code).
12533 * @param ppapszVector Pointer to the string pointer array.
12534 * @param pcEntries Pointer to the array size.
12535 * @param pszAppend The string to append.
12536 */
12537static int kwFullTestVectorAppend(const char ***ppapszVector, KU32 *pcEntries, char const *pszAppend)
12538{
12539 KU32 cEntries = *pcEntries;
12540 if (!(cEntries & 15))
12541 {
12542 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
12543 if (pvNew)
12544 *ppapszVector = (const char **)pvNew;
12545 else
12546 return kwErrPrintfRc(2, "Out of memory!\n");
12547 }
12548 (*ppapszVector)[cEntries] = pszAppend;
12549 (*ppapszVector)[++cEntries] = NULL;
12550 *pcEntries = cEntries;
12551 return 0;
12552}
12553
12554
12555/**
12556 * Parses arguments for --full-test.
12557 *
12558 * @returns 0 on success, non-zero on failure (exit code).
12559 */
12560static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
12561 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
12562{
12563 PKWONETEST pCur = *ppHead;
12564 int i;
12565 for (i = 0; i < argc; i++)
12566 {
12567 int rc = 0;
12568 const char *pszArg = argv[i];
12569 if (*pszArg == 'k' && kHlpStrComp(pszArg, "kSubmit") == 0)
12570 {
12571 if (*piState != 0)
12572 {
12573 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
12574 if (!pCur)
12575 return kwErrPrintfRc(2, "Out of memory!\n");
12576 pCur->fVirgin = K_TRUE;
12577 pCur->pszCwd = pszDefaultCwd;
12578 pCur->cRuns = 1;
12579 pCur->pNext = *ppHead;
12580 *ppHead = pCur;
12581 *piState = 0;
12582 }
12583 else if (!pCur->fVirgin)
12584 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
12585 pCur->pszJobSrc = pszJobSrc;
12586 pCur->iJobSrc = i;
12587 continue; /* (to stay virgin) */
12588 }
12589 else if (*pszArg == '-' && *piState == 0)
12590 {
12591 const char *pszValue = NULL;
12592 char ch = *++pszArg;
12593 pszArg++;
12594 if (ch == '-')
12595 {
12596 ch = '\0';
12597 if (*pszArg == '\0') /* -- */
12598 *piState = 2;
12599 /* Translate or handle long options: */
12600 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
12601 ch = 'E';
12602 else if (kHlpStrComp(pszArg, "special-env") == 0)
12603 ch = 's';
12604 else if (kHlpStrComp(pszArg, "default-env") == 0)
12605 {
12606 unsigned i;
12607 pCur->cEnvVars = 0;
12608 for (i = 0; environ[i] && rc == 0; i++)
12609 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
12610 }
12611 else if (kHlpStrComp(pszArg, "chdir") == 0)
12612 ch = 'C';
12613 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
12614 ch = 'P';
12615 else if (kHlpStrComp(pszArg, "response-file") == 0)
12616 ch = '@';
12617 else if (kHlpStrComp(pszArg, "runs") == 0)
12618 ch = 'R';
12619 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
12620 pCur->fWatcomBrainDamange = K_TRUE;
12621 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
12622 pCur->fNoPchCaching = K_TRUE;
12623 else if (kHlpStrComp(pszArg, "executable") == 0)
12624 ch = 'e';
12625 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12626 {
12627 __debugbreak();
12628 continue; /* (to stay virgin) */
12629 }
12630 else
12631 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12632 pszArg = "";
12633 }
12634
12635 while (ch != '\0' && rc == 0)
12636 {
12637 /* Fetch value if needed: */
12638 switch (ch)
12639 {
12640 case '@':
12641 case 'e':
12642 case 'E':
12643 case 's':
12644 case 'C':
12645 case 'R':
12646 if (*pszArg == ':' || *pszArg == '=')
12647 pszValue = &pszArg[1];
12648 else if (*pszArg)
12649 pszValue = pszArg;
12650 else if (i + 1 < argc)
12651 pszValue = argv[++i];
12652 else
12653 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
12654 pszArg = "";
12655 break;
12656 }
12657
12658 /* Handle the option: */
12659 switch (ch)
12660 {
12661 case 'E':
12662 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
12663 break;
12664 case 'C':
12665 pCur->pszCwd = pszValue;
12666 break;
12667 case 's':
12668 pCur->pszSpecialEnv = pszValue;
12669 break;
12670 case 'e':
12671 pCur->pszExecutable = pszValue;
12672 break;
12673 case 'P':
12674 *piState = 1;
12675 if (*pszArg)
12676 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
12677 break;
12678 case 'R':
12679 pCur->cRuns = atoi(pszValue);
12680 if ((int)pCur->cRuns < 0)
12681 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
12682 break;
12683 case '@':
12684 if (cRecursions < 5)
12685 {
12686 char *pszLeaked = NULL;
12687 int cArgs = 0;
12688 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
12689 if (papszArgsLeaked)
12690 {
12691 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
12692 cRecursions + 1, pszValue);
12693 pCur = *ppHead;
12694 }
12695 else
12696 return 2;
12697 }
12698 else
12699 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
12700 break;
12701 }
12702
12703 /* next */
12704 ch = *pszArg++;
12705 }
12706 }
12707 else if (*piState == 2)
12708 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
12709 else if (*piState == 1)
12710 {
12711 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
12712 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
12713 else
12714 *piState = 2;
12715 }
12716 else
12717 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
12718 if (rc)
12719 return rc;
12720 pCur->fVirgin = K_FALSE;
12721 }
12722 return 0;
12723}
12724
12725
12726/**
12727 * Handles what comes after --full-test.
12728 *
12729 * @returns Exit code.
12730 * @param argc Number of arguments after --full-test.
12731 * @param argv Arguments after --full-test.
12732 */
12733static int kwFullTestRun(int argc, char **argv)
12734{
12735 char szDefaultCwd[MAX_PATH];
12736 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
12737 KWONETEST FirstTest;
12738 PKWONETEST pHead = &FirstTest;
12739 PKWONETEST pCur;
12740 int iState = 0;
12741 int rcExit;
12742
12743 /*
12744 * Parse arguments.
12745 */
12746 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
12747 FirstTest.pszJobSrc = "command-line";
12748 FirstTest.iJobSrc = 1;
12749 FirstTest.fVirgin = K_TRUE;
12750 FirstTest.pszCwd = pszDefaultCwd;
12751 FirstTest.cRuns = 1;
12752
12753 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
12754 if (rcExit)
12755 return rcExit;
12756
12757 /*
12758 * Do the job. LIFO ordering (see kSubmit).
12759 */
12760 for (pCur = pHead; pCur; pCur = pCur->pNext)
12761 {
12762 if (!pCur->pszExecutable && pCur->papszArgs)
12763 pCur->pszExecutable = pCur->papszArgs[0];
12764 if ( pCur->pszExecutable
12765 && pCur->cArgs > 0
12766 && pCur->cEnvVars > 0)
12767 {
12768 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
12769 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
12770 unsigned iRun;
12771
12772 for (iRun = 0; iRun < pCur->cRuns; iRun++)
12773 {
12774 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
12775 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
12776 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
12777 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
12778
12779 KW_LOG(("rcExit=%d\n", rcExit));
12780 kwSandboxCleanupLate(&g_Sandbox);
12781
12782 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
12783 }
12784 kHlpFree(papszEnvVarsCopy);
12785 }
12786 else
12787 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
12788 pCur->pszExecutable ? "" : " No executable!",
12789 pCur->cArgs < 1 ? " No arguments!" : "",
12790 pCur->cEnvVars < 1 ? " No environment!" : "",
12791 pCur->iJobSrc, pCur->pszJobSrc);
12792 }
12793
12794 if (getenv("KWORKER_STATS") != NULL)
12795 kwPrintStats();
12796
12797# ifdef WITH_LOG_FILE
12798 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12799 CloseHandle(g_hLogFile);
12800# endif
12801 return rcExit;
12802}
12803
12804
12805/**
12806 * Helper for main() argument handling that sets the processor group if
12807 * possible.
12808 */
12809static void kwSetProcessorGroup(unsigned long uGroup)
12810{
12811 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
12812 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
12813 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
12814
12815 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
12816 if (pfnSetThreadGroupAffinity)
12817 {
12818 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
12819 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
12820 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
12821 if (NewAff.Mask && (WORD)uGroup == uGroup)
12822 {
12823 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
12824 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
12825 }
12826 else if (GetLastError() == NO_ERROR)
12827 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
12828 else
12829 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
12830 uGroup, GetLastError());
12831 }
12832 else
12833 {
12834 OSVERSIONINFOA VerInfo = {0};
12835 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
12836 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
12837 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
12838 }
12839}
12840
12841
12842int main(int argc, char **argv)
12843{
12844 KSIZE cbMsgBuf = 0;
12845 KU8 *pbMsgBuf = NULL;
12846 int i;
12847 HANDLE hPipe = INVALID_HANDLE_VALUE;
12848 const char *pszTmp;
12849 KFSLOOKUPERROR enmIgnored;
12850#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
12851 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
12852 kwSandboxVecXcptEmulateChained);
12853#endif
12854#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12855 HANDLE hCurProc = GetCurrentProcess();
12856 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12857 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12858 DWORD dwType;
12859#endif
12860
12861
12862#ifdef WITH_FIXED_VIRTUAL_ALLOCS
12863 /*
12864 * Reserve memory for cl.exe
12865 */
12866 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
12867 {
12868 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
12869 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
12870 g_aFixedVirtualAllocs[i].cbFixed,
12871 MEM_RESERVE, PAGE_READWRITE);
12872 if ( !g_aFixedVirtualAllocs[i].pvReserved
12873 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
12874 {
12875 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
12876 GetLastError());
12877 if (g_aFixedVirtualAllocs[i].pvReserved)
12878 {
12879 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
12880 g_aFixedVirtualAllocs[i].pvReserved = NULL;
12881 }
12882 }
12883 }
12884#endif
12885
12886 /*
12887 * Register our Control-C and Control-Break handlers.
12888 */
12889 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
12890 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
12891
12892 /*
12893 * Create the cache and mark the temporary directory as using the custom revision.
12894 */
12895 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
12896 if (!g_pFsCache)
12897 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
12898
12899 pszTmp = getenv("TEMP");
12900 if (pszTmp && *pszTmp != '\0')
12901 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12902 pszTmp = getenv("TMP");
12903 if (pszTmp && *pszTmp != '\0')
12904 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12905 pszTmp = getenv("TMPDIR");
12906 if (pszTmp && *pszTmp != '\0')
12907 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12908
12909 /*
12910 * Make g_abDefLdBuf executable.
12911 */
12912 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
12913 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
12914 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
12915 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
12916
12917#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12918 /*
12919 * Get and duplicate the console handles.
12920 */
12921 /* Standard output. */
12922 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
12923 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
12924 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12925 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
12926 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
12927 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
12928 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12929 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12930 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
12931 g_Sandbox.HandleStdOut.cRefs = 0x10001;
12932 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
12933 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
12934 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
12935 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
12936 {
12937 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
12938 g_Sandbox.cFixedHandles++;
12939 else
12940 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
12941 }
12942 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12943 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
12944
12945 /* Standard error. */
12946 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
12947 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
12948 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12949 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
12950 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
12951 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
12952 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12953 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12954 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
12955 g_Sandbox.HandleStdErr.cRefs = 0x10001;
12956 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
12957 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
12958 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
12959 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
12960 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
12961 {
12962 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
12963 g_Sandbox.cFixedHandles++;
12964 else
12965 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
12966 }
12967 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12968 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
12969
12970 /* Combined console buffer. */
12971 if (g_Sandbox.StdErr.fIsConsole)
12972 {
12973 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
12974 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12975 }
12976 else if (g_Sandbox.StdOut.fIsConsole)
12977 {
12978 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
12979 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12980 }
12981 else
12982 {
12983 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
12984 g_Sandbox.Combined.uCodepage = CP_ACP;
12985 }
12986 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
12987#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
12988
12989
12990 /*
12991 * Parse arguments.
12992 */
12993 for (i = 1; i < argc; i++)
12994 {
12995 if (strcmp(argv[i], "--pipe") == 0)
12996 {
12997 i++;
12998 if (i < argc)
12999 {
13000 char *pszEnd = NULL;
13001 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13002 if ( *argv[i]
13003 && pszEnd != NULL
13004 && *pszEnd == '\0'
13005 && u64Value != 0
13006 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13007 && (uintptr_t)u64Value == u64Value)
13008 hPipe = (HANDLE)(uintptr_t)u64Value;
13009 else
13010 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13011 }
13012 else
13013 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13014 }
13015 else if (strcmp(argv[i], "--volatile") == 0)
13016 {
13017 i++;
13018 if (i < argc)
13019 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13020 else
13021 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13022 }
13023 else if (strcmp(argv[i], "--test") == 0)
13024 return kwTestRun(argc - i - 1, &argv[i + 1]);
13025 else if (strcmp(argv[i], "--full-test") == 0)
13026 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13027 else if (strcmp(argv[i], "--priority") == 0)
13028 {
13029 i++;
13030 if (i < argc)
13031 {
13032 char *pszEnd = NULL;
13033 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13034 if ( *argv[i]
13035 && pszEnd != NULL
13036 && *pszEnd == '\0'
13037 && uValue >= 1
13038 && uValue <= 5)
13039 {
13040 DWORD dwClass;
13041 int dwPriority;
13042 switch (uValue)
13043 {
13044 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13045 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13046 default:
13047 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13048 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13049 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13050 }
13051 SetPriorityClass(GetCurrentProcess(), dwClass);
13052 if (dwPriority != INT_MAX)
13053 SetThreadPriority(GetCurrentThread(), dwPriority);
13054 }
13055 else
13056 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13057 }
13058 else
13059 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13060 }
13061 else if (strcmp(argv[i], "--group") == 0)
13062 {
13063 i++;
13064 if (i < argc)
13065 {
13066 char *pszEnd = NULL;
13067 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13068 if ( *argv[i]
13069 && pszEnd != NULL
13070 && *pszEnd == '\0'
13071 && uValue == (WORD)uValue)
13072 kwSetProcessorGroup(uValue);
13073 else
13074 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13075 }
13076 else
13077 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13078 }
13079 else if ( strcmp(argv[i], "--help") == 0
13080 || strcmp(argv[i], "-h") == 0
13081 || strcmp(argv[i], "-?") == 0)
13082 {
13083 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13084 "usage: kWorker <--help|-h>\n"
13085 "usage: kWorker <--version|-V>\n"
13086 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13087 "\n"
13088 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13089 return 0;
13090 }
13091 else if ( strcmp(argv[i], "--version") == 0
13092 || strcmp(argv[i], "-V") == 0)
13093 return kbuild_version(argv[0]);
13094 else
13095 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13096 }
13097
13098 /*
13099 * If no --pipe argument, then assume its standard input.
13100 * We need to carefully replace the CRT stdin with a handle to "nul".
13101 */
13102 if (hPipe == INVALID_HANDLE_VALUE)
13103 {
13104 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13105 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13106 {
13107 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13108 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13109 {
13110 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13111 if (fdNul >= 0)
13112 {
13113 if (_dup2(fdNul, 0) >= 0)
13114 {
13115 close(fdNul);
13116 hPipe = hDuplicate;
13117 }
13118 else
13119 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13120 }
13121 else
13122 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13123 }
13124 else
13125 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13126 }
13127 else
13128 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13129 GetFileType(hPipe), GetLastError());
13130 }
13131 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13132 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13133 GetFileType(hPipe), GetLastError());
13134 g_hPipe = hPipe;
13135
13136 /*
13137 * Serve the pipe.
13138 */
13139 for (;;)
13140 {
13141 KU32 cbMsg = 0;
13142 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13143 if (rc == 0)
13144 {
13145 /* Make sure the message length is within sane bounds. */
13146 if ( cbMsg > 4
13147 && cbMsg <= 256*1024*1024)
13148 {
13149 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13150 if (cbMsg + 4 <= cbMsgBuf)
13151 { /* likely */ }
13152 else
13153 {
13154 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13155 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13156 if (!pbMsgBuf)
13157 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13158 }
13159
13160 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13161 *(KU32 *)pbMsgBuf = cbMsg;
13162 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13163 if (rc == 0)
13164 {
13165 const char *psz;
13166
13167 pbMsgBuf[cbMsg] = '\0';
13168 pbMsgBuf[cbMsg + 1] = '\0';
13169 pbMsgBuf[cbMsg + 2] = '\0';
13170 pbMsgBuf[cbMsg + 3] = '\0';
13171
13172 /* The first string after the header is the command. */
13173 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13174 if ( strcmp(psz, "JOB") == 0
13175 && g_rcCtrlC == 0)
13176 {
13177 struct
13178 {
13179 KI32 rcExitCode;
13180 KU8 bExiting;
13181 KU8 abZero[3];
13182 } Reply;
13183 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13184 Reply.bExiting = g_fRestart;
13185 Reply.abZero[0] = 0;
13186 Reply.abZero[1] = 0;
13187 Reply.abZero[2] = 0;
13188 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13189 if ( rc == 0
13190 && !g_fRestart)
13191 {
13192 kwSandboxCleanupLate(&g_Sandbox);
13193 if (g_rcCtrlC == 0)
13194 continue;
13195 }
13196 }
13197 else
13198 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13199 }
13200 }
13201 else
13202 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13203 }
13204
13205 /*
13206 * If we're exitting because we're restarting, we need to delay till
13207 * kmk/kSubmit has read the result. Windows documentation says it
13208 * immediately discards pipe buffers once the pipe is broken by the
13209 * server (us). So, We flush the buffer and queues a 1 byte read
13210 * waiting for kSubmit to close the pipe when it receives the
13211 * bExiting = K_TRUE result.
13212 */
13213 if (g_fRestart)
13214 {
13215 DWORD cbIgnored = 1;
13216 KU8 b;
13217 FlushFileBuffers(hPipe);
13218 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13219 }
13220
13221 CloseHandle(hPipe);
13222#ifdef WITH_LOG_FILE
13223 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13224 CloseHandle(g_hLogFile);
13225#endif
13226 if (getenv("KWORKER_STATS") != NULL)
13227 kwPrintStats();
13228 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13229 }
13230}
13231
13232
13233/** @page pg_kWorker kSubmit / kWorker
13234 *
13235 * @section sec_kWorker_Motivation Motivation / Inspiration
13236 *
13237 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13238 * builds on machines "infested" by Anti Virus protection and disk encryption
13239 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13240 * got "infected".
13241 *
13242 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13243 * mainly a bunch of tiny assembly and C files being compiler a million times.
13244 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13245 * own toolchain from within the same process, saving a lot of process creation
13246 * and teardown overhead.
13247 *
13248 *
13249 * @section sec_kWorker_kSubmit About kSubmit
13250 *
13251 * When wanting to execute a job in a kWorker instance, it must be submitted
13252 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13253 * built into kmk and does not exist as an external program. The reason for
13254 * this is that it keep track of the kWorker instances.
13255 *
13256 * The kSubmit command has the --32-bit and --64-bit options for selecting
13257 * between 32-bit and 64-bit worker instance. We generally assume the user of
13258 * the command knows which bit count the executable has, so kSubmit is spared
13259 * the extra work of finding out.
13260 *
13261 * The kSubmit command shares a environment and current directory manipulation
13262 * with the kRedirect command, but not the file redirection. So long no file
13263 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13264 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13265 * and/or current directory changes to work.
13266 *
13267 * Unlike the kRedirect command, the kSubmit command can also specify an
13268 * internall post command to be executed after the main command succeeds.
13269 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13270 * information from Microsoft COFF object files and Watcom OMF object files and
13271 * is scheduled to replace kDepIDB.
13272 *
13273 *
13274 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13275 *
13276 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13277 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13278 * it. When the job is completed, kWorker writes a short reply with the exit
13279 * code and an internal status indicating whether it is going to restart.
13280 *
13281 * The kWorker intance will reply to kSubmit before completing all the internal
13282 * cleanup work, so as not to delay the next job execution unnecessarily. This
13283 * includes checking its own memory consumption and checking whether it needs
13284 * restarting. So, a decision to restart unfortunately have to wait till after
13285 * the next job has completed. This is a little bit unfortunate if the next job
13286 * requires a lot of memory and kWorker has already leaked/used a lot.
13287 *
13288 *
13289 * @section sec_kWorker_How_Works How kWorker Works
13290 *
13291 * kWorker will load the executable specified by kSubmit into memory and call
13292 * it's entrypoint in a lightly sandbox'ed environment.
13293 *
13294 *
13295 * @subsection ssec_kWorker_Loaing Image loading
13296 *
13297 * kWorker will manually load all the executable images into memory, fix them
13298 * up, and make a copy of the virgin image so it can be restored using memcpy
13299 * the next time it is used.
13300 *
13301 * Imported functions are monitored and replacements used for a few of them.
13302 * These replacements are serve the following purposes:
13303 * - Provide a different command line.
13304 * - Provide a different environment.
13305 * - Intercept process termination.
13306 * - Intercept thread creation (only linker is allowed to create threads).
13307 * - Intercept file reading for caching (header files, ++) as file system
13308 * access is made even slower by anti-virus software.
13309 * - Intercept crypto hash APIs to cache MD5 digests of header files
13310 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13311 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13312 * in memory as writing files grows expensive with encryption and
13313 * anti-virus software active.
13314 * - Intercept some file system queries to use the kFsCache instead of
13315 * going to the kernel and slowly worm thru the AV filter driver.
13316 * - Intercept standard output/error and console writes to aggressivly
13317 * buffer the output. The MS CRT does not buffer either when it goes to
13318 * the console, resulting in terrible performance and mixing up output
13319 * with other compile jobs.
13320 * This also allows us to filter out the annoying source file announcements
13321 * by cl.exe.
13322 * - Intercept VirtualAlloc and VirtualFree to prevent
13323 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13324 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13325 * the callbacks run after each job.
13326 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13327 * executables and tools using custom heaps (like the microsoft linker).
13328 * [exectuable images only]
13329 * - Intercept atexit and _onexit registration to be able run them after
13330 * each job instead of crashing as kWorker exits. This also helps avoid
13331 * some leaks. [executable image only]
13332 *
13333 * DLLs falls into two categories, system DLLs which we always load using the
13334 * native loader, and tool DLLs which can be handled like the executable or
13335 * optionally using the native loader. We maintain a hardcoded white listing of
13336 * tool DLLs we trust to load using the native loader.
13337 *
13338 * Imports of natively loaded DLLs are processed too, but we only replace a
13339 * subset of the functions compared to natively loaded excutable and DLL images.
13340 *
13341 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13342 * This is to speed up job execution.
13343 *
13344 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13345 * for each job run, but so far this hasn't been necessary.
13346 *
13347 *
13348 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
13349 *
13350 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
13351 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
13352 * intermediate representation between the first (c1/c1xx.dll) and second pass
13353 * (c2.dll).
13354 *
13355 * kWorker helps the compiler as best as it can. Given a little knowledge about
13356 * stable and volatile file system areas, it can do a lot of caching that a
13357 * normal compiler driver cannot easily do when given a single file.
13358 *
13359 *
13360 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
13361 *
13362 * The preprocessor part will open and process header files exactly as they are
13363 * encountered in the source files. If string.h is included by the main source
13364 * and five other header files, it will be searched for (include path), opened,
13365 * read, MD5-summed, and pre-processed six times. The last five times is just a
13366 * waste of time because of the guards or \#pragma once. A smart compiler would
13367 * make a little extra effort and realize this.
13368 *
13369 * kWorker will cache help the preprocessor by remembering places where the
13370 * header was not found with help of kFsCache, and cache the file in memory when
13371 * found. The first part is taken care of by intercepting GetFileAttributesW,
13372 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
13373 * cached, the file is kept open and the CreateFileW call returns a duplicate of
13374 * that handle. An internal handle table is used by ReadFile and CloseFile to
13375 * keep track of intercepted handles (also used for temporary file, temporary
13376 * file mappings, console buffering, and standard out/err buffering).
13377 *
13378 * PS. The header search optimization also comes in handy when cl.exe goes on
13379 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
13380 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
13381 * optionally compile the three pass DLLs as executables during development
13382 * and problem analysis.
13383 *
13384 *
13385 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
13386 *
13387 * The issues of the temporary files is pretty severe on the Dell machine used
13388 * for benchmarking with full AV and encryption. The synthetic benchmark
13389 * improved by 30% when kWorker implemented measures to keep them entirely in
13390 * memory.
13391 *
13392 * kWorker implement these by recognizing the filename pattern in CreateFileW
13393 * and creating/opening the given file as needed. The handle returned is a
13394 * duplicate of the current process, thus giving us a good chance of catching
13395 * API calls we're not intercepting.
13396 *
13397 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
13398 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
13399 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
13400 * UnmapViewOfFile.
13401 *
13402 *
13403 * @section sec_kWorker_Numbers Some measurements.
13404 *
13405 * - r2881 building src/VBox/Runtime:
13406 * - without: 2m01.016388s = 120.016388 s
13407 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
13408 * - r2884 building vbox/debug (r110512):
13409 * - without: 11m14.446609s = 674.446609 s
13410 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
13411 * - r2896 building vbox/debug (r110577):
13412 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
13413 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
13414 * MS Defender as AV):
13415 * - without: 10m24.990389s = 624.990389s
13416 * - with: 08m04.738184s = 484.738184s
13417 * - delta: 624.99s - 484.74s = 140.25s
13418 * - saved: 140.25/624.99 = 22% faster
13419 *
13420 *
13421 * @subsection subsec_kWorker_Early_Numbers Early Experiments
13422 *
13423 * These are some early experiments doing 1024 compilations of
13424 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
13425 * main function:
13426 *
13427 * Skylake (W10/amd64, only stdandard MS defender):
13428 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
13429 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
13430 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
13431 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
13432 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
13433 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
13434 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
13435 *
13436 * Dell (W7/amd64, infected by mcafee):
13437 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
13438 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
13439 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
13440 *
13441 * The command line:
13442 * @code{.cpp}
13443 "\"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"
13444 * @endcode
13445 */
13446
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