VirtualBox

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

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

kWorker: CreateFileW/A should must not trust negative caching for volatile areas (TEMP).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 509.5 KB
Line 
1/* $Id: kWorker.c 3365 2020-06-08 23:35:21Z bird $ */
2/** @file
3 * kWorker - experimental process reuse worker for Windows.
4 *
5 * Note! This module must be linked statically in order to avoid
6 * accidentally intercepting our own CRT calls.
7 */
8
9/*
10 * Copyright (c) 2016 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
11 *
12 * This file is part of kBuild.
13 *
14 * kBuild is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * kBuild is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
26 *
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33//#undef NDEBUG
34//#define K_STRICT 1
35//#define KW_LOG_ENABLED
36
37#define PSAPI_VERSION 1
38#include <k/kHlp.h>
39#include <k/kLdr.h>
40
41#include <stdio.h>
42#include <intrin.h>
43#include <setjmp.h>
44#include <ctype.h>
45#include <errno.h>
46#include <process.h>
47
48#include "nt/ntstat.h"
49#include "kbuild_version.h"
50
51#include "nt/ntstuff.h"
52#include "nt/nthlp.h"
53#include <psapi.h>
54
55#include "nt/kFsCache.h"
56#include "nt_fullpath.h"
57#include "win_get_processor_group_active_mask.h"
58#include "quote_argv.h"
59#include "md5.h"
60#include "console.h"
61
62#include "../kmk/kmkbuiltin.h"
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68/** @def WITH_TEMP_MEMORY_FILES
69 * Enables temporary memory files for cl.exe. */
70#define WITH_TEMP_MEMORY_FILES
71
72/** @def WITH_HASH_MD5_CACHE
73 * Enables caching of MD5 sums for cl.exe.
74 * This prevents wasting time on rehashing common headers each time
75 * they are included. */
76#define WITH_HASH_MD5_CACHE
77
78/** @def WITH_CRYPT_CTX_REUSE
79 * Enables reusing crypt contexts. The Visual C++ compiler always creates a
80 * context which is only used for MD5 and maybe some random bytes (VS 2010).
81 * So, only create it once and add a reference to it instead of creating new
82 * ones. Saves registry access among other things. */
83#define WITH_CRYPT_CTX_REUSE
84
85/** @def WITH_CONSOLE_OUTPUT_BUFFERING
86 * Enables buffering of all console output as well as removal of annoying
87 * source file echo by cl.exe. */
88#define WITH_CONSOLE_OUTPUT_BUFFERING
89
90/** @def WITH_STD_OUT_ERR_BUFFERING
91 * Enables buffering of standard output and standard error buffer as well as
92 * removal of annoying source file echo by cl.exe. */
93#define WITH_STD_OUT_ERR_BUFFERING
94
95/** @def WITH_LOG_FILE
96 * Log to file instead of stderr. */
97#define WITH_LOG_FILE
98
99/** @def WITH_HISTORY
100 * Keep history of the last jobs. For debugging. */
101#define WITH_HISTORY
102
103/** @def WITH_FIXED_VIRTUAL_ALLOCS
104 * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
105 * there is only one, but an important one, from cl.exe).
106 */
107#if K_ARCH == K_ARCH_X86_32
108# define WITH_FIXED_VIRTUAL_ALLOCS
109#endif
110
111/** @def WITH_PCH_CACHING
112 * Enables read caching of precompiled header files. */
113#if K_ARCH_BITS >= 64
114# define WITH_PCH_CACHING
115#endif
116
117
118#ifndef NDEBUG
119# define KW_LOG_ENABLED
120#endif
121
122/** @def KW_LOG
123 * Generic logging.
124 * @param a Argument list for kwDbgPrintf */
125#ifdef KW_LOG_ENABLED
126# define KW_LOG(a) kwDbgPrintf a
127#else
128# define KW_LOG(a) do { } while (0)
129#endif
130
131/** @def KWLDR_LOG
132 * Loader related logging.
133 * @param a Argument list for kwDbgPrintf */
134#ifdef KW_LOG_ENABLED
135# define KWLDR_LOG(a) kwDbgPrintf a
136#else
137# define KWLDR_LOG(a) do { } while (0)
138#endif
139
140
141/** @def KWFS_LOG
142 * FS cache logging.
143 * @param a Argument list for kwDbgPrintf */
144#ifdef KW_LOG_ENABLED
145# define KWFS_LOG(a) kwDbgPrintf a
146#else
147# define KWFS_LOG(a) do { } while (0)
148#endif
149
150/** @def KWOUT_LOG
151 * Output related logging.
152 * @param a Argument list for kwDbgPrintf */
153#ifdef KW_LOG_ENABLED
154# define KWOUT_LOG(a) kwDbgPrintf a
155#else
156# define KWOUT_LOG(a) do { } while (0)
157#endif
158
159/** @def KWCRYPT_LOG
160 * FS cache logging.
161 * @param a Argument list for kwDbgPrintf */
162#ifdef KW_LOG_ENABLED
163# define KWCRYPT_LOG(a) kwDbgPrintf a
164#else
165# define KWCRYPT_LOG(a) do { } while (0)
166#endif
167
168/** Converts a windows handle to a handle table index.
169 * @note We currently just mask off the 31th bit, and do no shifting or anything
170 * else to create an index of the handle.
171 * @todo consider shifting by 2 or 3. */
172#define KW_HANDLE_TO_INDEX(a_hHandle) ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
173/** Maximum handle value we can deal with. */
174#define KW_HANDLE_MAX 0x20000
175
176/** Max temporary file size (memory backed). */
177#if K_ARCH_BITS >= 64
178# define KWFS_TEMP_FILE_MAX (256*1024*1024)
179#else
180# define KWFS_TEMP_FILE_MAX (64*1024*1024)
181#endif
182
183/** Marks unfinished code. */
184#if 1
185# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
186#else
187# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
188#endif
189
190/** User data key for tools. */
191#define KW_DATA_KEY_TOOL (~(KUPTR)16381)
192/** User data key for a cached file. */
193#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
194
195/** String constant comma length. */
196#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
197
198
199/**
200 * Generate CRT slot wrapper functions.
201 */
202#define CRT_SLOT_FUNCTION_WRAPPER(a_RetTypeAndCallConv, a_FnName, a_aArgsDecl, a_aArgCall) \
203 static a_RetTypeAndCallConv a_FnName##00 a_aArgsDecl { const unsigned iCrtSlot = 0; return a_FnName##_wrapped a_aArgCall; } \
204 static a_RetTypeAndCallConv a_FnName##01 a_aArgsDecl { const unsigned iCrtSlot = 1; return a_FnName##_wrapped a_aArgCall; } \
205 static a_RetTypeAndCallConv a_FnName##02 a_aArgsDecl { const unsigned iCrtSlot = 2; return a_FnName##_wrapped a_aArgCall; } \
206 static a_RetTypeAndCallConv a_FnName##03 a_aArgsDecl { const unsigned iCrtSlot = 3; return a_FnName##_wrapped a_aArgCall; } \
207 static a_RetTypeAndCallConv a_FnName##04 a_aArgsDecl { const unsigned iCrtSlot = 4; return a_FnName##_wrapped a_aArgCall; } \
208 static a_RetTypeAndCallConv a_FnName##05 a_aArgsDecl { const unsigned iCrtSlot = 5; return a_FnName##_wrapped a_aArgCall; } \
209 static a_RetTypeAndCallConv a_FnName##06 a_aArgsDecl { const unsigned iCrtSlot = 6; return a_FnName##_wrapped a_aArgCall; } \
210 static a_RetTypeAndCallConv a_FnName##07 a_aArgsDecl { const unsigned iCrtSlot = 7; return a_FnName##_wrapped a_aArgCall; } \
211 static a_RetTypeAndCallConv a_FnName##08 a_aArgsDecl { const unsigned iCrtSlot = 8; return a_FnName##_wrapped a_aArgCall; } \
212 static a_RetTypeAndCallConv a_FnName##09 a_aArgsDecl { const unsigned iCrtSlot = 9; return a_FnName##_wrapped a_aArgCall; } \
213 static a_RetTypeAndCallConv a_FnName##10 a_aArgsDecl { const unsigned iCrtSlot = 10; return a_FnName##_wrapped a_aArgCall; } \
214 static a_RetTypeAndCallConv a_FnName##11 a_aArgsDecl { const unsigned iCrtSlot = 11; return a_FnName##_wrapped a_aArgCall; } \
215 static a_RetTypeAndCallConv a_FnName##12 a_aArgsDecl { const unsigned iCrtSlot = 12; return a_FnName##_wrapped a_aArgCall; } \
216 static a_RetTypeAndCallConv a_FnName##13 a_aArgsDecl { const unsigned iCrtSlot = 13; return a_FnName##_wrapped a_aArgCall; } \
217 static a_RetTypeAndCallConv a_FnName##14 a_aArgsDecl { const unsigned iCrtSlot = 14; return a_FnName##_wrapped a_aArgCall; } \
218 static a_RetTypeAndCallConv a_FnName##15 a_aArgsDecl { const unsigned iCrtSlot = 15; return a_FnName##_wrapped a_aArgCall; } \
219 static a_RetTypeAndCallConv a_FnName##16 a_aArgsDecl { const unsigned iCrtSlot = 16; return a_FnName##_wrapped a_aArgCall; } \
220 static a_RetTypeAndCallConv a_FnName##17 a_aArgsDecl { const unsigned iCrtSlot = 17; return a_FnName##_wrapped a_aArgCall; } \
221 static a_RetTypeAndCallConv a_FnName##18 a_aArgsDecl { const unsigned iCrtSlot = 18; return a_FnName##_wrapped a_aArgCall; } \
222 static a_RetTypeAndCallConv a_FnName##19 a_aArgsDecl { const unsigned iCrtSlot = 19; return a_FnName##_wrapped a_aArgCall; } \
223 static a_RetTypeAndCallConv a_FnName##20 a_aArgsDecl { const unsigned iCrtSlot = 20; return a_FnName##_wrapped a_aArgCall; } \
224 static a_RetTypeAndCallConv a_FnName##21 a_aArgsDecl { const unsigned iCrtSlot = 21; return a_FnName##_wrapped a_aArgCall; } \
225 static a_RetTypeAndCallConv a_FnName##22 a_aArgsDecl { const unsigned iCrtSlot = 22; return a_FnName##_wrapped a_aArgCall; } \
226 static a_RetTypeAndCallConv a_FnName##23 a_aArgsDecl { const unsigned iCrtSlot = 23; return a_FnName##_wrapped a_aArgCall; } \
227 static a_RetTypeAndCallConv a_FnName##24 a_aArgsDecl { const unsigned iCrtSlot = 24; return a_FnName##_wrapped a_aArgCall; } \
228 static a_RetTypeAndCallConv a_FnName##25 a_aArgsDecl { const unsigned iCrtSlot = 25; return a_FnName##_wrapped a_aArgCall; } \
229 static a_RetTypeAndCallConv a_FnName##26 a_aArgsDecl { const unsigned iCrtSlot = 26; return a_FnName##_wrapped a_aArgCall; } \
230 static a_RetTypeAndCallConv a_FnName##27 a_aArgsDecl { const unsigned iCrtSlot = 27; return a_FnName##_wrapped a_aArgCall; } \
231 static a_RetTypeAndCallConv a_FnName##28 a_aArgsDecl { const unsigned iCrtSlot = 28; return a_FnName##_wrapped a_aArgCall; } \
232 static a_RetTypeAndCallConv a_FnName##29 a_aArgsDecl { const unsigned iCrtSlot = 29; return a_FnName##_wrapped a_aArgCall; } \
233 static a_RetTypeAndCallConv a_FnName##30 a_aArgsDecl { const unsigned iCrtSlot = 30; return a_FnName##_wrapped a_aArgCall; } \
234 static a_RetTypeAndCallConv a_FnName##31 a_aArgsDecl { const unsigned iCrtSlot = 31; return a_FnName##_wrapped a_aArgCall; } \
235 static const KUPTR a_FnName[] = \
236 { \
237 (KUPTR)a_FnName##00, \
238 (KUPTR)a_FnName##01, \
239 (KUPTR)a_FnName##02, \
240 (KUPTR)a_FnName##03, \
241 (KUPTR)a_FnName##04, \
242 (KUPTR)a_FnName##05, \
243 (KUPTR)a_FnName##06, \
244 (KUPTR)a_FnName##07, \
245 (KUPTR)a_FnName##08, \
246 (KUPTR)a_FnName##09, \
247 (KUPTR)a_FnName##10, \
248 (KUPTR)a_FnName##11, \
249 (KUPTR)a_FnName##12, \
250 (KUPTR)a_FnName##13, \
251 (KUPTR)a_FnName##14, \
252 (KUPTR)a_FnName##15, \
253 (KUPTR)a_FnName##16, \
254 (KUPTR)a_FnName##17, \
255 (KUPTR)a_FnName##18, \
256 (KUPTR)a_FnName##19, \
257 (KUPTR)a_FnName##20, \
258 (KUPTR)a_FnName##21, \
259 (KUPTR)a_FnName##22, \
260 (KUPTR)a_FnName##23, \
261 (KUPTR)a_FnName##24, \
262 (KUPTR)a_FnName##25, \
263 (KUPTR)a_FnName##26, \
264 (KUPTR)a_FnName##27, \
265 (KUPTR)a_FnName##28, \
266 (KUPTR)a_FnName##29, \
267 (KUPTR)a_FnName##30, \
268 (KUPTR)a_FnName##31, \
269 }
270
271
272/*********************************************************************************************************************************
273* Structures and Typedefs *
274*********************************************************************************************************************************/
275typedef enum KWLOCATION
276{
277 KWLOCATION_INVALID = 0,
278 KWLOCATION_EXE_DIR,
279 KWLOCATION_IMPORTER_DIR,
280 KWLOCATION_SYSTEM32,
281 KWLOCATION_UNKNOWN_NATIVE,
282 KWLOCATION_UNKNOWN,
283} KWLOCATION;
284
285typedef enum KWMODSTATE
286{
287 KWMODSTATE_INVALID = 0,
288 KWMODSTATE_NEEDS_BITS,
289 KWMODSTATE_NEEDS_INIT,
290 KWMODSTATE_BEING_INITED,
291 KWMODSTATE_INIT_FAILED,
292 KWMODSTATE_READY,
293} KWMODSTATE;
294
295typedef struct KWMODULE *PKWMODULE;
296typedef struct KWMODULE
297{
298 /** Pointer to the next image withe the same hash. */
299 PKWMODULE pNextHash;
300 /** Pointer to the next image in the global list. */
301 PKWMODULE pNextList;
302 /** The normalized path to the image. */
303 const char *pszPath;
304 /** The hash of the program path. */
305 KU32 uHashPath;
306 /** Number of references. */
307 KU32 cRefs;
308 /** UTF-16 version of pszPath. */
309 const wchar_t *pwszPath;
310 /** The offset of the filename in pszPath. */
311 KU16 offFilename;
312 /** The offset of the filename in pwszPath. */
313 KU16 offFilenameW;
314 /** Set if executable. */
315 KBOOL fExe;
316 /** Set if native module entry. */
317 KBOOL fNative;
318 /** Loader module handle. */
319 PKLDRMOD pLdrMod;
320 /** The windows module handle. */
321 HMODULE hOurMod;
322 /** Parent (real) module if this is a virtual API module (api-ms-*.dll or
323 * ext-ms-*.dll). Referenced. */
324 PKWMODULE pVirtualApiMod;
325 /** The of the loaded image bits. */
326 KSIZE cbImage;
327 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
328 KU8 iCrtSlot;
329 /** Loop prevention when working the tree. */
330 KBOOL fVisited;
331 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
332 KBOOL fNeedReInit;
333 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
334 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
335 KU8 fReInitOnMsPdbSrvEndpointChange;
336 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
337 char *pszMsPdbSrvEndpoint;
338
339 union
340 {
341 /** Data for a manually loaded image. */
342 struct
343 {
344 /** Where we load the image. */
345 KU8 *pbLoad;
346 /** Virgin copy of the image. */
347 KU8 *pbCopy;
348 /** Ldr pvBits argument. This is NULL till we've successfully resolved
349 * the imports. */
350 void *pvBits;
351 /** The state. */
352 KWMODSTATE enmState;
353 /** The re-init state. */
354 KWMODSTATE enmReInitState;
355#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
356 /** The number of entries in the table. */
357 KU32 cFunctions;
358 /** The function table address (in the copy). */
359 PRUNTIME_FUNCTION paFunctions;
360 /** Set if we've already registered a function table already. */
361 KBOOL fRegisteredFunctionTable;
362#endif
363 /** Set if we share memory with other executables. */
364 KBOOL fUseLdBuf;
365 /** Set after the first whole image copy is done. */
366 KBOOL fCanDoQuick;
367 /** Number of quick copy chunks. */
368 KU8 cQuickCopyChunks;
369 /** Number of quick zero chunks. */
370 KU8 cQuickZeroChunks;
371 /** Quicker image copy instructions that skips non-writable parts when
372 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
373 * image. */
374 struct
375 {
376 /** The copy destination. */
377 KU8 *pbDst;
378 /** The copy source. */
379 KU8 const *pbSrc;
380 /** How much to copy. */
381 KSIZE cbToCopy;
382 } aQuickCopyChunks[3];
383 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
384 struct
385 {
386 /** Where to start zeroing. */
387 KU8 *pbDst;
388 /** How much to zero. */
389 KSIZE cbToZero;
390 } aQuickZeroChunks[3];
391
392 /** 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#ifdef WITH_TEMP_MEMORY_FILES
649 KWHANDLETYPE_TEMP_FILE,
650 KWHANDLETYPE_TEMP_FILE_MAPPING,
651#endif
652 KWHANDLETYPE_OUTPUT_BUF
653} KWHANDLETYPE;
654
655/** Handle data. */
656typedef struct KWHANDLE
657{
658 KWHANDLETYPE enmType;
659 /** Number of references */
660 KU32 cRefs;
661 /** The current file offset. */
662 KU32 offFile;
663 /** Handle access. */
664 KU32 dwDesiredAccess;
665 /** The handle. */
666 HANDLE hHandle;
667
668 /** Type specific data. */
669 union
670 {
671 /** The file system object. */
672 PKFSWCACHEDFILE pCachedFile;
673#ifdef WITH_TEMP_MEMORY_FILES
674 /** Temporary file handle or mapping handle. */
675 PKWFSTEMPFILE pTempFile;
676#endif
677#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
678 /** Buffered output stream. */
679 PKWOUTPUTSTREAMBUF pOutBuf;
680#endif
681 } u;
682} KWHANDLE;
683typedef KWHANDLE *PKWHANDLE;
684
685/**
686 * Tracking one of our memory mappings.
687 */
688typedef struct KWMEMMAPPING
689{
690 /** Number of references. */
691 KU32 cRefs;
692 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
693 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
694 KWHANDLETYPE enmType;
695 /** The mapping address. */
696 PVOID pvMapping;
697 /** Type specific data. */
698 union
699 {
700 /** The file system object. */
701 PKFSWCACHEDFILE pCachedFile;
702#ifdef WITH_TEMP_MEMORY_FILES
703 /** Temporary file handle or mapping handle. */
704 PKWFSTEMPFILE pTempFile;
705#endif
706 } u;
707} KWMEMMAPPING;
708/** Pointer to a memory mapping tracker. */
709typedef KWMEMMAPPING *PKWMEMMAPPING;
710
711
712/** Pointer to a VirtualAlloc tracker entry. */
713typedef struct KWVIRTALLOC *PKWVIRTALLOC;
714/**
715 * Tracking an VirtualAlloc allocation.
716 */
717typedef struct KWVIRTALLOC
718{
719 PKWVIRTALLOC pNext;
720 void *pvAlloc;
721 KSIZE cbAlloc;
722 /** This is KU32_MAX if not a preallocated chunk. */
723 KU32 idxPreAllocated;
724} KWVIRTALLOC;
725
726
727/** Pointer to a heap (HeapCreate) tracker entry. */
728typedef struct KWHEAP *PKWHEAP;
729/**
730 * Tracking an heap (HeapCreate)
731 */
732typedef struct KWHEAP
733{
734 PKWHEAP pNext;
735 HANDLE hHeap;
736} KWHEAP;
737
738
739/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
740typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
741/**
742 * Tracking an FlsAlloc/TlsAlloc index.
743 */
744typedef struct KWLOCALSTORAGE
745{
746 PKWLOCALSTORAGE pNext;
747 KU32 idx;
748} KWLOCALSTORAGE;
749
750
751/** Pointer to an at exit callback record */
752typedef struct KWEXITCALLACK *PKWEXITCALLACK;
753/**
754 * At exit callback record.
755 */
756typedef struct KWEXITCALLACK
757{
758 PKWEXITCALLACK pNext;
759 _onexit_t pfnCallback;
760 /** At exit doesn't have an exit code. */
761 KBOOL fAtExit;
762} KWEXITCALLACK;
763
764
765typedef enum KWTOOLTYPE
766{
767 KWTOOLTYPE_INVALID = 0,
768 KWTOOLTYPE_SANDBOXED,
769 KWTOOLTYPE_WATCOM,
770 KWTOOLTYPE_EXEC,
771 KWTOOLTYPE_END
772} KWTOOLTYPE;
773
774typedef enum KWTOOLHINT
775{
776 KWTOOLHINT_INVALID = 0,
777 KWTOOLHINT_NONE,
778 KWTOOLHINT_VISUAL_CPP_CL,
779 KWTOOLHINT_VISUAL_CPP_LINK,
780 KWTOOLHINT_END
781} KWTOOLHINT;
782
783
784/**
785 * A kWorker tool.
786 */
787typedef struct KWTOOL
788{
789 /** The user data core structure. */
790 KFSUSERDATA Core;
791
792 /** The normalized path to the program. */
793 const char *pszPath;
794 /** UTF-16 version of pszPath. */
795 wchar_t const *pwszPath;
796 /** The kind of tool. */
797 KWTOOLTYPE enmType;
798
799 union
800 {
801 struct
802 {
803 /** The main entry point. */
804 KUPTR uMainAddr;
805 /** The executable. */
806 PKWMODULE pExe;
807 /** List of dynamically loaded modules.
808 * These will be kept loaded till the tool is destroyed (if we ever do that). */
809 PKWDYNLOAD pDynLoadHead;
810 /** Module array sorted by hOurMod. */
811 PKWMODULE *papModules;
812 /** Number of entries in papModules. */
813 KU32 cModules;
814
815 /** Tool hint (for hacks and such). */
816 KWTOOLHINT enmHint;
817 } Sandboxed;
818 } u;
819} KWTOOL;
820/** Pointer to a tool. */
821typedef struct KWTOOL *PKWTOOL;
822
823
824typedef struct KWSANDBOX *PKWSANDBOX;
825typedef struct KWSANDBOX
826{
827 /** Jump buffer (first for alignment reasons). */
828 jmp_buf JmpBuf;
829 /** The tool currently running in the sandbox. */
830 PKWTOOL pTool;
831 /** The thread ID of the main thread (owner of JmpBuf). */
832 DWORD idMainThread;
833 /** Copy of the NT TIB of the main thread. */
834 NT_TIB TibMainThread;
835 /** The NT_TIB::ExceptionList value inside the try case.
836 * We restore this prior to the longjmp. */
837 void *pOutXcptListHead;
838 /** The exit code in case of longjmp. */
839 int rcExitCode;
840 /** Set if we're running. */
841 KBOOL fRunning;
842 /** Whether to disable caching of ".pch" files. */
843 KBOOL fNoPchCaching;
844
845 /** The command line. */
846 char *pszCmdLine;
847 /** The UTF-16 command line. */
848 wchar_t *pwszCmdLine;
849 /** Number of arguments in papszArgs. */
850 int cArgs;
851 /** The argument vector. */
852 char **papszArgs;
853 /** The argument vector. */
854 wchar_t **papwszArgs;
855
856 /** The _pgmptr msvcrt variable. */
857 char *pgmptr;
858 /** The _wpgmptr msvcrt variable. */
859 wchar_t *wpgmptr;
860
861 /** The _initenv msvcrt variable. */
862 char **initenv;
863 /** The _winitenv msvcrt variable. */
864 wchar_t **winitenv;
865
866 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
867 KSIZE cEnvVarsAllocated;
868 /** The _environ msvcrt variable. */
869 char **environ;
870 /** The _wenviron msvcrt variable. */
871 wchar_t **wenviron;
872 /** The shadow _environ msvcrt variable. */
873 char **papszEnvVars;
874 /** The shadow _wenviron msvcrt variable. */
875 wchar_t **papwszEnvVars;
876
877
878 /** Handle table. */
879 PKWHANDLE *papHandles;
880 /** Size of the handle table. */
881 KU32 cHandles;
882 /** Number of active handles in the table. */
883 KU32 cActiveHandles;
884 /** Number of handles in the handle table that will not be freed. */
885 KU32 cFixedHandles;
886 /** Total number of leaked handles. */
887 KU32 cLeakedHandles;
888
889 /** Number of active memory mappings in paMemMappings. */
890 KU32 cMemMappings;
891 /** The allocated size of paMemMappings. */
892 KU32 cMemMappingsAlloc;
893 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
894 PKWMEMMAPPING paMemMappings;
895
896#ifdef WITH_TEMP_MEMORY_FILES
897 /** Head of the list of temporary file. */
898 PKWFSTEMPFILE pTempFileHead;
899#endif
900
901 /** Critical section protecting pVirtualAllocHead. */
902 CRITICAL_SECTION VirtualAllocLock;
903 /** Head of the virtual alloc allocations. */
904 PKWVIRTALLOC pVirtualAllocHead;
905 /** Head of the heap list (HeapCreate).
906 * This is only done from images we forcibly restore. */
907 PKWHEAP pHeapHead;
908 /** Head of the FlsAlloc indexes. */
909 PKWLOCALSTORAGE pFlsAllocHead;
910 /** Head of the TlsAlloc indexes. */
911 PKWLOCALSTORAGE pTlsAllocHead;
912
913 /** The at exit callback head.
914 * This is only done from images we forcibly restore. */
915 PKWEXITCALLACK pExitCallbackHead;
916
917 MY_UNICODE_STRING SavedCommandLine;
918
919#ifdef WITH_HASH_MD5_CACHE
920 /** The special MD5 hash instance. */
921 PKWHASHMD5 pHashHead;
922 /** ReadFile sets these while CryptHashData claims and clears them.
923 *
924 * This is part of the heuristics we use for MD5 caching for header files. The
925 * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
926 * header, then passes the same buffer and read byte count to CryptHashData.
927 */
928 struct
929 {
930 /** The cached file last read from. */
931 PKFSWCACHEDFILE pCachedFile;
932 /** The file offset of the last cached read. */
933 KU32 offRead;
934 /** The number of bytes read last. */
935 KU32 cbRead;
936 /** The buffer pointer of the last read. */
937 void *pvRead;
938 } LastHashRead;
939#endif
940
941#ifdef WITH_CRYPT_CTX_REUSE
942 /** Reusable crypt contexts. */
943 struct
944 {
945 /** The creation provider type. */
946 KU32 dwProvType;
947 /** The creation flags. */
948 KU32 dwFlags;
949 /** The length of the container name. */
950 KU32 cwcContainer;
951 /** The length of the provider name. */
952 KU32 cwcProvider;
953 /** The container name string. */
954 wchar_t *pwszContainer;
955 /** The provider name string. */
956 wchar_t *pwszProvider;
957 /** The context handle. */
958 HCRYPTPROV hProv;
959 } aCryptCtxs[4];
960 /** Number of reusable crypt conexts in aCryptCtxs. */
961 KU32 cCryptCtxs;
962#endif
963
964
965#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
966 /** The internal standard output handle. */
967 KWHANDLE HandleStdOut;
968 /** The internal standard error handle. */
969 KWHANDLE HandleStdErr;
970 /** Standard output (and whatever else) buffer. */
971 KWOUTPUTSTREAMBUF StdOut;
972 /** Standard error buffer. */
973 KWOUTPUTSTREAMBUF StdErr;
974 /** Combined buffer of completed lines. */
975 KWCONSOLEOUTPUT Combined;
976#endif
977} KWSANDBOX;
978
979
980/** A CRT slot. */
981typedef struct KWCRTSLOT
982{
983 KU32 iSlot;
984
985 /** The CRT module data. */
986 PKWMODULE pModule;
987 /** Pointer to the malloc function. */
988 void * (__cdecl *pfnMalloc)(size_t);
989 /** Pointer to the beginthreadex function. */
990 uintptr_t (__cdecl *pfnBeginThreadEx)(void *, unsigned, unsigned (__stdcall *)(void *), void *, unsigned, unsigned *);
991
992} KWCRTSLOT;
993typedef KWCRTSLOT *PKWCRTSLOT;
994
995
996/** Replacement function entry. */
997typedef struct KWREPLACEMENTFUNCTION
998{
999 /** The function name. */
1000 const char *pszFunction;
1001 /** The length of the function name. */
1002 KSIZE cchFunction;
1003 /** The module name (optional). */
1004 const char *pszModule;
1005 /** The replacement function, data address or CRT slot function array. */
1006 KUPTR pfnReplacement;
1007 /** Only replace in the executable.
1008 * @todo fix the reinitialization of non-native DLLs! */
1009 KBOOL fOnlyExe;
1010 /** Set if pfnReplacement points to a CRT slot function array. */
1011 KBOOL fCrtSlotArray;
1012} KWREPLACEMENTFUNCTION;
1013typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
1014
1015#if 0
1016/** Replacement function entry. */
1017typedef struct KWREPLACEMENTDATA
1018{
1019 /** The function name. */
1020 const char *pszFunction;
1021 /** The length of the function name. */
1022 KSIZE cchFunction;
1023 /** The module name (optional). */
1024 const char *pszModule;
1025 /** Function providing the replacement. */
1026 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
1027} KWREPLACEMENTDATA;
1028typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
1029#endif
1030
1031/**
1032 * One test job (--full-test).
1033 */
1034typedef struct KWONETEST
1035{
1036 /** Where this job originated. */
1037 const char *pszJobSrc;
1038 /** The argument number it started with. */
1039 unsigned iJobSrc;
1040 /** Set if virgin, clear if modified. */
1041 KBOOL fVirgin;
1042
1043 /** Number of runs to give it. */
1044 unsigned cRuns;
1045
1046 /** @name kSubmitHandleJobUnpacked arguments
1047 * @{ */
1048 const char *pszExecutable;
1049 const char *pszCwd;
1050 KU32 cArgs;
1051 const char **papszArgs;
1052 KU32 cEnvVars;
1053 const char **papszEnvVars;
1054 const char *pszSpecialEnv;
1055 KBOOL fWatcomBrainDamange;
1056 KBOOL fNoPchCaching;
1057 KU32 cPostCmdArgs;
1058 const char **papszPostCmdArgs;
1059 /** @} */
1060
1061 /** Pointer to the next one. */
1062 struct KWONETEST *pNext;
1063} KWONETEST;
1064/** Pointer to one test job. */
1065typedef KWONETEST *PKWONETEST;
1066
1067
1068/*********************************************************************************************************************************
1069* Global Variables *
1070*********************************************************************************************************************************/
1071/** The sandbox data. */
1072static KWSANDBOX g_Sandbox;
1073
1074/** The module currently occupying g_abDefLdBuf. */
1075static PKWMODULE g_pModInLdBuf = NULL;
1076
1077/** The module that previuosly occupied g_abDefLdBuf. */
1078static PKWMODULE g_pModPrevInLdBuf = NULL;
1079
1080/** Module list head. */
1081static PKWMODULE g_pModuleHead = NULL;
1082/** Where to insert the next module. */
1083static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1084
1085/** Module hash table. */
1086static PKWMODULE g_apModules[127];
1087
1088/** GetModuleHandle cache. */
1089static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1090{
1091#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1092 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1093#if 1
1094 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1095 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1096#endif
1097 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1098};
1099
1100/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1101static PKWMODULE g_pModPendingTlsAlloc = NULL;
1102
1103/** The 1KB TLS DLLs. */
1104static KWTLSDLL g_aTls1KDlls[] =
1105{
1106 { L"kWorkerTls1K.dll", K_FALSE },
1107 { L"kWorkerTls1K01.dll", K_FALSE },
1108 { L"kWorkerTls1K02.dll", K_FALSE },
1109 { L"kWorkerTls1K03.dll", K_FALSE },
1110 { L"kWorkerTls1K04.dll", K_FALSE },
1111 { L"kWorkerTls1K05.dll", K_FALSE },
1112 { L"kWorkerTls1K06.dll", K_FALSE },
1113 { L"kWorkerTls1K07.dll", K_FALSE },
1114 { L"kWorkerTls1K08.dll", K_FALSE },
1115 { L"kWorkerTls1K09.dll", K_FALSE },
1116 { L"kWorkerTls1K10.dll", K_FALSE },
1117 { L"kWorkerTls1K11.dll", K_FALSE },
1118 { L"kWorkerTls1K12.dll", K_FALSE },
1119 { L"kWorkerTls1K13.dll", K_FALSE },
1120 { L"kWorkerTls1K14.dll", K_FALSE },
1121 { L"kWorkerTls1K15.dll", K_FALSE },
1122};
1123
1124/** The 64KB TLS DLLs. */
1125static KWTLSDLL g_aTls64KDlls[] =
1126{
1127 { L"kWorkerTls64K.dll", K_FALSE },
1128};
1129
1130/** The 512KB TLS DLLs. */
1131static KWTLSDLL g_aTls512KDlls[] =
1132{
1133 { L"kWorkerTls512K.dll", K_FALSE },
1134};
1135
1136/** The TLS DLLs grouped by size. */
1137static KWTLSDLLENTRY const g_aTlsDlls[] =
1138{
1139 { 1024, K_ELEMENTS(g_aTls1KDlls), g_aTls1KDlls },
1140 { 64*1024, K_ELEMENTS(g_aTls64KDlls), g_aTls64KDlls },
1141 { 512*1024, K_ELEMENTS(g_aTls512KDlls), g_aTls512KDlls },
1142};
1143
1144/** CRT slots.
1145 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1146static KWCRTSLOT g_aCrtSlots[32];
1147
1148/** windbg .reload statements. vs */
1149char g_szReloads[4096];
1150/** Current offset into g_szReloads. */
1151KU32 volatile g_cchReloads;
1152
1153/** The file system cache. */
1154static PKFSCACHE g_pFsCache;
1155/** The current directory (referenced). */
1156static PKFSOBJ g_pCurDirObj = NULL;
1157#ifdef KBUILD_OS_WINDOWS
1158/** The windows system32 directory (referenced). */
1159static PKFSDIR g_pWinSys32 = NULL;
1160#endif
1161
1162/** Verbosity level. */
1163static int g_cVerbose = 2;
1164
1165/** Whether we should restart the worker. */
1166static KBOOL g_fRestart = K_FALSE;
1167
1168/** The process group this worker is tied to (--group option), -1 if none. */
1169static KI32 g_iProcessGroup = -1;
1170
1171/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1172static int volatile g_rcCtrlC = 0;
1173
1174/** The communication pipe handle. We break this when we see Ctrl-C such. */
1175#ifdef KBUILD_OS_WINDOWS
1176static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1177#else
1178static int g_hPipe = -1;
1179#endif
1180
1181
1182/* Further down. */
1183extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1184extern KU32 const g_cSandboxReplacements;
1185
1186extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1187extern KU32 const g_cSandboxNativeReplacements;
1188
1189extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1190extern KU32 const g_cSandboxGetProcReplacements;
1191
1192
1193/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1194 * cover the default executable link address of 0x400000.
1195 * @remarks Early main() makes it read+write+executable. Attempts as having
1196 * it as a separate section failed because the linker insists on
1197 * writing out every zero in the uninitialized section, resulting in
1198 * really big binaries. */
1199__declspec(align(0x1000))
1200static KU8 g_abDefLdBuf[16*1024*1024];
1201
1202#ifdef WITH_LOG_FILE
1203/** Log file handle. */
1204static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1205#endif
1206
1207
1208#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1209/** Virtual address space reserved for CL.EXE heap manager.
1210 *
1211 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1212 * address. It's among other things used for precompiled headers, which
1213 * seemingly have addresses hardcoded into them and won't work if mapped
1214 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1215 * for it. (The /Zm option may affect this allocation.)
1216 */
1217static struct
1218{
1219 /** The memory address we need. */
1220 KUPTR const uFixed;
1221 /** How much we need to fix. */
1222 KSIZE const cbFixed;
1223 /** What we actually got, NULL if given back. */
1224 void *pvReserved;
1225 /** Whether it is in use or not. */
1226 KBOOL fInUse;
1227} g_aFixedVirtualAllocs[] =
1228{
1229# if K_ARCH == K_ARCH_X86_32
1230 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1231 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1232 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1233# else
1234 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1235# endif
1236};
1237#endif
1238
1239
1240#ifdef WITH_HISTORY
1241/** The job history. */
1242static char *g_apszHistory[32];
1243/** Index of the next history entry. */
1244static unsigned g_iHistoryNext = 0;
1245#endif
1246
1247
1248/** Number of jobs executed. */
1249static KU32 g_cJobs;
1250/** Number of tools. */
1251static KU32 g_cTools;
1252/** Number of modules. */
1253static KU32 g_cModules;
1254/** Number of non-native modules. */
1255static KU32 g_cNonNativeModules;
1256/** Number of read-cached files. */
1257static KU32 g_cReadCachedFiles;
1258/** Total size of read-cached files. */
1259static KSIZE g_cbReadCachedFiles;
1260
1261/** Total number of ReadFile calls. */
1262static KSIZE g_cReadFileCalls;
1263/** Total bytes read via ReadFile. */
1264static KSIZE g_cbReadFileTotal;
1265/** Total number of read from read-cached files. */
1266static KSIZE g_cReadFileFromReadCached;
1267/** Total bytes read from read-cached files. */
1268static KSIZE g_cbReadFileFromReadCached;
1269/** Total number of read from in-memory temporary files. */
1270static KSIZE g_cReadFileFromInMemTemp;
1271/** Total bytes read from in-memory temporary files. */
1272static KSIZE g_cbReadFileFromInMemTemp;
1273
1274/** Total number of WriteFile calls. */
1275static KSIZE g_cWriteFileCalls;
1276/** Total bytes written via WriteFile. */
1277static KSIZE g_cbWriteFileTotal;
1278/** Total number of written to from in-memory temporary files. */
1279static KSIZE g_cWriteFileToInMemTemp;
1280/** Total bytes written to in-memory temporary files. */
1281static KSIZE g_cbWriteFileToInMemTemp;
1282
1283
1284/*********************************************************************************************************************************
1285* Internal Functions *
1286*********************************************************************************************************************************/
1287static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1288static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1289 const char *pszSearchPath, PKWMODULE *ppMod);
1290static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1291static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName);
1292static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule);
1293static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod);
1294static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1295static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1296#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1297static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1298#endif
1299static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1300
1301
1302
1303
1304/**
1305 * Debug printing.
1306 * @param pszFormat Debug format string.
1307 * @param ... Format argument.
1308 */
1309static void kwDbgPrintfV(const char *pszFormat, va_list va)
1310{
1311 if (g_cVerbose >= 2)
1312 {
1313 DWORD const dwSavedErr = GetLastError();
1314#ifdef WITH_LOG_FILE
1315 DWORD dwIgnored;
1316 char szTmp[2048];
1317 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1318 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1319 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1320 cch += cchPrefix;
1321 else
1322 {
1323 cch = sizeof(szTmp) - 1;
1324 szTmp[cch] = '\0';
1325 }
1326
1327 if (g_hLogFile == INVALID_HANDLE_VALUE)
1328 {
1329 wchar_t wszFilename[128];
1330 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1331 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1332 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1333 }
1334
1335 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1336#else
1337 fprintf(stderr, "debug: ");
1338 vfprintf(stderr, pszFormat, va);
1339#endif
1340
1341 SetLastError(dwSavedErr);
1342 }
1343}
1344
1345
1346/**
1347 * Debug printing.
1348 * @param pszFormat Debug format string.
1349 * @param ... Format argument.
1350 */
1351static void kwDbgPrintf(const char *pszFormat, ...)
1352{
1353 if (g_cVerbose >= 2)
1354 {
1355 va_list va;
1356 va_start(va, pszFormat);
1357 kwDbgPrintfV(pszFormat, va);
1358 va_end(va);
1359 }
1360}
1361
1362
1363/**
1364 * Debugger printing.
1365 * @param pszFormat Debug format string.
1366 * @param ... Format argument.
1367 */
1368static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1369{
1370 if (IsDebuggerPresent())
1371 {
1372 DWORD const dwSavedErr = GetLastError();
1373 char szTmp[2048];
1374
1375 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1376 OutputDebugStringA(szTmp);
1377
1378 SetLastError(dwSavedErr);
1379 }
1380}
1381
1382
1383/**
1384 * Debugger printing.
1385 * @param pszFormat Debug format string.
1386 * @param ... Format argument.
1387 */
1388static void kwDebuggerPrintf(const char *pszFormat, ...)
1389{
1390 va_list va;
1391 va_start(va, pszFormat);
1392 kwDebuggerPrintfV(pszFormat, va);
1393 va_end(va);
1394}
1395
1396
1397
1398/**
1399 * Error printing.
1400 * @param pszFormat Message format string.
1401 * @param ... Format argument.
1402 */
1403static void kwErrPrintfV(const char *pszFormat, va_list va)
1404{
1405 DWORD const dwSavedErr = GetLastError();
1406
1407 fprintf(stderr, "kWorker: error: ");
1408 vfprintf(stderr, pszFormat, va);
1409 fflush(stderr); /* In case it's a pipe. */
1410
1411 SetLastError(dwSavedErr);
1412}
1413
1414
1415/**
1416 * Error printing.
1417 * @param pszFormat Message format string.
1418 * @param ... Format argument.
1419 */
1420static void kwErrPrintf(const char *pszFormat, ...)
1421{
1422 va_list va;
1423 va_start(va, pszFormat);
1424 kwErrPrintfV(pszFormat, va);
1425 va_end(va);
1426}
1427
1428
1429/**
1430 * Error printing.
1431 * @return rc;
1432 * @param rc Return value
1433 * @param pszFormat Message format string.
1434 * @param ... Format argument.
1435 */
1436static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1437{
1438 va_list va;
1439 va_start(va, pszFormat);
1440 kwErrPrintfV(pszFormat, va);
1441 va_end(va);
1442 return rc;
1443}
1444
1445
1446#ifdef K_STRICT
1447
1448KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1449{
1450 DWORD const dwSavedErr = GetLastError();
1451
1452 fprintf(stderr,
1453 "\n"
1454 "!!Assertion failed!!\n"
1455 "Expression: %s\n"
1456 "Function : %s\n"
1457 "File: %s\n"
1458 "Line: %d\n"
1459 , pszExpr, pszFunction, pszFile, iLine);
1460
1461 SetLastError(dwSavedErr);
1462}
1463
1464
1465KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1466{
1467 DWORD const dwSavedErr = GetLastError();
1468 va_list va;
1469
1470 va_start(va, pszFormat);
1471 fprintf(stderr, pszFormat, va);
1472 va_end(va);
1473
1474 SetLastError(dwSavedErr);
1475}
1476
1477#endif /* K_STRICT */
1478
1479
1480/**
1481 * Hashes a string.
1482 *
1483 * @returns 32-bit string hash.
1484 * @param pszString String to hash.
1485 */
1486static KU32 kwStrHash(const char *pszString)
1487{
1488 /* This algorithm was created for sdbm (a public-domain reimplementation of
1489 ndbm) database library. it was found to do well in scrambling bits,
1490 causing better distribution of the keys and fewer splits. it also happens
1491 to be a good general hashing function with good distribution. the actual
1492 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1493 is the faster version used in gawk. [there is even a faster, duff-device
1494 version] the magic constant 65599 was picked out of thin air while
1495 experimenting with different constants, and turns out to be a prime.
1496 this is one of the algorithms used in berkeley db (see sleepycat) and
1497 elsewhere. */
1498 KU32 uHash = 0;
1499 KU32 uChar;
1500 while ((uChar = (unsigned char)*pszString++) != 0)
1501 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1502 return uHash;
1503}
1504
1505
1506/**
1507 * Hashes a string.
1508 *
1509 * @returns The string length.
1510 * @param pszString String to hash.
1511 * @param puHash Where to return the 32-bit string hash.
1512 */
1513static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1514{
1515 const char * const pszStart = pszString;
1516 KU32 uHash = 0;
1517 KU32 uChar;
1518 while ((uChar = (unsigned char)*pszString) != 0)
1519 {
1520 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1521 pszString++;
1522 }
1523 *puHash = uHash;
1524 return pszString - pszStart;
1525}
1526
1527
1528/**
1529 * Hashes a string.
1530 *
1531 * @returns The string length in wchar_t units.
1532 * @param pwszString String to hash.
1533 * @param puHash Where to return the 32-bit string hash.
1534 */
1535static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1536{
1537 const wchar_t * const pwszStart = pwszString;
1538 KU32 uHash = 0;
1539 KU32 uChar;
1540 while ((uChar = *pwszString) != 0)
1541 {
1542 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1543 pwszString++;
1544 }
1545 *puHash = uHash;
1546 return pwszString - pwszStart;
1547}
1548
1549
1550/**
1551 * Converts the given string to unicode.
1552 *
1553 * @returns Length of the resulting string in wchar_t's.
1554 * @param pszSrc The source string.
1555 * @param pwszDst The destination buffer.
1556 * @param cwcDst The size of the destination buffer in wchar_t's.
1557 */
1558static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1559{
1560 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1561 KSIZE offDst = 0;
1562 while (offDst < cwcDst)
1563 {
1564 char ch = *pszSrc++;
1565 pwszDst[offDst++] = ch;
1566 if (!ch)
1567 return offDst - 1;
1568 kHlpAssert((unsigned)ch < 127);
1569 }
1570
1571 pwszDst[offDst - 1] = '\0';
1572 return offDst;
1573}
1574
1575
1576/**
1577 * Converts the given string to UTF-16, allocating the buffer.
1578 *
1579 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1580 * the source string.
1581 * @param pchSrc The source string.
1582 * @param cchSrc The length of the source string.
1583 */
1584static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1585{
1586 DWORD const dwErrSaved = GetLastError();
1587 KSIZE cwcBuf = cchSrc + 1;
1588 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1589 if (pwszBuf)
1590 {
1591 if (cchSrc > 0)
1592 {
1593 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1594 if (cwcRet > 0)
1595 {
1596 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1597 pwszBuf[cwcRet] = '\0';
1598 }
1599 else
1600 {
1601 kHlpFree(pwszBuf);
1602
1603 /* Figure the length and allocate the right buffer size. */
1604 SetLastError(NO_ERROR);
1605 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1606 if (cwcRet)
1607 {
1608 cwcBuf = cwcRet + 2;
1609 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1610 if (pwszBuf)
1611 {
1612 SetLastError(NO_ERROR);
1613 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1614 if (cwcRet)
1615 {
1616 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1617 pwszBuf[cwcRet] = '\0';
1618 }
1619 else
1620 {
1621 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1622 kHlpFree(pwszBuf);
1623 pwszBuf = NULL;
1624 }
1625 }
1626 }
1627 else
1628 {
1629 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1630 pwszBuf = NULL;
1631 }
1632 }
1633 }
1634 else
1635 pwszBuf[0] = '\0';
1636 }
1637 SetLastError(dwErrSaved);
1638 return pwszBuf;
1639}
1640
1641
1642/**
1643 * Converts the given UTF-16 to a normal string.
1644 *
1645 * @returns Length of the resulting string.
1646 * @param pwszSrc The source UTF-16 string.
1647 * @param pszDst The destination buffer.
1648 * @param cbDst The size of the destination buffer in bytes.
1649 */
1650static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1651{
1652 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1653 KSIZE offDst = 0;
1654 while (offDst < cbDst)
1655 {
1656 wchar_t wc = *pwszSrc++;
1657 pszDst[offDst++] = (char)wc;
1658 if (!wc)
1659 return offDst - 1;
1660 kHlpAssert((unsigned)wc < 127);
1661 }
1662
1663 pszDst[offDst - 1] = '\0';
1664 return offDst;
1665}
1666
1667
1668/**
1669 * Converts the given UTF-16 to ASSI, allocating the buffer.
1670 *
1671 * @returns Pointer to the new heap allocation containing the ANSI version of
1672 * the source string.
1673 * @param pwcSrc The source string.
1674 * @param cwcSrc The length of the source string.
1675 */
1676static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1677{
1678 DWORD const dwErrSaved = GetLastError();
1679 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1680 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1681 if (pszBuf)
1682 {
1683 if (cwcSrc > 0)
1684 {
1685 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1686 if (cchRet > 0)
1687 {
1688 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1689 pszBuf[cchRet] = '\0';
1690 }
1691 else
1692 {
1693 kHlpFree(pszBuf);
1694
1695 /* Figure the length and allocate the right buffer size. */
1696 SetLastError(NO_ERROR);
1697 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1698 if (cchRet)
1699 {
1700 cbBuf = cchRet + 2;
1701 pszBuf = (char *)kHlpAlloc(cbBuf);
1702 if (pszBuf)
1703 {
1704 SetLastError(NO_ERROR);
1705 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1706 if (cchRet)
1707 {
1708 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1709 pszBuf[cchRet] = '\0';
1710 }
1711 else
1712 {
1713 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1714 kHlpFree(pszBuf);
1715 pszBuf = NULL;
1716 }
1717 }
1718 }
1719 else
1720 {
1721 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1722 pszBuf = NULL;
1723 }
1724 }
1725 }
1726 else
1727 pszBuf[0] = '\0';
1728 }
1729 SetLastError(dwErrSaved);
1730 return pszBuf;
1731}
1732
1733
1734
1735/** UTF-16 string length. */
1736static KSIZE kwUtf16Len(wchar_t const *pwsz)
1737{
1738 KSIZE cwc = 0;
1739 while (*pwsz != '\0')
1740 cwc++, pwsz++;
1741 return cwc;
1742}
1743
1744/**
1745 * Copy out the UTF-16 string following the convension of GetModuleFileName
1746 */
1747static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1748{
1749 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1750 if (cwcSrc + 1 <= cwcDst)
1751 {
1752 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1753 return (DWORD)cwcSrc;
1754 }
1755 if (cwcDst > 0)
1756 {
1757 KSIZE cwcDstTmp = cwcDst - 1;
1758 pwszDst[cwcDstTmp] = '\0';
1759 if (cwcDstTmp > 0)
1760 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1761 }
1762 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1763 return (DWORD)cwcDst;
1764}
1765
1766
1767/**
1768 * Copy out the ANSI string following the convension of GetModuleFileName
1769 */
1770static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1771{
1772 KSIZE cchSrc = kHlpStrLen(pszSrc);
1773 if (cchSrc + 1 <= cbDst)
1774 {
1775 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1776 return (DWORD)cchSrc;
1777 }
1778 if (cbDst > 0)
1779 {
1780 KSIZE cbDstTmp = cbDst - 1;
1781 pszDst[cbDstTmp] = '\0';
1782 if (cbDstTmp > 0)
1783 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1784 }
1785 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1786 return (DWORD)cbDst;
1787}
1788
1789
1790/**
1791 * Normalizes the path so we get a consistent hash.
1792 *
1793 * @returns status code.
1794 * @param pszPath The path.
1795 * @param pszNormPath The output buffer.
1796 * @param cbNormPath The size of the output buffer.
1797 */
1798static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1799{
1800 KFSLOOKUPERROR enmError;
1801 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1802 if (pFsObj)
1803 {
1804 KBOOL fRc;
1805 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1806 kFsCacheObjRelease(g_pFsCache, pFsObj);
1807 if (fRc)
1808 return 0;
1809 return KERR_BUFFER_OVERFLOW;
1810 }
1811 return KERR_FILE_NOT_FOUND;
1812}
1813
1814
1815/**
1816 * Get the pointer to the filename part of the path.
1817 *
1818 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1819 * @returns Pointer to the terminator char if no filename.
1820 * @param pszPath The path to parse.
1821 */
1822static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1823{
1824 const wchar_t *pwszLast = NULL;
1825 for (;;)
1826 {
1827 wchar_t wc = *pwszPath;
1828#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1829 if (wc == '/' || wc == '\\' || wc == ':')
1830 {
1831 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1832 /* nothing */;
1833 pwszLast = pwszPath;
1834 }
1835#else
1836 if (wc == '/')
1837 {
1838 while ((wc = *++pszFilename) == '/')
1839 /* betsuni */;
1840 pwszLast = pwszPath;
1841 }
1842#endif
1843 if (!wc)
1844 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1845 pwszPath++;
1846 }
1847}
1848
1849
1850
1851/**
1852 * Retains a new reference to the given module
1853 * @returns pMod
1854 * @param pMod The module to retain.
1855 */
1856static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1857{
1858 kHlpAssert(pMod->cRefs > 0);
1859 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1860 pMod->cRefs++;
1861 return pMod;
1862}
1863
1864
1865/**
1866 * Releases a module reference.
1867 *
1868 * @param pMod The module to release.
1869 */
1870static void kwLdrModuleRelease(PKWMODULE pMod)
1871{
1872 if (--pMod->cRefs == 0)
1873 {
1874 /* Unlink it from the hash table. */
1875 if (!pMod->fExe)
1876 {
1877 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1878 if (g_apModules[idx] == pMod)
1879 g_apModules[idx] = pMod->pNextHash;
1880 else
1881 {
1882 PKWMODULE pPrev = g_apModules[idx];
1883 kHlpAssert(pPrev != NULL);
1884 while (pPrev->pNextHash != pMod)
1885 {
1886 pPrev = pPrev->pNextHash;
1887 kHlpAssert(pPrev != NULL);
1888 }
1889 pPrev->pNextHash = pMod->pNextHash;
1890 }
1891 }
1892
1893 /* Unlink it from the list. */
1894 if (pMod != g_pModuleHead)
1895 {
1896 PKWMODULE pPrev = g_pModuleHead;
1897 while (pPrev)
1898 {
1899 if (pPrev->pNextList == pMod)
1900 {
1901 pPrev->pNextList = pMod->pNextList;
1902 if (!pMod->pNextList)
1903 g_ppModuleNext = &pPrev->pNextList;
1904 break;
1905 }
1906 pPrev = pPrev->pNextList;
1907 }
1908 kHlpAssert(pPrev != NULL);
1909 }
1910 else
1911 {
1912 g_pModuleHead = pMod->pNextList;
1913 if (!pMod->pNextList)
1914 g_ppModuleNext = &g_pModuleHead;
1915 }
1916
1917 /* Release import modules. */
1918 if (!pMod->fNative)
1919 {
1920 KSIZE idx = pMod->u.Manual.cImpMods;
1921 while (idx-- > 0)
1922 if (pMod->u.Manual.apImpMods[idx])
1923 {
1924 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1925 pMod->u.Manual.apImpMods[idx] = NULL;
1926 }
1927 }
1928
1929 /* Free our resources. */
1930 kLdrModClose(pMod->pLdrMod);
1931 pMod->pLdrMod = NULL;
1932
1933 if (!pMod->fNative)
1934 {
1935 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1936 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1937 }
1938
1939 if (pMod->iCrtSlot != KU8_MAX)
1940 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
1941
1942 if (pMod->pszMsPdbSrvEndpoint)
1943 {
1944 kHlpFree(pMod->pszMsPdbSrvEndpoint);
1945 pMod->pszMsPdbSrvEndpoint = NULL;
1946 }
1947
1948 kHlpFree(pMod);
1949 }
1950 else
1951 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1952}
1953
1954
1955/**
1956 * Links the module into the module hash table.
1957 *
1958 * @returns pMod
1959 * @param pMod The module to link.
1960 */
1961static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
1962{
1963 if (!pMod->fExe)
1964 {
1965 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1966 pMod->pNextHash = g_apModules[idx];
1967 g_apModules[idx] = pMod;
1968 }
1969
1970 pMod->pNextList = NULL;
1971 *g_ppModuleNext = pMod;
1972 g_ppModuleNext = &pMod->pNextList;
1973
1974 return pMod;
1975}
1976
1977
1978/**
1979 * Replaces imports for this module according to g_aSandboxNativeReplacements.
1980 *
1981 * @param pMod The natively loaded module to process.
1982 */
1983static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
1984{
1985 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
1986 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
1987 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
1988 IMAGE_NT_HEADERS const *pNtHdrs;
1989 IMAGE_DATA_DIRECTORY const *pDirEnt;
1990
1991 kHlpAssert(pMod->fNative);
1992
1993 /*
1994 * Locate the export descriptors.
1995 */
1996 /* MZ header. */
1997 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
1998 {
1999 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
2000 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
2001 }
2002 else
2003 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
2004
2005 /* Check PE header. */
2006 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2007 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
2008
2009 /* Locate the import descriptor array. */
2010 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2011 if ( pDirEnt->Size > 0
2012 && pDirEnt->VirtualAddress != 0)
2013 {
2014 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
2015 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
2016 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
2017 KU8 *pbProtRange = NULL;
2018 SIZE_T cbProtRange = 0;
2019 DWORD fOldProt = 0;
2020 KU32 const cbPage = 0x1000;
2021 BOOL fRc;
2022
2023
2024 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
2025 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
2026 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
2027
2028 /*
2029 * Walk the import descriptor array.
2030 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
2031 */
2032 while ( cLeft-- > 0
2033 && pImpDesc->Name > 0
2034 && pImpDesc->FirstThunk > 0)
2035 {
2036 KU32 iThunk;
2037 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
2038 PKWMODULE pImportMod = NULL;
2039 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
2040 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
2041 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
2042 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
2043 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
2044 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
2045 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
2046
2047 /* Iterate the thunks. */
2048 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
2049 {
2050 KUPTR const off = paOrgThunks[iThunk].u1.Function;
2051 kHlpAssertReturnVoid(off < cbImage);
2052 if (!IMAGE_SNAP_BY_ORDINAL(off))
2053 {
2054 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
2055 KSIZE const cchSymbol = kHlpStrLen((const char *)&pName->Name[0]);
2056 KU32 i = g_cSandboxNativeReplacements;
2057 while (i-- > 0)
2058 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
2059 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
2060 {
2061 if ( !g_aSandboxNativeReplacements[i].pszModule
2062 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
2063 {
2064 KWLDR_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
2065
2066 /* The .rdata section is normally read-only, so we need to make it writable first. */
2067 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
2068 {
2069 /* Restore previous .rdata page. */
2070 if (fOldProt)
2071 {
2072 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
2073 kHlpAssert(fRc);
2074 fOldProt = 0;
2075 }
2076
2077 /* Query attributes for the current .rdata page. */
2078 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
2079 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
2080 kHlpAssert(cbProtRange);
2081 if (cbProtRange)
2082 {
2083 switch (ProtInfo.Protect)
2084 {
2085 case PAGE_READWRITE:
2086 case PAGE_WRITECOPY:
2087 case PAGE_EXECUTE_READWRITE:
2088 case PAGE_EXECUTE_WRITECOPY:
2089 /* Already writable, nothing to do. */
2090 fRc = TRUE;
2091 break;
2092
2093 default:
2094 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2095 case PAGE_READONLY:
2096 cbProtRange = cbPage;
2097 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2098 break;
2099
2100 case PAGE_EXECUTE:
2101 case PAGE_EXECUTE_READ:
2102 cbProtRange = cbPage;
2103 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2104 break;
2105 }
2106 kHlpAssertStmt(fRc, fOldProt = 0);
2107 }
2108 }
2109
2110 /*
2111 * Unslotted replacements are simple.
2112 */
2113 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2114 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2115 else
2116 {
2117 /*
2118 * Must find our module entry for this module, possibly creating one.
2119 */
2120 if (!pImportMod)
2121 {
2122 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2123 K_TRUE /*fAlwaysPresent*/);
2124 if (!pImportMod)
2125 {
2126 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2127 pszImport, pMod->pszPath);
2128 break;
2129 }
2130 }
2131 paThunks[iThunk].u1.AddressOfData
2132 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2133 }
2134 break;
2135 }
2136 }
2137 }
2138 }
2139
2140
2141 /* Next import descriptor. */
2142 pImpDesc++;
2143 }
2144
2145
2146 if (fOldProt)
2147 {
2148 DWORD fIgnore = 0;
2149 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2150 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2151 }
2152 }
2153
2154}
2155
2156
2157/**
2158 * Creates a module from a native kLdr module handle.
2159 *
2160 * @returns Module w/ 1 reference on success, NULL on failure.
2161 * @param pLdrMod The native kLdr module.
2162 * @param pszPath The normalized path to the module.
2163 * @param cbPath The module path length with terminator.
2164 * @param uHashPath The module path hash.
2165 * @param fDoReplacements Whether to do import replacements on this
2166 * module.
2167 */
2168static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2169 KBOOL fDoReplacements, PKWMODULE pVirtualApiMod)
2170{
2171 /*
2172 * Create the entry.
2173 */
2174 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2175 if (pMod)
2176 {
2177 pMod->pwszPath = (wchar_t *)(pMod + 1);
2178 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2179 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2180 pMod->uHashPath = uHashPath;
2181 pMod->cRefs = 1;
2182 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2183 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2184 pMod->fExe = K_FALSE;
2185 pMod->fNative = K_TRUE;
2186 pMod->pLdrMod = pLdrMod;
2187 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2188 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2189 pMod->iCrtSlot = KU8_MAX;
2190 pMod->fNeedReInit = K_FALSE;
2191 pMod->pszMsPdbSrvEndpoint = NULL;
2192 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2193 pMod->pVirtualApiMod = pVirtualApiMod;
2194 if (pVirtualApiMod)
2195 kwLdrModuleRetain(pVirtualApiMod);
2196
2197 if (fDoReplacements)
2198 {
2199 DWORD const dwSavedErr = GetLastError();
2200 kwLdrModuleDoNativeImportReplacements(pMod);
2201 SetLastError(dwSavedErr);
2202 }
2203
2204 KWLDR_LOG(("New module: %p LB %#010x %s (native%s%s)\n",
2205 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath,
2206 pVirtualApiMod ? ", virtual api => " : "", pVirtualApiMod ? pVirtualApiMod->pszPath : ""));
2207 g_cModules++;
2208 return kwLdrModuleLink(pMod);
2209 }
2210 return NULL;
2211}
2212
2213
2214
2215/**
2216 * Creates a module using the native loader.
2217 *
2218 * @returns Module w/ 1 reference on success, NULL on failure.
2219 * @param pszPath The normalized path to the module.
2220 * @param uHashPath The module path hash.
2221 * @param fDoReplacements Whether to do import replacements on this
2222 * module.
2223 */
2224static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2225{
2226 PKLDRMOD pLdrMod;
2227 int rc;
2228
2229 /*
2230 * HACK ALERT! Make sure the application path is searched when looking for
2231 * imports in the module we're loading.
2232 */
2233 /** @todo improve on this hack! */
2234 PKWMODULE pExe = g_Sandbox.pTool ? g_Sandbox.pTool->u.Sandboxed.pExe : NULL;
2235 if (pExe)
2236 {
2237 /* HACK ALERT! */
2238 wchar_t *pwzFilename = (wchar_t *)&pExe->pwszPath[pExe->offFilenameW];
2239 wchar_t wcSaved = pExe->pwszPath[pExe->offFilenameW];
2240 *pwzFilename = '\0';
2241 if (!SetDllDirectoryW(pExe->pwszPath))
2242 kwErrPrintf("SetDllDirectoryW failed: %u\n", GetLastError());
2243 *pwzFilename = wcSaved;
2244 }
2245
2246 /*
2247 * Load the library and create a module structure for it.
2248 */
2249 rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2250 if (rc == 0)
2251 {
2252 KSIZE cchPath = kHlpStrLen(pszPath);
2253 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, cchPath + 1, uHashPath,
2254 fDoReplacements, NULL /*pVirtualApiMod*/);
2255 if (pMod)
2256 return pMod;
2257 kLdrModClose(pLdrMod);
2258 }
2259 return NULL;
2260}
2261
2262
2263/**
2264 * Checks if the given name could be a virtual API module or not.
2265 */
2266static KBOOL kwLdrIsVirtualApiModule(const char *pszName, KSIZE cchName)
2267{
2268 if (cchName <= 7)
2269 return K_FALSE;
2270 switch (*pszName)
2271 {
2272 default:
2273 return K_FALSE;
2274 case 'a':
2275 case 'A':
2276 if (pszName[1] != 'p' && pszName[1] != 'P')
2277 return K_FALSE;
2278 if (pszName[2] != 'i' && pszName[2] != 'I')
2279 return K_FALSE;
2280 break;
2281 case 'e':
2282 case 'E':
2283 if (pszName[1] != 'x' && pszName[1] != 'X')
2284 return K_FALSE;
2285 if (pszName[2] != 't' && pszName[2] != 'T')
2286 return K_FALSE;
2287 break;
2288 }
2289 if (pszName[3] != '-')
2290 return K_FALSE;
2291 if (pszName[4] != 'm' && pszName[4] != 'M')
2292 return K_FALSE;
2293 if (pszName[5] != 's' && pszName[5] != 'S')
2294 return K_FALSE;
2295 if (pszName[6] != '-')
2296 return K_FALSE;
2297 return K_TRUE;
2298}
2299
2300
2301/**
2302 * Try load what seems to be a virtual API DLL.
2303 *
2304 * This is a worker for kwLdrModuleResolveAndLookup and
2305 * kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule.
2306 *
2307 * @returns Pointer to module on success, NULL on failure.
2308 * @param pszName The name of the module. This must be
2309 * normalized already!
2310 * @param cchName The length of the name.
2311 */
2312static PKWMODULE kwLdrModuleTryLoadVirtualDll(const char *pszName, KSIZE cchName)
2313{
2314 HMODULE hModule;
2315
2316 /*
2317 * Look it up in the hash table.
2318 */
2319 KU32 const uHashPath = kwStrHash(pszName);
2320 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2321 PKWMODULE pMod = g_apModules[idxHash];
2322 if (pMod)
2323 {
2324 do
2325 {
2326 if ( pMod->uHashPath == uHashPath
2327 && kHlpStrComp(pMod->pszPath, pszName) == 0)
2328 return kwLdrModuleRetain(pMod);
2329 pMod = pMod->pNextHash;
2330 } while (pMod);
2331 }
2332
2333 /*
2334 * Not found. Try load it.
2335 */
2336 hModule = LoadLibraryA(pszName);
2337 if (!hModule)
2338 {
2339 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s failed (%u)\n", pszName, GetLastError()));
2340 return NULL;
2341 }
2342
2343 /*
2344 * Loaded successfully. Create a module for the real module.
2345 */
2346 pMod = kwLdrModuleForLoadedNativeByHandle(hModule, K_FALSE /*fEnsureCrtSlot*/, pszName);
2347 if (pMod)
2348 {
2349 /* Create a module for the virtual API name too, unless it is actually a real DLL. */
2350 if (stricmp(&pMod->pszPath[pMod->offFilename], pszName) != 0)
2351 {
2352 PKLDRMOD pLdrMod;
2353 int rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2354 if (rc == 0)
2355 {
2356 PKWMODULE pVirtMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszName, cchName + 1, kwStrHash(pszName),
2357 K_FALSE /*fDoReplacements*/, pMod /*pVirtualApiMod*/);
2358 if (pVirtMod)
2359 {
2360 kwLdrModuleRelease(pMod);
2361 pMod = pVirtMod;
2362 }
2363 else
2364 {
2365 kLdrModClose(pLdrMod);
2366 kwErrPrintf("out of memory\n");
2367 }
2368 }
2369 else
2370 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
2371 }
2372 else
2373 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s -> %s - A real DLL!\n", pszName, pMod->pszPath));
2374 }
2375
2376 return pMod;
2377}
2378
2379
2380/**
2381 * Sets up the quick zero & copy tables for the non-native module.
2382 *
2383 * This is a worker for kwLdrModuleCreateNonNative.
2384 *
2385 * @param pMod The module.
2386 */
2387static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2388{
2389 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2390 KU32 cSegs = pMod->pLdrMod->cSegments;
2391 KU32 iSeg;
2392
2393 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2394 pMod->u.Manual.cQuickCopyChunks = 0;
2395 pMod->u.Manual.cQuickZeroChunks = 0;
2396
2397 for (iSeg = 0; iSeg < cSegs; iSeg++)
2398 switch (paSegs[iSeg].enmProt)
2399 {
2400 case KPROT_READWRITE:
2401 case KPROT_WRITECOPY:
2402 case KPROT_EXECUTE_READWRITE:
2403 case KPROT_EXECUTE_WRITECOPY:
2404 if (paSegs[iSeg].cbMapped)
2405 {
2406 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2407 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2408 {
2409 /*
2410 * Check for trailing zero words.
2411 */
2412 KSIZE cbTrailingZeros;
2413 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2414 && (paSegs[iSeg].cbMapped & 7) == 0
2415 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2416 {
2417 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2418 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2419 KSIZE idxFirstZero = cNatural;
2420 while (idxFirstZero > 0)
2421 if (pauNatural[--idxFirstZero] == 0)
2422 { /* likely */ }
2423 else
2424 {
2425 idxFirstZero++;
2426 break;
2427 }
2428 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2429 if (cbTrailingZeros < 128)
2430 cbTrailingZeros = 0;
2431 }
2432 else
2433 cbTrailingZeros = 0;
2434
2435 /*
2436 * Add quick copy entry.
2437 */
2438 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2439 {
2440 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2441 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2442 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2443 pMod->u.Manual.cQuickCopyChunks = (KU8)(iChunk + 1);
2444 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2445 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2446 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2447 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2448 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2449 }
2450
2451 /*
2452 * Add quick zero entry.
2453 */
2454 if (cbTrailingZeros)
2455 {
2456 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2457 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2458 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2459 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2460 pMod->u.Manual.cQuickZeroChunks = (KU8)(iZero + 1);
2461 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2462 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2463 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2464 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2465 }
2466 }
2467 else
2468 {
2469 /*
2470 * We're out of quick copy table entries, so just copy the whole darn thing.
2471 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2472 */
2473 kHlpAssertFailed();
2474 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2475 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2476 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2477 pMod->u.Manual.cQuickCopyChunks = 1;
2478 KWLDR_LOG(("Quick copy not possible!\n"));
2479 return;
2480 }
2481 }
2482 break;
2483
2484 default:
2485 break;
2486 }
2487}
2488
2489
2490/**
2491 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2492 *
2493 * @param hDll The DLL handle.
2494 * @param idxTls The allocated TLS index.
2495 * @param ppfnTlsCallback Pointer to the TLS callback table entry.
2496 */
2497__declspec(dllexport) void kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, PIMAGE_TLS_CALLBACK *ppfnTlsCallback)
2498{
2499 /*
2500 * Do the module initialization thing first.
2501 */
2502 PKWMODULE pMod = g_pModPendingTlsAlloc;
2503 if (pMod)
2504 {
2505 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2506 LIST_ENTRY *pHead;
2507 LIST_ENTRY *pCur;
2508
2509 pMod->u.Manual.idxTls = idxTls;
2510 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2511
2512 /*
2513 * Try sabotage the DLL name so we can load this module again.
2514 */
2515/** @todo this doesn't work W10 18363 */
2516 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2517 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2518 {
2519 LDR_DATA_TABLE_ENTRY *pMte;
2520 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2521 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2522 {
2523 PUNICODE_STRING pStr = &pMte->FullDllName;
2524 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2525 pStr->Buffer[--off]++;
2526 pStr->Buffer[--off]++;
2527 pStr->Buffer[--off]++;
2528 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2529 break;
2530 }
2531 }
2532 }
2533}
2534
2535
2536/**
2537 * Allocates and initializes TLS variables.
2538 *
2539 * @returns 0 on success, non-zero failure.
2540 * @param pMod The module.
2541 */
2542static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2543{
2544 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2545 IMAGE_NT_HEADERS const *pNtHdrs;
2546 IMAGE_DATA_DIRECTORY const *pTlsDir;
2547
2548 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2549 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2550 else
2551 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2552 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2553
2554 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2555 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2556 {
2557 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2558 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2559 KU32 iEntry;
2560 KU32 iTlsDll;
2561 KU32 iTlsDllSub;
2562 KUPTR offIndex;
2563 KUPTR offCallbacks;
2564 KUPTR const *puCallbacks;
2565 KSIZE cbData;
2566 const wchar_t *pwszTlsDll;
2567 HMODULE hmodTlsDll;
2568
2569 /*
2570 * Check and log.
2571 */
2572 for (iEntry = 0; iEntry < cEntries; iEntry++)
2573 {
2574 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2575 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2576 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2577 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2578 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2579 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2580 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2581
2582 if (offIndex >= pMod->cbImage)
2583 {
2584 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2585 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2586 return -1;
2587 }
2588 if (offCallbacks >= pMod->cbImage)
2589 {
2590 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2591 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2592 return -1;
2593 }
2594 while (*puCallbacks != 0)
2595 {
2596 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2597 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2598 puCallbacks++;
2599 }
2600 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2601 {
2602 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2603 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2604 return -1;
2605 }
2606 }
2607
2608 if (cEntries > 1)
2609 {
2610 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2611 return -1;
2612 }
2613
2614 /*
2615 * Make the allocation by loading a new instance of one of the TLS dlls.
2616 * The DLL will make a call to kwLdrTlsAllocationHook.
2617 */
2618 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2619 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2620 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2621 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2622
2623 /** @todo find better strategy here. Like temporary copy or whatever when
2624 * there is more than a single user. */
2625 for (iTlsDll = 0; cbData > g_aTlsDlls[iTlsDll].cbTls;)
2626 if (++iTlsDll >= K_ELEMENTS(g_aTlsDlls))
2627 {
2628 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2629 return -1;
2630 }
2631 for (iTlsDllSub = 0; g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed;)
2632 if (++iTlsDllSub >= g_aTlsDlls[iTlsDll].cDlls)
2633 {
2634 kwErrPrintf("No unused TLS DLLs for %s of size %u!\n", pMod->pszPath, (unsigned)cbData);
2635 return -1;
2636 }
2637
2638 g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed = K_TRUE;
2639 pwszTlsDll = g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].pwszName;
2640
2641 pMod->u.Manual.idxTls = KU32_MAX;
2642 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2643 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2644 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2645 pMod->u.Manual.cTlsCallbacks = 0;
2646 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2647 pMod->u.Manual.cTlsCallbacks++;
2648 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2649
2650 g_pModPendingTlsAlloc = pMod;
2651 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2652 g_pModPendingTlsAlloc = NULL;
2653 if (hmodTlsDll == NULL)
2654 {
2655 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2656 return -1;
2657 }
2658 if (pMod->u.Manual.idxTls == KU32_MAX)
2659 {
2660 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2661 return -1;
2662 }
2663
2664 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2665 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x\n",
2666 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData));
2667 }
2668 return 0;
2669}
2670
2671
2672/**
2673 * Creates a module using the our own loader.
2674 *
2675 * @returns Module w/ 1 reference on success, NULL on failure.
2676 * @param pszPath The normalized path to the module.
2677 * @param uHashPath The module path hash.
2678 * @param fExe K_TRUE if this is an executable image, K_FALSE
2679 * if not. Executable images does not get entered
2680 * into the global module table.
2681 * @param pExeMod The executable module of the process (for
2682 * resolving imports). NULL if fExe is set.
2683 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2684 */
2685static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2686 PKWMODULE pExeMod, const char *pszSearchPath)
2687{
2688 /*
2689 * Open the module and check the type.
2690 */
2691 PKLDRMOD pLdrMod;
2692 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2693 if (rc == 0)
2694 {
2695 switch (pLdrMod->enmType)
2696 {
2697 case KLDRTYPE_EXECUTABLE_FIXED:
2698 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2699 case KLDRTYPE_EXECUTABLE_PIC:
2700 if (!fExe)
2701 rc = KERR_GENERAL_FAILURE;
2702 break;
2703
2704 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2705 case KLDRTYPE_SHARED_LIBRARY_PIC:
2706 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2707 if (fExe)
2708 rc = KERR_GENERAL_FAILURE;
2709 break;
2710
2711 default:
2712 rc = KERR_GENERAL_FAILURE;
2713 break;
2714 }
2715 if (rc == 0)
2716 {
2717 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2718 if (cImports >= 0)
2719 {
2720 /*
2721 * Create the entry.
2722 */
2723 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2724 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2725 + sizeof(pMod) * cImports
2726 + cbPath
2727 + cbPath * 2 * sizeof(wchar_t));
2728 if (pMod)
2729 {
2730 KBOOL fFixed;
2731
2732 pMod->cRefs = 1;
2733 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2734 pMod->uHashPath = uHashPath;
2735 pMod->fExe = fExe;
2736 pMod->fNative = K_FALSE;
2737 pMod->pLdrMod = pLdrMod;
2738 pMod->iCrtSlot = KU8_MAX;
2739 pMod->fNeedReInit = K_FALSE;
2740 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2741 pMod->pszMsPdbSrvEndpoint = NULL;
2742 pMod->u.Manual.cImpMods = (KU32)cImports;
2743#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2744 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2745#endif
2746 pMod->u.Manual.fUseLdBuf = K_FALSE;
2747 pMod->u.Manual.fCanDoQuick = K_FALSE;
2748 pMod->u.Manual.cQuickZeroChunks = 0;
2749 pMod->u.Manual.cQuickCopyChunks = 0;
2750 pMod->u.Manual.idxTls = KU32_MAX;
2751 pMod->u.Manual.offTlsInitData = KU32_MAX;
2752 pMod->u.Manual.cbTlsInitData = 0;
2753 pMod->u.Manual.cbTlsAlloc = 0;
2754 pMod->u.Manual.cTlsCallbacks = 0;
2755 pMod->u.Manual.offTlsCallbacks = 0;
2756 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2757 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2758 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2759 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2760
2761 /*
2762 * Figure out where to load it and get memory there.
2763 */
2764 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2765 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2766 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2767 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2768 if ( !fFixed
2769 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2770 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2771 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2772 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2773 else
2774 pMod->u.Manual.fUseLdBuf = K_TRUE;
2775 if (rc == 0)
2776 {
2777 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2778 if (rc == 0)
2779 {
2780 KI32 iImp;
2781 KU32 cchReloads;
2782
2783 /*
2784 * Link the module (unless it's an executable image) and process the imports.
2785 */
2786 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2787 kwLdrModuleLink(pMod);
2788 KWLDR_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2789 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2790 KWLDR_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2791 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2792 cchReloads = g_cchReloads;
2793 if (cchReloads + 80 < sizeof(g_szReloads))
2794 {
2795 cchReloads += _snprintf(&g_szReloads[cchReloads], sizeof(g_szReloads) - cchReloads,
2796 "%s.reload /f %s=%p\n", cchReloads ? "; " : "",
2797 pMod->pszPath, pMod->u.Manual.pbLoad);
2798 g_cchReloads = cchReloads;
2799 }
2800
2801 for (iImp = 0; iImp < cImports; iImp++)
2802 {
2803 char szName[1024];
2804 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2805 if (rc == 0)
2806 {
2807 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2808 &pMod->u.Manual.apImpMods[iImp]);
2809 if (rc == 0)
2810 continue;
2811 }
2812 break;
2813 }
2814
2815 if (rc == 0)
2816 {
2817 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2818 kwLdrModuleGetImportCallback, pMod);
2819 if (rc == 0)
2820 {
2821#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2822 /*
2823 * Find the function table. No validation here because the
2824 * loader did that already, right...
2825 */
2826 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2827 IMAGE_NT_HEADERS const *pNtHdrs;
2828 IMAGE_DATA_DIRECTORY const *pXcptDir;
2829 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2830 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2831 else
2832 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2833 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2834 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2835 if (pXcptDir->Size > 0)
2836 {
2837 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2838 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2839 == pXcptDir->Size);
2840 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2841 }
2842 else
2843 {
2844 pMod->u.Manual.cFunctions = 0;
2845 pMod->u.Manual.paFunctions = NULL;
2846 }
2847#endif
2848
2849 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2850
2851 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2852 if (rc == 0)
2853 {
2854 /*
2855 * Final finish.
2856 */
2857 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2858 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2859 pMod->u.Manual.enmReInitState = KWMODSTATE_NEEDS_BITS;
2860 if ( g_Sandbox.pTool
2861 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
2862 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
2863 && !pMod->fExe)
2864 pMod->u.Manual.enmReInitState = KWMODSTATE_READY;
2865 g_cModules++;
2866 g_cNonNativeModules++;
2867 return pMod;
2868 }
2869 }
2870 else
2871 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
2872 }
2873
2874 kwLdrModuleRelease(pMod);
2875 return NULL;
2876 }
2877
2878 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2879 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2880 }
2881 else if (fFixed)
2882 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
2883 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
2884 else
2885 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2886 }
2887 }
2888 }
2889 kLdrModClose(pLdrMod);
2890 }
2891 else
2892 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
2893 return NULL;
2894}
2895
2896
2897/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
2898static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
2899 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
2900{
2901 PKWMODULE pCurMod = (PKWMODULE)pvUser;
2902 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
2903 int rc;
2904 K_NOREF(pMod);
2905
2906 if (pImpMod->fNative)
2907 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
2908 iSymbol, pchSymbol, cchSymbol, pszVersion,
2909 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2910 puValue, pfKind);
2911 else
2912 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
2913 iSymbol, pchSymbol, cchSymbol, pszVersion,
2914 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2915 puValue, pfKind);
2916 if (rc == 0)
2917 {
2918 KU32 i = g_cSandboxReplacements;
2919 while (i-- > 0)
2920 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
2921 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
2922 {
2923 if ( !g_aSandboxReplacements[i].pszModule
2924 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
2925 {
2926 if ( pCurMod->fExe
2927 || !g_aSandboxReplacements[i].fOnlyExe)
2928 {
2929 KWLDR_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
2930 if (!g_aSandboxReplacements[i].fCrtSlotArray)
2931 *puValue = g_aSandboxReplacements[i].pfnReplacement;
2932 else
2933 {
2934 if (pImpMod->iCrtSlot == KU8_MAX)
2935 {
2936 rc = kwLdrModuleCreateCrtSlot(pImpMod);
2937 if (rc)
2938 KWLDR_LOG(("kwLdrModuleGetImportCallback: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
2939 }
2940 *puValue = ((KUPTR *)g_aSandboxReplacements[i].pfnReplacement)[pImpMod->iCrtSlot];
2941 }
2942 }
2943 break;
2944 }
2945 }
2946 }
2947
2948 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
2949 KWLDR_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
2950 return rc;
2951
2952}
2953
2954
2955/**
2956 * Gets the main entrypoint for a module.
2957 *
2958 * @returns 0 on success, KERR on failure
2959 * @param pMod The module.
2960 * @param puAddrMain Where to return the address.
2961 */
2962static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
2963{
2964 KLDRADDR uLdrAddrMain;
2965 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
2966 if (rc == 0)
2967 {
2968 *puAddrMain = (KUPTR)uLdrAddrMain;
2969 return 0;
2970 }
2971 return rc;
2972}
2973
2974
2975/**
2976 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
2977 *
2978 * @returns K_TRUE/K_FALSE.
2979 * @param pszFilename The filename (no path).
2980 * @param enmLocation The location.
2981 */
2982static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
2983{
2984 if (enmLocation != KWLOCATION_SYSTEM32)
2985 return K_TRUE;
2986 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2987 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2988 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
2989 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
2990 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
2991 || kHlpStrNICompAscii(pszFilename, TUPLE("api-ms-win-crt-")) == 0
2992 ;
2993}
2994
2995
2996/**
2997 * Lazily initializes the g_pWinSys32 variable.
2998 */
2999static PKFSDIR kwLdrResolveWinSys32(void)
3000{
3001 KFSLOOKUPERROR enmError;
3002 PKFSDIR pWinSys32;
3003
3004 /* Get the path first. */
3005 char szSystem32[MAX_PATH];
3006 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
3007 {
3008 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
3009 strcpy(szSystem32, "C:\\Windows\\System32");
3010 }
3011
3012 /* Look it up and verify it. */
3013 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
3014 if (pWinSys32)
3015 {
3016 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
3017 {
3018 g_pWinSys32 = pWinSys32;
3019 return pWinSys32;
3020 }
3021
3022 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
3023 }
3024 else
3025 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
3026 return NULL;
3027}
3028
3029
3030/**
3031 * Whether we can load this DLL natively or not.
3032 *
3033 * @returns K_TRUE/K_FALSE.
3034 * @param pszFilename The filename (no path).
3035 * @param enmLocation The location.
3036 * @param pszFullPath The full filename and path.
3037 */
3038static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
3039{
3040 if (enmLocation == KWLOCATION_SYSTEM32)
3041 return K_TRUE;
3042 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
3043 return K_TRUE;
3044 if ( enmLocation == KWLOCATION_UNKNOWN
3045 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3046 return K_TRUE;
3047
3048 /* If the location is unknown, we must check if it's some dynamic loading
3049 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
3050 if (enmLocation == KWLOCATION_UNKNOWN)
3051 {
3052 PKFSDIR pWinSys32 = g_pWinSys32;
3053 if (!pWinSys32)
3054 pWinSys32 = kwLdrResolveWinSys32();
3055 if (pWinSys32)
3056 {
3057 KFSLOOKUPERROR enmError;
3058 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
3059 if (pFsObj)
3060 {
3061 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
3062 kFsCacheObjRelease(g_pFsCache, pFsObj);
3063 if (fInWinSys32)
3064 return K_TRUE;
3065 }
3066 }
3067 }
3068
3069 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3070 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3071 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3072 || kHlpStrNICompAscii(pszFilename, TUPLE("tbbmalloc")) == 0
3073 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3074 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3075 || ( enmLocation != KWLOCATION_UNKNOWN
3076 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)));
3077}
3078
3079
3080/**
3081 * Check if the path leads to a regular file (that exists).
3082 *
3083 * @returns K_TRUE / K_FALSE
3084 * @param pszPath Path to the file to check out.
3085 */
3086static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
3087{
3088 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
3089 KSIZE cchPath = kHlpStrLen(pszPath);
3090 if ( cchPath > 3
3091 && pszPath[cchPath - 4] == '.'
3092 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
3093 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
3094 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
3095 {
3096 KFSLOOKUPERROR enmError;
3097 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3098 if (pFsObj)
3099 {
3100 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
3101 kFsCacheObjRelease(g_pFsCache, pFsObj);
3102 return fRc;
3103 }
3104 }
3105 else
3106 {
3107 BirdStat_T Stat;
3108 int rc = birdStatFollowLink(pszPath, &Stat);
3109 if (rc == 0)
3110 {
3111 if (S_ISREG(Stat.st_mode))
3112 return K_TRUE;
3113 }
3114 }
3115 return K_FALSE;
3116}
3117
3118
3119/**
3120 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
3121 *
3122 * If the file exists, we consult the module hash table before trying to load it
3123 * off the disk.
3124 *
3125 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
3126 * failure.
3127 * @param pszPath The name of the import module.
3128 * @param enmLocation The location we're searching. This is used in
3129 * the heuristics for determining if we can use the
3130 * native loader or need to sandbox the DLL.
3131 * @param pExe The executable (optional).
3132 * @param pszSearchPath The PATH to search (optional).
3133 */
3134static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
3135{
3136 /*
3137 * Does the file exists and is it a regular file?
3138 */
3139 if (kwLdrModuleIsRegularFile(pszPath))
3140 {
3141 /*
3142 * Yes! Normalize it and look it up in the hash table.
3143 */
3144 char szNormPath[1024];
3145 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
3146 if (rc == 0)
3147 {
3148 const char *pszName;
3149 KU32 const uHashPath = kwStrHash(szNormPath);
3150 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3151 PKWMODULE pMod = g_apModules[idxHash];
3152 if (pMod)
3153 {
3154 do
3155 {
3156 if ( pMod->uHashPath == uHashPath
3157 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3158 return kwLdrModuleRetain(pMod);
3159 pMod = pMod->pNextHash;
3160 } while (pMod);
3161 }
3162
3163 /*
3164 * Not in the hash table, so we have to load it from scratch.
3165 */
3166 pszName = kHlpGetFilename(szNormPath);
3167 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
3168 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
3169 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
3170 else
3171 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
3172 if (pMod)
3173 return pMod;
3174 return (PKWMODULE)~(KUPTR)0;
3175 }
3176 }
3177 return NULL;
3178}
3179
3180
3181/**
3182 * Gets a reference to the module by the given name.
3183 *
3184 * We must do the search path thing, as our hash table may multiple DLLs with
3185 * the same base name due to different tools version and similar. We'll use a
3186 * modified search sequence, though. No point in searching the current
3187 * directory for instance.
3188 *
3189 * @returns 0 on success, KERR on failure.
3190 * @param pszName The name of the import module.
3191 * @param pExe The executable (optional).
3192 * @param pImporter The module doing the importing (optional).
3193 * @param pszSearchPath The PATH to search (optional).
3194 * @param ppMod Where to return the module pointer w/ reference.
3195 */
3196static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
3197 const char *pszSearchPath, PKWMODULE *ppMod)
3198{
3199 KSIZE const cchName = kHlpStrLen(pszName);
3200 char szPath[1024];
3201 char *psz;
3202 PKWMODULE pMod = NULL;
3203 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
3204 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
3205
3206 /* Virtual API module. Normalize and try load it. */
3207 if (pMod == NULL && cchName > 7 && kwLdrIsVirtualApiModule(pszName, cchName))
3208 {
3209 if (cchName + cchSuffix >= sizeof(szPath))
3210 return KERR_BUFFER_OVERFLOW;
3211 kHlpMemCopy(szPath, pszName, cchName);
3212 if (fNeedSuffix)
3213 kHlpMemCopy(&szPath[cchName], ".dll", sizeof(".dll"));
3214 szPath[cchName + cchSuffix] = '\0';
3215 _strlwr(szPath);
3216 pMod = kwLdrModuleTryLoadVirtualDll(szPath, cchName + cchSuffix);
3217 }
3218
3219 /* The import path. */
3220 if (pMod == NULL && pImporter != NULL)
3221 {
3222 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
3223 return KERR_BUFFER_OVERFLOW;
3224
3225 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
3226 if (fNeedSuffix)
3227 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3228 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
3229 }
3230
3231 /* Application directory first. */
3232 if (pMod == NULL && pExe != NULL && pExe != pImporter)
3233 {
3234 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
3235 return KERR_BUFFER_OVERFLOW;
3236 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
3237 if (fNeedSuffix)
3238 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3239 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
3240 }
3241
3242 /* The windows directory. */
3243 if (pMod == NULL)
3244 {
3245 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
3246 if ( cchDir <= 2
3247 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
3248 return KERR_BUFFER_OVERFLOW;
3249 szPath[cchDir++] = '\\';
3250 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
3251 if (fNeedSuffix)
3252 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3253 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3254 }
3255
3256 /* The path. */
3257 if ( pMod == NULL
3258 && pszSearchPath)
3259 {
3260 const char *pszCur = pszSearchPath;
3261 while (*pszCur != '\0')
3262 {
3263 /* Find the end of the component */
3264 KSIZE cch = 0;
3265 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
3266 cch++;
3267
3268 if ( cch > 0 /* wrong, but whatever */
3269 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
3270 {
3271 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
3272 if ( szPath[cch - 1] != ':'
3273 && szPath[cch - 1] != '/'
3274 && szPath[cch - 1] != '\\')
3275 *pszDst++ = '\\';
3276 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
3277 if (fNeedSuffix)
3278 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
3279 *pszDst = '\0';
3280
3281 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3282 if (pMod)
3283 break;
3284 }
3285
3286 /* Advance */
3287 pszCur += cch;
3288 while (*pszCur == ';')
3289 pszCur++;
3290 }
3291 }
3292
3293 /* Return. */
3294 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3295 {
3296 *ppMod = pMod;
3297 return 0;
3298 }
3299 *ppMod = NULL;
3300 return KERR_GENERAL_FAILURE;
3301}
3302
3303
3304/**
3305 * Creates a CRT slot for the given module.
3306 *
3307 * @returns 0 on success, non-zero on failure.
3308 * @param pModule The module.
3309 */
3310static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3311{
3312 KSIZE iSlot;
3313 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3314 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3315 if (g_aCrtSlots[iSlot].pModule == NULL)
3316 {
3317 KLDRADDR uAddr;
3318 int rc;
3319
3320 /* Do the linking: */
3321 g_aCrtSlots[iSlot].pModule = pModule;
3322 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3323 pModule->iCrtSlot = (KU8)iSlot;
3324
3325 /* resolve symbols: */
3326 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3327 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3328 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3329 if (rc != 0)
3330 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3331
3332 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "_beginthreadex", 14,
3333 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3334 *(KUPTR *)&g_aCrtSlots[iSlot].pfnBeginThreadEx = rc == 0 ? (KUPTR)uAddr : 0;
3335 //if (rc != 0)
3336 // kwErrPrintf("Failed to resolved '_beginthreadex' in '%s': %d\n", pModule->pszPath, rc);
3337
3338 return 0;
3339 }
3340 kwErrPrintf("Out of CRT slots!\n");
3341 return KERR_NO_MEMORY;
3342}
3343
3344
3345/**
3346 * Locates the module structure for an already loaded native module.
3347 *
3348 * This will create a module structure if needed.
3349 *
3350 * @returns Pointer to the module structure on success, NULL on failure.
3351 * @param hModule The native module handle.
3352 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3353 * @param pszLogName The name to use for logging/errors.
3354 */
3355static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName)
3356{
3357 /*
3358 * Get a normalized path for it.
3359 */
3360 char szModPath[1024];
3361 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3362 {
3363 char szNormPath[1024];
3364 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3365 if (rc == 0)
3366 {
3367 /*
3368 * Hash the path and look it up.
3369 */
3370 KU32 uHashPath;
3371 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3372 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3373 PKWMODULE pMod = g_apModules[idxHash];
3374 if (pMod)
3375 {
3376 do
3377 {
3378 if ( pMod->uHashPath == uHashPath
3379 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3380 {
3381 kwLdrModuleRetain(pMod);
3382 break;
3383 }
3384 pMod = pMod->pNextHash;
3385 } while (pMod);
3386 }
3387
3388 /*
3389 * If not in the hash table, so create a module entry.
3390 */
3391 if (!pMod)
3392 {
3393 PKLDRMOD pLdrMod;
3394 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3395 if (rc == 0)
3396 {
3397 /** @todo more accurately determine location */
3398 const char *pszFilename = kHlpGetFilename(szNormPath);
3399 KBOOL fDoReplacements = kwLdrModuleShouldDoNativeReplacements(pszFilename, KWLOCATION_SYSTEM32);
3400 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3401 fDoReplacements, NULL /*pVirtualApiMod*/);
3402 if (!pMod)
3403 {
3404 kLdrModClose(pLdrMod);
3405 kwErrPrintf("out of memory\n");
3406 }
3407 }
3408 else
3409 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszLogName, rc);
3410 }
3411 if (pMod)
3412 {
3413 /*
3414 * Create a CRT slot for the module if necessary.
3415 */
3416 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3417 return pMod;
3418 rc = kwLdrModuleCreateCrtSlot(pMod);
3419 if (rc == 0)
3420 return pMod;
3421 kwLdrModuleRelease(pMod);
3422 }
3423 }
3424 else
3425 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszLogName, GetLastError());
3426 }
3427 else
3428 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszLogName, GetLastError());
3429 return NULL;
3430}
3431
3432
3433/**
3434 * Locates the module structure for an already loaded native module.
3435 *
3436 * This will create a module structure if needed.
3437 *
3438 * @returns Pointer to the module structure on success, NULL on failure.
3439 * @param pszName The name of the module.
3440 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3441 * @param fAlwaysPresent Whether the module is expected to always be present,
3442 * or not. If not, complain less.
3443 */
3444static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3445{
3446 /*
3447 * Locate the module handle and pass it to kwLdrModuleForLoadedNativeByHandle.
3448 */
3449 HANDLE hModule = GetModuleHandleA(pszName);
3450 if (hModule)
3451 return kwLdrModuleForLoadedNativeByHandle(hModule, fEnsureCrtSlot, pszName);
3452 if (fAlwaysPresent)
3453 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3454 return NULL;
3455}
3456
3457
3458/**
3459 * Does the TLS memory initialization for a module on the current thread.
3460 *
3461 * @returns 0 on success, error on failure.
3462 * @param pMod The module.
3463 */
3464static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3465{
3466 if (pMod->u.Manual.idxTls != KU32_MAX)
3467 {
3468 PTEB pTeb = NtCurrentTeb();
3469 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3470 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3471 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3472 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3473 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3474 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3475 if (pMod->u.Manual.cbTlsInitData)
3476 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3477 }
3478 return 0;
3479}
3480
3481
3482/**
3483 * Does the TLS callbacks for a module.
3484 *
3485 * @param pMod The module.
3486 * @param dwReason The callback reason.
3487 */
3488static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3489{
3490 if (pMod->u.Manual.cTlsCallbacks)
3491 {
3492 PIMAGE_TLS_CALLBACK *pCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3493 do
3494 {
3495 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *pCallback, pMod->hOurMod, dwReason));
3496 (*pCallback)(pMod->hOurMod, dwReason, 0);
3497 } while (*++pCallback);
3498 }
3499}
3500
3501
3502/**
3503 * Does module initialization starting at @a pMod.
3504 *
3505 * This is initially used on the executable. Later it is used by the
3506 * LoadLibrary interceptor.
3507 *
3508 * @returns 0 on success, error on failure.
3509 * @param pMod The module to initialize.
3510 */
3511static int kwLdrModuleInitTree(PKWMODULE pMod)
3512{
3513 int rc = 0;
3514 if (!pMod->fNative)
3515 {
3516 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3517 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3518
3519 /*
3520 * Need to copy bits?
3521 */
3522 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3523 {
3524 if (pMod->u.Manual.fUseLdBuf)
3525 {
3526#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3527 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3528 {
3529 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3530 kHlpAssert(fRc); K_NOREF(fRc);
3531 }
3532#endif
3533 g_pModPrevInLdBuf = g_pModInLdBuf;
3534 g_pModInLdBuf = pMod;
3535 }
3536
3537 /* Do quick zeroing and copying when we can. */
3538 pMod->u.Manual.fCanDoQuick = K_FALSE;
3539 if ( pMod->u.Manual.fCanDoQuick
3540 && ( !pMod->u.Manual.fUseLdBuf
3541 || g_pModPrevInLdBuf == pMod))
3542 {
3543 /* Zero first. */
3544 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3545 switch (pMod->u.Manual.cQuickZeroChunks)
3546 {
3547 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3548 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3549 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3550 case 0: break;
3551 }
3552
3553 /* Then copy. */
3554 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3555 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3556 switch (pMod->u.Manual.cQuickCopyChunks)
3557 {
3558 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3559 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3560 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3561 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3562 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3563 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3564 case 0: break;
3565 }
3566 }
3567 /* Must copy the whole image. */
3568 else
3569 {
3570 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3571 pMod->u.Manual.fCanDoQuick = K_TRUE;
3572 }
3573 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3574 }
3575
3576#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3577 /*
3578 * Need to register function table?
3579 */
3580 if ( !pMod->u.Manual.fRegisteredFunctionTable
3581 && pMod->u.Manual.cFunctions > 0)
3582 {
3583 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3584 pMod->u.Manual.cFunctions,
3585 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3586 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3587 }
3588#endif
3589
3590
3591 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3592 {
3593 /*
3594 * Must do imports first, but mark our module as being initialized to avoid
3595 * endless recursion should there be a dependency loop.
3596 */
3597 KSIZE iImp;
3598 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3599
3600 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3601 {
3602 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3603 if (rc != 0)
3604 return rc;
3605 }
3606
3607 /* Do TLS allocations for module init? */
3608 rc = kwLdrCallTlsAllocateAndInit(pMod);
3609 if (rc != 0)
3610 return rc;
3611 if (pMod->u.Manual.cTlsCallbacks > 0)
3612 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3613
3614 /* Finally call the entry point. */
3615 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3616 if (rc == 0)
3617 pMod->u.Manual.enmState = KWMODSTATE_READY;
3618 else
3619 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3620 }
3621 }
3622 /*
3623 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3624 * _MSPDBSRV_ENDPOINT_ changes value.
3625 */
3626 else if (pMod->fNeedReInit)
3627 {
3628 int rc2;
3629 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3630 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3631 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3632 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3633 if (!rc && !rc2)
3634 { /* likely */ }
3635 else
3636 {
3637 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3638 if (rc2 && !rc)
3639 rc = rc2;
3640 }
3641 pMod->fNeedReInit = K_FALSE;
3642 }
3643 return rc;
3644}
3645
3646
3647/**
3648 * Looks up a module handle for a tool.
3649 *
3650 * @returns Referenced loader module on success, NULL on if not found.
3651 * @param pTool The tool.
3652 * @param hmod The module handle.
3653 */
3654static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3655{
3656 KUPTR const uHMod = (KUPTR)hmod;
3657 PKWMODULE *papMods;
3658 KU32 iEnd;
3659 KU32 i;
3660 PKWDYNLOAD pDynLoad;
3661
3662 if (pTool)
3663 { /* likely */ }
3664 else
3665 return NULL;
3666
3667 /* The executable. */
3668 if ( hmod == NULL
3669 || (pTool->u.Sandboxed.pExe && pTool->u.Sandboxed.pExe->hOurMod == hmod))
3670 {
3671 if (pTool->u.Sandboxed.pExe)
3672 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3673 return NULL;
3674 }
3675
3676 /*
3677 * Binary lookup using the module table.
3678 */
3679 papMods = pTool->u.Sandboxed.papModules;
3680 iEnd = pTool->u.Sandboxed.cModules;
3681 if (iEnd)
3682 {
3683 KU32 iStart = 0;
3684 i = iEnd / 2;
3685 for (;;)
3686 {
3687 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
3688 if (uHMod < uHModCur)
3689 {
3690 iEnd = i--;
3691 if (iStart <= i)
3692 { }
3693 else
3694 break;
3695 }
3696 else if (uHMod != uHModCur)
3697 {
3698 iStart = ++i;
3699 if (i < iEnd)
3700 { }
3701 else
3702 break;
3703 }
3704 /* We've got a match. Always return the non-virtual module (first) when there is one. */
3705 else if (!papMods[i]->pVirtualApiMod)
3706 return kwLdrModuleRetain(papMods[i]);
3707 else
3708 {
3709 while (i > 0 && papMods[i - 1]->pVirtualApiMod && papMods[i - 1]->hOurMod == hmod)
3710 i--;
3711 return kwLdrModuleRetain(papMods[i]);
3712 }
3713
3714 i = iStart + (iEnd - iStart) / 2;
3715 }
3716
3717#ifndef NDEBUG
3718 iStart = pTool->u.Sandboxed.cModules;
3719 while (--iStart > 0)
3720 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3721 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3722#endif
3723 }
3724
3725 /*
3726 * Dynamically loaded images.
3727 */
3728 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3729 if (pDynLoad->hmod == hmod)
3730 {
3731 if (pDynLoad->pMod)
3732 return kwLdrModuleRetain(pDynLoad->pMod);
3733 KWFS_TODO();
3734 return NULL;
3735 }
3736
3737 return NULL;
3738}
3739
3740/**
3741 * Adds the given module to the tool import table.
3742 *
3743 * @returns 0 on success, non-zero on failure.
3744 * @param pTool The tool.
3745 * @param pMod The module.
3746 */
3747static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3748{
3749 /*
3750 * Binary lookup. Locating the right slot for it, return if already there.
3751 */
3752 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3753 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3754 KU32 iEnd = pTool->u.Sandboxed.cModules;
3755 KU32 i;
3756 if (iEnd)
3757 {
3758 KU32 iStart = 0;
3759 i = iEnd / 2;
3760 for (;;)
3761 {
3762 PKWMODULE pCurMod = papMods[i];
3763 KUPTR const uHModCur = (KUPTR)pCurMod->hOurMod;
3764 if (uHMod < uHModCur)
3765 {
3766 iEnd = i;
3767 if (iStart < i)
3768 { }
3769 else
3770 break;
3771 }
3772 else if (uHMod != uHModCur)
3773 {
3774 iStart = ++i;
3775 if (i < iEnd)
3776 { }
3777 else
3778 break;
3779 }
3780 else
3781 {
3782 /* Already there in the table. The non-virtual module must be the first
3783 entry if we've got duplicate hmod values because of virtual modules. */
3784 if (pMod != pCurMod)
3785 {
3786 /* Skip to the last module with the same hmod. */
3787 while (i + 1 < iEnd && (KUPTR)(pCurMod = papMods[i + 1])->hOurMod == uHMod)
3788 {
3789 if (pMod == pCurMod)
3790 return 0;
3791 i++;
3792 }
3793
3794 /* Then scan backwards till the first one. */
3795 while (i > 0 && (KUPTR)(pCurMod = papMods[i - 1])->hOurMod == uHMod)
3796 {
3797 if (pMod == pCurMod)
3798 return 0;
3799 i--;
3800 }
3801 pCurMod = papMods[i];
3802 if (pMod != pCurMod)
3803 {
3804 if (pMod->pVirtualApiMod && !pCurMod->pVirtualApiMod)
3805 i++;
3806 break;
3807 }
3808 }
3809 return 0;
3810 }
3811
3812 i = iStart + (iEnd - iStart) / 2;
3813 }
3814#ifndef NDEBUG
3815 iStart = pTool->u.Sandboxed.cModules;
3816 while (--iStart > 0)
3817 {
3818 kHlpAssert(papMods[iStart] != pMod);
3819 kHlpAssert( (KUPTR)papMods[iStart]->hOurMod != uHMod
3820 || pMod->pVirtualApiMod
3821 || papMods[iStart]->pVirtualApiMod);
3822 }
3823 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod <= uHMod);
3824 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod >= uHMod);
3825#endif
3826 }
3827 else
3828 i = 0;
3829
3830 /*
3831 * Grow the table?
3832 */
3833 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3834 {
3835 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3836 if (!pvNew)
3837 return KERR_NO_MEMORY;
3838 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3839 }
3840
3841 /* Insert it. */
3842 if (i != pTool->u.Sandboxed.cModules)
3843 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3844 papMods[i] = kwLdrModuleRetain(pMod);
3845 pTool->u.Sandboxed.cModules++;
3846 KWLDR_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3847 return 0;
3848}
3849
3850
3851/**
3852 * Adds the given module and all its imports to the
3853 *
3854 * @returns 0 on success, non-zero on failure.
3855 * @param pTool The tool.
3856 * @param pMod The module.
3857 */
3858static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
3859{
3860 int rc = kwToolAddModule(pTool, pMod);
3861 if (pMod->pVirtualApiMod && rc == 0)
3862 rc = kwToolAddModule(pTool, pMod->pVirtualApiMod);
3863 if (!pMod->fNative && rc == 0)
3864 {
3865 KSIZE iImp = pMod->u.Manual.cImpMods;
3866 while (iImp-- > 0)
3867 {
3868 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
3869 if (rc == 0)
3870 { }
3871 else
3872 break;
3873 }
3874 }
3875
3876 return 0;
3877}
3878
3879
3880/**
3881 * Creates a tool entry and inserts it.
3882 *
3883 * @returns Pointer to the tool entry. NULL on failure.
3884 * @param pToolFsObj The file object of the tool. The created tool
3885 * will be associated with it.
3886 *
3887 * A reference is donated by the caller and must be
3888 * released.
3889 * @param pszSearchPath The PATH environment variable value, or NULL.
3890 */
3891static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
3892{
3893 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
3894 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
3895 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
3896 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
3897 if (pTool)
3898 {
3899 KBOOL fRc;
3900 pTool->pwszPath = (wchar_t const *)(pTool + 1);
3901 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
3902 kHlpAssert(fRc); K_NOREF(fRc);
3903
3904 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
3905 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
3906 kHlpAssert(fRc);
3907
3908 pTool->enmType = KWTOOLTYPE_SANDBOXED;
3909 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
3910 NULL /*pEexeMod*/, pszSearchPath);
3911 if (pTool->u.Sandboxed.pExe)
3912 {
3913 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
3914 if (rc == 0)
3915 {
3916 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
3917 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
3918 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
3919 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
3920 else
3921 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
3922 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
3923 }
3924 else
3925 {
3926 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
3927 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
3928 pTool->u.Sandboxed.pExe = NULL;
3929 pTool->enmType = KWTOOLTYPE_EXEC;
3930 }
3931 }
3932 else
3933 pTool->enmType = KWTOOLTYPE_EXEC;
3934
3935 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3936 g_cTools++;
3937 return pTool;
3938 }
3939 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3940 return NULL;
3941}
3942
3943
3944/**
3945 * Looks up the given tool, creating a new tool table entry if necessary.
3946 *
3947 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
3948 * @param pszExe The executable for the tool (not normalized).
3949 * @param cEnvVars Number of environment varibles.
3950 * @param papszEnvVars Environment variables. For getting the PATH.
3951 */
3952static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
3953{
3954 /*
3955 * We associate the tools instances with the file system objects.
3956 *
3957 * We'd like to do the lookup without invaliding the volatile parts of the
3958 * cache, thus the double lookup here. The cache gets invalidate later on.
3959 */
3960 KFSLOOKUPERROR enmError;
3961 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3962 if ( !pToolFsObj
3963 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
3964 {
3965 kFsCacheInvalidateCustomBoth(g_pFsCache);
3966 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3967 }
3968 if (pToolFsObj)
3969 {
3970 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
3971 {
3972 const char *pszSearchPath;
3973 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
3974 if (pTool)
3975 {
3976 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3977 return pTool;
3978 }
3979
3980 /*
3981 * Need to create a new tool.
3982 */
3983 pszSearchPath = NULL;
3984 while (cEnvVars-- > 0)
3985 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
3986 {
3987 pszSearchPath = &papszEnvVars[cEnvVars][5];
3988 break;
3989 }
3990
3991 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
3992 if (pTool)
3993 return pTool;
3994
3995 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
3996 }
3997 else
3998 {
3999 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4000 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
4001 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
4002 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
4003 }
4004 }
4005 else
4006 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
4007 return NULL;
4008}
4009
4010
4011
4012/*
4013 *
4014 * File system cache.
4015 * File system cache.
4016 * File system cache.
4017 *
4018 */
4019
4020
4021/**
4022 * This is for kDep.
4023 */
4024int kwFsPathExists(const char *pszPath)
4025{
4026 BirdTimeSpec_T TsIgnored;
4027 KFSLOOKUPERROR enmError;
4028 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
4029 if (pFsObj)
4030 {
4031 kFsCacheObjRelease(g_pFsCache, pFsObj);
4032 return 1;
4033 }
4034 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
4035}
4036
4037
4038/* duplicated in dir-nt-bird.c */
4039void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
4040{
4041 KFSLOOKUPERROR enmError;
4042 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
4043 if (pPathObj)
4044 {
4045 KSIZE off = pPathObj->cchParent;
4046 if (off > 0)
4047 {
4048 KSIZE offEnd = off + pPathObj->cchName;
4049 if (offEnd < cbFull)
4050 {
4051 PKFSDIR pAncestor;
4052
4053 pszFull[off + pPathObj->cchName] = '\0';
4054 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
4055
4056 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4057 {
4058 kHlpAssert(off > 1);
4059 kHlpAssert(pAncestor != NULL);
4060 kHlpAssert(pAncestor->Obj.cchName > 0);
4061 pszFull[--off] = '/';
4062 off -= pAncestor->Obj.cchName;
4063 kHlpAssert(pAncestor->Obj.cchParent == off);
4064 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4065 }
4066 kFsCacheObjRelease(g_pFsCache, pPathObj);
4067 return;
4068 }
4069 }
4070 else
4071 {
4072 if ((size_t)pPathObj->cchName + 1 < cbFull)
4073 {
4074 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
4075 pszFull[pPathObj->cchName] = '/';
4076 pszFull[pPathObj->cchName + 1] = '\0';
4077
4078 kFsCacheObjRelease(g_pFsCache, pPathObj);
4079 return;
4080 }
4081 }
4082
4083 /* do fallback. */
4084 kHlpAssertFailed();
4085 kFsCacheObjRelease(g_pFsCache, pPathObj);
4086 }
4087
4088 nt_fullpath(pszPath, pszFull, cbFull);
4089}
4090
4091
4092/**
4093 * Helper for getting the extension of a UTF-16 path.
4094 *
4095 * @returns Pointer to the extension or the terminator.
4096 * @param pwszPath The path.
4097 * @param pcwcExt Where to return the length of the extension.
4098 */
4099static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
4100{
4101 wchar_t const *pwszName = pwszPath;
4102 wchar_t const *pwszExt = NULL;
4103 for (;;)
4104 {
4105 wchar_t const wc = *pwszPath++;
4106 if (wc == '.')
4107 pwszExt = pwszPath;
4108 else if (wc == '/' || wc == '\\' || wc == ':')
4109 {
4110 pwszName = pwszPath;
4111 pwszExt = NULL;
4112 }
4113 else if (wc == '\0')
4114 {
4115 if (pwszExt)
4116 {
4117 *pcwcExt = pwszPath - pwszExt - 1;
4118 return pwszExt;
4119 }
4120 *pcwcExt = 0;
4121 return pwszPath - 1;
4122 }
4123 }
4124}
4125
4126
4127
4128/**
4129 * Parses the argument string passed in as pszSrc.
4130 *
4131 * @returns size of the processed arguments.
4132 * @param pszSrc Pointer to the commandline that's to be parsed.
4133 * @param pcArgs Where to return the number of arguments.
4134 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
4135 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
4136 *
4137 * @remarks Lifted from startuphacks-win.c
4138 */
4139static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
4140{
4141 int bs;
4142 char chQuote;
4143 char *pfFlags;
4144 int cbArgs;
4145 int cArgs;
4146
4147#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
4148#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
4149#define WHITE(c) ((c) == ' ' || (c) == '\t')
4150
4151#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
4152#define _ARG_RESPONSE 0x02 /* Argument read from response file */
4153#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
4154#define _ARG_ENV 0x08 /* Argument from environment */
4155#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
4156
4157 cArgs = 0;
4158 cbArgs = 0;
4159
4160#if 0
4161 /* argv[0] */
4162 PUTC((char)_ARG_NONZERO);
4163 PUTV;
4164 for (;;)
4165 {
4166 PUTC(*pszSrc);
4167 if (*pszSrc == 0)
4168 break;
4169 ++pszSrc;
4170 }
4171 ++pszSrc;
4172#endif
4173
4174 for (;;)
4175 {
4176 while (WHITE(*pszSrc))
4177 ++pszSrc;
4178 if (*pszSrc == 0)
4179 break;
4180 pfFlags = pchPool;
4181 PUTC((unsigned char)_ARG_NONZERO);
4182 PUTV;
4183 bs = 0; chQuote = 0;
4184 for (;;)
4185 {
4186 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
4187 {
4188 while (bs >= 2)
4189 {
4190 PUTC('\\');
4191 bs -= 2;
4192 }
4193 if (bs & 1)
4194 PUTC(*pszSrc);
4195 else
4196 {
4197 chQuote = chQuote ? 0 : *pszSrc;
4198 if (pfFlags != NULL)
4199 *pfFlags |= _ARG_DQUOTE;
4200 }
4201 bs = 0;
4202 }
4203 else if (*pszSrc == '\\')
4204 ++bs;
4205 else
4206 {
4207 while (bs != 0)
4208 {
4209 PUTC('\\');
4210 --bs;
4211 }
4212 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
4213 break;
4214 PUTC(*pszSrc);
4215 }
4216 ++pszSrc;
4217 }
4218 PUTC(0);
4219 }
4220
4221 *pcArgs = cArgs;
4222 return cbArgs;
4223}
4224
4225
4226
4227
4228/*
4229 *
4230 * Process and thread related APIs.
4231 * Process and thread related APIs.
4232 * Process and thread related APIs.
4233 *
4234 */
4235
4236/** Common worker for ExitProcess(), exit() and friends. */
4237static void WINAPI kwSandboxDoExit(int uExitCode)
4238{
4239 if (g_Sandbox.idMainThread == GetCurrentThreadId())
4240 {
4241 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
4242
4243 g_Sandbox.rcExitCode = (int)uExitCode;
4244
4245 /* Before we jump, restore the TIB as we're not interested in any
4246 exception chain stuff installed by the sandboxed executable. */
4247 *pTib = g_Sandbox.TibMainThread;
4248 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
4249
4250 longjmp(g_Sandbox.JmpBuf, 1);
4251 }
4252 KWFS_TODO();
4253}
4254
4255
4256/** ExitProcess replacement. */
4257static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
4258{
4259 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
4260 kwSandboxDoExit((int)uExitCode);
4261}
4262
4263
4264/** ExitProcess replacement. */
4265static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
4266{
4267 if (hProcess == GetCurrentProcess())
4268 kwSandboxDoExit(uExitCode);
4269 KWFS_TODO();
4270 return TerminateProcess(hProcess, uExitCode);
4271}
4272
4273
4274/** Normal CRT exit(). */
4275static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
4276{
4277 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
4278 kwSandboxDoExit(rcExitCode);
4279}
4280
4281
4282/** Quick CRT _exit(). */
4283static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
4284{
4285 /* Quick. */
4286 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
4287 kwSandboxDoExit(rcExitCode);
4288}
4289
4290
4291/** Return to caller CRT _cexit(). */
4292static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
4293{
4294 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
4295 kwSandboxDoExit(rcExitCode);
4296}
4297
4298
4299/** Quick return to caller CRT _c_exit(). */
4300static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
4301{
4302 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
4303 kwSandboxDoExit(rcExitCode);
4304}
4305
4306
4307/** Runtime error and exit _amsg_exit(). */
4308static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
4309{
4310 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
4311 kwSandboxDoExit(255);
4312}
4313
4314
4315/** CRT - terminate(). */
4316static void __cdecl kwSandbox_msvcrt_terminate(void)
4317{
4318 KW_LOG(("\nRuntime - terminate!\n"));
4319 kwSandboxDoExit(254);
4320}
4321
4322
4323/** CRT - _onexit */
4324static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
4325{
4326 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4327 {
4328 PKWEXITCALLACK pCallback;
4329 KW_LOG(("_onexit(%p)\n", pfnFunc));
4330 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4331
4332 pCallback = kHlpAlloc(sizeof(*pCallback));
4333 if (pCallback)
4334 {
4335 pCallback->pfnCallback = pfnFunc;
4336 pCallback->fAtExit = K_FALSE;
4337 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4338 g_Sandbox.pExitCallbackHead = pCallback;
4339 return pfnFunc;
4340 }
4341 return NULL;
4342 }
4343 //KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
4344 //return pfnFunc;
4345}
4346
4347
4348/** CRT - atexit */
4349static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
4350{
4351 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4352 {
4353 PKWEXITCALLACK pCallback;
4354 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4355 KW_LOG(("atexit(%p)\n", pfnFunc));
4356
4357 pCallback = kHlpAlloc(sizeof(*pCallback));
4358 if (pCallback)
4359 {
4360 pCallback->pfnCallback = (_onexit_t)pfnFunc;
4361 pCallback->fAtExit = K_TRUE;
4362 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4363 g_Sandbox.pExitCallbackHead = pCallback;
4364 return 0;
4365 }
4366 return -1;
4367 }
4368 //KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4369 //return 0;
4370}
4371
4372
4373/** Kernel32 - SetConsoleCtrlHandler(). */
4374static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4375{
4376 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4377 return TRUE;
4378}
4379
4380
4381/** The CRT internal __getmainargs() API. */
4382static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4383 int dowildcard, int const *piNewMode)
4384{
4385 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4386 *pargc = g_Sandbox.cArgs;
4387 *pargv = g_Sandbox.papszArgs;
4388 *penvp = g_Sandbox.environ;
4389
4390 /** @todo startinfo points at a newmode (setmode) value. */
4391 return 0;
4392}
4393
4394
4395/** The CRT internal __wgetmainargs() API. */
4396static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4397 int dowildcard, int const *piNewMode)
4398{
4399 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4400 *pargc = g_Sandbox.cArgs;
4401 *pargv = g_Sandbox.papwszArgs;
4402 *penvp = g_Sandbox.wenviron;
4403
4404 /** @todo startinfo points at a newmode (setmode) value. */
4405 return 0;
4406}
4407
4408
4409
4410/** Kernel32 - GetCommandLineA() */
4411static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4412{
4413 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4414 return g_Sandbox.pszCmdLine;
4415}
4416
4417
4418/** Kernel32 - GetCommandLineW() */
4419static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4420{
4421 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4422 return g_Sandbox.pwszCmdLine;
4423}
4424
4425
4426/** Kernel32 - GetStartupInfoA() */
4427static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4428{
4429 KW_LOG(("GetStartupInfoA\n"));
4430 GetStartupInfoA(pStartupInfo);
4431 pStartupInfo->lpReserved = NULL;
4432 pStartupInfo->lpTitle = NULL;
4433 pStartupInfo->lpReserved2 = NULL;
4434 pStartupInfo->cbReserved2 = 0;
4435}
4436
4437
4438/** Kernel32 - GetStartupInfoW() */
4439static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4440{
4441 KW_LOG(("GetStartupInfoW\n"));
4442 GetStartupInfoW(pStartupInfo);
4443 pStartupInfo->lpReserved = NULL;
4444 pStartupInfo->lpTitle = NULL;
4445 pStartupInfo->lpReserved2 = NULL;
4446 pStartupInfo->cbReserved2 = 0;
4447}
4448
4449
4450/** CRT - __p___argc(). */
4451static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4452{
4453 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4454 return &g_Sandbox.cArgs;
4455}
4456
4457
4458/** CRT - __p___argv(). */
4459static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4460{
4461 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4462 return &g_Sandbox.papszArgs;
4463}
4464
4465
4466/** CRT - __p___sargv(). */
4467static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4468{
4469 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4470 return &g_Sandbox.papwszArgs;
4471}
4472
4473
4474/** CRT - __p__acmdln(). */
4475static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4476{
4477 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4478 return (char **)&g_Sandbox.pszCmdLine;
4479}
4480
4481
4482/** CRT - __p__acmdln(). */
4483static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4484{
4485 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4486 return &g_Sandbox.pwszCmdLine;
4487}
4488
4489
4490/** CRT - __p__pgmptr(). */
4491static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4492{
4493 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4494 return &g_Sandbox.pgmptr;
4495}
4496
4497
4498/** CRT - __p__wpgmptr(). */
4499static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4500{
4501 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4502 return &g_Sandbox.wpgmptr;
4503}
4504
4505
4506/** CRT - _get_pgmptr(). */
4507static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4508{
4509 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4510 *ppszValue = g_Sandbox.pgmptr;
4511 return 0;
4512}
4513
4514
4515/** CRT - _get_wpgmptr(). */
4516static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4517{
4518 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4519 *ppwszValue = g_Sandbox.wpgmptr;
4520 return 0;
4521}
4522
4523/** Just in case. */
4524static void kwSandbox_msvcrt__wincmdln(void)
4525{
4526 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4527 KWFS_TODO();
4528}
4529
4530
4531/** Just in case. */
4532static void kwSandbox_msvcrt__wwincmdln(void)
4533{
4534 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4535 KWFS_TODO();
4536}
4537
4538/** CreateThread interceptor. */
4539static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4540 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4541 DWORD fFlags, PDWORD pidThread)
4542{
4543 HANDLE hThread = NULL;
4544 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4545 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4546 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4547 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4548 {
4549 /* Allow link::DbgThread. */
4550 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4551 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4552 }
4553 else
4554 KWFS_TODO();
4555 return hThread;
4556}
4557
4558
4559/** _beginthread - create a new thread. */
4560static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4561{
4562 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4563 KWFS_TODO();
4564 return 0;
4565}
4566
4567
4568/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4569static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4570 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4571 unsigned fCreate, unsigned *pidThread)
4572{
4573 /*
4574 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4575 * whatever it needs to.
4576 */
4577 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4578 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4579 pfnThreadProc, pvUser, fCreate, pidThread));
4580 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4581 {
4582 uintptr_t rcRet;
4583 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4584 if (!s_pfnReal)
4585 {
4586 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4587 if (!s_pfnReal)
4588 {
4589 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4590 __debugbreak();
4591 }
4592 }
4593 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4594 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4595 return rcRet;
4596 }
4597
4598 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4599 KWFS_TODO();
4600 return 0;
4601}
4602
4603
4604/** _beginthreadex - create a new thread. */
4605static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex_wrapped(void *pvSecAttr, unsigned cbStack,
4606 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4607 unsigned fCreate, unsigned *pidThread, PKWCRTSLOT pSlot)
4608{
4609 /*
4610 * Since the VC++ 12 (VS 2013) compiler, the 2nd pass is now threaded.
4611 * Let it do whatever it needs to.
4612 */
4613 KW_LOG(("kwSandbox_msvcrt__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4614 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4615 pfnThreadProc, pvUser, fCreate, pidThread));
4616 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4617 && pSlot->pfnBeginThreadEx)
4618 {
4619 uintptr_t rcRet = pSlot->pfnBeginThreadEx(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4620 KW_LOG(("kwSandbox_msvcrt__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4621 return rcRet;
4622 }
4623
4624 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4625 KWFS_TODO();
4626 return 0;
4627}
4628
4629CRT_SLOT_FUNCTION_WRAPPER(uintptr_t __cdecl, kwSandbox_msvcrt__beginthreadex,
4630 (void *pvSecAttr, unsigned cbStack, unsigned (__stdcall *pfnThreadProc)(void *),
4631 void *pvUser, unsigned fCreate, unsigned *pidThread),
4632 (pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread, &g_aCrtSlots[iCrtSlot]));
4633
4634
4635
4636/*
4637 *
4638 * Environment related APIs.
4639 * Environment related APIs.
4640 * Environment related APIs.
4641 *
4642 */
4643
4644/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4645static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4646{
4647 char *pszzEnv;
4648 char *pszCur;
4649 KSIZE cbNeeded = 1;
4650 KSIZE iVar = 0;
4651
4652 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4653
4654 /* Figure how space much we need first. */
4655 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4656 cbNeeded += kHlpStrLen(pszCur) + 1;
4657
4658 /* Allocate it. */
4659 pszzEnv = kHlpAlloc(cbNeeded);
4660 if (pszzEnv)
4661 {
4662 char *psz = pszzEnv;
4663 iVar = 0;
4664 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4665 {
4666 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4667 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4668 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4669 }
4670 *psz++ = '\0';
4671 kHlpAssert((KUPTR)(psz - pszzEnv) == cbNeeded);
4672 }
4673
4674 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4675#if 0
4676 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4677 pszCur = pszzEnv;
4678 iVar = 0;
4679 while (*pszCur)
4680 {
4681 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4682 iVar++;
4683 pszCur += kHlpStrLen(pszCur) + 1;
4684 }
4685 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4686 pszCur++;
4687 fprintf(stderr, "ended at %p, after %u bytes (expected %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4688#endif
4689 return pszzEnv;
4690}
4691
4692
4693/** Kernel32 - GetEnvironmentStrings */
4694static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4695{
4696 KW_LOG(("GetEnvironmentStrings!\n"));
4697 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4698}
4699
4700
4701/** Kernel32 - GetEnvironmentStringsW */
4702static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4703{
4704 wchar_t *pwszzEnv;
4705 wchar_t *pwszCur;
4706 KSIZE cwcNeeded = 1;
4707 KSIZE iVar = 0;
4708
4709 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4710
4711 /* Figure how space much we need first. */
4712 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4713 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4714
4715 /* Allocate it. */
4716 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4717 if (pwszzEnv)
4718 {
4719 wchar_t *pwsz = pwszzEnv;
4720 iVar = 0;
4721 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4722 {
4723 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4724 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4725 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4726 }
4727 *pwsz++ = '\0';
4728 kHlpAssert((KUPTR)(pwsz - pwszzEnv) == cwcNeeded);
4729 }
4730
4731 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4732 return pwszzEnv;
4733}
4734
4735
4736/** Kernel32 - FreeEnvironmentStringsA */
4737static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4738{
4739 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4740 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4741 kHlpFree(pszzEnv);
4742 return TRUE;
4743}
4744
4745
4746/** Kernel32 - FreeEnvironmentStringsW */
4747static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4748{
4749 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4750 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4751 kHlpFree(pwszzEnv);
4752 return TRUE;
4753}
4754
4755
4756/**
4757 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4758 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4759 *
4760 * @returns 0 on success, non-zero on failure.
4761 * @param pSandbox The sandbox.
4762 * @param cMin Minimum size, including terminator.
4763 */
4764static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4765{
4766 void *pvNew;
4767 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4768 KSIZE cNew = cOld + 256;
4769 while (cNew < cMin)
4770 cNew += 256;
4771
4772 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4773 if (pvNew)
4774 {
4775 pSandbox->environ = (char **)pvNew;
4776 pSandbox->environ[cOld] = NULL;
4777
4778 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4779 if (pvNew)
4780 {
4781 pSandbox->papszEnvVars = (char **)pvNew;
4782 pSandbox->papszEnvVars[cOld] = NULL;
4783
4784 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4785 if (pvNew)
4786 {
4787 pSandbox->wenviron = (wchar_t **)pvNew;
4788 pSandbox->wenviron[cOld] = NULL;
4789
4790 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4791 if (pvNew)
4792 {
4793 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4794 pSandbox->papwszEnvVars[cOld] = NULL;
4795
4796 pSandbox->cEnvVarsAllocated = cNew;
4797 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4798 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4799 return 0;
4800 }
4801 }
4802 }
4803 }
4804 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4805 return KERR_NO_MEMORY;
4806}
4807
4808
4809/**
4810 * Sets an environment variable, ANSI style.
4811 *
4812 * @returns 0 on success, non-zero on failure.
4813 * @param pSandbox The sandbox.
4814 * @param pchVar The variable name.
4815 * @param cchVar The length of the name.
4816 * @param pszValue The value.
4817 */
4818static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4819{
4820 /* Allocate and construct the new strings. */
4821 KSIZE cchTmp = kHlpStrLen(pszValue);
4822 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4823 if (pszNew)
4824 {
4825 wchar_t *pwszNew;
4826 kHlpMemCopy(pszNew, pchVar, cchVar);
4827 pszNew[cchVar] = '=';
4828 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4829 cchTmp += cchVar + 1;
4830 pszNew[cchTmp] = '\0';
4831
4832 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4833 if (pwszNew)
4834 {
4835 /* Look it up. */
4836 KSIZE iVar = 0;
4837 char *pszEnv;
4838 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4839 {
4840 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4841 && pszEnv[cchVar] == '=')
4842 {
4843 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4844 " iVar=%d: %p='%s' and %p='%ls'\n",
4845 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4846 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4847 iVar, pszNew, pszNew, pwszNew, pwszNew));
4848
4849 kHlpFree(pSandbox->papszEnvVars[iVar]);
4850 pSandbox->papszEnvVars[iVar] = pszNew;
4851 pSandbox->environ[iVar] = pszNew;
4852
4853 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4854 pSandbox->papwszEnvVars[iVar] = pwszNew;
4855 pSandbox->wenviron[iVar] = pwszNew;
4856 return 0;
4857 }
4858 iVar++;
4859 }
4860
4861 /* Not found, do we need to grow the table first? */
4862 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4863 kwSandboxGrowEnv(pSandbox, iVar + 2);
4864 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4865 {
4866 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4867
4868 pSandbox->papszEnvVars[iVar + 1] = NULL;
4869 pSandbox->papszEnvVars[iVar] = pszNew;
4870 pSandbox->environ[iVar + 1] = NULL;
4871 pSandbox->environ[iVar] = pszNew;
4872
4873 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4874 pSandbox->papwszEnvVars[iVar] = pwszNew;
4875 pSandbox->wenviron[iVar + 1] = NULL;
4876 pSandbox->wenviron[iVar] = pwszNew;
4877 return 0;
4878 }
4879
4880 kHlpFree(pwszNew);
4881 }
4882 kHlpFree(pszNew);
4883 }
4884 KW_LOG(("Out of memory!\n"));
4885 return 0;
4886}
4887
4888
4889/**
4890 * Sets an environment variable, UTF-16 style.
4891 *
4892 * @returns 0 on success, non-zero on failure.
4893 * @param pSandbox The sandbox.
4894 * @param pwcVar The variable name.
4895 * @param cwcVar The length of the name.
4896 * @param pwszValue The value.
4897 */
4898static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4899{
4900 /* Allocate and construct the new strings. */
4901 KSIZE cwcTmp = kwUtf16Len(pwszValue);
4902 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4903 if (pwszNew)
4904 {
4905 char *pszNew;
4906 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4907 pwszNew[cwcVar] = '=';
4908 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4909 cwcTmp += cwcVar + 1;
4910 pwszNew[cwcVar] = '\0';
4911
4912 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4913 if (pszNew)
4914 {
4915 /* Look it up. */
4916 KSIZE iVar = 0;
4917 wchar_t *pwszEnv;
4918 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4919 {
4920 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4921 && pwszEnv[cwcVar] == '=')
4922 {
4923 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4924 " iVar=%d: %p='%s' and %p='%ls'\n",
4925 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4926 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4927 iVar, pszNew, pszNew, pwszNew, pwszNew));
4928
4929 kHlpFree(pSandbox->papszEnvVars[iVar]);
4930 pSandbox->papszEnvVars[iVar] = pszNew;
4931 pSandbox->environ[iVar] = pszNew;
4932
4933 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4934 pSandbox->papwszEnvVars[iVar] = pwszNew;
4935 pSandbox->wenviron[iVar] = pwszNew;
4936 return 0;
4937 }
4938 iVar++;
4939 }
4940
4941 /* Not found, do we need to grow the table first? */
4942 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4943 kwSandboxGrowEnv(pSandbox, iVar + 2);
4944 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4945 {
4946 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4947
4948 pSandbox->papszEnvVars[iVar + 1] = NULL;
4949 pSandbox->papszEnvVars[iVar] = pszNew;
4950 pSandbox->environ[iVar + 1] = NULL;
4951 pSandbox->environ[iVar] = pszNew;
4952
4953 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4954 pSandbox->papwszEnvVars[iVar] = pwszNew;
4955 pSandbox->wenviron[iVar + 1] = NULL;
4956 pSandbox->wenviron[iVar] = pwszNew;
4957 return 0;
4958 }
4959
4960 kHlpFree(pwszNew);
4961 }
4962 kHlpFree(pszNew);
4963 }
4964 KW_LOG(("Out of memory!\n"));
4965 return 0;
4966}
4967
4968
4969/** ANSI unsetenv worker. */
4970static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4971{
4972 KSIZE iVar = 0;
4973 char *pszEnv;
4974 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4975 {
4976 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4977 && pszEnv[cchVar] == '=')
4978 {
4979 KSIZE cVars = iVar;
4980 while (pSandbox->papszEnvVars[cVars])
4981 cVars++;
4982 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4983 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4984
4985 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4986 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4987 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4988
4989 kHlpFree(pSandbox->papszEnvVars[iVar]);
4990 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4991 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4992 pSandbox->papszEnvVars[cVars] = NULL;
4993 pSandbox->environ[cVars] = NULL;
4994
4995 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4996 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4997 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4998 pSandbox->papwszEnvVars[cVars] = NULL;
4999 pSandbox->wenviron[cVars] = NULL;
5000 return 0;
5001 }
5002 iVar++;
5003 }
5004 return KERR_ENVVAR_NOT_FOUND;
5005}
5006
5007
5008/** UTF-16 unsetenv worker. */
5009static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5010{
5011 KSIZE iVar = 0;
5012 wchar_t *pwszEnv;
5013 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5014 {
5015 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5016 && pwszEnv[cwcVar] == '=')
5017 {
5018 KSIZE cVars = iVar;
5019 while (pSandbox->papwszEnvVars[cVars])
5020 cVars++;
5021 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
5022 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
5023
5024 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5025 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5026 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5027
5028 kHlpFree(pSandbox->papszEnvVars[iVar]);
5029 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5030 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5031 pSandbox->papszEnvVars[cVars] = NULL;
5032 pSandbox->environ[cVars] = NULL;
5033
5034 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5035 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5036 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5037 pSandbox->papwszEnvVars[cVars] = NULL;
5038 pSandbox->wenviron[cVars] = NULL;
5039 return 0;
5040 }
5041 iVar++;
5042 }
5043 return KERR_ENVVAR_NOT_FOUND;
5044}
5045
5046
5047
5048/** ANSI getenv worker. */
5049static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5050{
5051 KSIZE iVar = 0;
5052 char *pszEnv;
5053 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
5054 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5055 && pszEnv[cchVar] == '=')
5056 return &pszEnv[cchVar + 1];
5057 return NULL;
5058}
5059
5060
5061/** UTF-16 getenv worker. */
5062static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5063{
5064 KSIZE iVar = 0;
5065 wchar_t *pwszEnv;
5066 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
5067 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5068 && pwszEnv[cwcVar] == '=')
5069 return &pwszEnv[cwcVar + 1];
5070 return NULL;
5071}
5072
5073
5074/** Kernel32 - GetEnvironmentVariableA() */
5075static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
5076{
5077 char *pszFoundValue;
5078 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5079
5080 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5081 if (pszFoundValue)
5082 {
5083 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
5084 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
5085 return cchRet;
5086 }
5087 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
5088 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5089 return 0;
5090}
5091
5092
5093/** Kernel32 - GetEnvironmentVariableW() */
5094static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
5095{
5096 wchar_t *pwszFoundValue;
5097 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5098
5099 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5100 if (pwszFoundValue)
5101 {
5102 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
5103 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
5104 return cchRet;
5105 }
5106 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
5107 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5108 return 0;
5109}
5110
5111
5112/** Kernel32 - SetEnvironmentVariableA() */
5113static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
5114{
5115 int rc;
5116 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5117
5118 if (pszValue)
5119 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5120 else
5121 {
5122 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5123 rc = 0; //??
5124 }
5125 if (rc == 0)
5126 {
5127 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
5128 return TRUE;
5129 }
5130 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5131 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
5132 return FALSE;
5133}
5134
5135
5136/** Kernel32 - SetEnvironmentVariableW() */
5137static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
5138{
5139 int rc;
5140 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5141
5142 if (pwszValue)
5143 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5144 else
5145 {
5146 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5147 rc = 0; //??
5148 }
5149 if (rc == 0)
5150 {
5151 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
5152 return TRUE;
5153 }
5154 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5155 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
5156 return FALSE;
5157}
5158
5159
5160/** Kernel32 - ExpandEnvironmentStringsA() */
5161static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
5162{
5163 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5164 KWFS_TODO();
5165 return 0;
5166}
5167
5168
5169/** Kernel32 - ExpandEnvironmentStringsW() */
5170static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
5171{
5172 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5173 KWFS_TODO();
5174 return 0;
5175}
5176
5177
5178/** CRT - _putenv(). */
5179static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
5180{
5181 int rc;
5182 char const *pszEqual;
5183 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5184
5185 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
5186 if (pszEqual)
5187 {
5188 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
5189 if (rc == 0)
5190 { }
5191 else
5192 rc = -1;
5193 }
5194 else
5195 {
5196 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
5197 rc = 0;
5198 }
5199 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
5200 return rc;
5201}
5202
5203
5204/** CRT - _wputenv(). */
5205static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
5206{
5207 int rc;
5208 wchar_t const *pwszEqual;
5209 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5210
5211 pwszEqual = wcschr(pwszVarEqualValue, '=');
5212 if (pwszEqual)
5213 {
5214 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
5215 if (rc == 0)
5216 { }
5217 else
5218 rc = -1;
5219 }
5220 else
5221 {
5222 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
5223 rc = 0;
5224 }
5225 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
5226 return rc;
5227}
5228
5229
5230/** CRT - _putenv_s(). */
5231static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
5232{
5233 char const *pszEqual;
5234 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5235
5236 pszEqual = kHlpStrChr(pszVar, '=');
5237 if (pszEqual == NULL)
5238 {
5239 if (pszValue)
5240 {
5241 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5242 if (rc == 0)
5243 {
5244 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
5245 return 0;
5246 }
5247 }
5248 else
5249 {
5250 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5251 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
5252 return 0;
5253 }
5254 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
5255 return ENOMEM;
5256 }
5257 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
5258 return EINVAL;
5259}
5260
5261
5262/** CRT - _wputenv_s(). */
5263static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
5264{
5265 wchar_t const *pwszEqual;
5266 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5267
5268 pwszEqual = wcschr(pwszVar, '=');
5269 if (pwszEqual == NULL)
5270 {
5271 if (pwszValue)
5272 {
5273 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5274 if (rc == 0)
5275 {
5276 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
5277 return 0;
5278 }
5279 }
5280 else
5281 {
5282 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5283 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
5284 return 0;
5285 }
5286 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
5287 return ENOMEM;
5288 }
5289 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
5290 return EINVAL;
5291}
5292
5293
5294/** CRT - get pointer to the __initenv variable (initial environment). */
5295static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
5296{
5297 KW_LOG(("__p___initenv\n"));
5298 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5299 KWFS_TODO();
5300 return &g_Sandbox.initenv;
5301}
5302
5303
5304/** CRT - get pointer to the __winitenv variable (initial environment). */
5305static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
5306{
5307 KW_LOG(("__p___winitenv\n"));
5308 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5309 KWFS_TODO();
5310 return &g_Sandbox.winitenv;
5311}
5312
5313
5314/** CRT - get pointer to the _environ variable (current environment). */
5315static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
5316{
5317 KW_LOG(("__p__environ\n"));
5318 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5319 return &g_Sandbox.environ;
5320}
5321
5322
5323/** CRT - get pointer to the _wenviron variable (current environment). */
5324static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
5325{
5326 KW_LOG(("__p__wenviron\n"));
5327 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5328 return &g_Sandbox.wenviron;
5329}
5330
5331
5332/** CRT - get the _environ variable (current environment).
5333 * @remarks Not documented or prototyped? */
5334static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
5335{
5336 KWFS_TODO(); /** @todo check the callers expectations! */
5337 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5338 *ppapszEnviron = g_Sandbox.environ;
5339 return 0;
5340}
5341
5342
5343/** CRT - get the _wenviron variable (current environment).
5344 * @remarks Not documented or prototyped? */
5345static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
5346{
5347 KWFS_TODO(); /** @todo check the callers expectations! */
5348 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5349 *ppapwszEnviron = g_Sandbox.wenviron;
5350 return 0;
5351}
5352
5353
5354/** CRT - _wdupenv_s() (see _tdupenv_s(). */
5355static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
5356 PKWCRTSLOT pSlot)
5357{
5358 errno_t rc;
5359 wchar_t *pwszValue;
5360 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5361
5362 if (ppwszValue)
5363 {
5364 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
5365 if (pwszValue)
5366 {
5367 size_t cwcValue = wcslen(pwszValue);
5368 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
5369 if (pwszDst)
5370 {
5371 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
5372 pwszDst[cwcValue] = '\0';
5373 *ppwszValue = pwszDst;
5374 if (pcwcValue)
5375 *pcwcValue = cwcValue;
5376 rc = 0;
5377 }
5378 else
5379 {
5380 *ppwszValue = NULL;
5381 if (pcwcValue)
5382 *pcwcValue = 0;
5383 rc = ENOMEM;
5384 }
5385 }
5386 else
5387 {
5388 *ppwszValue = NULL;
5389 if (pcwcValue)
5390 *pcwcValue = 0;
5391 rc = 0;
5392 }
5393 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5394 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5395 }
5396 else
5397 {
5398 /*
5399 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5400 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5401 * was set to a value.
5402 */
5403 if (pcwcValue)
5404 *pcwcValue = 0;
5405 rc = EINVAL;
5406 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5407 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5408 }
5409 return rc;
5410}
5411CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5412 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5413 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5414
5415
5416
5417/*
5418 *
5419 * Loader related APIs
5420 * Loader related APIs
5421 * Loader related APIs
5422 *
5423 */
5424
5425/**
5426 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5427 */
5428static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5429{
5430 /* Load it first. */
5431 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5432 if (hmod)
5433 {
5434 pDynLoad->hmod = hmod;
5435 pDynLoad->pMod = NULL; /* indicates special */
5436
5437 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5438 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5439 KWLDR_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5440 }
5441 else
5442 kHlpFree(pDynLoad);
5443 return hmod;
5444}
5445
5446
5447/**
5448 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5449 */
5450static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5451{
5452 static const char s_szDll[] = ".dll";
5453 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5454 PKWMODULE pMod;
5455 char szNormPath[256];
5456
5457 /*
5458 * Lower case it and make sure it ends with .dll.
5459 */
5460 if (cbFilename > sizeof(szNormPath))
5461 {
5462 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5463 return NULL;
5464 }
5465 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5466 _strlwr(szNormPath);
5467 kHlpAssert(cbFilename > 7 /* api-ms- */ );
5468 if (strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5469 {
5470 if (cbFilename + sizeof(s_szDll) - 1 > sizeof(szNormPath))
5471 {
5472 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5473 return NULL;
5474 }
5475
5476 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5477 cbFilename += sizeof(s_szDll) - 1;
5478 }
5479
5480 /*
5481 * Try load it.
5482 */
5483 pMod = kwLdrModuleTryLoadVirtualDll(szNormPath, cbFilename - 1);
5484 if (pMod)
5485 {
5486 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5487
5488 pDynLoad->pMod = pMod;
5489 pDynLoad->hmod = pMod->hOurMod;
5490
5491 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5492 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5493 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5494 return pDynLoad->hmod;
5495 }
5496 kHlpFree(pDynLoad);
5497 return NULL;
5498}
5499
5500
5501/** Kernel32 - LoadLibraryExA() */
5502static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5503{
5504 KSIZE cchFilename = kHlpStrLen(pszFilename);
5505 const char *pszSearchPath;
5506 PKWDYNLOAD pDynLoad;
5507 PKWMODULE pMod;
5508 int rc;
5509 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5510 //fprintf(stderr, "LoadLibraryExA: %s, %#x\n", pszFilename, fFlags);
5511
5512 /*
5513 * Deal with a couple of extremely unlikely special cases right away.
5514 */
5515 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5516 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5517 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5518 { /* likely */ }
5519 else
5520 {
5521 KWFS_TODO();
5522 return LoadLibraryExA(pszFilename, hFile, fFlags);
5523 }
5524
5525 /*
5526 * Check if we've already got a dynload entry for this one.
5527 */
5528 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5529 if ( pDynLoad->cchRequest == cchFilename
5530 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5531 {
5532 if (pDynLoad->pMod)
5533 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5534 else
5535 rc = 0;
5536 if (rc == 0)
5537 {
5538 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5539 return pDynLoad->hmod;
5540 }
5541 SetLastError(ERROR_DLL_INIT_FAILED);
5542 return NULL;
5543 }
5544
5545 /*
5546 * Allocate a dynload entry for the request.
5547 */
5548 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5549 if (pDynLoad)
5550 {
5551 pDynLoad->cchRequest = cchFilename;
5552 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5553 }
5554 else
5555 {
5556 KWLDR_LOG(("LoadLibraryExA: Out of memory!\n"));
5557 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5558 return NULL;
5559 }
5560
5561 /*
5562 * Deal with resource / data DLLs.
5563 */
5564 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5565 | LOAD_LIBRARY_AS_DATAFILE
5566 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5567 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5568 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5569
5570 /*
5571 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5572 */
5573 if ( kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5574 && kHlpIsFilenameOnly(pszFilename))
5575 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5576
5577 /*
5578 * Normal library loading.
5579 * We start by being very lazy and reusing the code for resolving imports.
5580 */
5581 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5582 if (!kHlpIsFilenameOnly(pszFilename))
5583 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5584 else
5585 {
5586 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5587 if (rc != 0)
5588 pMod = NULL;
5589 }
5590 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5591 {
5592 /* Enter it into the tool module table and dynamic link request cache. */
5593 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5594
5595 pDynLoad->pMod = pMod;
5596 pDynLoad->hmod = pMod->hOurMod;
5597
5598 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5599 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5600
5601 /*
5602 * Make sure it's initialized (need to link it first since DllMain may
5603 * use loader APIs).
5604 */
5605 rc = kwLdrModuleInitTree(pMod);
5606 if (rc == 0)
5607 {
5608 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5609 return pDynLoad->hmod;
5610 }
5611
5612 SetLastError(ERROR_DLL_INIT_FAILED);
5613 }
5614 else
5615 {
5616 KWFS_TODO();
5617 kHlpFree(pDynLoad);
5618 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5619 }
5620 return NULL;
5621}
5622
5623
5624/** Kernel32 - LoadLibraryExA() for native overloads */
5625static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5626{
5627 char szPath[1024];
5628 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5629
5630 /*
5631 * We may have to help resolved unqualified DLLs living in the executable directory.
5632 */
5633 if (kHlpIsFilenameOnly(pszFilename))
5634 {
5635 KSIZE cchFilename = kHlpStrLen(pszFilename);
5636 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5637 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5638 {
5639 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5640 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5641 if (kwFsPathExists(szPath))
5642 {
5643 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5644 pszFilename = szPath;
5645 }
5646 }
5647
5648 if (pszFilename != szPath)
5649 {
5650 KSIZE cchSuffix = 0;
5651 KBOOL fNeedSuffix = K_FALSE;
5652 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5653 while (*pszCur != '\0')
5654 {
5655 /* Find the end of the component */
5656 KSIZE cch = 0;
5657 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5658 cch++;
5659
5660 if ( cch > 0 /* wrong, but whatever */
5661 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5662 {
5663 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5664 if ( szPath[cch - 1] != ':'
5665 && szPath[cch - 1] != '/'
5666 && szPath[cch - 1] != '\\')
5667 *pszDst++ = '\\';
5668 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5669 if (fNeedSuffix)
5670 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5671 *pszDst = '\0';
5672
5673 if (kwFsPathExists(szPath))
5674 {
5675 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5676 pszFilename = szPath;
5677 break;
5678 }
5679 }
5680
5681 /* Advance */
5682 pszCur += cch;
5683 while (*pszCur == ';')
5684 pszCur++;
5685 }
5686 }
5687 }
5688
5689 return LoadLibraryExA(pszFilename, hFile, fFlags);
5690}
5691
5692
5693/** Kernel32 - LoadLibraryExW() */
5694static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5695{
5696 char szTmp[4096];
5697 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5698 if (cchTmp < sizeof(szTmp))
5699 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5700
5701 KWFS_TODO();
5702 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5703 return NULL;
5704}
5705
5706/** Kernel32 - LoadLibraryA() */
5707static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5708{
5709 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5710}
5711
5712
5713/** Kernel32 - LoadLibraryW() */
5714static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5715{
5716 char szTmp[4096];
5717 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5718 if (cchTmp < sizeof(szTmp))
5719 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5720 KWFS_TODO();
5721 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5722 return NULL;
5723}
5724
5725
5726/** Kernel32 - FreeLibrary() */
5727static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5728{
5729 /* Ignored, we like to keep everything loaded. */
5730 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5731 return TRUE;
5732}
5733
5734
5735/** Worker for GetModuleHandleA/W for handling cached modules. */
5736static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5737{
5738 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5739 if (hmod)
5740 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5741 hmod, g_aGetModuleHandleCache[i].pszName));
5742 else
5743 {
5744 /*
5745 * The first time around we have to make sure we have a module table
5746 * entry for it, if not we add one. We need to add it to the tools
5747 * module list to for it to work.
5748 */
5749 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
5750 g_aGetModuleHandleCache[i].fAlwaysPresent);
5751 if (pMod)
5752 {
5753 hmod = pMod->hOurMod;
5754 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5755 {
5756 kwToolAddModule(g_Sandbox.pTool, pMod);
5757 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5758 hmod, g_aGetModuleHandleCache[i].pszName));
5759 }
5760 else
5761 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5762 hmod, g_aGetModuleHandleCache[i].pszName));
5763
5764 }
5765 }
5766 return hmod;
5767}
5768
5769
5770/** Kernel32 - GetModuleHandleA() */
5771static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5772{
5773 KSIZE i;
5774 KSIZE cchModule;
5775 PKWDYNLOAD pDynLoad;
5776 KSIZE cchSuffix;
5777 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5778 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5779
5780 /*
5781 * The executable.
5782 */
5783 if (pszModule == NULL)
5784 {
5785 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5786 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5787 }
5788
5789 /*
5790 * If no path of suffix, pretend it ends with .DLL.
5791 */
5792 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5793
5794 /*
5795 * Cache of system modules we've seen queried.
5796 */
5797 cchModule = kHlpStrLen(pszModule);
5798 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5799 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5800 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5801 || ( cchSuffix > 0
5802 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5803 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5804 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5805 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5806
5807 /*
5808 * Modules we've dynamically loaded.
5809 */
5810 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5811 if (pDynLoad->pMod)
5812 {
5813 const char *pszPath = pDynLoad->pMod->pszPath;
5814 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
5815 if ( stricmp(pszPath, pszModule) == 0
5816 || stricmp(pszName, pszModule) == 0
5817 || ( cchSuffix > 0
5818 && strnicmp(pszName, pszModule, cchModule) == 0
5819 && stricmp(&pszName[cchModule], ".dll") == 0))
5820 {
5821 if ( pDynLoad->pMod->fNative
5822 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5823 {
5824 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
5825 return pDynLoad->hmod;
5826 }
5827 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
5828 SetLastError(ERROR_MOD_NOT_FOUND);
5829 return NULL;
5830 }
5831 }
5832
5833 /*
5834 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5835 * to and go via the g_aGetModuleHandleCache cache.
5836 */
5837/** @todo virtual api DLLs */
5838 if (kHlpStrNICompAscii(pszModule, "api-ms-win-", 11) == 0)
5839 {
5840 HMODULE hmod = GetModuleHandleA(pszModule);
5841 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
5842 if (hmod)
5843 {
5844 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5845 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
5846 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5847 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
5848 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5849 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
5850 if (hmod == GetModuleHandleW(L"UCRTBASE.DLL"))
5851 return kwSandbox_Kernel32_GetModuleHandleA("UCRTBASE.DLL");
5852 }
5853 else
5854 dwErr = GetLastError();
5855 }
5856
5857 kwErrPrintf("pszModule=%s\n", pszModule);
5858 KWFS_TODO();
5859 SetLastError(ERROR_MOD_NOT_FOUND);
5860 return NULL;
5861}
5862
5863
5864/** Kernel32 - GetModuleHandleW() */
5865static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
5866{
5867 KSIZE i;
5868 KSIZE cwcModule;
5869 PKWDYNLOAD pDynLoad;
5870 KSIZE cwcSuffix;
5871 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5872 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5873
5874 /*
5875 * The executable.
5876 */
5877 if (pwszModule == NULL)
5878 {
5879 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5880 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5881 }
5882
5883 /*
5884 * If no path of suffix, pretend it ends with .DLL.
5885 */
5886 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
5887
5888 /*
5889 * Cache of system modules we've seen queried.
5890 */
5891 cwcModule = kwUtf16Len(pwszModule);
5892 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5893 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
5894 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5895 || ( cwcSuffix > 0
5896 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
5897 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
5898 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
5899 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5900
5901 /*
5902 * Modules we've dynamically loaded.
5903 */
5904 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5905 if (pDynLoad->pMod)
5906 {
5907 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
5908 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
5909 if ( _wcsicmp(pwszPath, pwszModule) == 0
5910 || _wcsicmp(pwszName, pwszModule) == 0
5911 || ( cwcSuffix
5912 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
5913 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
5914 {
5915 if ( pDynLoad->pMod->fNative
5916 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5917 {
5918 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5919 return pDynLoad->hmod;
5920 }
5921 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
5922 SetLastError(ERROR_MOD_NOT_FOUND);
5923 return NULL;
5924 }
5925 }
5926
5927 /*
5928 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5929 * to and go via the g_aGetModuleHandleCache cache.
5930 */
5931 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
5932 {
5933 HMODULE hmod = GetModuleHandleW(pwszModule);
5934 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
5935 if (hmod)
5936 {
5937 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5938 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
5939 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5940 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
5941 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5942 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
5943 }
5944 else
5945 dwErr = GetLastError();
5946 }
5947
5948 kwErrPrintf("pwszModule=%ls\n", pwszModule);
5949 KWFS_TODO();
5950 SetLastError(dwErr);
5951 return NULL;
5952}
5953
5954
5955/** Used to debug dynamically resolved procedures. */
5956static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5957{
5958#ifdef _MSC_VER
5959 __debugbreak();
5960#else
5961 KWFS_TODO();
5962#endif
5963 return ~(UINT)0;
5964}
5965
5966
5967#ifndef NDEBUG
5968/*
5969 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5970 */
5971# if K_ARCH == K_ARCH_X86_32
5972static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5973# else
5974static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5975# endif
5976typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5977typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5978typedef struct KWCXINTERCEPTORENTRY
5979{
5980 PFNINVOKECOMPILERPASSW pfnOrg;
5981 PKWMODULE pModule;
5982 PFNINVOKECOMPILERPASSW pfnWrap;
5983} KWCXINTERCEPTORENTRY;
5984
5985static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5986 KWCXINTERCEPTORENTRY *pEntry)
5987{
5988 int i;
5989 KIPTR rcExit;
5990 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5991 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5992 for (i = 0; i < cArgs; i++)
5993 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5994
5995 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5996
5997 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5998 return rcExit;
5999}
6000
6001static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
6002static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
6003static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
6004
6005static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
6006{
6007 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
6008 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
6009 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
6010};
6011
6012static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6013{
6014 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
6015}
6016
6017static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6018{
6019 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
6020}
6021
6022static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6023{
6024 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
6025}
6026
6027#endif /* !NDEBUG */
6028
6029
6030/** Kernel32 - GetProcAddress() */
6031static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6032{
6033 KSIZE i;
6034 PKWMODULE pMod;
6035 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6036
6037 /*
6038 * Try locate the module.
6039 */
6040 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6041 if (pMod)
6042 {
6043 KLDRADDR uValue;
6044 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
6045 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
6046 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
6047 KU32_MAX /*iSymbol*/,
6048 pszProc,
6049 kHlpStrLen(pszProc),
6050 NULL /*pszVersion*/,
6051 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
6052 &uValue,
6053 NULL /*pfKind*/);
6054 if (rc == 0)
6055 {
6056 //static int s_cDbgGets = 0;
6057 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
6058 KU32 i = g_cSandboxGetProcReplacements;
6059 while (i-- > 0)
6060 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
6061 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
6062 {
6063 if ( !g_aSandboxGetProcReplacements[i].pszModule
6064 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
6065 {
6066 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
6067 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
6068 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
6069 {
6070 if (!g_aSandboxReplacements[i].fCrtSlotArray)
6071 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
6072 else
6073 {
6074 if (pMod->iCrtSlot == KU8_MAX)
6075 {
6076 int rc = kwLdrModuleCreateCrtSlot(pMod);
6077 if (rc)
6078 {
6079 KW_LOG(("GetProcAddress: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
6080 SetLastError(ERROR_INTERNAL_ERROR);
6081 return NULL;
6082 }
6083 }
6084 uValue = ((KUPTR *)g_aSandboxGetProcReplacements[i].pfnReplacement)[pMod->iCrtSlot];
6085 }
6086
6087 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6088 }
6089 kwLdrModuleRelease(pMod);
6090 return (FARPROC)(KUPTR)uValue;
6091 }
6092 }
6093
6094#ifndef NDEBUG
6095 /* Intercept the compiler pass method, dumping arguments. */
6096 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
6097 {
6098 KU32 i;
6099 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6100 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6101 {
6102 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6103 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6104 break;
6105 }
6106 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6107 while (i-- > 0)
6108 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6109 {
6110 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6111 g_aCxInterceptorEntries[i].pModule = pMod;
6112 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6113 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6114 break;
6115 }
6116 }
6117#endif
6118 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6119 kwLdrModuleRelease(pMod);
6120 //s_cDbgGets++;
6121 //if (s_cGets >= 3)
6122 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6123 return (FARPROC)(KUPTR)uValue;
6124 }
6125
6126 KWFS_TODO();
6127 SetLastError(ERROR_PROC_NOT_FOUND);
6128 kwLdrModuleRelease(pMod);
6129 return NULL;
6130 }
6131
6132 /*
6133 * Hmm... could be a cached module-by-name.
6134 */
6135 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6136 if (g_aGetModuleHandleCache[i].hmod == hmod)
6137 return GetProcAddress(hmod, pszProc);
6138
6139 KWFS_TODO();
6140 return GetProcAddress(hmod, pszProc);
6141}
6142
6143
6144/** Kernel32 - GetModuleFileNameA() */
6145static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6146{
6147 PKWMODULE pMod;
6148 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6149
6150 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6151 if (pMod != NULL)
6152 {
6153 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6154 kwLdrModuleRelease(pMod);
6155 return cbRet;
6156 }
6157 KWFS_TODO();
6158 return 0;
6159}
6160
6161
6162/** Kernel32 - GetModuleFileNameW() */
6163static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6164{
6165 PKWMODULE pMod;
6166 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6167
6168 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6169 if (pMod)
6170 {
6171 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6172 kwLdrModuleRelease(pMod);
6173 return cwcRet;
6174 }
6175
6176 KWFS_TODO();
6177 return 0;
6178}
6179
6180
6181/** NtDll - RtlPcToFileHeader
6182 * This is necessary for msvcr100.dll!CxxThrowException. */
6183static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6184{
6185 PVOID pvRet;
6186
6187 /*
6188 * Do a binary lookup of the module table for the current tool.
6189 * This will give us a
6190 */
6191 if (g_Sandbox.fRunning)
6192 {
6193 KUPTR const uPC = (KUPTR)pvPC;
6194 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6195 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6196 KU32 i;
6197 if (iEnd)
6198 {
6199 KU32 iStart = 0;
6200 i = iEnd / 2;
6201 for (;;)
6202 {
6203 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6204 if (uPC < uHModCur)
6205 {
6206 iEnd = i;
6207 if (iStart < i)
6208 { }
6209 else
6210 break;
6211 }
6212 else if (uPC != uHModCur)
6213 {
6214 iStart = ++i;
6215 if (i < iEnd)
6216 { }
6217 else
6218 break;
6219 }
6220 else
6221 {
6222 /* This isn't supposed to happen. */
6223 break;
6224 }
6225
6226 i = iStart + (iEnd - iStart) / 2;
6227 }
6228
6229 /* For reasons of simplicity (= copy & paste), we end up with the
6230 module after the one we're interested in here. */
6231 i--;
6232 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6233 && papMods[i]->pLdrMod)
6234 {
6235 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6236 if (uRvaPC < papMods[i]->cbImage)
6237 {
6238 *ppvImageBase = papMods[i]->hOurMod;
6239 pvRet = papMods[i]->hOurMod;
6240 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6241 return pvRet;
6242 }
6243 }
6244 }
6245 else
6246 i = 0;
6247 }
6248
6249 /*
6250 * Call the regular API.
6251 */
6252 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6253 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6254 return pvRet;
6255}
6256
6257
6258/*
6259 *
6260 * File access APIs (for speeding them up).
6261 * File access APIs (for speeding them up).
6262 * File access APIs (for speeding them up).
6263 *
6264 */
6265
6266
6267/**
6268 * Converts a lookup error to a windows error code.
6269 *
6270 * @returns The windows error code.
6271 * @param enmError The lookup error.
6272 */
6273static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6274{
6275 switch (enmError)
6276 {
6277 case KFSLOOKUPERROR_NOT_FOUND:
6278 case KFSLOOKUPERROR_NOT_DIR:
6279 return ERROR_FILE_NOT_FOUND;
6280
6281 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6282 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6283 return ERROR_PATH_NOT_FOUND;
6284
6285 case KFSLOOKUPERROR_PATH_TOO_LONG:
6286 return ERROR_FILENAME_EXCED_RANGE;
6287
6288 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6289 return ERROR_NOT_ENOUGH_MEMORY;
6290
6291 default:
6292 return ERROR_PATH_NOT_FOUND;
6293 }
6294}
6295
6296#ifdef WITH_TEMP_MEMORY_FILES
6297
6298/**
6299 * Checks for a cl.exe temporary file.
6300 *
6301 * There are quite a bunch of these. They seems to be passing data between the
6302 * first and second compiler pass. Since they're on disk, they get subjected to
6303 * AV software screening and normal file consistency rules. So, not necessarily
6304 * a very efficient way of handling reasonably small amounts of data.
6305 *
6306 * We make the files live in virtual memory by intercepting their opening,
6307 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6308 *
6309 * @returns K_TRUE / K_FALSE
6310 * @param pwszFilename The file name being accessed.
6311 */
6312static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6313{
6314 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6315 if (pwszName)
6316 {
6317 /* The name starts with _CL_... */
6318 if ( pwszName[0] == '_'
6319 && pwszName[1] == 'C'
6320 && pwszName[2] == 'L'
6321 && pwszName[3] == '_' )
6322 {
6323 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6324 this check by just checking that it's alpha numerical ascii from here on. */
6325 wchar_t wc;
6326 pwszName += 4;
6327 while ((wc = *pwszName++) != '\0')
6328 {
6329 if (wc < 127 && iswalnum(wc))
6330 { /* likely */ }
6331 else
6332 return K_FALSE;
6333 }
6334 return K_TRUE;
6335 }
6336
6337 /* In VC2019 there is also one {UUID} file in temp: */
6338 if (pwszName[0] == '{')
6339 {
6340 KSIZE cwcName = kwUtf16Len(pwszName);
6341 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6342 && pwszName[37] == '}'
6343 && iswalnum(pwszName[1]) // 4
6344 && iswalnum(pwszName[2]) // 4
6345 && iswalnum(pwszName[3]) // 6
6346 && iswalnum(pwszName[4]) // 5
6347 && iswalnum(pwszName[5]) // d
6348 && iswalnum(pwszName[6]) // d
6349 && iswalnum(pwszName[7]) // d
6350 && iswalnum(pwszName[8]) // 9
6351 && pwszName[9] == '-' // -
6352 && iswalnum(pwszName[10]) // e
6353 && iswalnum(pwszName[11]) // 4
6354 && iswalnum(pwszName[12]) // 9
6355 && iswalnum(pwszName[13]) // 4
6356 && pwszName[14] == '-' // -
6357 && iswalnum(pwszName[15]) // 4
6358 && iswalnum(pwszName[16]) // 7
6359 && iswalnum(pwszName[17]) // 1
6360 && iswalnum(pwszName[18]) // b
6361 && pwszName[19] == '-' // -
6362 && iswalnum(pwszName[20]) // 9
6363 && iswalnum(pwszName[21]) // 9
6364 && iswalnum(pwszName[22]) // 6
6365 && iswalnum(pwszName[23]) // b
6366 && pwszName[24] == '-' // -
6367 && iswalnum(pwszName[25]) // 9
6368 && iswalnum(pwszName[26]) // b
6369 && iswalnum(pwszName[27]) // 5
6370 && iswalnum(pwszName[28]) // 5
6371 && iswalnum(pwszName[29]) // 6
6372 && iswalnum(pwszName[30]) // e
6373 && iswalnum(pwszName[31]) // 2
6374 && iswalnum(pwszName[32]) // 5
6375 && iswalnum(pwszName[33]) // a
6376 && iswalnum(pwszName[34]) // 3
6377 && iswalnum(pwszName[35]) // f
6378 && iswalnum(pwszName[36])) // 8
6379 return K_TRUE;
6380 }
6381 }
6382 return K_FALSE;
6383}
6384
6385
6386/**
6387 * Creates a handle to a temporary file.
6388 *
6389 * @returns The handle on success.
6390 * INVALID_HANDLE_VALUE and SetLastError on failure.
6391 * @param pTempFile The temporary file.
6392 * @param dwDesiredAccess The desired access to the handle.
6393 * @param fMapping Whether this is a mapping (K_TRUE) or file
6394 * (K_FALSE) handle type.
6395 */
6396static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6397{
6398 /*
6399 * Create a handle to the temporary file.
6400 */
6401 HANDLE hFile = INVALID_HANDLE_VALUE;
6402 HANDLE hProcSelf = GetCurrentProcess();
6403 if (DuplicateHandle(hProcSelf, hProcSelf,
6404 hProcSelf, &hFile,
6405 SYNCHRONIZE, FALSE,
6406 0 /*dwOptions*/))
6407 {
6408 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6409 if (pHandle)
6410 {
6411 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6412 pHandle->cRefs = 1;
6413 pHandle->offFile = 0;
6414 pHandle->hHandle = hFile;
6415 pHandle->dwDesiredAccess = dwDesiredAccess;
6416 pHandle->u.pTempFile = pTempFile;
6417 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6418 {
6419 pTempFile->cActiveHandles++;
6420 kHlpAssert(pTempFile->cActiveHandles >= 1);
6421 kHlpAssert(pTempFile->cActiveHandles <= 2);
6422 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6423 return hFile;
6424 }
6425
6426 kHlpFree(pHandle);
6427 }
6428 else
6429 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6430 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6431 }
6432 else
6433 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6434 return INVALID_HANDLE_VALUE;
6435}
6436
6437
6438static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6439 KBOOL *pfFallback)
6440{
6441 HANDLE hFile;
6442 DWORD dwErr;
6443
6444 /*
6445 * Check if we've got an existing temp file.
6446 * ASSUME exact same path for now.
6447 */
6448 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6449 PKWFSTEMPFILE pTempFile;
6450 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6451 {
6452 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6453 if ( pTempFile->cwcPath == cwcFilename
6454 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6455 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6456 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6457 break;
6458 }
6459
6460 /*
6461 * Create a new temporary file instance if not found.
6462 */
6463 *pfFallback = K_FALSE;
6464 if (pTempFile == NULL)
6465 {
6466 KSIZE cbFilename;
6467
6468 switch (dwCreationDisposition)
6469 {
6470 case CREATE_ALWAYS:
6471 case OPEN_ALWAYS:
6472 case CREATE_NEW:
6473 dwErr = NO_ERROR;
6474 break;
6475
6476 case OPEN_EXISTING:
6477 case TRUNCATE_EXISTING:
6478 *pfFallback = K_TRUE;
6479 kHlpAssertFailed();
6480 SetLastError(ERROR_FILE_NOT_FOUND);
6481 return INVALID_HANDLE_VALUE;
6482
6483 default:
6484 kHlpAssertFailed();
6485 SetLastError(ERROR_INVALID_PARAMETER);
6486 return INVALID_HANDLE_VALUE;
6487 }
6488
6489 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6490 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6491 if (pTempFile)
6492 {
6493 pTempFile->cwcPath = (KU16)cwcFilename;
6494 pTempFile->cbFile = 0;
6495 pTempFile->cbFileAllocated = 0;
6496 pTempFile->cActiveHandles = 0;
6497 pTempFile->cMappings = 0;
6498 pTempFile->cSegs = 0;
6499 pTempFile->paSegs = NULL;
6500 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6501
6502 pTempFile->pNext = g_Sandbox.pTempFileHead;
6503 g_Sandbox.pTempFileHead = pTempFile;
6504 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6505 }
6506 else
6507 {
6508 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6509 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6510 return INVALID_HANDLE_VALUE;
6511 }
6512 }
6513 else
6514 {
6515 switch (dwCreationDisposition)
6516 {
6517 case OPEN_EXISTING:
6518 dwErr = NO_ERROR;
6519 break;
6520 case OPEN_ALWAYS:
6521 dwErr = ERROR_ALREADY_EXISTS;
6522 break;
6523
6524 case TRUNCATE_EXISTING:
6525 case CREATE_ALWAYS:
6526 kHlpAssertFailed();
6527 pTempFile->cbFile = 0;
6528 dwErr = ERROR_ALREADY_EXISTS;
6529 break;
6530
6531 case CREATE_NEW:
6532 kHlpAssertFailed();
6533 SetLastError(ERROR_FILE_EXISTS);
6534 return INVALID_HANDLE_VALUE;
6535
6536 default:
6537 kHlpAssertFailed();
6538 SetLastError(ERROR_INVALID_PARAMETER);
6539 return INVALID_HANDLE_VALUE;
6540 }
6541 }
6542
6543 /*
6544 * Create a handle to the temporary file.
6545 */
6546 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6547 if (hFile != INVALID_HANDLE_VALUE)
6548 SetLastError(dwErr);
6549 return hFile;
6550}
6551
6552#endif /* WITH_TEMP_MEMORY_FILES */
6553
6554/**
6555 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6556 *
6557 * @returns K_TRUE if cacheable, K_FALSE if not.
6558 * @param wcFirst The first extension character.
6559 * @param wcSecond The second extension character.
6560 * @param wcThird The third extension character.
6561 * @param fAttrQuery Set if it's for an attribute query, clear if for
6562 * file creation.
6563 */
6564static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6565{
6566 /* C++ header without an extension or a directory. */
6567 if (wcFirst == '\0')
6568 {
6569 /** @todo exclude temporary files... */
6570 return K_TRUE;
6571 }
6572
6573 /* C Header: .h */
6574 if (wcFirst == 'h' || wcFirst == 'H')
6575 {
6576 if (wcSecond == '\0')
6577 return K_TRUE;
6578
6579 /* C++ Header: .hpp, .hxx */
6580 if ( (wcSecond == 'p' || wcSecond == 'P')
6581 && (wcThird == 'p' || wcThird == 'P'))
6582 return K_TRUE;
6583 if ( (wcSecond == 'x' || wcSecond == 'X')
6584 && (wcThird == 'x' || wcThird == 'X'))
6585 return K_TRUE;
6586 }
6587 /* Misc starting with i. */
6588 else if (wcFirst == 'i' || wcFirst == 'I')
6589 {
6590 if (wcSecond != '\0')
6591 {
6592 if (wcSecond == 'n' || wcSecond == 'N')
6593 {
6594 /* C++ inline header: .inl */
6595 if (wcThird == 'l' || wcThird == 'L')
6596 return K_TRUE;
6597
6598 /* Assembly include file: .inc */
6599 if (wcThird == 'c' || wcThird == 'C')
6600 return K_TRUE;
6601 }
6602 }
6603 }
6604 /* Assembly header: .mac */
6605 else if (wcFirst == 'm' || wcFirst == 'M')
6606 {
6607 if (wcSecond == 'a' || wcSecond == 'A')
6608 {
6609 if (wcThird == 'c' || wcThird == 'C')
6610 return K_TRUE;
6611 }
6612 }
6613#ifdef WITH_PCH_CACHING
6614 /* Precompiled header: .pch */
6615 else if (wcFirst == 'p' || wcFirst == 'P')
6616 {
6617 if (wcSecond == 'c' || wcSecond == 'C')
6618 {
6619 if (wcThird == 'h' || wcThird == 'H')
6620 return !g_Sandbox.fNoPchCaching;
6621 }
6622 }
6623#endif
6624#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6625 /* Linker - Object file: .obj */
6626 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6627 {
6628 if (wcSecond == 'b' || wcSecond == 'B')
6629 {
6630 if (wcThird == 'j' || wcThird == 'J')
6631 return K_TRUE;
6632 }
6633 }
6634#endif
6635 else if (fAttrQuery)
6636 {
6637 /* Dynamic link library: .dll */
6638 if (wcFirst == 'd' || wcFirst == 'D')
6639 {
6640 if (wcSecond == 'l' || wcSecond == 'L')
6641 {
6642 if (wcThird == 'l' || wcThird == 'L')
6643 return K_TRUE;
6644 }
6645 }
6646 /* Executable file: .exe */
6647 else if (wcFirst == 'e' || wcFirst == 'E')
6648 {
6649 if (wcSecond == 'x' || wcSecond == 'X')
6650 {
6651 if (wcThird == 'e' || wcThird == 'E')
6652 return K_TRUE;
6653 }
6654 }
6655 /* Response file: .rsp */
6656 else if (wcFirst == 'r' || wcFirst == 'R')
6657 {
6658 if (wcSecond == 's' || wcSecond == 'S')
6659 {
6660 if (wcThird == 'p' || wcThird == 'P')
6661 return !g_Sandbox.fNoPchCaching;
6662 }
6663 }
6664 /* Linker: */
6665 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6666 {
6667 /* Object file: .obj */
6668 if (wcFirst == 'o' || wcFirst == 'O')
6669 {
6670 if (wcSecond == 'b' || wcSecond == 'B')
6671 {
6672 if (wcThird == 'j' || wcThird == 'J')
6673 return K_TRUE;
6674 }
6675 }
6676 /* Library file: .lib */
6677 else if (wcFirst == 'l' || wcFirst == 'L')
6678 {
6679 if (wcSecond == 'i' || wcSecond == 'I')
6680 {
6681 if (wcThird == 'b' || wcThird == 'B')
6682 return K_TRUE;
6683 }
6684 }
6685 /* Linker definition file: .def */
6686 else if (wcFirst == 'd' || wcFirst == 'D')
6687 {
6688 if (wcSecond == 'e' || wcSecond == 'E')
6689 {
6690 if (wcThird == 'f' || wcThird == 'F')
6691 return K_TRUE;
6692 }
6693 }
6694 }
6695 }
6696
6697 return K_FALSE;
6698}
6699
6700
6701/**
6702 * Checks if the file extension indicates that the file/dir is something we
6703 * ought to cache.
6704 *
6705 * @returns K_TRUE if cachable, K_FALSE if not.
6706 * @param pszExt The kHlpGetExt result.
6707 * @param fAttrQuery Set if it's for an attribute query, clear if for
6708 * file creation.
6709 */
6710static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6711{
6712 wchar_t const wcFirst = *pszExt;
6713 if (wcFirst)
6714 {
6715 wchar_t const wcSecond = pszExt[1];
6716 if (wcSecond)
6717 {
6718 wchar_t const wcThird = pszExt[2];
6719 if (pszExt[3] == '\0')
6720 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6721 return K_FALSE;
6722 }
6723 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6724 }
6725 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6726}
6727
6728
6729/**
6730 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6731 * should be cached.
6732 *
6733 * @returns K_TRUE if cachable, K_FALSE if not.
6734 * @param pwszPath The UTF-16 path to examine.
6735 * @param fAttrQuery Set if it's for an attribute query, clear if for
6736 * file creation.
6737 */
6738static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6739{
6740 KSIZE cwcExt;
6741 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6742 switch (cwcExt)
6743 {
6744 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6745 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6746 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6747 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6748 }
6749 return K_FALSE;
6750}
6751
6752
6753
6754/**
6755 * Creates a new
6756 *
6757 * @returns
6758 * @param pFsObj .
6759 * @param pwszFilename .
6760 */
6761static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6762{
6763 HANDLE hFile;
6764 MY_IO_STATUS_BLOCK Ios;
6765 MY_OBJECT_ATTRIBUTES ObjAttr;
6766 MY_UNICODE_STRING UniStr;
6767 MY_NTSTATUS rcNt;
6768 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6769
6770 /*
6771 * Open the file relative to the parent directory.
6772 */
6773 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6774 kHlpAssert(pFsObj->pParent);
6775 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6776
6777 Ios.Information = ~(ULONG_PTR)0;
6778 Ios.u.Status = -1;
6779
6780 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6781 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6782 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6783
6784 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6785
6786 rcNt = g_pfnNtCreateFile(&hFile,
6787 GENERIC_READ | SYNCHRONIZE,
6788 &ObjAttr,
6789 &Ios,
6790 NULL, /*cbFileInitialAlloc */
6791 FILE_ATTRIBUTE_NORMAL,
6792 FILE_SHARE_READ,
6793 FILE_OPEN,
6794 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6795 NULL, /*pEaBuffer*/
6796 0); /*cbEaBuffer*/
6797 if (MY_NT_SUCCESS(rcNt))
6798 {
6799 /*
6800 * Read the whole file into memory.
6801 */
6802 LARGE_INTEGER cbFile;
6803 if (GetFileSizeEx(hFile, &cbFile))
6804 {
6805 if ( cbFile.QuadPart >= 0
6806#ifdef WITH_PCH_CACHING
6807 && ( cbFile.QuadPart < 16*1024*1024
6808 || ( cbFile.QuadPart < 96*1024*1024
6809 && pFsObj->cchName > 4
6810 && !g_Sandbox.fNoPchCaching
6811 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
6812#endif
6813 )
6814 {
6815 KU32 cbCache = (KU32)cbFile.QuadPart;
6816 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
6817 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
6818 if (hMapping != NULL)
6819 {
6820 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
6821 if (pbCache)
6822 {
6823 /*
6824 * Create the cached file object.
6825 */
6826 PKFSWCACHEDFILE pCachedFile;
6827 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
6828 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
6829 sizeof(*pCachedFile) + cbPath);
6830 if (pCachedFile)
6831 {
6832 pCachedFile->hCached = hFile;
6833 pCachedFile->hSection = hMapping;
6834 pCachedFile->cbCached = cbCache;
6835 pCachedFile->pbCached = pbCache;
6836 pCachedFile->pFsObj = pFsObj;
6837 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
6838 kFsCacheObjRetain(pFsObj);
6839
6840 g_cReadCachedFiles++;
6841 g_cbReadCachedFiles += cbCache;
6842
6843 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
6844 return pCachedFile;
6845 }
6846
6847 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
6848 }
6849 else
6850 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
6851 CloseHandle(hMapping);
6852 }
6853 else
6854 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
6855 }
6856 else
6857 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
6858 }
6859 else
6860 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
6861 g_pfnNtClose(hFile);
6862 }
6863 else
6864 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
6865 return NULL;
6866}
6867
6868
6869/**
6870 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
6871 */
6872static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
6873 KBOOL fIsFileHandle, HANDLE *phFile)
6874{
6875 HANDLE hProcSelf = GetCurrentProcess();
6876 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
6877 hProcSelf, phFile,
6878 dwDesiredAccess, fInheritHandle,
6879 0 /*dwOptions*/))
6880 {
6881 /*
6882 * Create handle table entry for the duplicate handle.
6883 */
6884 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6885 if (pHandle)
6886 {
6887 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
6888 pHandle->cRefs = 1;
6889 pHandle->offFile = 0;
6890 pHandle->hHandle = *phFile;
6891 pHandle->dwDesiredAccess = dwDesiredAccess;
6892 pHandle->u.pCachedFile = pCachedFile;
6893 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
6894 return K_TRUE;
6895
6896 kHlpFree(pHandle);
6897 }
6898 else
6899 KWFS_LOG(("Out of memory for handle!\n"));
6900
6901 CloseHandle(*phFile);
6902 *phFile = INVALID_HANDLE_VALUE;
6903 }
6904 else
6905 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
6906 return K_FALSE;
6907}
6908
6909
6910/**
6911 * Kernel32 - Common code for CreateFileW and CreateFileA.
6912 */
6913static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
6914{
6915 *phFile = INVALID_HANDLE_VALUE;
6916
6917 /*
6918 * At the moment we only handle existing files.
6919 */
6920 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
6921 {
6922 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
6923 kHlpAssert(pFsObj->fHaveStats);
6924 if ( pCachedFile != NULL
6925 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
6926 {
6927 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
6928 return K_TRUE;
6929 }
6930 }
6931 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
6932
6933 /* Do fallback, please. */
6934 return K_FALSE;
6935}
6936
6937
6938/** Kernel32 - CreateFileA */
6939static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6940 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6941 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6942{
6943 HANDLE hFile;
6944
6945 /*
6946 * Check for include files and similar that we do read-only caching of.
6947 */
6948 if (dwCreationDisposition == FILE_OPEN_IF)
6949 {
6950 if ( dwDesiredAccess == GENERIC_READ
6951 || dwDesiredAccess == FILE_GENERIC_READ)
6952 {
6953 if (dwShareMode & FILE_SHARE_READ)
6954 {
6955 if ( !pSecAttrs
6956 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6957 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6958 {
6959 const char *pszExt = kHlpGetExt(pszFilename);
6960 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
6961 {
6962 KFSLOOKUPERROR enmError;
6963 PKFSOBJ pFsObj;
6964 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6965
6966 pFsObj = kFsCacheLookupA(g_pFsCache, pszFilename, &enmError);
6967 if (pFsObj)
6968 {
6969 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
6970 {
6971 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
6972 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
6973 kFsCacheObjRelease(g_pFsCache, pFsObj);
6974 if (fRc)
6975 {
6976 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6977 return hFile;
6978 }
6979 }
6980 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
6981 {
6982 KWFS_LOG(("CreateFileA(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6983 return INVALID_HANDLE_VALUE;
6984 }
6985 /* Always fall back on missing files in volatile areas. */
6986 }
6987 /* These are for nasm and yasm header searching. Cache will already
6988 have checked the directories for the file, no need to call
6989 CreateFile to do it again. */
6990 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6991 {
6992 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6993 return INVALID_HANDLE_VALUE;
6994 }
6995 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6996 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6997 {
6998 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6999 return INVALID_HANDLE_VALUE;
7000 }
7001
7002 /* fallback */
7003 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7004 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7005 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
7006 return hFile;
7007 }
7008 }
7009 }
7010 }
7011 }
7012
7013 /*
7014 * Okay, normal.
7015 */
7016 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7017 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7018 if (hFile != INVALID_HANDLE_VALUE)
7019 {
7020 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
7021 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
7022 }
7023 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7024 return hFile;
7025}
7026
7027
7028/** Kernel32 - CreateFileW */
7029static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7030 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7031 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7032{
7033 HANDLE hFile;
7034
7035#ifdef WITH_TEMP_MEMORY_FILES
7036 /*
7037 * Check for temporary files (cl.exe only).
7038 */
7039 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7040 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7041 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7042 && kwFsIsClTempFileW(pwszFilename))
7043 {
7044 KBOOL fFallback = K_FALSE;
7045 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7046 if (!fFallback)
7047 {
7048 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7049 return hFile;
7050 }
7051 }
7052#endif
7053
7054 /*
7055 * Check for include files and similar that we do read-only caching of.
7056 */
7057 if (dwCreationDisposition == FILE_OPEN_IF)
7058 {
7059 if ( dwDesiredAccess == GENERIC_READ
7060 || dwDesiredAccess == FILE_GENERIC_READ)
7061 {
7062 if (dwShareMode & FILE_SHARE_READ)
7063 {
7064 if ( !pSecAttrs
7065 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7066 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7067 {
7068 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7069 {
7070 KFSLOOKUPERROR enmError;
7071 PKFSOBJ pFsObj;
7072 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7073
7074 pFsObj = kFsCacheLookupW(g_pFsCache, pwszFilename, &enmError);
7075 if (pFsObj)
7076 {
7077 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7078 {
7079 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7080 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7081 kFsCacheObjRelease(g_pFsCache, pFsObj);
7082 if (fRc)
7083 {
7084 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7085 return hFile;
7086 }
7087 }
7088 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7089 {
7090 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7091 return INVALID_HANDLE_VALUE;
7092 }
7093 /* Always fall back on missing files in volatile areas. */
7094 }
7095 /* These are for nasm and yasm style header searching. Cache will
7096 already have checked the directories for the file, no need to call
7097 CreateFile to do it again. */
7098 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7099 {
7100 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7101 return INVALID_HANDLE_VALUE;
7102 }
7103 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7104 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7105 {
7106 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7107 return INVALID_HANDLE_VALUE;
7108 }
7109
7110 /* fallback */
7111 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7112 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7113 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7114 return hFile;
7115 }
7116 }
7117 else
7118 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7119 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7120 }
7121 else
7122 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7123 }
7124 else
7125 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7126 }
7127 else
7128 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7129
7130 /*
7131 * Okay, normal.
7132 */
7133 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7134 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7135 if (hFile != INVALID_HANDLE_VALUE)
7136 {
7137 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
7138 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
7139 }
7140 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7141 return hFile;
7142}
7143
7144
7145/** Kernel32 - SetFilePointer */
7146static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7147{
7148 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7149 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7150 if (idxHandle < g_Sandbox.cHandles)
7151 {
7152 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7153 if (pHandle != NULL)
7154 {
7155 KU32 cbFile;
7156 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7157 switch (pHandle->enmType)
7158 {
7159 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7160 cbFile = pHandle->u.pCachedFile->cbCached;
7161 break;
7162#ifdef WITH_TEMP_MEMORY_FILES
7163 case KWHANDLETYPE_TEMP_FILE:
7164 cbFile = pHandle->u.pTempFile->cbFile;
7165 break;
7166 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7167#endif
7168 case KWHANDLETYPE_OUTPUT_BUF:
7169 default:
7170 kHlpAssertFailed();
7171 SetLastError(ERROR_INVALID_FUNCTION);
7172 return INVALID_SET_FILE_POINTER;
7173 }
7174
7175 switch (dwMoveMethod)
7176 {
7177 case FILE_BEGIN:
7178 break;
7179 case FILE_CURRENT:
7180 offMove += pHandle->offFile;
7181 break;
7182 case FILE_END:
7183 offMove += cbFile;
7184 break;
7185 default:
7186 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7187 SetLastError(ERROR_INVALID_PARAMETER);
7188 return INVALID_SET_FILE_POINTER;
7189 }
7190 if (offMove >= 0)
7191 {
7192 if (offMove >= (KSSIZE)cbFile)
7193 {
7194#ifdef WITH_TEMP_MEMORY_FILES
7195 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7196 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7197#endif
7198 offMove = (KSSIZE)cbFile;
7199#ifdef WITH_TEMP_MEMORY_FILES
7200 /* For writable files, seeking beyond the end is fine, but check that we've got
7201 the type range for the request. */
7202 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7203 {
7204 kHlpAssertMsgFailed(("%#llx\n", offMove));
7205 SetLastError(ERROR_SEEK);
7206 return INVALID_SET_FILE_POINTER;
7207 }
7208#endif
7209 }
7210 pHandle->offFile = (KU32)offMove;
7211 }
7212 else
7213 {
7214 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7215 SetLastError(ERROR_NEGATIVE_SEEK);
7216 return INVALID_SET_FILE_POINTER;
7217 }
7218 if (pcbMoveHi)
7219 *pcbMoveHi = (KU64)offMove >> 32;
7220 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7221 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7222 SetLastError(NO_ERROR);
7223 return (KU32)offMove;
7224 }
7225 }
7226 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7227 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7228}
7229
7230
7231/** Kernel32 - SetFilePointerEx */
7232static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7233 DWORD dwMoveMethod)
7234{
7235 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7236 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7237 if (idxHandle < g_Sandbox.cHandles)
7238 {
7239 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7240 if (pHandle != NULL)
7241 {
7242 KI64 offMyMove = offMove.QuadPart;
7243 KU32 cbFile;
7244 switch (pHandle->enmType)
7245 {
7246 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7247 cbFile = pHandle->u.pCachedFile->cbCached;
7248 break;
7249#ifdef WITH_TEMP_MEMORY_FILES
7250 case KWHANDLETYPE_TEMP_FILE:
7251 cbFile = pHandle->u.pTempFile->cbFile;
7252 break;
7253 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7254#endif
7255 case KWHANDLETYPE_OUTPUT_BUF:
7256 default:
7257 kHlpAssertFailed();
7258 SetLastError(ERROR_INVALID_FUNCTION);
7259 return INVALID_SET_FILE_POINTER;
7260 }
7261
7262 switch (dwMoveMethod)
7263 {
7264 case FILE_BEGIN:
7265 break;
7266 case FILE_CURRENT:
7267 offMyMove += pHandle->offFile;
7268 break;
7269 case FILE_END:
7270 offMyMove += cbFile;
7271 break;
7272 default:
7273 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7274 SetLastError(ERROR_INVALID_PARAMETER);
7275 return INVALID_SET_FILE_POINTER;
7276 }
7277 if (offMyMove >= 0)
7278 {
7279 if (offMyMove >= (KSSIZE)cbFile)
7280 {
7281#ifdef WITH_TEMP_MEMORY_FILES
7282 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7283 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7284#endif
7285 offMyMove = (KSSIZE)cbFile;
7286#ifdef WITH_TEMP_MEMORY_FILES
7287 /* For writable files, seeking beyond the end is fine, but check that we've got
7288 the type range for the request. */
7289 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7290 {
7291 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7292 SetLastError(ERROR_SEEK);
7293 return INVALID_SET_FILE_POINTER;
7294 }
7295#endif
7296 }
7297 pHandle->offFile = (KU32)offMyMove;
7298 }
7299 else
7300 {
7301 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7302 SetLastError(ERROR_NEGATIVE_SEEK);
7303 return INVALID_SET_FILE_POINTER;
7304 }
7305 if (poffNew)
7306 poffNew->QuadPart = offMyMove;
7307 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7308 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7309 return TRUE;
7310 }
7311 }
7312 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7313 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7314}
7315
7316
7317/** Kernel32 - ReadFile */
7318static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7319 LPOVERLAPPED pOverlapped)
7320{
7321 BOOL fRet;
7322 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7323 g_cReadFileCalls++;
7324 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7325 if (idxHandle < g_Sandbox.cHandles)
7326 {
7327 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7328 if (pHandle != NULL)
7329 {
7330 switch (pHandle->enmType)
7331 {
7332 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7333 {
7334 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7335 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7336 if (cbActually > cbToRead)
7337 cbActually = cbToRead;
7338
7339#ifdef WITH_HASH_MD5_CACHE
7340 if (g_Sandbox.pHashHead)
7341 {
7342 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7343 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7344 g_Sandbox.LastHashRead.cbRead = cbActually;
7345 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7346 }
7347#endif
7348
7349 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7350 pHandle->offFile += cbActually;
7351
7352 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7353 *pcbActuallyRead = cbActually;
7354
7355 g_cbReadFileFromReadCached += cbActually;
7356 g_cbReadFileTotal += cbActually;
7357 g_cReadFileFromReadCached++;
7358
7359 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7360 return TRUE;
7361 }
7362
7363#ifdef WITH_TEMP_MEMORY_FILES
7364 case KWHANDLETYPE_TEMP_FILE:
7365 {
7366 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7367 KU32 cbActually;
7368 if (pHandle->offFile < pTempFile->cbFile)
7369 {
7370 cbActually = pTempFile->cbFile - pHandle->offFile;
7371 if (cbActually > cbToRead)
7372 cbActually = cbToRead;
7373
7374 /* Copy the data. */
7375 if (cbActually > 0)
7376 {
7377 KU32 cbLeft;
7378 KU32 offSeg;
7379 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7380
7381 /* Locate the segment containing the byte at offFile. */
7382 KU32 iSeg = pTempFile->cSegs - 1;
7383 kHlpAssert(pTempFile->cSegs > 0);
7384 while (paSegs[iSeg].offData > pHandle->offFile)
7385 iSeg--;
7386
7387 /* Copy out the data. */
7388 cbLeft = cbActually;
7389 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7390 for (;;)
7391 {
7392 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7393 if (cbAvail >= cbLeft)
7394 {
7395 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7396 break;
7397 }
7398
7399 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7400 cbLeft -= cbAvail;
7401 offSeg = 0;
7402 iSeg++;
7403 kHlpAssert(iSeg < pTempFile->cSegs);
7404 }
7405
7406 /* Update the file offset. */
7407 pHandle->offFile += cbActually;
7408 }
7409 }
7410 /* Read does not commit file space, so return zero bytes. */
7411 else
7412 cbActually = 0;
7413
7414 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7415 *pcbActuallyRead = cbActually;
7416
7417 g_cbReadFileTotal += cbActually;
7418 g_cbReadFileFromInMemTemp += cbActually;
7419 g_cReadFileFromInMemTemp++;
7420
7421 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7422 return TRUE;
7423 }
7424
7425 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7426#endif /* WITH_TEMP_MEMORY_FILES */
7427 case KWHANDLETYPE_OUTPUT_BUF:
7428 default:
7429 kHlpAssertFailed();
7430 SetLastError(ERROR_INVALID_FUNCTION);
7431 *pcbActuallyRead = 0;
7432 return FALSE;
7433 }
7434 }
7435 }
7436
7437 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7438 if (fRet && pcbActuallyRead)
7439 g_cbReadFileTotal += *pcbActuallyRead;
7440 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7441 return fRet;
7442}
7443
7444
7445/** Kernel32 - ReadFileEx */
7446static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7447 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7448{
7449 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7450 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7451 if (idxHandle < g_Sandbox.cHandles)
7452 {
7453 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7454 if (pHandle != NULL)
7455 {
7456 kHlpAssertFailed();
7457 }
7458 }
7459
7460 KWFS_LOG(("ReadFile(%p)\n", hFile));
7461 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7462}
7463
7464#ifdef WITH_STD_OUT_ERR_BUFFERING
7465
7466/**
7467 * Write something to a handle, making sure everything is actually written.
7468 *
7469 * @param hHandle Where to write it to.
7470 * @param pchBuf What to write
7471 * @param cchToWrite How much to write.
7472 */
7473static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7474{
7475 if (cchToWrite > 0)
7476 {
7477 DWORD cchWritten = 0;
7478 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7479 {
7480 if (cchWritten == cchToWrite)
7481 { /* likely */ }
7482 else
7483 {
7484 do
7485 {
7486 pchBuf += cchWritten;
7487 cchToWrite -= cchWritten;
7488 cchWritten = 0;
7489 } while ( cchToWrite > 0
7490 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7491 }
7492 }
7493 else
7494 kHlpAssertFailed();
7495 }
7496}
7497
7498
7499/**
7500 * Worker for WriteFile when the output isn't going to the console.
7501 *
7502 * @param pSandbox The sandbox.
7503 * @param pOutBuf The output buffer.
7504 * @param pchBuffer What to write.
7505 * @param cchToWrite How much to write.
7506 */
7507static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7508{
7509 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7510 { /* likely */ }
7511 else
7512 {
7513 /* No realloc, max size is 64KB. */
7514 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7515 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7516 if (!pOutBuf->u.Fully.pchBuf)
7517 {
7518 while ( !pOutBuf->u.Fully.pchBuf
7519 && pOutBuf->u.Fully.cchBufAlloc > 64)
7520 {
7521 pOutBuf->u.Fully.cchBufAlloc /= 2;
7522 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7523 }
7524 if (!pOutBuf->u.Fully.pchBuf)
7525 {
7526 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7527 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7528 }
7529 }
7530 }
7531
7532 /*
7533 * Special case: Output ends with newline and fits in the buffer.
7534 */
7535 if ( cchToWrite > 1
7536 && pchBuffer[cchToWrite - 1] == '\n'
7537 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7538 {
7539 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7540 pOutBuf->u.Fully.cchBuf += cchToWrite;
7541 }
7542 else
7543 {
7544 /*
7545 * Work thru the text line by line, flushing the buffer when
7546 * appropriate. The buffer is not a line buffer here, it's a
7547 * full buffer.
7548 */
7549 while (cchToWrite > 0)
7550 {
7551 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7552 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7553 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7554 {
7555 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7556 pOutBuf->u.Fully.cchBuf += cchLine;
7557 }
7558 /*
7559 * Option one: Flush the buffer and the current line.
7560 *
7561 * We choose this one when the line won't ever fit, or when we have
7562 * an incomplete line in the buffer.
7563 */
7564 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7565 || pOutBuf->u.Fully.cchBuf == 0
7566 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7567 {
7568 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7569 if (pOutBuf->u.Fully.cchBuf > 0)
7570 {
7571 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7572 pOutBuf->u.Fully.cchBuf = 0;
7573 }
7574 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7575 }
7576 /*
7577 * Option two: Only flush the lines in the buffer.
7578 */
7579 else
7580 {
7581 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7582 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7583 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7584 pOutBuf->u.Fully.cchBuf = cchLine;
7585 }
7586
7587 /* advance */
7588 pchBuffer += cchLine;
7589 cchToWrite -= cchLine;
7590 }
7591 }
7592}
7593
7594#endif /* WITH_STD_OUT_ERR_BUFFERING */
7595
7596#ifdef WITH_TEMP_MEMORY_FILES
7597static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7598{
7599 KU32 cbMinFile = offFile + cbNeeded;
7600 if (cbMinFile >= offFile)
7601 {
7602 /* Calc how much space we've already allocated and */
7603 if (cbMinFile <= pTempFile->cbFileAllocated)
7604 return K_TRUE;
7605
7606 /* Grow the file. */
7607 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7608 {
7609 int rc;
7610 KU32 cSegs = pTempFile->cSegs;
7611 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7612 do
7613 {
7614 /* grow the segment array? */
7615 if ((cSegs % 16) == 0)
7616 {
7617 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7618 if (!pvNew)
7619 return K_FALSE;
7620 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7621 }
7622
7623 /* Use page alloc here to simplify mapping later. */
7624 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7625 if (rc == 0)
7626 { /* likely */ }
7627 else
7628 {
7629 cbNewSeg = 64*1024;
7630 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7631 if (rc != 0)
7632 {
7633 kHlpAssertFailed();
7634 return K_FALSE;
7635 }
7636 }
7637 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7638 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7639 pTempFile->cbFileAllocated += cbNewSeg;
7640 pTempFile->cSegs = ++cSegs;
7641
7642 } while (pTempFile->cbFileAllocated < cbMinFile);
7643
7644 return K_TRUE;
7645 }
7646 }
7647
7648 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7649 return K_FALSE;
7650}
7651#endif /* WITH_TEMP_MEMORY_FILES */
7652
7653
7654#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7655/** Kernel32 - WriteFile */
7656static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7657 LPOVERLAPPED pOverlapped)
7658{
7659 BOOL fRet;
7660 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7661 g_cWriteFileCalls++;
7662 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7663 if (idxHandle < g_Sandbox.cHandles)
7664 {
7665 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7666 if (pHandle != NULL)
7667 {
7668 switch (pHandle->enmType)
7669 {
7670# ifdef WITH_TEMP_MEMORY_FILES
7671 case KWHANDLETYPE_TEMP_FILE:
7672 {
7673 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7674
7675 kHlpAssert(!pOverlapped);
7676 kHlpAssert(pcbActuallyWritten);
7677
7678 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7679 {
7680 KU32 cbLeft;
7681 KU32 offSeg;
7682
7683 /* Locate the segment containing the byte at offFile. */
7684 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7685 KU32 iSeg = pTempFile->cSegs - 1;
7686 kHlpAssert(pTempFile->cSegs > 0);
7687 while (paSegs[iSeg].offData > pHandle->offFile)
7688 iSeg--;
7689
7690 /* Copy in the data. */
7691 cbLeft = cbToWrite;
7692 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7693 for (;;)
7694 {
7695 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7696 if (cbAvail >= cbLeft)
7697 {
7698 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7699 break;
7700 }
7701
7702 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7703 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7704 cbLeft -= cbAvail;
7705 offSeg = 0;
7706 iSeg++;
7707 kHlpAssert(iSeg < pTempFile->cSegs);
7708 }
7709
7710 /* Update the file offset. */
7711 pHandle->offFile += cbToWrite;
7712 if (pHandle->offFile > pTempFile->cbFile)
7713 pTempFile->cbFile = pHandle->offFile;
7714
7715 *pcbActuallyWritten = cbToWrite;
7716
7717 g_cbWriteFileTotal += cbToWrite;
7718 g_cbWriteFileToInMemTemp += cbToWrite;
7719 g_cWriteFileToInMemTemp++;
7720
7721 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7722 return TRUE;
7723 }
7724
7725 kHlpAssertFailed();
7726 *pcbActuallyWritten = 0;
7727 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7728 return FALSE;
7729 }
7730# endif
7731
7732 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7733 kHlpAssertFailed();
7734 SetLastError(ERROR_ACCESS_DENIED);
7735 *pcbActuallyWritten = 0;
7736 return FALSE;
7737
7738# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7739 /*
7740 * Standard output & error.
7741 */
7742 case KWHANDLETYPE_OUTPUT_BUF:
7743 {
7744 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7745 if (pOutBuf->fIsConsole)
7746 {
7747 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7748 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7749 }
7750 else
7751 {
7752# ifdef WITH_STD_OUT_ERR_BUFFERING
7753 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7754 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7755 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7756# else
7757 kHlpAssertFailed();
7758# endif
7759 }
7760 if (pcbActuallyWritten)
7761 *pcbActuallyWritten = cbToWrite;
7762 g_cbWriteFileTotal += cbToWrite;
7763 return TRUE;
7764 }
7765# endif
7766
7767 default:
7768#ifdef WITH_TEMP_MEMORY_FILES
7769 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7770#endif
7771 kHlpAssertFailed();
7772 SetLastError(ERROR_INVALID_FUNCTION);
7773 *pcbActuallyWritten = 0;
7774 return FALSE;
7775 }
7776 }
7777 }
7778
7779 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7780 if (fRet && pcbActuallyWritten)
7781 g_cbWriteFileTotal += *pcbActuallyWritten;
7782 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7783 return fRet;
7784}
7785
7786
7787/** Kernel32 - WriteFileEx */
7788static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7789 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7790{
7791 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7792 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7793 if (idxHandle < g_Sandbox.cHandles)
7794 {
7795 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7796 if (pHandle != NULL)
7797 {
7798 kHlpAssertFailed();
7799 }
7800 }
7801
7802 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
7803 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
7804}
7805
7806#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
7807
7808#ifdef WITH_TEMP_MEMORY_FILES
7809
7810/** Kernel32 - SetEndOfFile; */
7811static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
7812{
7813 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7814 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7815 if (idxHandle < g_Sandbox.cHandles)
7816 {
7817 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7818 if (pHandle != NULL)
7819 {
7820 switch (pHandle->enmType)
7821 {
7822 case KWHANDLETYPE_TEMP_FILE:
7823 {
7824 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7825 if ( pHandle->offFile > pTempFile->cbFile
7826 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
7827 {
7828 kHlpAssertFailed();
7829 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7830 return FALSE;
7831 }
7832
7833 pTempFile->cbFile = pHandle->offFile;
7834 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
7835 return TRUE;
7836 }
7837
7838 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7839 kHlpAssertFailed();
7840 SetLastError(ERROR_ACCESS_DENIED);
7841 return FALSE;
7842
7843# ifdef WITH_CONSOLE_OUTPUT_BUFFERING
7844 case KWHANDLETYPE_OUTPUT_BUF:
7845 kHlpAssertFailed();
7846 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
7847 return FALSE;
7848# endif
7849
7850 default:
7851 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7852 kHlpAssertFailed();
7853 SetLastError(ERROR_INVALID_FUNCTION);
7854 return FALSE;
7855 }
7856 }
7857 }
7858
7859 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
7860 return SetEndOfFile(hFile);
7861}
7862
7863
7864/** Kernel32 - GetFileType */
7865static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
7866{
7867 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7868 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7869 if (idxHandle < g_Sandbox.cHandles)
7870 {
7871 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7872 if (pHandle != NULL)
7873 {
7874 switch (pHandle->enmType)
7875 {
7876 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7877 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
7878 return FILE_TYPE_DISK;
7879
7880 case KWHANDLETYPE_TEMP_FILE:
7881 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
7882 return FILE_TYPE_DISK;
7883
7884#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
7885 case KWHANDLETYPE_OUTPUT_BUF:
7886 {
7887 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7888 DWORD fRet;
7889 if (pOutBuf->fFileType != KU8_MAX)
7890 {
7891 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
7892 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
7893 }
7894 else
7895 {
7896 fRet = GetFileType(hFile);
7897 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
7898 }
7899 return fRet;
7900 }
7901#endif
7902
7903 }
7904 }
7905 }
7906
7907 KWFS_LOG(("GetFileType(%p)\n", hFile));
7908 return GetFileType(hFile);
7909}
7910
7911
7912/** Kernel32 - GetFileSize */
7913static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
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 if (pcbHighDword)
7923 *pcbHighDword = 0;
7924 SetLastError(NO_ERROR);
7925 switch (pHandle->enmType)
7926 {
7927 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7928 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7929 return pHandle->u.pCachedFile->cbCached;
7930
7931 case KWHANDLETYPE_TEMP_FILE:
7932 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7933 return pHandle->u.pTempFile->cbFile;
7934
7935 case KWHANDLETYPE_OUTPUT_BUF:
7936 /* do default */
7937 break;
7938
7939 default:
7940 kHlpAssertFailed();
7941 SetLastError(ERROR_INVALID_FUNCTION);
7942 return INVALID_FILE_SIZE;
7943 }
7944 }
7945 }
7946
7947 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
7948 return GetFileSize(hFile, pcbHighDword);
7949}
7950
7951
7952/** Kernel32 - GetFileSizeEx */
7953static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
7954{
7955 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7956 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7957 if (idxHandle < g_Sandbox.cHandles)
7958 {
7959 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7960 if (pHandle != NULL)
7961 {
7962 switch (pHandle->enmType)
7963 {
7964 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7965 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7966 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
7967 return TRUE;
7968
7969 case KWHANDLETYPE_TEMP_FILE:
7970 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7971 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
7972 return TRUE;
7973
7974 case KWHANDLETYPE_OUTPUT_BUF:
7975 /* do default */
7976 break;
7977
7978 default:
7979 kHlpAssertFailed();
7980 SetLastError(ERROR_INVALID_FUNCTION);
7981 return INVALID_FILE_SIZE;
7982 }
7983 }
7984 }
7985
7986 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
7987 return GetFileSizeEx(hFile, pcbFile);
7988}
7989
7990
7991/** Kernel32 - CreateFileMappingW */
7992static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
7993 DWORD fProtect, DWORD dwMaximumSizeHigh,
7994 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
7995{
7996 HANDLE hMapping;
7997 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7998 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7999 if (idxHandle < g_Sandbox.cHandles)
8000 {
8001 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8002 if (pHandle != NULL)
8003 {
8004 switch (pHandle->enmType)
8005 {
8006 case KWHANDLETYPE_TEMP_FILE:
8007 {
8008 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8009 if ( ( fProtect == PAGE_READONLY
8010 || fProtect == PAGE_EXECUTE_READ)
8011 && dwMaximumSizeHigh == 0
8012 && ( dwMaximumSizeLow == 0
8013 || dwMaximumSizeLow == pTempFile->cbFile)
8014 && pwszName == NULL)
8015 {
8016 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
8017 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
8018 return hMapping;
8019 }
8020 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
8021 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8022 SetLastError(ERROR_ACCESS_DENIED);
8023 return INVALID_HANDLE_VALUE;
8024 }
8025
8026 /* moc.exe benefits from this. */
8027 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8028 {
8029 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8030 if ( ( fProtect == PAGE_READONLY
8031 || fProtect == PAGE_EXECUTE_READ)
8032 && dwMaximumSizeHigh == 0
8033 && ( dwMaximumSizeLow == 0
8034 || dwMaximumSizeLow == pCachedFile->cbCached)
8035 && pwszName == NULL)
8036 {
8037 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
8038 K_FALSE /*fIsFileHandle*/, &hMapping))
8039 { /* likely */ }
8040 else
8041 hMapping = NULL;
8042 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8043 return hMapping;
8044 }
8045
8046 /* Do fallback (for .pch). */
8047 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8048 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8049 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8050
8051 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8052 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8053 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8054 return hMapping;
8055 }
8056
8057 /** @todo read cached memory mapped files too for moc. */
8058 }
8059 }
8060 }
8061
8062 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8063 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8064 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8065 return hMapping;
8066}
8067
8068
8069/** Kernel32 - MapViewOfFile */
8070static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8071 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8072{
8073 PVOID pvRet;
8074 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
8075 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8076 if (idxHandle < g_Sandbox.cHandles)
8077 {
8078 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8079 if (pHandle != NULL)
8080 {
8081 KU32 idxMapping;
8082
8083 /*
8084 * Ensure one free entry in the mapping tracking table first,
8085 * since this is common to both temporary and cached files.
8086 */
8087 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8088 { /* likely */ }
8089 else
8090 {
8091 void *pvNew;
8092 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8093 if (cNew)
8094 cNew *= 2;
8095 else
8096 cNew = 32;
8097 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8098 if (pvNew)
8099 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8100 else
8101 {
8102 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8103 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8104 return NULL;
8105 }
8106 g_Sandbox.cMemMappingsAlloc = cNew;
8107 }
8108
8109 /*
8110 * Type specific work.
8111 */
8112 switch (pHandle->enmType)
8113 {
8114 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8115 case KWHANDLETYPE_TEMP_FILE:
8116 case KWHANDLETYPE_OUTPUT_BUF:
8117 default:
8118 kHlpAssertFailed();
8119 SetLastError(ERROR_INVALID_OPERATION);
8120 return NULL;
8121
8122 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8123 {
8124 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8125 if ( dwDesiredAccess == FILE_MAP_READ
8126 && offFileHigh == 0
8127 && offFileLow == 0
8128 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8129 {
8130 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8131 if (pTempFile->cSegs != 1)
8132 {
8133 KU32 iSeg;
8134 KU32 cbLeft;
8135 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8136 KU8 *pbAll = NULL;
8137 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8138 if (rc != 0)
8139 {
8140 kHlpAssertFailed();
8141 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8142 return NULL;
8143 }
8144
8145 cbLeft = pTempFile->cbFile;
8146 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8147 {
8148 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8149 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8150 cbLeft -= cbToCopy;
8151 }
8152
8153 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8154 {
8155 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8156 pTempFile->paSegs[iSeg].pbData = NULL;
8157 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8158 }
8159
8160 pTempFile->cSegs = 1;
8161 pTempFile->cbFileAllocated = cbAll;
8162 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8163 pTempFile->paSegs[0].pbData = pbAll;
8164 pTempFile->paSegs[0].offData = 0;
8165 }
8166
8167 pTempFile->cMappings++;
8168 kHlpAssert(pTempFile->cMappings == 1);
8169
8170 pvRet = pTempFile->paSegs[0].pbData;
8171 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8172 break;
8173 }
8174
8175 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8176 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8177 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8178 return NULL;
8179 }
8180
8181 /*
8182 * This is simple in comparison to the above temporary file code.
8183 */
8184 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8185 {
8186 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8187 if ( dwDesiredAccess == FILE_MAP_READ
8188 && offFileHigh == 0
8189 && offFileLow == 0
8190 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8191 {
8192 pvRet = pCachedFile->pbCached;
8193 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8194 break;
8195 }
8196 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8197 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8198 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8199 return NULL;
8200 }
8201 }
8202
8203 /*
8204 * Insert into the mapping tracking table. This is common
8205 * and we should only get here with a non-NULL pvRet.
8206 *
8207 * Note! We could look for duplicates and do ref counting, but it's
8208 * easier to just append for now.
8209 */
8210 kHlpAssert(pvRet != NULL);
8211 idxMapping = g_Sandbox.cMemMappings;
8212 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8213
8214 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8215 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8216 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8217 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8218 g_Sandbox.cMemMappings++;
8219
8220 return pvRet;
8221 }
8222 }
8223
8224 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8225 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8226 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8227 return pvRet;
8228}
8229
8230
8231/** Kernel32 - MapViewOfFileEx */
8232static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8233 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8234{
8235 PVOID pvRet;
8236 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
8237 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8238 if (idxHandle < g_Sandbox.cHandles)
8239 {
8240 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8241 if (pHandle != NULL)
8242 {
8243 switch (pHandle->enmType)
8244 {
8245 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8246 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8247 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8248 if (!pvMapAddr)
8249 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8250 kHlpAssertFailed();
8251 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8252 return NULL;
8253
8254 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8255 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8256 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8257 if (!pvMapAddr)
8258 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8259 /* We can use fallback here as the handle is an actual section handle. */
8260 break;
8261
8262 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8263 case KWHANDLETYPE_TEMP_FILE:
8264 case KWHANDLETYPE_OUTPUT_BUF:
8265 default:
8266 kHlpAssertFailed();
8267 SetLastError(ERROR_INVALID_OPERATION);
8268 return NULL;
8269 }
8270 }
8271 }
8272
8273 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8274 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8275 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8276 return pvRet;
8277
8278}
8279
8280/** Kernel32 - UnmapViewOfFile */
8281static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8282{
8283 /*
8284 * Consult the memory mapping tracker.
8285 */
8286 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8287 KU32 idxMapping = g_Sandbox.cMemMappings;
8288 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8289 while (idxMapping-- > 0)
8290 if (paMemMappings[idxMapping].pvMapping == pvBase)
8291 {
8292 /* Type specific stuff. */
8293 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8294 {
8295 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8296 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8297 }
8298 else
8299 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8300
8301 /* Deref and probably free it. */
8302 if (--paMemMappings[idxMapping].cRefs == 0)
8303 {
8304 g_Sandbox.cMemMappings--;
8305 if (idxMapping != g_Sandbox.cMemMappings)
8306 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8307 }
8308 return TRUE;
8309 }
8310
8311 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8312 return UnmapViewOfFile(pvBase);
8313}
8314
8315/** @todo UnmapViewOfFileEx */
8316
8317#endif /* WITH_TEMP_MEMORY_FILES */
8318
8319
8320/** Kernel32 - DuplicateHandle */
8321static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8322 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8323{
8324 BOOL fRet;
8325
8326 /*
8327 * We must catch our handles being duplicated.
8328 */
8329 if (hSrcProc == GetCurrentProcess())
8330 {
8331 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
8332 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8333 if (idxHandle < g_Sandbox.cHandles)
8334 {
8335 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8336 if (pHandle)
8337 {
8338 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8339 if (fRet)
8340 {
8341 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8342 {
8343 pHandle->cRefs++;
8344 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8345 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8346 pHandle->enmType, pHandle->cRefs));
8347 }
8348 else
8349 {
8350 fRet = FALSE;
8351 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8352 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8353 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8354 }
8355 }
8356 else
8357 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8358 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8359 return fRet;
8360 }
8361 }
8362 }
8363
8364 /*
8365 * Not one of ours, just do what the caller asks and log it.
8366 */
8367 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8368 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8369 fInheritHandle, dwOptions, fRet, *phNew));
8370 return fRet;
8371}
8372
8373
8374/** Kernel32 - CloseHandle */
8375static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8376{
8377 BOOL fRet;
8378 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8379 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
8380 if (idxHandle < g_Sandbox.cHandles)
8381 {
8382 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8383 if (pHandle)
8384 {
8385 /* Prevent the closing of the standard output and error handles. */
8386 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8387 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
8388 {
8389 fRet = CloseHandle(hObject);
8390 if (fRet)
8391 {
8392 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
8393 g_Sandbox.papHandles[idxHandle] = NULL;
8394 g_Sandbox.cActiveHandles--;
8395 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8396 if (--pHandle->cRefs == 0)
8397 {
8398#ifdef WITH_TEMP_MEMORY_FILES
8399 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8400 {
8401 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8402 pHandle->u.pTempFile->cActiveHandles--;
8403 }
8404#endif
8405 kHlpFree(pHandle);
8406 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8407 }
8408 else
8409 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8410 }
8411 else
8412 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8413 }
8414 else
8415 {
8416#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8417 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8418 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8419#else
8420 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of stdXXX!\n", hObject));
8421#endif
8422 fRet = TRUE;
8423 }
8424 return fRet;
8425 }
8426 }
8427
8428 fRet = CloseHandle(hObject);
8429 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8430 return fRet;
8431}
8432
8433
8434/** Kernel32 - GetFileAttributesA. */
8435static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8436{
8437 DWORD fRet;
8438 const char *pszExt = kHlpGetExt(pszFilename);
8439 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8440 {
8441 KFSLOOKUPERROR enmError;
8442 PKFSOBJ pFsObj;
8443 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8444
8445 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8446 if (pFsObj)
8447 {
8448 kHlpAssert(pFsObj->fHaveStats);
8449 fRet = pFsObj->Stats.st_attribs;
8450 kFsCacheObjRelease(g_pFsCache, pFsObj);
8451 }
8452 else
8453 {
8454 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8455 fRet = INVALID_FILE_ATTRIBUTES;
8456 }
8457
8458 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8459 return fRet;
8460 }
8461
8462 fRet = GetFileAttributesA(pszFilename);
8463 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8464 return fRet;
8465}
8466
8467
8468/** Kernel32 - GetFileAttributesW. */
8469static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8470{
8471 DWORD fRet;
8472 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8473 {
8474 KFSLOOKUPERROR enmError;
8475 PKFSOBJ pFsObj;
8476 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8477
8478 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8479 if (pFsObj)
8480 {
8481 kHlpAssert(pFsObj->fHaveStats);
8482 fRet = pFsObj->Stats.st_attribs;
8483 kFsCacheObjRelease(g_pFsCache, pFsObj);
8484 }
8485 else
8486 {
8487 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8488 fRet = INVALID_FILE_ATTRIBUTES;
8489 }
8490#ifndef NDEBUG
8491 {
8492 DWORD fCheck = GetFileAttributesW(pwszFilename);
8493 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8494 }
8495#endif
8496 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8497 return fRet;
8498 }
8499
8500 fRet = GetFileAttributesW(pwszFilename);
8501 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8502 return fRet;
8503}
8504
8505
8506/** Kernel32 - GetFileAttributesExA. */
8507static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8508 WIN32_FILE_ATTRIBUTE_DATA *pData)
8509{
8510 BOOL fRet;
8511 const char *pszExt = kHlpGetExt(pszFilename);
8512 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8513 {
8514 KFSLOOKUPERROR enmError;
8515 PKFSOBJ pFsObj;
8516 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8517
8518 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8519 if (pFsObj)
8520 {
8521 kHlpAssert(pFsObj->fHaveStats);
8522 if (enmLevel == GetFileExInfoStandard)
8523 {
8524 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8525 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8526 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8527 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8528 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8529 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8530 kFsCacheObjRelease(g_pFsCache, pFsObj);
8531 fRet = TRUE;
8532 }
8533 else
8534 {
8535 kFsCacheObjRelease(g_pFsCache, pFsObj);
8536 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8537 }
8538 }
8539 else
8540 {
8541 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8542 fRet = FALSE;
8543 }
8544
8545#ifdef K_STRICT
8546 {
8547 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8548 DWORD const dwErrSaved = GetLastError();
8549 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8550 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8551 if (fRetCheck && fRet)
8552 {
8553# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8554 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8555 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8556 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8557 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8558 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8559 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8560 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8561 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8562 }
8563 else
8564 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8565 SetLastError(dwErrSaved);
8566 }
8567#endif
8568 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8569 return fRet;
8570 }
8571
8572 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8573 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8574 return fRet;
8575}
8576
8577
8578/** Kernel32 - GetFileAttributesExW. */
8579static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8580 WIN32_FILE_ATTRIBUTE_DATA *pData)
8581{
8582 BOOL fRet;
8583 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8584 {
8585 KFSLOOKUPERROR enmError;
8586 PKFSOBJ pFsObj;
8587 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8588
8589 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8590 if (pFsObj)
8591 {
8592 kHlpAssert(pFsObj->fHaveStats);
8593 if (enmLevel == GetFileExInfoStandard)
8594 {
8595 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8596 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8597 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8598 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8599 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8600 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8601 kFsCacheObjRelease(g_pFsCache, pFsObj);
8602 fRet = TRUE;
8603 }
8604 else
8605 {
8606 kFsCacheObjRelease(g_pFsCache, pFsObj);
8607 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8608 }
8609 }
8610 else
8611 {
8612 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8613 fRet = FALSE;
8614 }
8615
8616#ifdef K_STRICT
8617 {
8618 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8619 DWORD const dwErrSaved = GetLastError();
8620 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8621 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8622 if (fRetCheck && fRet)
8623 {
8624# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8625 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8626 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8627 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8628 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8629 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8630 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8631 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8632 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8633 }
8634 else
8635 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8636 SetLastError(dwErrSaved);
8637 }
8638#endif
8639 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8640 return fRet;
8641 }
8642
8643 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8644 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8645 return fRet;
8646}
8647
8648
8649/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8650 * directory containing each include file. We cache the result to speed
8651 * things up a little. */
8652static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8653{
8654 DWORD cwcRet;
8655 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8656 {
8657 KFSLOOKUPERROR enmError;
8658 PKFSOBJ pObj;
8659 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8660
8661 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8662 if (pObj)
8663 {
8664 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8665 {
8666 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8667 {
8668 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8669
8670 /* Should preserve trailing slash on directory paths. */
8671 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8672 {
8673 if ( cwcRet + 1 < cwcShortPath
8674 && pwszShortPath[cwcRet - 1] != '\\')
8675 {
8676 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8677 if ( cwcIn > 0
8678 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8679 {
8680 pwszShortPath[cwcRet++] = '\\';
8681 pwszShortPath[cwcRet] = '\0';
8682 }
8683 }
8684 }
8685
8686 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8687 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8688 kFsCacheObjRelease(g_pFsCache, pObj);
8689 return cwcRet;
8690 }
8691
8692 /* fall back for complicated cases. */
8693 }
8694 kFsCacheObjRelease(g_pFsCache, pObj);
8695 }
8696 }
8697 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8698 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8699 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8700 return cwcRet;
8701}
8702
8703
8704#ifdef WITH_TEMP_MEMORY_FILES
8705/** Kernel32 - DeleteFileW
8706 * Skip deleting the in-memory files. */
8707static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8708{
8709 BOOL fRc;
8710 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8711 && kwFsIsClTempFileW(pwszFilename))
8712 {
8713 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8714 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8715 fRc = TRUE;
8716 }
8717 else
8718 {
8719 fRc = DeleteFileW(pwszFilename);
8720 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8721 }
8722 return fRc;
8723}
8724#endif /* WITH_TEMP_MEMORY_FILES */
8725
8726
8727
8728#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8729
8730/*
8731 *
8732 * Console output buffering.
8733 * Console output buffering.
8734 * Console output buffering.
8735 *
8736 */
8737
8738
8739/**
8740 * Write a wide char string to the console.
8741 *
8742 * @param pSandbox The sandbox which output buffer to flush.
8743 */
8744static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8745{
8746 if (cwcToWrite > 0)
8747 {
8748 DWORD cwcWritten = 0;
8749 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8750 {
8751 if (cwcWritten == cwcToWrite)
8752 { /* likely */ }
8753 else
8754 {
8755 DWORD off = 0;
8756 do
8757 {
8758 off += cwcWritten;
8759 cwcWritten = 0;
8760 } while ( off < cwcToWrite
8761 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8762 kHlpAssert(off == cwcWritten);
8763 }
8764 }
8765 else
8766 kHlpAssertFailed();
8767 pSandbox->Combined.cFlushes++;
8768 }
8769}
8770
8771
8772/**
8773 * Flushes the combined console output buffer.
8774 *
8775 * @param pSandbox The sandbox which output buffer to flush.
8776 */
8777static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8778{
8779 if (pSandbox->Combined.cwcBuf > 0)
8780 {
8781 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8782 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8783 pSandbox->Combined.cwcBuf = 0;
8784 }
8785}
8786
8787
8788/**
8789 * For handling combined buffer overflow cases line by line.
8790 *
8791 * @param pSandbox The sandbox.
8792 * @param pwcBuf What to add to the combined buffer. Usually a
8793 * line, unless we're really low on buffer space.
8794 * @param cwcBuf The length of what to add.
8795 * @param fBrokenLine Whether this is a broken line.
8796 */
8797static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8798{
8799 if (fBrokenLine)
8800 kwSandboxConsoleFlushCombined(pSandbox);
8801 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8802 {
8803 kwSandboxConsoleFlushCombined(pSandbox);
8804 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
8805 }
8806 else
8807 {
8808 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
8809 pSandbox->Combined.cwcBuf += cwcBuf;
8810 }
8811}
8812
8813
8814/**
8815 * Called to final flush a line buffer via the combined buffer (if applicable).
8816 *
8817 * @param pSandbox The sandbox.
8818 * @param pLineBuf The line buffer.
8819 * @param pszName The line buffer name (for logging)
8820 */
8821static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
8822{
8823 if (pLineBuf->fIsConsole)
8824 {
8825 if (pLineBuf->u.Con.cwcBuf > 0)
8826 {
8827 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
8828
8829 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
8830 {
8831 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
8832 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
8833 }
8834 else
8835 {
8836 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
8837 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
8838 }
8839 pLineBuf->u.Con.cwcBuf = 0;
8840 }
8841 }
8842#ifdef WITH_STD_OUT_ERR_BUFFERING
8843 else if (pLineBuf->u.Fully.cchBuf > 0)
8844 {
8845 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
8846
8847 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
8848 pLineBuf->u.Fully.cchBuf = 0;
8849 }
8850#endif
8851}
8852
8853
8854/**
8855 * Called at the end of sandboxed execution to flush both stream buffers.
8856 *
8857 * @param pSandbox The sandbox.
8858 */
8859static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
8860{
8861 /*
8862 * First do the cl.exe source file supression trick, if applicable.
8863 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
8864 * handle.
8865 */
8866 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8867 && pSandbox->Combined.cFlushes == 0)
8868 {
8869 if ( pSandbox->StdOut.fIsConsole
8870 || pSandbox->StdErr.fIsConsole)
8871 {
8872 if ( pSandbox->Combined.cwcBuf >= 3
8873 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
8874 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
8875 {
8876 KI32 off = pSandbox->Combined.cwcBuf - 1;
8877 if (pSandbox->Combined.wszBuf[off] == '\n')
8878 {
8879 KBOOL fOk = K_TRUE;
8880 while (off-- > 0)
8881 {
8882 wchar_t const wc = pSandbox->Combined.wszBuf[off];
8883 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
8884 { /* likely */ }
8885 else
8886 {
8887 fOk = K_FALSE;
8888 break;
8889 }
8890 }
8891 if (fOk)
8892 {
8893 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
8894 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8895 pSandbox->Combined.cwcBuf = 0;
8896 return;
8897 }
8898 }
8899 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
8900 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8901 }
8902 }
8903#ifdef WITH_STD_OUT_ERR_BUFFERING
8904 /*
8905 * Otherwise, it goes to standard output (redirected).
8906 */
8907 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
8908 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
8909 {
8910 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
8911 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
8912 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
8913
8914 if (pchBuf[off] == '\n')
8915 {
8916 KBOOL fOk = K_TRUE;
8917 if (pchBuf[off - 1] == '\r')
8918 off--;
8919 while (off-- > 0)
8920 {
8921 char const ch = pchBuf[off];
8922 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
8923 { /* likely */ }
8924 else
8925 {
8926 fOk = K_FALSE;
8927 break;
8928 }
8929 }
8930 if (fOk)
8931 {
8932 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
8933 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8934 pSandbox->StdOut.u.Fully.cchBuf = 0;
8935 return;
8936 }
8937 }
8938 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
8939 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8940 }
8941#endif
8942 }
8943
8944 /*
8945 * Flush the two line buffer, then the combined buffer.
8946 */
8947 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
8948 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
8949 kwSandboxConsoleFlushCombined(pSandbox);
8950}
8951
8952
8953/**
8954 * Writes a string to the given output stream.
8955 *
8956 * @param pSandbox The sandbox.
8957 * @param pLineBuf The line buffer for the output stream.
8958 * @param pwcBuffer The buffer to write.
8959 * @param cwcToWrite The number of wchar_t's in the buffer.
8960 */
8961static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
8962{
8963 kHlpAssert(pLineBuf->fIsConsole);
8964 if (cwcToWrite > 0)
8965 {
8966 /*
8967 * First, find the start of the last incomplete line so we can figure
8968 * out how much line buffering we need to do.
8969 */
8970 KU32 cchLastIncompleteLine;
8971 KU32 offLastIncompleteLine = cwcToWrite;
8972 while ( offLastIncompleteLine > 0
8973 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
8974 offLastIncompleteLine--;
8975 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
8976
8977 /* Was there anything to line buffer? */
8978 if (offLastIncompleteLine < cwcToWrite)
8979 {
8980 /* Need to grow the line buffer? */
8981 KU32 cwcNeeded = offLastIncompleteLine == 0
8982 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
8983 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
8984 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
8985 {
8986 void *pvNew;
8987 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
8988 while (cwcNew < cwcNeeded)
8989 cwcNew *= 2;
8990 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
8991 if (pvNew)
8992 {
8993 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8994 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
8995 }
8996 else
8997 {
8998 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
8999 if (pvNew)
9000 {
9001 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9002 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
9003 }
9004 else
9005 {
9006 /* This isn't perfect, but it will have to do for now. */
9007 if (pLineBuf->u.Con.cwcBuf > 0)
9008 {
9009 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9010 K_TRUE /*fBrokenLine*/);
9011 pLineBuf->u.Con.cwcBuf = 0;
9012 }
9013 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
9014 return;
9015 }
9016 }
9017 }
9018
9019 /*
9020 * Handle the case where we only add to the line buffer.
9021 */
9022 if (offLastIncompleteLine == 0)
9023 {
9024 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
9025 pLineBuf->u.Con.cwcBuf += cwcToWrite;
9026 return;
9027 }
9028 }
9029
9030 /*
9031 * If there is sufficient combined buffer to handle this request, this is rather simple.
9032 */
9033 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
9034 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9035 {
9036 if (pLineBuf->u.Con.cwcBuf > 0)
9037 {
9038 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9039 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9040 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9041 pLineBuf->u.Con.cwcBuf = 0;
9042 }
9043
9044 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9045 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9046 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9047 }
9048 else
9049 {
9050 /*
9051 * Do line-by-line processing of the input, flusing the combined buffer
9052 * when it becomes necessary. We may have to write lines
9053 */
9054 KU32 off = 0;
9055 KU32 offNextLine = 0;
9056
9057 /* If there are buffered chars, we handle the first line outside the
9058 main loop. We must try our best outputting it as a complete line. */
9059 if (pLineBuf->u.Con.cwcBuf > 0)
9060 {
9061 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9062 offNextLine++;
9063 offNextLine++;
9064 kHlpAssert(offNextLine <= offLastIncompleteLine);
9065
9066 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9067 {
9068 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9069 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9070 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9071 pLineBuf->u.Con.cwcBuf = 0;
9072
9073 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9074 pSandbox->Combined.cwcBuf += offNextLine;
9075 }
9076 else
9077 {
9078 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9079 if (cwcLeft > 0)
9080 {
9081 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9082 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9083 pLineBuf->u.Con.cwcBuf += cwcCopy;
9084 off += cwcCopy;
9085 }
9086 if (pLineBuf->u.Con.cwcBuf > 0)
9087 {
9088 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9089 K_TRUE /*fBrokenLine*/);
9090 pLineBuf->u.Con.cwcBuf = 0;
9091 }
9092 if (off < offNextLine)
9093 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9094 }
9095 off = offNextLine;
9096 }
9097
9098 /* Deal with the remaining lines */
9099 while (off < offLastIncompleteLine)
9100 {
9101 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9102 offNextLine++;
9103 offNextLine++;
9104 kHlpAssert(offNextLine <= offLastIncompleteLine);
9105 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9106 off = offNextLine;
9107 }
9108 }
9109
9110 /*
9111 * Buffer any remaining incomplete line chars.
9112 */
9113 if (cchLastIncompleteLine)
9114 {
9115 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9116 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9117 }
9118 }
9119}
9120
9121
9122/**
9123 * Worker for WriteConsoleA and WriteFile.
9124 *
9125 * @param pSandbox The sandbox.
9126 * @param pLineBuf The line buffer.
9127 * @param pchBuffer What to write.
9128 * @param cchToWrite How much to write.
9129 */
9130static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9131{
9132 /*
9133 * Convert it to wide char and use the 'W' to do the work.
9134 */
9135 int cwcRet;
9136 KU32 cwcBuf = cchToWrite * 2 + 1;
9137 wchar_t *pwcBufFree = NULL;
9138 wchar_t *pwcBuf;
9139 kHlpAssert(pLineBuf->fIsConsole);
9140
9141 if (cwcBuf <= 4096)
9142 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9143 else
9144 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9145
9146 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9147 if (cwcRet > 0)
9148 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9149 else
9150 {
9151 DWORD cchWritten;
9152 kHlpAssertFailed();
9153
9154 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9155 if (pLineBuf->u.Con.cwcBuf > 0)
9156 {
9157 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9158 pLineBuf->u.Con.cwcBuf = 0;
9159 }
9160 kwSandboxConsoleFlushCombined(pSandbox);
9161
9162 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9163 {
9164 if (cchWritten >= cchToWrite)
9165 { /* likely */ }
9166 else
9167 {
9168 KU32 off = 0;
9169 do
9170 {
9171 off += cchWritten;
9172 cchWritten = 0;
9173 } while ( off < cchToWrite
9174 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9175 }
9176 }
9177 }
9178
9179 if (pwcBufFree)
9180 kHlpFree(pwcBufFree);
9181}
9182
9183
9184/** Kernel32 - WriteConsoleA */
9185BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9186 PVOID pvReserved)
9187{
9188 BOOL fRc;
9189 PKWOUTPUTSTREAMBUF pLineBuf;
9190 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9191
9192 if (hConOutput == g_Sandbox.StdErr.hOutput)
9193 pLineBuf = &g_Sandbox.StdErr;
9194 else
9195 pLineBuf = &g_Sandbox.StdOut;
9196 if (pLineBuf->fIsConsole)
9197 {
9198 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9199
9200 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9201 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9202 if (pcbWritten)
9203 *pcbWritten = cbToWrite;
9204 fRc = TRUE;
9205 }
9206 else
9207 {
9208 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9209 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9210 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9211 }
9212 return fRc;
9213}
9214
9215
9216/** Kernel32 - WriteConsoleW */
9217BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9218 PVOID pvReserved)
9219{
9220 BOOL fRc;
9221 PKWOUTPUTSTREAMBUF pLineBuf;
9222 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9223
9224 if (hConOutput == g_Sandbox.StdErr.hOutput)
9225 pLineBuf = &g_Sandbox.StdErr;
9226 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9227 pLineBuf = &g_Sandbox.StdOut;
9228 else
9229 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9230 if (pLineBuf->fIsConsole)
9231 {
9232 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9233
9234 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9235 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9236 if (pcwcWritten)
9237 *pcwcWritten = cwcToWrite;
9238 fRc = TRUE;
9239 }
9240 else
9241 {
9242 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9243 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9244 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9245 }
9246 return fRc;
9247}
9248
9249#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9250
9251
9252
9253/*
9254 *
9255 * Virtual memory leak prevension.
9256 * Virtual memory leak prevension.
9257 * Virtual memory leak prevension.
9258 *
9259 */
9260
9261#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9262
9263/** For debug logging. */
9264# ifndef NDEBUG
9265static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9266{
9267 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9268 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9269 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9270 if (cbMemInfo != 0)
9271 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9272 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9273 MemInfo.BaseAddress,
9274 MemInfo.AllocationBase,
9275 MemInfo.RegionSize,
9276 MemInfo.State,
9277 MemInfo.Protect,
9278 MemInfo.Type));
9279}
9280# else
9281# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9282# endif
9283
9284/**
9285 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9286 *
9287 * @param idxFixed The fixed allocation index to "free".
9288 */
9289static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9290{
9291 BOOL fRc;
9292 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9293 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9294 kHlpAssert(fRc); K_NOREF(fRc);
9295 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9296 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9297}
9298
9299#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9300
9301
9302/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9303 * location (~78MB in 32-bit 2010 compiler). */
9304static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9305{
9306 PVOID pvMem;
9307 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9308 {
9309 KU32 idxPreAllocated = KU32_MAX;
9310
9311#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9312 /*
9313 * Look for a pre-reserved CL.exe heap allocation.
9314 */
9315 pvMem = NULL;
9316 if ( pvAddr != 0
9317 && (fAllocType & MEM_RESERVE))
9318 {
9319 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9320 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9321 while (idxFixed-- > 0)
9322 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9323 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9324 {
9325 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9326 {
9327 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9328 {
9329 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9330 pvMem = pvAddr;
9331 idxPreAllocated = idxFixed;
9332 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9333 pvAddr, cb, fAllocType, fProt, pvMem));
9334 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9335 SetLastError(NO_ERROR);
9336 break;
9337 }
9338 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9339 }
9340 else
9341 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9342 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9343 }
9344 }
9345 if (!pvMem)
9346#endif
9347 {
9348 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9349 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9350 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9351 if (pvAddr && pvAddr != pvMem)
9352 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9353 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9354 }
9355
9356 if (pvMem)
9357 {
9358 /*
9359 * Track it.
9360 */
9361 PKWVIRTALLOC pTracker;
9362
9363 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9364 pTracker = g_Sandbox.pVirtualAllocHead;
9365 while ( pTracker
9366 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9367 pTracker = pTracker->pNext;
9368 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9369 if (!pTracker)
9370 {
9371 DWORD dwErr = GetLastError();
9372 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9373 if (pTracker)
9374 {
9375 pTracker->pvAlloc = pvMem;
9376 pTracker->cbAlloc = cb;
9377 pTracker->idxPreAllocated = idxPreAllocated;
9378 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9379 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9380 g_Sandbox.pVirtualAllocHead = pTracker;
9381 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9382 }
9383 SetLastError(dwErr);
9384 }
9385 }
9386 }
9387 else
9388 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9389 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9390 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9391 return pvMem;
9392}
9393
9394
9395/** Kernel32 - VirtualFree. */
9396static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9397{
9398 BOOL fRc;
9399 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9400 {
9401 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9402 if (dwFreeType & MEM_RELEASE)
9403 {
9404 PKWVIRTALLOC pTracker;
9405 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9406 pTracker = g_Sandbox.pVirtualAllocHead;
9407 if (pTracker)
9408 {
9409 if (pTracker->pvAlloc == pvAddr)
9410 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9411 else
9412 {
9413 PKWVIRTALLOC pPrev;
9414 do
9415 {
9416 pPrev = pTracker;
9417 pTracker = pTracker->pNext;
9418 } while (pTracker && pTracker->pvAlloc != pvAddr);
9419 if (pTracker)
9420 pPrev->pNext = pTracker->pNext;
9421 }
9422 if (pTracker)
9423 {
9424#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9425 if (pTracker->idxPreAllocated != KU32_MAX)
9426 {
9427 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9428 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9429 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9430 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9431 kHlpFree(pTracker);
9432 return TRUE;
9433 }
9434#endif
9435
9436 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9437 if (fRc)
9438 kHlpFree(pTracker);
9439 else
9440 {
9441 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9442 g_Sandbox.pVirtualAllocHead = pTracker;
9443 }
9444 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9445 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9446 return fRc;
9447 }
9448
9449 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9450 }
9451 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9452 }
9453 }
9454
9455#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9456 /*
9457 * Protect our fixed allocations (this isn't just paranoia, btw.).
9458 */
9459 if (dwFreeType & MEM_RELEASE)
9460 {
9461 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9462 while (idxFixed-- > 0)
9463 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9464 {
9465 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9466 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9467 return TRUE;
9468 }
9469 }
9470#endif
9471
9472 /*
9473 * Not tracker or not actually free the virtual range.
9474 */
9475 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9476 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9477 return fRc;
9478}
9479
9480
9481/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9482HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9483{
9484 HANDLE hHeap;
9485 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9486
9487 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9488 if (hHeap != NULL)
9489 {
9490 DWORD dwErr = GetLastError();
9491 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9492 if (pTracker)
9493 {
9494 pTracker->hHeap = hHeap;
9495 pTracker->pNext = g_Sandbox.pHeapHead;
9496 g_Sandbox.pHeapHead = pTracker;
9497 }
9498
9499 SetLastError(dwErr);
9500 }
9501 return hHeap;
9502
9503}
9504
9505
9506/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9507BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9508{
9509 BOOL fRc = HeapDestroy(hHeap);
9510 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9511 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9512 if (fRc)
9513 {
9514 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9515 if (pTracker)
9516 {
9517 if (pTracker->hHeap == hHeap)
9518 g_Sandbox.pHeapHead = pTracker->pNext;
9519 else
9520 {
9521 PKWHEAP pPrev;
9522 do
9523 {
9524 pPrev = pTracker;
9525 pTracker = pTracker->pNext;
9526 } while (pTracker && pTracker->hHeap == hHeap);
9527 if (pTracker)
9528 pPrev->pNext = pTracker->pNext;
9529 }
9530 if (pTracker)
9531 kHlpFree(pTracker);
9532 else
9533 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9534 }
9535 }
9536
9537 return fRc;
9538}
9539
9540
9541
9542/*
9543 *
9544 * Thread/Fiber local storage leak prevention.
9545 * Thread/Fiber local storage leak prevention.
9546 * Thread/Fiber local storage leak prevention.
9547 *
9548 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9549 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9550 * we're leaking these indexes, but more importantely we crash during
9551 * worker exit since the callback is triggered multiple times.
9552 */
9553
9554
9555/** Kernel32 - FlsAlloc */
9556DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9557{
9558 DWORD idxFls = FlsAlloc(pfnCallback);
9559 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9560 if (idxFls != FLS_OUT_OF_INDEXES)
9561 {
9562 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9563 if (pTracker)
9564 {
9565 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9566 pTracker->idx = idxFls;
9567 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9568 g_Sandbox.pFlsAllocHead = pTracker;
9569 }
9570 }
9571
9572 return idxFls;
9573}
9574
9575/** Kernel32 - FlsFree */
9576BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9577{
9578 BOOL fRc = FlsFree(idxFls);
9579 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9580 if (fRc)
9581 {
9582 PKWLOCALSTORAGE pTracker;
9583 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9584
9585 pTracker = g_Sandbox.pFlsAllocHead;
9586 if (pTracker)
9587 {
9588 if (pTracker->idx == idxFls)
9589 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9590 else
9591 {
9592 PKWLOCALSTORAGE pPrev;
9593 do
9594 {
9595 pPrev = pTracker;
9596 pTracker = pTracker->pNext;
9597 } while (pTracker && pTracker->idx != idxFls);
9598 if (pTracker)
9599 pPrev->pNext = pTracker->pNext;
9600 }
9601 if (pTracker)
9602 {
9603 pTracker->idx = FLS_OUT_OF_INDEXES;
9604 pTracker->pNext = NULL;
9605 kHlpFree(pTracker);
9606 }
9607 }
9608 }
9609 return fRc;
9610}
9611
9612
9613/** Kernel32 - TlsAlloc */
9614DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9615{
9616 DWORD idxTls = TlsAlloc();
9617 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9618 if (idxTls != TLS_OUT_OF_INDEXES)
9619 {
9620 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9621 if (pTracker)
9622 {
9623 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9624 pTracker->idx = idxTls;
9625 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9626 g_Sandbox.pTlsAllocHead = pTracker;
9627 }
9628 }
9629
9630 return idxTls;
9631}
9632
9633/** Kernel32 - TlsFree */
9634BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9635{
9636 BOOL fRc = TlsFree(idxTls);
9637 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9638 if (fRc)
9639 {
9640 PKWLOCALSTORAGE pTracker;
9641 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9642
9643 pTracker = g_Sandbox.pTlsAllocHead;
9644 if (pTracker)
9645 {
9646 if (pTracker->idx == idxTls)
9647 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9648 else
9649 {
9650 PKWLOCALSTORAGE pPrev;
9651 do
9652 {
9653 pPrev = pTracker;
9654 pTracker = pTracker->pNext;
9655 } while (pTracker && pTracker->idx != idxTls);
9656 if (pTracker)
9657 pPrev->pNext = pTracker->pNext;
9658 }
9659 if (pTracker)
9660 {
9661 pTracker->idx = TLS_OUT_OF_INDEXES;
9662 pTracker->pNext = NULL;
9663 kHlpFree(pTracker);
9664 }
9665 }
9666 }
9667 return fRc;
9668}
9669
9670
9671
9672/*
9673 *
9674 * Header file hashing.
9675 * Header file hashing.
9676 * Header file hashing.
9677 *
9678 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9679 * indicated that ~12% of the time was spent doing MD5 caluclation when
9680 * rebuiling openssl. The hashing it done right after reading the source
9681 * via ReadFile, same buffers and sizes.
9682 */
9683
9684#ifdef WITH_HASH_MD5_CACHE
9685
9686/** AdvApi32 - CryptCreateHash */
9687static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9688 HCRYPTHASH *phHash)
9689{
9690 BOOL fRc;
9691
9692 /*
9693 * Only do this for cl.exe when it request normal MD5.
9694 */
9695 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9696 {
9697 /** @todo do generic caching of hash results. Need SHA-1 and SHA-256 for
9698 * more recent compilers. */
9699 if (idAlg == CALG_MD5)
9700 {
9701 if (hKey == 0)
9702 {
9703 if (dwFlags == 0)
9704 {
9705 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9706 if (pHash)
9707 {
9708 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9709 pHash->uMagic = KWHASHMD5_MAGIC;
9710 pHash->cbHashed = 0;
9711 pHash->fGoneBad = K_FALSE;
9712 pHash->fFallbackMode = K_FALSE;
9713 pHash->fFinal = K_FALSE;
9714
9715 /* link it. */
9716 pHash->pNext = g_Sandbox.pHashHead;
9717 g_Sandbox.pHashHead = pHash;
9718
9719 *phHash = (KUPTR)pHash;
9720 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9721 hProv, *phHash, TRUE));
9722 return TRUE;
9723 }
9724
9725 kwErrPrintf("CryptCreateHash: out of memory!\n");
9726 }
9727 else
9728 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9729 }
9730 else
9731 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9732 }
9733 //else
9734 // kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9735 }
9736
9737 /*
9738 * Fallback.
9739 */
9740 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9741 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9742 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9743 return fRc;
9744}
9745
9746
9747/** AdvApi32 - CryptHashData */
9748static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9749{
9750 BOOL fRc;
9751 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9752 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9753 while (pHash && (KUPTR)pHash != hHash)
9754 pHash = pHash->pNext;
9755 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9756 hHash, pHash, pbData, cbData, dwFlags));
9757 if (pHash)
9758 {
9759 /*
9760 * Validate the state.
9761 */
9762 if ( pHash->uMagic == KWHASHMD5_MAGIC
9763 && !pHash->fFinal)
9764 {
9765 if (!pHash->fFallbackMode)
9766 {
9767 /*
9768 * Does this match the previous ReadFile call to a cached file?
9769 * If it doesn't, try falling back.
9770 */
9771 if ( g_Sandbox.LastHashRead.cbRead == cbData
9772 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9773 {
9774 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9775 if ( pCachedFile
9776 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9777 {
9778
9779 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9780 {
9781 if ( pHash->pCachedFile == NULL
9782 && pHash->cbHashed == 0)
9783 pHash->pCachedFile = pCachedFile;
9784 if (pHash->pCachedFile == pCachedFile)
9785 {
9786 pHash->cbHashed += cbData;
9787 g_Sandbox.LastHashRead.pCachedFile = NULL;
9788 g_Sandbox.LastHashRead.pvRead = NULL;
9789 g_Sandbox.LastHashRead.cbRead = 0;
9790 g_Sandbox.LastHashRead.offRead = 0;
9791 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9792 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9793 return TRUE;
9794 }
9795
9796 /* Note! it's possible to fall back here too, if necessary. */
9797 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
9798 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
9799 }
9800 else
9801 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
9802 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
9803 }
9804 else if (!pCachedFile)
9805 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
9806 else
9807 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
9808 }
9809 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
9810 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
9811 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
9812 if (pHash->cbHashed == 0)
9813 pHash->fFallbackMode = K_TRUE;
9814 if (pHash->fFallbackMode)
9815 {
9816 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
9817 pHash->fFallbackMode = K_TRUE;
9818 MD5Init(&pHash->Md5Ctx);
9819 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9820 pHash->cbHashed = cbData;
9821 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
9822 hHash, pbData, cbData, dwFlags));
9823 return TRUE;
9824 }
9825 pHash->fGoneBad = K_TRUE;
9826 SetLastError(ERROR_INVALID_PARAMETER);
9827 fRc = FALSE;
9828 }
9829 else
9830 {
9831 /* fallback. */
9832 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9833 pHash->cbHashed += cbData;
9834 fRc = TRUE;
9835 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
9836 hHash, pbData, cbData, dwFlags));
9837 }
9838 }
9839 /*
9840 * Bad handle state.
9841 */
9842 else
9843 {
9844 if (pHash->uMagic != KWHASHMD5_MAGIC)
9845 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
9846 else
9847 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
9848 SetLastError((DWORD)NTE_BAD_HASH);
9849 fRc = FALSE;
9850 }
9851 }
9852 else
9853 {
9854
9855 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
9856 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
9857 }
9858 return fRc;
9859}
9860
9861
9862/** AdvApi32 - CryptGetHashParam */
9863static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
9864 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
9865{
9866 BOOL fRc;
9867 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9868 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9869 while (pHash && (KUPTR)pHash != hHash)
9870 pHash = pHash->pNext;
9871 if (pHash)
9872 {
9873 if (pHash->uMagic == KWHASHMD5_MAGIC)
9874 {
9875 if (dwFlags == 0)
9876 {
9877 DWORD cbRet;
9878 void *pvRet;
9879 union
9880 {
9881 DWORD dw;
9882 } uBuf;
9883
9884 switch (dwParam)
9885 {
9886 case HP_HASHVAL:
9887 {
9888 /* Check the hash progress. */
9889 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
9890 if (pCachedFile)
9891 {
9892 if ( pCachedFile->cbCached == pHash->cbHashed
9893 && !pHash->fGoneBad)
9894 {
9895 if (pCachedFile->fValidMd5)
9896 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
9897 else
9898 {
9899 MD5Init(&pHash->Md5Ctx);
9900 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
9901 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
9902 pCachedFile->fValidMd5 = K_TRUE;
9903 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
9904 }
9905 pvRet = pCachedFile->abMd5Digest;
9906 }
9907 else
9908 {
9909 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
9910 from what I can tell, so just deal with it. */
9911 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
9912 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
9913 pHash, pCachedFile, pCachedFile->szPath));
9914 pHash->fFallbackMode = K_TRUE;
9915 pHash->pCachedFile = NULL;
9916 MD5Init(&pHash->Md5Ctx);
9917 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
9918 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9919 pvRet = pHash->abDigest;
9920 }
9921 pHash->fFinal = K_TRUE;
9922 cbRet = 16;
9923 break;
9924 }
9925 else if (pHash->fFallbackMode)
9926 {
9927 if (!pHash->fFinal)
9928 {
9929 pHash->fFinal = K_TRUE;
9930 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9931 }
9932 pvRet = pHash->abDigest;
9933 cbRet = 16;
9934 break;
9935 }
9936 else
9937 {
9938 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
9939 SetLastError(ERROR_INVALID_SERVER_STATE);
9940 }
9941 return FALSE;
9942 }
9943
9944 case HP_HASHSIZE:
9945 uBuf.dw = 16;
9946 pvRet = &uBuf;
9947 cbRet = sizeof(DWORD);
9948 break;
9949
9950 case HP_ALGID:
9951 uBuf.dw = CALG_MD5;
9952 pvRet = &uBuf;
9953 cbRet = sizeof(DWORD);
9954 break;
9955
9956 default:
9957 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
9958 SetLastError((DWORD)NTE_BAD_TYPE);
9959 return FALSE;
9960 }
9961
9962 /*
9963 * Copy out cbRet from pvRet.
9964 */
9965 if (pbData)
9966 {
9967 if (*pcbData >= cbRet)
9968 {
9969 *pcbData = cbRet;
9970 kHlpMemCopy(pbData, pvRet, cbRet);
9971 if (cbRet == 4)
9972 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
9973 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
9974 else if (cbRet == 16)
9975 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",
9976 dwParam, pHash, pHash->pCachedFile, cbRet,
9977 pbData[0], pbData[1], pbData[2], pbData[3],
9978 pbData[4], pbData[5], pbData[6], pbData[7],
9979 pbData[8], pbData[9], pbData[10], pbData[11],
9980 pbData[12], pbData[13], pbData[14], pbData[15]));
9981 else
9982 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
9983 dwParam, pHash, pHash->pCachedFile, cbRet));
9984 return TRUE;
9985 }
9986
9987 kHlpMemCopy(pbData, pvRet, *pcbData);
9988 }
9989 SetLastError(ERROR_MORE_DATA);
9990 *pcbData = cbRet;
9991 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
9992 }
9993 else
9994 {
9995 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
9996 SetLastError((DWORD)NTE_BAD_FLAGS);
9997 }
9998 }
9999 else
10000 {
10001 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
10002 SetLastError((DWORD)NTE_BAD_HASH);
10003 }
10004 fRc = FALSE;
10005 }
10006 /*
10007 * Regular handle.
10008 */
10009 else
10010 {
10011 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
10012 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
10013 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
10014 }
10015
10016 return fRc;
10017}
10018
10019
10020/** AdvApi32 - CryptDestroyHash */
10021static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
10022{
10023 BOOL fRc;
10024 PKWHASHMD5 pPrev = NULL;
10025 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
10026 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10027 while (pHash && (KUPTR)pHash != hHash)
10028 {
10029 pPrev = pHash;
10030 pHash = pHash->pNext;
10031 }
10032 if (pHash)
10033 {
10034 if (pHash->uMagic == KWHASHMD5_MAGIC)
10035 {
10036 pHash->uMagic = 0;
10037 if (!pPrev)
10038 g_Sandbox.pHashHead = pHash->pNext;
10039 else
10040 pPrev->pNext = pHash->pNext;
10041 kHlpFree(pHash);
10042 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
10043 fRc = TRUE;
10044 }
10045 else
10046 {
10047 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10048 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10049 SetLastError(ERROR_INVALID_HANDLE);
10050 fRc = FALSE;
10051 }
10052 }
10053 /*
10054 * Regular handle.
10055 */
10056 else
10057 {
10058 fRc = CryptDestroyHash(hHash);
10059 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10060 }
10061 return fRc;
10062}
10063
10064#endif /* WITH_HASH_MD5_CACHE */
10065
10066
10067/*
10068 *
10069 * Reuse crypt context.
10070 * Reuse crypt context.
10071 * Reuse crypt context.
10072 *
10073 *
10074 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10075 *
10076 */
10077
10078#ifdef WITH_CRYPT_CTX_REUSE
10079
10080/** AdvApi32 - CryptAcquireContextW. */
10081static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10082 DWORD dwProvType, DWORD dwFlags)
10083{
10084 BOOL fRet;
10085
10086 /*
10087 * Lookup reusable context based on the input.
10088 */
10089 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10090 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10091 KU32 iCtx = g_Sandbox.cCryptCtxs;
10092 while (iCtx-- > 0)
10093 {
10094 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10095 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10096 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10097 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10098 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10099 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10100 {
10101 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10102 {
10103 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10104 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10105 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10106 return TRUE;
10107 }
10108 }
10109 }
10110
10111 /*
10112 * Create it and enter it into the reused array if possible.
10113 */
10114 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10115 if (fRet)
10116 {
10117 iCtx = g_Sandbox.cCryptCtxs;
10118 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10119 {
10120 /* Try duplicate the input strings. */
10121 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10122 (cwcContainer + 1) * sizeof(wchar_t));
10123 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10124 {
10125 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10126 (cwcProvider + 1) * sizeof(wchar_t));
10127 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10128 {
10129 /* Add a couple of references just to be on the safe side and all that. */
10130 HCRYPTPROV hProv = *phProv;
10131 if (CryptContextAddRef(hProv, NULL, 0))
10132 {
10133 if (CryptContextAddRef(hProv, NULL, 0))
10134 {
10135 /* Okay, finish the entry and return success */
10136 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10137 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10138 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10139 g_Sandbox.cCryptCtxs = iCtx + 1;
10140
10141 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10142 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10143 return TRUE;
10144 }
10145 CryptReleaseContext(hProv, 0);
10146 }
10147 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10148
10149 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10150 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10151 }
10152 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10153 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10154 }
10155 }
10156 else
10157 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10158 }
10159
10160 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10161 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10162 return fRet;
10163}
10164
10165
10166/** AdvApi32 - CryptReleaseContext */
10167static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10168{
10169 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10170 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10171 return fRet;
10172}
10173
10174
10175/** AdvApi32 - CryptContextAddRef */
10176static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10177{
10178 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10179 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10180 return fRet;
10181}
10182
10183#endif /* WITH_CRYPT_CTX_REUSE */
10184
10185/*
10186 *
10187 * Structured exception handling.
10188 * Structured exception handling.
10189 * Structured exception handling.
10190 *
10191 */
10192#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10193
10194# define EH_NONCONTINUABLE KU32_C(0x00000001)
10195# define EH_UNWINDING KU32_C(0x00000002)
10196# define EH_EXIT_UNWIND KU32_C(0x00000004)
10197# define EH_STACK_INVALID KU32_C(0x00000008)
10198# define EH_NESTED_CALL KU32_C(0x00000010)
10199
10200typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10201 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10202typedef struct _EXCEPTION_REGISTRATION_RECORD
10203{
10204 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10205 PFNXCPTHANDLER pfnXcptHandler;
10206};
10207
10208
10209/**
10210 * Calls @a pfnHandler.
10211 */
10212static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10213 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10214 PFNXCPTHANDLER pfnHandler)
10215{
10216# if 1
10217 /* This is a more robust version that isn't subject to calling
10218 convension cleanup disputes and such. */
10219 KU32 uSavedEdi;
10220 KU32 uSavedEsi;
10221 KU32 uSavedEbx;
10222 KU32 rcHandler;
10223
10224 __asm
10225 {
10226 mov [uSavedEdi], edi
10227 mov [uSavedEsi], esi
10228 mov [uSavedEbx], ebx
10229 mov esi, esp
10230 mov edi, esp
10231 mov edi, [pXcptRec]
10232 mov edx, [pRegRec]
10233 mov eax, [pXcptCtx]
10234 mov ebx, [ppRegRec]
10235 mov ecx, [pfnHandler]
10236 sub esp, 16
10237 and esp, 0fffffff0h
10238 mov [esp ], edi
10239 mov [esp + 4], edx
10240 mov [esp + 8], eax
10241 mov [esp + 12], ebx
10242 mov edi, esi
10243 call ecx
10244 mov esp, esi
10245 cmp esp, edi
10246 je stack_ok
10247 int 3
10248 stack_ok:
10249 mov edi, [uSavedEdi]
10250 mov esi, [uSavedEsi]
10251 mov ebx, [uSavedEbx]
10252 mov [rcHandler], eax
10253 }
10254 return rcHandler;
10255# else
10256 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10257# endif
10258}
10259
10260
10261/**
10262 * Vectored exception handler that emulates x86 chained exception handler.
10263 *
10264 * This is necessary because the RtlIsValidHandler check fails for self loaded
10265 * code and prevents cl.exe from working. (On AMD64 we can register function
10266 * tables, but on X86 cooking your own handling seems to be the only viabke
10267 * alternative.)
10268 *
10269 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10270 * @param pXcptPtrs The exception details.
10271 */
10272static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10273{
10274 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10275 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10276 if (g_Sandbox.fRunning)
10277 {
10278 HANDLE const hCurProc = GetCurrentProcess();
10279 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10280 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10281 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10282 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10283 {
10284 /* Read the exception record in a safe manner. */
10285 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10286 DWORD cbActuallyRead = 0;
10287 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10288 && cbActuallyRead == sizeof(RegRec))
10289 {
10290 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10291 KU32 rcHandler;
10292 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10293 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10294 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10295 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10296 if (rcHandler == ExceptionContinueExecution)
10297 {
10298 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10299 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10300 return EXCEPTION_CONTINUE_EXECUTION;
10301 }
10302
10303 if (rcHandler == ExceptionContinueSearch)
10304 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10305 else if (rcHandler == ExceptionNestedException)
10306 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10307 else
10308 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10309 }
10310 else
10311 {
10312 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10313 break;
10314 }
10315
10316 /*
10317 * Next.
10318 */
10319 pRegRec = RegRec.pPrevRegRec;
10320 }
10321 }
10322 return EXCEPTION_CONTINUE_SEARCH;
10323}
10324
10325
10326/** NtDll,Kernel32 - RtlUnwind */
10327static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10328 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10329{
10330 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10331 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10332 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10333 if (g_Sandbox.fRunning)
10334 {
10335 HANDLE const hCurProc = GetCurrentProcess();
10336 PCONTEXT pXcptCtx = NULL;
10337 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10338
10339 /*
10340 * Update / create an exception record.
10341 */
10342 if (pXcptRec)
10343 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10344 else
10345 {
10346 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10347 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10348 pXcptRec->ExceptionCode = STATUS_UNWIND;
10349 pXcptRec->ExceptionFlags = EH_UNWINDING;
10350 }
10351 if (!pStopXcptRec)
10352 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10353
10354 /*
10355 * Walk the chain till we find pStopXctpRec.
10356 */
10357 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10358 && pRegRec != NULL
10359 && pRegRec != pStopXcptRec)
10360 {
10361 /* Read the exception record in a safe manner. */
10362 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10363 DWORD cbActuallyRead = 0;
10364 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10365 && cbActuallyRead == sizeof(RegRec))
10366 {
10367 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10368 KU32 rcHandler;
10369 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10370 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10371 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10372 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10373
10374 if (rcHandler == ExceptionContinueSearch)
10375 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10376 else if (rcHandler == ExceptionCollidedUnwind)
10377 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10378 else
10379 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10380 }
10381 else
10382 {
10383 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10384 break;
10385 }
10386
10387 /*
10388 * Pop next.
10389 */
10390 pTib->ExceptionList = RegRec.pPrevRegRec;
10391 pRegRec = RegRec.pPrevRegRec;
10392 }
10393 return;
10394 }
10395
10396 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10397}
10398
10399#endif /* WINDOWS + X86 */
10400
10401
10402/*
10403 *
10404 * Misc function only intercepted while debugging.
10405 * Misc function only intercepted while debugging.
10406 * Misc function only intercepted while debugging.
10407 *
10408 */
10409
10410#ifndef NDEBUG
10411
10412/** CRT - memcpy */
10413static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10414{
10415 KU8 const *pbSrc = (KU8 const *)pvSrc;
10416 KU8 *pbDst = (KU8 *)pvDst;
10417 KSIZE cbLeft = cb;
10418 while (cbLeft-- > 0)
10419 *pbDst++ = *pbSrc++;
10420 return pvDst;
10421}
10422
10423
10424/** CRT - memset */
10425static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10426{
10427 KU8 *pbDst = (KU8 *)pvDst;
10428 KSIZE cbLeft = cb;
10429 while (cbLeft-- > 0)
10430 *pbDst++ = (KU8)bFiller;
10431 return pvDst;
10432}
10433
10434#endif /* NDEBUG */
10435
10436
10437
10438/**
10439 * Functions that needs replacing for sandboxed execution.
10440 */
10441KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10442{
10443 /*
10444 * Kernel32.dll and friends.
10445 */
10446 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10447 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10448
10449 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10450 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10451 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10452 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10453 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10454 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10455 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10456 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10457 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10458 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10459 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10460
10461 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10462 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10463 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10464 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10465
10466 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10467
10468 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10469 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10470 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10471 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10472 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10473 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10474 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10475 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10476 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10477 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10478 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10479
10480 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10481 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10482 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10483 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10484#ifdef WITH_TEMP_MEMORY_FILES
10485 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10486 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10487 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10488 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10489 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10490 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10491 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10492 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10493 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10494 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10495#endif
10496 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10497 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10498 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10499 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10500 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10501 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10502 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10503 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10504 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10505#ifdef WITH_TEMP_MEMORY_FILES
10506 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10507#endif
10508
10509#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10510 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10511 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10512#endif
10513
10514 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10515 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10516
10517 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10518 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10519
10520 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10521 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10522 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10523 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10524
10525 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10526
10527#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10528 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10529#endif
10530
10531#ifdef WITH_HASH_MD5_CACHE
10532 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10533 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10534 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10535 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10536#endif
10537
10538#ifdef WITH_CRYPT_CTX_REUSE
10539 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10540 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10541 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10542#endif
10543
10544 /*
10545 * MS Visual C++ CRTs.
10546 */
10547 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10548 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10549 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10550 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10551 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10552 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10553
10554 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10555 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10556 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10557
10558 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10559 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10560 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
10561
10562 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
10563 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
10564 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
10565 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
10566 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
10567 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
10568 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
10569 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
10570 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
10571 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
10572 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
10573 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
10574 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
10575 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
10576 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
10577 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
10578 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
10579 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
10580 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
10581 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
10582
10583 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
10584 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
10585 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
10586 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
10587 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
10588 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
10589 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
10590 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
10591 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
10592 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
10593 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
10594 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
10595 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
10596 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
10597
10598#ifndef NDEBUG
10599 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
10600 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
10601#endif
10602};
10603/** Number of entries in g_aReplacements. */
10604KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
10605
10606
10607/**
10608 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
10609 * execution.
10610 */
10611KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
10612{
10613 /*
10614 * Kernel32.dll and friends.
10615 */
10616 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10617 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10618
10619#if 0
10620 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10621#endif
10622
10623 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10624 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10625 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10626 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10627#ifdef WITH_TEMP_MEMORY_FILES
10628 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10629 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10630 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10631 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10632 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10633 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10634 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10635 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10636 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10637 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10638#endif
10639 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10640 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10641 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10642 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10643 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10644 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10645 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10646 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10647 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10648#ifdef WITH_TEMP_MEMORY_FILES
10649 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10650#endif
10651 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10652 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10653
10654#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10655 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10656 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10657#endif
10658
10659#ifdef WITH_HASH_MD5_CACHE
10660 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10661 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10662 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10663 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10664#endif
10665
10666 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10667
10668#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10669 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10670#endif
10671
10672 /*
10673 * MS Visual C++ CRTs.
10674 */
10675 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10676 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10677 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10678 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10679 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10680 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10681 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10682
10683#if 0 /* used by mspdbXXX.dll */
10684 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10685 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10686#endif
10687};
10688/** Number of entries in g_aSandboxNativeReplacements. */
10689KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10690
10691
10692/**
10693 * Functions that needs replacing when queried by GetProcAddress.
10694 */
10695KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10696{
10697 /*
10698 * Kernel32.dll and friends.
10699 */
10700 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10701 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10702 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10703 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10704};
10705/** Number of entries in g_aSandboxGetProcReplacements. */
10706KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10707
10708
10709/**
10710 * Control handler.
10711 *
10712 * @returns TRUE if handled, FALSE if not.
10713 * @param dwCtrlType The signal.
10714 */
10715static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10716{
10717 DWORD cbIgn;
10718 int volatile rc; /* volatile for debugging */
10719 int volatile rcPrev;
10720 const char *pszMsg;
10721 switch (dwCtrlType)
10722 {
10723 case CTRL_C_EVENT:
10724 rc = 9;
10725 pszMsg = "kWorker: Ctrl-C\r\n";
10726 break;
10727
10728 case CTRL_BREAK_EVENT:
10729 rc = 10;
10730 pszMsg = "kWorker: Ctrl-Break\r\n";
10731 break;
10732
10733 case CTRL_CLOSE_EVENT:
10734 rc = 11;
10735 pszMsg = "kWorker: console closed\r\n";
10736 break;
10737
10738 case CTRL_LOGOFF_EVENT:
10739 rc = 11;
10740 pszMsg = "kWorker: logoff event\r\n";
10741 break;
10742
10743 case CTRL_SHUTDOWN_EVENT:
10744 rc = 11;
10745 pszMsg = "kWorker: shutdown event\r\n";
10746 break;
10747
10748 default:
10749 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10750 return TRUE;
10751 }
10752
10753 /*
10754 * Terminate the process after 5 seconds.
10755 * If we get here a second time we just terminate the process ourselves.
10756 *
10757 * Note! We do no try call exit() here as it turned out to deadlock a lot
10758 * flusing file descriptors (stderr back when we first wrote to it).
10759 */
10760 rcPrev = g_rcCtrlC;
10761 g_rcCtrlC = rc;
10762 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10763 if (rcPrev == 0)
10764 {
10765 int i;
10766 for (i = 0; i < 10; i++)
10767 {
10768 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10769 Sleep(500);
10770 }
10771 }
10772 TerminateProcess(GetCurrentProcess(), rc);
10773 return TRUE;
10774}
10775
10776
10777#if 0
10778/**
10779 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10780 */
10781static void kwSandboxResetModuleVisited(void)
10782{
10783 PKWMODULE pMod = g_pModuleHead;
10784 while (pMod)
10785 {
10786 pMod->fVisited = K_FALSE;
10787 pMod = pMod->pNextList;
10788 }
10789}
10790
10791
10792/**
10793 * Used by kwSandboxExec to reset the state of the module tree.
10794 *
10795 * This is done recursively.
10796 *
10797 * @param pMod The root of the tree to consider.
10798 */
10799static void kwSandboxResetModuleState(PKWMODULE pMod)
10800{
10801 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
10802 if (!pMod->fNative)
10803 {
10804 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
10805 if (!pMod->fVisited) /* Avoid loops. */
10806 {
10807 KSIZE iImp;
10808 pMod->fVisited = K_TRUE;
10809 iImp = pMod->u.Manual.cImpMods;
10810 while (iImp-- > 0)
10811 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
10812 }
10813 }
10814 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10815 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
10816 {
10817 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10818 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10819 {
10820 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10821 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10822 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10823 pMod->pszPath, pszValue ? pszValue : "<null>"));
10824 }
10825 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10826 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10827 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10828 pMod->pszPath, pszValue ? pszValue : "<null>"));
10829 else
10830 {
10831 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10832 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10833 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10834 if (pszValue != NULL)
10835 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10836 else
10837 pMod->pszMsPdbSrvEndpoint = NULL;
10838 pMod->fNeedReInit = K_TRUE;
10839 }
10840 }
10841}
10842#else
10843/**
10844 * Used by kwSandboxExec to reset the state of the module tree.
10845 */
10846static void kwSandboxResetModuleState(void)
10847{
10848 PKWMODULE pMod = g_pModuleHead;
10849 while (pMod)
10850 {
10851 if (!pMod->fNative)
10852 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
10853 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10854 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
10855 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
10856 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
10857 {
10858 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10859 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10860 {
10861 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10862 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10863 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10864 pMod->pszPath, pszValue ? pszValue : "<null>"));
10865 }
10866 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10867 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10868 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10869 pMod->pszPath, pszValue ? pszValue : "<null>"));
10870 else
10871 {
10872 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10873 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10874 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10875 if (pszValue != NULL)
10876 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10877 else
10878 pMod->pszMsPdbSrvEndpoint = NULL;
10879 pMod->fNeedReInit = K_TRUE;
10880 }
10881 }
10882
10883 pMod = pMod->pNextList;
10884 }
10885}
10886#endif
10887
10888static PPEB kwSandboxGetProcessEnvironmentBlock(void)
10889{
10890#if K_ARCH == K_ARCH_X86_32
10891 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
10892#elif K_ARCH == K_ARCH_AMD64
10893 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
10894#else
10895# error "Port me!"
10896#endif
10897}
10898
10899
10900/**
10901 * Enters the given handle into the handle table.
10902 *
10903 * @returns K_TRUE on success, K_FALSE on failure.
10904 * @param pSandbox The sandbox.
10905 * @param pHandle The handle.
10906 * @param hHandle The handle value to enter it under (for the
10907 * duplicate handle API).
10908 */
10909static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
10910{
10911 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
10912 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
10913
10914 /*
10915 * Grow handle table.
10916 */
10917 if (idxHandle >= pSandbox->cHandles)
10918 {
10919 void *pvNew;
10920 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
10921 while (cHandles <= idxHandle)
10922 cHandles *= 2;
10923 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
10924 if (!pvNew)
10925 {
10926 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
10927 return K_FALSE;
10928 }
10929 pSandbox->papHandles = (PKWHANDLE *)pvNew;
10930 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
10931 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
10932 pSandbox->cHandles = cHandles;
10933 }
10934
10935 /*
10936 * Check that the entry is unused then insert it.
10937 */
10938 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
10939 pSandbox->papHandles[idxHandle] = pHandle;
10940 pSandbox->cActiveHandles++;
10941 return K_TRUE;
10942}
10943
10944
10945/**
10946 * Creates a correctly quoted ANSI command line string from the given argv.
10947 *
10948 * @returns Pointer to the command line.
10949 * @param cArgs Number of arguments.
10950 * @param papszArgs The argument vector.
10951 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10952 * @param pcbCmdLine Where to return the command line length,
10953 * including one terminator.
10954 */
10955static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
10956{
10957 KU32 i;
10958 KSIZE cbCmdLine;
10959 char *pszCmdLine;
10960
10961 /* Make a copy of the argument vector that we'll be quoting. */
10962 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
10963 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
10964
10965 /* Quote the arguments that need it. */
10966 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
10967
10968 /* figure out cmd line length. */
10969 cbCmdLine = 0;
10970 for (i = 0; i < cArgs; i++)
10971 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
10972 *pcbCmdLine = cbCmdLine;
10973
10974 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
10975 if (pszCmdLine)
10976 {
10977 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
10978 if (papszQuotedArgs[0] != papszArgs[0])
10979 free(papszQuotedArgs[0]);
10980
10981 for (i = 1; i < cArgs; i++)
10982 {
10983 *psz++ = ' ';
10984 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
10985 if (papszQuotedArgs[i] != papszArgs[i])
10986 free(papszQuotedArgs[i]);
10987 }
10988 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
10989
10990 *psz++ = '\0';
10991 *psz++ = '\0';
10992 }
10993
10994 return pszCmdLine;
10995}
10996
10997
10998
10999static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
11000 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11001 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11002{
11003 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11004 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11005 wchar_t *pwcPool;
11006 KSIZE cbStrings;
11007 KSIZE cwc;
11008 KSIZE cbCmdLine;
11009 KU32 i;
11010
11011 /* Simple stuff. */
11012 pSandbox->rcExitCode = 256;
11013 pSandbox->pTool = pTool;
11014 pSandbox->idMainThread = GetCurrentThreadId();
11015 pSandbox->pgmptr = (char *)pTool->pszPath;
11016 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
11017#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11018 if (pSandbox->StdOut.fIsConsole)
11019 pSandbox->StdOut.u.Con.cwcBuf = 0;
11020 else
11021 pSandbox->StdOut.u.Fully.cchBuf = 0;
11022 if (pSandbox->StdErr.fIsConsole)
11023 pSandbox->StdErr.u.Con.cwcBuf = 0;
11024 else
11025 pSandbox->StdErr.u.Fully.cchBuf = 0;
11026 pSandbox->Combined.cwcBuf = 0;
11027 pSandbox->Combined.cFlushes = 0;
11028#endif
11029 pSandbox->fNoPchCaching = fNoPchCaching;
11030 pSandbox->cArgs = cArgs;
11031 pSandbox->papszArgs = (char **)papszArgs;
11032 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
11033 if (!pSandbox->pszCmdLine)
11034 return KERR_NO_MEMORY;
11035
11036 /*
11037 * Convert command line and argv to UTF-16.
11038 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
11039 */
11040 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
11041 if (!pSandbox->papwszArgs)
11042 return KERR_NO_MEMORY;
11043 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
11044 for (i = 0; i < cArgs; i++)
11045 {
11046 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
11047 pSandbox->papwszArgs[i] = pwcPool;
11048 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11049 pwcPool++;
11050 }
11051 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11052 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11053
11054 /*
11055 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11056 */
11057 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11058 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11059 if (!pSandbox->pwszCmdLine)
11060 return KERR_NO_MEMORY;
11061 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11062
11063 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11064 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11065 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11066
11067 /*
11068 * Setup the environment.
11069 */
11070 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11071 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11072 {
11073 KU32 iDst = 0;
11074 for (i = 0; i < cEnvVars; i++)
11075 {
11076 const char *pszVar = papszEnvVars[i];
11077 KSIZE cchVar = kHlpStrLen(pszVar);
11078 const char *pszEqual;
11079 if ( cchVar > 0
11080 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11081 {
11082 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11083 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11084 if (pszCopy && pwszCopy)
11085 {
11086 pSandbox->papszEnvVars[iDst] = pszCopy;
11087 pSandbox->environ[iDst] = pszCopy;
11088 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11089 pSandbox->wenviron[iDst] = pwszCopy;
11090
11091 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11092 if ( (pszEqual - pszVar) == 4
11093 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11094 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11095 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11096 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11097 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11098 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11099
11100 iDst++;
11101 }
11102 else
11103 {
11104 kHlpFree(pszCopy);
11105 kHlpFree(pwszCopy);
11106 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11107 }
11108 }
11109 else
11110 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11111 }
11112 pSandbox->papszEnvVars[iDst] = NULL;
11113 pSandbox->environ[iDst] = NULL;
11114 pSandbox->papwszEnvVars[iDst] = NULL;
11115 pSandbox->wenviron[iDst] = NULL;
11116 }
11117 else
11118 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11119
11120 /*
11121 * Invalidate the volatile parts of cache (kBuild output directory,
11122 * temporary directory, whatever).
11123 */
11124 kFsCacheInvalidateCustomBoth(g_pFsCache);
11125
11126#ifdef WITH_HISTORY
11127 /*
11128 * Record command line in debug history.
11129 */
11130 kHlpFree(g_apszHistory[g_iHistoryNext]);
11131 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11132 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11133#endif
11134
11135 return 0;
11136}
11137
11138
11139/**
11140 * Does sandbox cleanup between jobs.
11141 *
11142 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11143 * influence the result, so that kmk can get on with things ASAP.
11144 *
11145 * @param pSandbox The sandbox.
11146 */
11147static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11148{
11149 PROCESS_MEMORY_COUNTERS MemInfo;
11150 PKWVIRTALLOC pTracker;
11151 PKWHEAP pHeap;
11152 PKWLOCALSTORAGE pLocalStorage;
11153#ifdef WITH_HASH_MD5_CACHE
11154 PKWHASHMD5 pHash;
11155#endif
11156#ifdef WITH_TEMP_MEMORY_FILES
11157 PKWFSTEMPFILE pTempFile;
11158#endif
11159 PKWEXITCALLACK pExitCallback;
11160
11161 /*
11162 * First stuff that may cause code to run.
11163 */
11164
11165 /* Do exit callback first. */
11166 pExitCallback = g_Sandbox.pExitCallbackHead;
11167 g_Sandbox.pExitCallbackHead = NULL;
11168 while (pExitCallback)
11169 {
11170 PKWEXITCALLACK pNext = pExitCallback->pNext;
11171 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11172 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11173 __try
11174 {
11175 pExitCallback->pfnCallback();
11176 }
11177 __except (EXCEPTION_EXECUTE_HANDLER)
11178 {
11179 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11180 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11181 kHlpAssertFailed();
11182 }
11183 kHlpFree(pExitCallback);
11184 pExitCallback = pNext;
11185 }
11186
11187 /* Free left behind FlsAlloc leaks. */
11188 pLocalStorage = g_Sandbox.pFlsAllocHead;
11189 g_Sandbox.pFlsAllocHead = NULL;
11190 while (pLocalStorage)
11191 {
11192 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11193 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11194 FlsFree(pLocalStorage->idx);
11195 kHlpFree(pLocalStorage);
11196 pLocalStorage = pNext;
11197 }
11198
11199 /* Free left behind TlsAlloc leaks. */
11200 pLocalStorage = g_Sandbox.pTlsAllocHead;
11201 g_Sandbox.pTlsAllocHead = NULL;
11202 while (pLocalStorage)
11203 {
11204 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11205 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11206 TlsFree(pLocalStorage->idx);
11207 kHlpFree(pLocalStorage);
11208 pLocalStorage = pNext;
11209 }
11210
11211
11212 /*
11213 * Then free resources associated with the sandbox run.
11214 */
11215
11216 /* Open handles, except fixed handles (stdout and stderr). */
11217 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11218 {
11219 KU32 idxHandle = pSandbox->cHandles;
11220 while (idxHandle-- > 0)
11221 if (pSandbox->papHandles[idxHandle] == NULL)
11222 { /* likely */ }
11223 else
11224 {
11225 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11226 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11227 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11228 {
11229 pSandbox->papHandles[idxHandle] = NULL;
11230 pSandbox->cLeakedHandles++;
11231
11232 switch (pHandle->enmType)
11233 {
11234 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11235 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11236 idxHandle, pHandle->hHandle, pHandle->cRefs));
11237 break;
11238 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11239 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11240 idxHandle, pHandle->hHandle, pHandle->cRefs));
11241 break;
11242 case KWHANDLETYPE_OUTPUT_BUF:
11243 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11244 idxHandle, pHandle->hHandle, pHandle->cRefs));
11245 break;
11246#ifdef WITH_TEMP_MEMORY_FILES
11247 case KWHANDLETYPE_TEMP_FILE:
11248 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11249 idxHandle, pHandle->hHandle, pHandle->cRefs));
11250 pHandle->u.pTempFile->cActiveHandles--;
11251 break;
11252 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11253 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11254 idxHandle, pHandle->hHandle, pHandle->cRefs));
11255 pHandle->u.pTempFile->cActiveHandles--;
11256 break;
11257#endif
11258 default:
11259 kHlpAssertFailed();
11260 }
11261 if (--pHandle->cRefs == 0)
11262 kHlpFree(pHandle);
11263 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11264 break;
11265 }
11266 }
11267 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11268 }
11269
11270 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11271 g_Sandbox.cMemMappings = 0;
11272
11273#ifdef WITH_TEMP_MEMORY_FILES
11274 /* The temporary files aren't externally visible, they're all in memory. */
11275 pTempFile = pSandbox->pTempFileHead;
11276 pSandbox->pTempFileHead = NULL;
11277 while (pTempFile)
11278 {
11279 PKWFSTEMPFILE pNext = pTempFile->pNext;
11280 KU32 iSeg = pTempFile->cSegs;
11281 while (iSeg-- > 0)
11282 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11283 kHlpFree(pTempFile->paSegs);
11284 pTempFile->pNext = NULL;
11285 kHlpFree(pTempFile);
11286
11287 pTempFile = pNext;
11288 }
11289#endif
11290
11291 /* Free left behind HeapCreate leaks. */
11292 pHeap = g_Sandbox.pHeapHead;
11293 g_Sandbox.pHeapHead = NULL;
11294 while (pHeap != NULL)
11295 {
11296 PKWHEAP pNext = pHeap->pNext;
11297 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11298 HeapDestroy(pHeap->hHeap);
11299 pHeap = pNext;
11300 }
11301
11302 /* Free left behind VirtualAlloc leaks. */
11303 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11304 pTracker = g_Sandbox.pVirtualAllocHead;
11305 g_Sandbox.pVirtualAllocHead = NULL;
11306 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11307 while (pTracker)
11308 {
11309 PKWVIRTALLOC pNext = pTracker->pNext;
11310 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11311
11312#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11313 if (pTracker->idxPreAllocated != KU32_MAX)
11314 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11315 else
11316#endif
11317 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11318 kHlpFree(pTracker);
11319 pTracker = pNext;
11320 }
11321
11322 /* Free the environment. */
11323 if (pSandbox->papszEnvVars)
11324 {
11325 KU32 i;
11326 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11327 kHlpFree(pSandbox->papszEnvVars[i]);
11328 pSandbox->environ[0] = NULL;
11329 pSandbox->papszEnvVars[0] = NULL;
11330
11331 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11332 kHlpFree(pSandbox->papwszEnvVars[i]);
11333 pSandbox->wenviron[0] = NULL;
11334 pSandbox->papwszEnvVars[0] = NULL;
11335 }
11336
11337#ifdef WITH_HASH_MD5_CACHE
11338 /*
11339 * Hash handles.
11340 */
11341 pHash = pSandbox->pHashHead;
11342 pSandbox->pHashHead = NULL;
11343 while (pHash)
11344 {
11345 PKWHASHMD5 pNext = pHash->pNext;
11346 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11347 kHlpFree(pHash);
11348 pHash = pNext;
11349 }
11350#endif
11351
11352 /*
11353 * Check the memory usage. If it's getting high, trigger a respawn
11354 * after the next job.
11355 */
11356 MemInfo.WorkingSetSize = 0;
11357 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11358 {
11359 /* The first time thru, we figure out approximately when to restart
11360 based on installed RAM and CPU threads. */
11361 static KU64 s_cbMaxWorkingSet = 0;
11362 if (s_cbMaxWorkingSet != 0)
11363 { /* likely */ }
11364 else
11365 {
11366 SYSTEM_INFO SysInfo;
11367 MEMORYSTATUSEX GlobalMemInfo;
11368 const char *pszValue;
11369
11370 /* Calculate a reasonable estimate. */
11371 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11372 GetNativeSystemInfo(&SysInfo);
11373
11374 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11375 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11376 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11377#if K_ARCH_BITS >= 64
11378 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11379#else
11380 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11381#endif
11382 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11383 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11384
11385 /* User limit. */
11386 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11387 if (pszValue != NULL)
11388 {
11389 char *pszNext;
11390 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11391 if (*pszNext == '\0' || *pszNext == 'M')
11392 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11393 else if (*pszNext == 'K')
11394 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11395 else if (*pszNext == 'G')
11396 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11397 else
11398 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11399 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11400 }
11401
11402 /* Clamp it a little. */
11403 if (s_cbMaxWorkingSet < 168*1024*1024)
11404 s_cbMaxWorkingSet = 168*1024*1024;
11405#if K_ARCH_BITS < 64
11406 else
11407 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11408 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11409 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11410 : 1536*1024*1024 /* got 4GB VA */);
11411#endif
11412 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11413 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11414 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11415 }
11416
11417 /* Finally the check. */
11418 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11419 {
11420 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11421 g_fRestart = K_TRUE;
11422 }
11423 }
11424
11425 /*
11426 * The CRT has a max of 8192 handles, so we better restart after a while if
11427 * someone is leaking handles or we risk running out of descriptors.
11428 *
11429 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11430 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11431 */
11432 if (pSandbox->cLeakedHandles > 6000)
11433 {
11434 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11435 g_fRestart = K_TRUE;
11436 }
11437}
11438
11439
11440/**
11441 * Does essential cleanups and restoring, anything externally visible.
11442 *
11443 * All cleanups that aren't externally visible are postponed till after we've
11444 * informed kmk of the result, so it can be done in the dead time between jobs.
11445 *
11446 * @param pSandbox The sandbox.
11447 */
11448static void kwSandboxCleanup(PKWSANDBOX pSandbox)
11449{
11450 /*
11451 * Restore the parent command line string.
11452 */
11453 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11454 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11455 pProcParams->CommandLine = pSandbox->SavedCommandLine;
11456#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11457 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
11458 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
11459#endif
11460}
11461
11462
11463static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11464 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11465{
11466 int rcExit = 42;
11467 int rc;
11468
11469 /*
11470 * Initialize the sandbox environment.
11471 */
11472 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
11473 if (rc == 0)
11474 {
11475 /*
11476 * Do module initialization.
11477 */
11478#if 0
11479 //kwSandboxResetModuleVisited();
11480 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
11481#else
11482 kwSandboxResetModuleState();
11483#endif
11484 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
11485 if (rc == 0)
11486 {
11487 /*
11488 * Call the main function.
11489 */
11490#if K_ARCH == K_ARCH_AMD64
11491 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
11492#elif K_ARCH == K_ARCH_X86_32
11493 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
11494#else
11495# error "Port me!"
11496#endif
11497
11498 /* Save the NT TIB first (should do that here, not in some other function). */
11499 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
11500 pSandbox->TibMainThread = *pTib;
11501
11502 /* Make the call in a guarded fashion. */
11503#if K_ARCH == K_ARCH_AMD64
11504 /* AMD64 */
11505 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
11506 __try
11507 {
11508 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11509 if (setjmp(pSandbox->JmpBuf) == 0)
11510 {
11511 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11512 pSandbox->fRunning = K_TRUE;
11513 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
11514 pSandbox->fRunning = K_FALSE;
11515 }
11516 else
11517 rcExit = pSandbox->rcExitCode;
11518 }
11519#elif K_ARCH == K_ARCH_X86_32
11520 /* x86 (see _tmainCRTStartup) */
11521 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
11522 __try
11523 {
11524 pSandbox->pOutXcptListHead = pTib->ExceptionList;
11525 if (setjmp(pSandbox->JmpBuf) == 0)
11526 {
11527 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
11528 pSandbox->fRunning = K_TRUE;
11529 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
11530 pSandbox->fRunning = K_FALSE;
11531 }
11532 else
11533 rcExit = pSandbox->rcExitCode;
11534 }
11535#endif
11536 __except (EXCEPTION_EXECUTE_HANDLER)
11537 {
11538 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
11539#ifdef WITH_HISTORY
11540 {
11541 KU32 cPrinted = 0;
11542 while (cPrinted++ < 5)
11543 {
11544 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
11545 if (g_apszHistory[idx])
11546 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
11547 }
11548 }
11549#endif
11550 rcExit = 512;
11551 }
11552 pSandbox->fRunning = K_FALSE;
11553
11554 /* Now, restore the NT TIB. */
11555 *pTib = pSandbox->TibMainThread;
11556 }
11557 else
11558 rcExit = 42 + 4;
11559
11560 /*
11561 * Flush and clean up the essential bits only, postpone whatever we
11562 * can till after we've replied to kmk.
11563 */
11564#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11565 kwSandboxConsoleFlushAll(&g_Sandbox);
11566#endif
11567 kwSandboxCleanup(&g_Sandbox);
11568 /** @todo Flush sandboxed native CRTs too. */
11569 }
11570 else
11571 rcExit = 42 + 3;
11572
11573 return rcExit;
11574}
11575
11576
11577/**
11578 * Does the post command part of a job (optional).
11579 *
11580 * @returns The exit code of the job.
11581 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11582 * @param papszPostCmdArgs The post command and its argument.
11583 */
11584static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11585{
11586 const char *pszCmd = papszPostCmdArgs[0];
11587
11588 /* Allow the kmk builtin prefix. */
11589 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
11590 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
11591 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
11592
11593 /* Command switch. */
11594 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
11595 {
11596 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
11597 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
11598 }
11599
11600 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
11601}
11602
11603
11604/**
11605 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11606 */
11607static unsigned kwGetCurrentProcessorGroup(void)
11608{
11609 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
11610 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
11611 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
11612 if (pfnGetter)
11613 {
11614 GROUP_AFFINITY GroupAffinity;
11615 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
11616 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
11617 return GroupAffinity.Group;
11618 }
11619 return 0;
11620}
11621
11622
11623/**
11624 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
11625 */
11626static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
11627{
11628 KSIZE cchRet = 0;
11629 HANDLE hToken;
11630 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
11631 {
11632 DWORD cbRet;
11633 TOKEN_STATISTICS TokenStats;
11634 memset(&TokenStats, 0, sizeof(TokenStats));
11635 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
11636 cchRet = sprintf(pszValue, "%" KX64_PRI,
11637 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
11638 else
11639 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
11640 CloseHandle(hToken);
11641 }
11642 else
11643 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
11644 return cchRet;
11645}
11646
11647
11648/**
11649 * Look for and expand the special environment variable.
11650 *
11651 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
11652 * couldn't accuratly determine. Currently the following variables are
11653 * implemented:
11654 * - "@@PROCESSOR_GROUP@@" - The processor group number.
11655 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
11656 * - "@@PID@@" - The kWorker process ID.
11657 * - "@@@@" - Escaped "@@".
11658 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
11659 */
11660static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
11661{
11662 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
11663 KU32 i = cEnvVars;
11664 while (i-- > 0)
11665 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
11666 && papszEnvVars[i][cchSpecialEnv] == '=')
11667 {
11668 /* We will expand stuff like @@NAME@@ */
11669 const char *pszValue = papszEnvVars[i];
11670 KSIZE offDst = 0;
11671 char szTmp[1024];
11672 for (;;)
11673 {
11674 const char *pszAt = kHlpStrChr(pszValue, '@');
11675 while (pszAt && pszAt[1] != '@')
11676 pszAt = kHlpStrChr(pszAt + 1, '@');
11677 if (pszAt)
11678 {
11679 KSIZE cchSrc = pszAt - pszValue;
11680 if (offDst + cchSrc < sizeof(szTmp))
11681 {
11682 char szSrc[64];
11683
11684 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11685 offDst += cchSrc;
11686 pszValue = pszAt + 2;
11687
11688 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11689 {
11690 pszValue += 15;
11691 if (g_iProcessGroup == -1)
11692 g_iProcessGroup = kwGetCurrentProcessorGroup();
11693 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11694 }
11695 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11696 {
11697 pszValue += 19;
11698 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11699 }
11700 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11701 {
11702 pszValue += 5;
11703 cchSrc = sprintf(szSrc, "%d", getpid());
11704 }
11705 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
11706 {
11707 pszValue += 2;
11708 szSrc[0] = '@';
11709 szSrc[1] = '@';
11710 szSrc[2] = '\0';
11711 cchSrc = 2;
11712 }
11713 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
11714 {
11715 static unsigned int s_iCounter = 0;
11716 pszValue += 15;
11717 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
11718 }
11719 else
11720 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
11721 pszValue - 2);
11722 if (offDst + cchSrc < sizeof(szTmp))
11723 {
11724 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
11725 offDst += cchSrc;
11726 continue;
11727 }
11728 }
11729 }
11730 else
11731 {
11732 KSIZE cchSrc = kHlpStrLen(pszValue);
11733 if (offDst + cchSrc < sizeof(szTmp))
11734 {
11735 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11736 offDst += cchSrc;
11737 break;
11738 }
11739 }
11740 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
11741 }
11742 szTmp[offDst] = '\0';
11743
11744 /* Return a copy of it: */
11745 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
11746 if (papszEnvVars[i])
11747 {
11748 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
11749 return 0;
11750 }
11751 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
11752 }
11753
11754 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
11755}
11756
11757
11758/**
11759 * Part 2 of the "JOB" command handler.
11760 *
11761 * @returns The exit code of the job.
11762 * @param pszExecutable The executable to execute.
11763 * @param pszCwd The current working directory of the job.
11764 * @param cArgs The number of arguments.
11765 * @param papszArgs The argument vector.
11766 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11767 * @param cEnvVars The number of environment variables.
11768 * @param papszEnvVars The environment vector.
11769 * @param pszSpecialEnv Name of special environment variable that
11770 * requires selective expansion here.
11771 * @param fNoPchCaching Whether to disable precompiled header file
11772 * caching. Avoid trouble when creating them.
11773 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11774 * @param papszPostCmdArgs The post command and its argument.
11775 */
11776static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
11777 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11778 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
11779 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11780{
11781 int rcExit;
11782 PKWTOOL pTool;
11783 char *pszSpecialEnvFree = NULL;
11784
11785 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
11786 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
11787#ifdef KW_LOG_ENABLED
11788 {
11789 KU32 i;
11790 for (i = 0; i < cArgs; i++)
11791 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
11792 for (i = 0; i < cPostCmdArgs; i++)
11793 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
11794 }
11795#endif
11796 g_cJobs++;
11797
11798 /*
11799 * Expand pszSpecialEnv if present.
11800 */
11801 if (*pszSpecialEnv)
11802 {
11803 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
11804 if (!rcExit)
11805 { /* likely */ }
11806 else
11807 return rcExit;
11808 }
11809
11810 /*
11811 * Lookup the tool.
11812 */
11813 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
11814 if (pTool)
11815 {
11816 /*
11817 * Change the directory if we're going to execute the job inside
11818 * this process. Then invoke the tool type specific handler.
11819 */
11820 switch (pTool->enmType)
11821 {
11822 case KWTOOLTYPE_SANDBOXED:
11823 case KWTOOLTYPE_WATCOM:
11824 {
11825 /* Change dir. */
11826 KFSLOOKUPERROR enmError;
11827 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
11828 if ( pNewCurDir == g_pCurDirObj
11829 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
11830 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11831 else if (SetCurrentDirectoryA(pszCwd))
11832 {
11833 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
11834 g_pCurDirObj = pNewCurDir;
11835 }
11836 else
11837 {
11838 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
11839 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11840 rcExit = 42 + 1;
11841 break;
11842 }
11843
11844 /* Call specific handler. */
11845 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
11846 {
11847 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
11848 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
11849 cEnvVars, papszEnvVars, fNoPchCaching);
11850 }
11851 else
11852 {
11853 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
11854 rcExit = 42 + 2;
11855 }
11856 break;
11857 }
11858
11859 case KWTOOLTYPE_EXEC:
11860 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
11861 rcExit = 42 + 2;
11862 break;
11863
11864 default:
11865 kHlpAssertFailed();
11866 kwErrPrintf("Internal tool type corruption!!\n");
11867 rcExit = 42 + 2;
11868 g_fRestart = K_TRUE;
11869 break;
11870 }
11871
11872 /*
11873 * Do the post command, if present.
11874 */
11875 if (cPostCmdArgs && rcExit == 0)
11876 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
11877 }
11878 else
11879 rcExit = 42 + 1;
11880 if (pszSpecialEnvFree)
11881 {
11882 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
11883 kHlpFree(pszSpecialEnvFree);
11884 }
11885 return rcExit;
11886}
11887
11888
11889/**
11890 * Handles a "JOB" command.
11891 *
11892 * @returns The exit code of the job.
11893 * @param pszMsg Points to the "JOB" command part of the message.
11894 * @param cbMsg Number of message bytes at @a pszMsg. There are
11895 * 4 more zero bytes after the message body to
11896 * simplify parsing.
11897 */
11898static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
11899{
11900 int rcExit = 42;
11901
11902 /*
11903 * Unpack the message.
11904 */
11905 const char *pszExecutable;
11906 KSIZE cbTmp;
11907
11908 pszMsg += sizeof("JOB");
11909 cbMsg -= sizeof("JOB");
11910
11911 /* Executable name. */
11912 pszExecutable = pszMsg;
11913 cbTmp = kHlpStrLen(pszMsg) + 1;
11914 pszMsg += cbTmp;
11915 if ( cbTmp < cbMsg
11916 && cbTmp > 2)
11917 {
11918 const char *pszCwd;
11919 cbMsg -= cbTmp;
11920
11921 /* Current working directory. */
11922 pszCwd = pszMsg;
11923 cbTmp = kHlpStrLen(pszMsg) + 1;
11924 pszMsg += cbTmp;
11925 if ( cbTmp + sizeof(KU32) < cbMsg
11926 && cbTmp >= 2)
11927 {
11928 KU32 cArgs;
11929 cbMsg -= cbTmp;
11930
11931 /* Argument count. */
11932 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
11933 pszMsg += sizeof(cArgs);
11934 cbMsg -= sizeof(cArgs);
11935
11936 if (cArgs > 0 && cArgs < 4096)
11937 {
11938 /* The argument vector. */
11939 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
11940 if (papszArgs)
11941 {
11942 KU32 i;
11943 for (i = 0; i < cArgs; i++)
11944 {
11945 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
11946 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
11947 pszMsg += cbTmp;
11948 if (cbTmp < cbMsg)
11949 cbMsg -= cbTmp;
11950 else
11951 {
11952 cbMsg = 0;
11953 break;
11954 }
11955
11956 }
11957 papszArgs[cArgs] = 0;
11958
11959 /* Environment variable count. */
11960 if (cbMsg > sizeof(KU32))
11961 {
11962 KU32 cEnvVars;
11963 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
11964 pszMsg += sizeof(cEnvVars);
11965 cbMsg -= sizeof(cEnvVars);
11966
11967 if (cEnvVars >= 0 && cEnvVars < 4096)
11968 {
11969 /* The argument vector. */
11970 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
11971 if (papszEnvVars)
11972 {
11973 for (i = 0; i < cEnvVars; i++)
11974 {
11975 papszEnvVars[i] = pszMsg;
11976 cbTmp = kHlpStrLen(pszMsg) + 1;
11977 pszMsg += cbTmp;
11978 if (cbTmp < cbMsg)
11979 cbMsg -= cbTmp;
11980 else
11981 {
11982 cbMsg = 0;
11983 break;
11984 }
11985 }
11986 papszEnvVars[cEnvVars] = 0;
11987
11988 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
11989 if (cbMsg >= sizeof(KU8) * 2)
11990 {
11991 KBOOL fWatcomBrainDamange = *pszMsg++;
11992 KBOOL fNoPchCaching = *pszMsg++;
11993 cbMsg -= 2;
11994
11995 /* Name of special enviornment variable requiring selective expansion. */
11996 if (cbMsg >= 1)
11997 {
11998 const char *pszSpecialEnv = pszMsg;
11999 cbTmp = kHlpStrLen(pszMsg);
12000 pszMsg += cbTmp + 1;
12001 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
12002
12003 /* Post command argument count (can be zero). */
12004 if (cbMsg >= sizeof(KU32))
12005 {
12006 KU32 cPostCmdArgs;
12007 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
12008 pszMsg += sizeof(cPostCmdArgs);
12009 cbMsg -= sizeof(cPostCmdArgs);
12010
12011 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
12012 {
12013 char const *apszPostCmdArgs[32+1];
12014 for (i = 0; i < cPostCmdArgs; i++)
12015 {
12016 apszPostCmdArgs[i] = pszMsg;
12017 cbTmp = kHlpStrLen(pszMsg) + 1;
12018 pszMsg += cbTmp;
12019 if ( cbTmp < cbMsg
12020 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
12021 cbMsg -= cbTmp;
12022 else
12023 {
12024 cbMsg = KSIZE_MAX;
12025 break;
12026 }
12027 }
12028 if (cbMsg == 0)
12029 {
12030 apszPostCmdArgs[cPostCmdArgs] = NULL;
12031
12032 /*
12033 * The next step.
12034 */
12035 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
12036 cArgs, papszArgs, fWatcomBrainDamange,
12037 cEnvVars, papszEnvVars, pszSpecialEnv,
12038 fNoPchCaching,
12039 cPostCmdArgs, apszPostCmdArgs);
12040 }
12041 else if (cbMsg == KSIZE_MAX)
12042 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
12043 else
12044 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
12045 }
12046 else
12047 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
12048 }
12049 else
12050 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
12051 }
12052 else
12053 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12054 }
12055 else
12056 kwErrPrintf("Detected bogus message unpacking flags!\n");
12057 kHlpFree((void *)papszEnvVars);
12058 }
12059 else
12060 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12061 }
12062 else
12063 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12064 }
12065 else
12066 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12067 kHlpFree((void *)papszArgs);
12068 }
12069 else
12070 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12071 }
12072 else
12073 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12074 }
12075 else
12076 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12077 }
12078 else
12079 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12080 return rcExit;
12081}
12082
12083
12084/**
12085 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12086 *
12087 * @retval 0 on success.
12088 * @retval -1 on error (fully bitched).
12089 *
12090 * @param hPipe The pipe handle.
12091 * @param pvBuf The buffer to write out out.
12092 * @param cbToWrite The number of bytes to write.
12093 */
12094static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12095{
12096 KU8 const *pbBuf = (KU8 const *)pvBuf;
12097 KU32 cbLeft = cbToWrite;
12098 while (g_rcCtrlC == 0)
12099 {
12100 DWORD cbActuallyWritten = 0;
12101 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12102 {
12103 cbLeft -= cbActuallyWritten;
12104 if (!cbLeft)
12105 return 0;
12106 pbBuf += cbActuallyWritten;
12107 }
12108 else
12109 {
12110 DWORD dwErr = GetLastError();
12111 if (cbLeft == cbToWrite)
12112 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12113 else
12114 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12115 return -1;
12116 }
12117 }
12118 return -1;
12119}
12120
12121
12122/**
12123 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12124 *
12125 * @retval 0 on success.
12126 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12127 * @retval -1 on error (fully bitched).
12128 * @param hPipe The pipe handle.
12129 * @param pvBuf The buffer to read into.
12130 * @param cbToRead The number of bytes to read.
12131 * @param fShutdownOkay Whether connection shutdown while reading the
12132 * first byte is okay or not.
12133 */
12134static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12135{
12136 KU8 *pbBuf = (KU8 *)pvBuf;
12137 KU32 cbLeft = cbToRead;
12138 while (g_rcCtrlC == 0)
12139 {
12140 DWORD cbActuallyRead = 0;
12141 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12142 {
12143 cbLeft -= cbActuallyRead;
12144 if (!cbLeft)
12145 return 0;
12146 pbBuf += cbActuallyRead;
12147 }
12148 else
12149 {
12150 DWORD dwErr = GetLastError();
12151 if (cbLeft == cbToRead)
12152 {
12153 if ( fMayShutdown
12154 && dwErr == ERROR_BROKEN_PIPE)
12155 return 1;
12156 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12157 }
12158 else
12159 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12160 return -1;
12161 }
12162 }
12163 return -1;
12164}
12165
12166
12167/**
12168 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12169 *
12170 * @returns pszBuf
12171 * @param pszBuf The buffer (sufficiently large).
12172 * @param uValue The value.
12173 */
12174static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12175{
12176 char szTmp[64];
12177 char *psz = &szTmp[63];
12178 int cch = 4;
12179
12180 *psz-- = '\0';
12181 do
12182 {
12183 if (--cch == 0)
12184 {
12185 *psz-- = ' ';
12186 cch = 3;
12187 }
12188 *psz-- = (uValue % 10) + '0';
12189 uValue /= 10;
12190 } while (uValue != 0);
12191
12192 return strcpy(pszBuf, psz + 1);
12193}
12194
12195
12196/**
12197 * Prints statistics.
12198 */
12199static void kwPrintStats(void)
12200{
12201 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12202 MEMORYSTATUSEX MemStatus;
12203 IO_COUNTERS IoCounters;
12204 DWORD cHandles;
12205 KSIZE cMisses;
12206 char szBuf[16*1024];
12207 int off = 0;
12208 char szPrf[24];
12209 char sz1[64];
12210 char sz2[64];
12211 char sz3[64];
12212 char sz4[64];
12213
12214 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12215
12216 szBuf[off++] = '\n';
12217
12218 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12219 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12220 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12221 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12222 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12223
12224 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12225 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12226
12227 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12228 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12229 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12230
12231 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12232 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12233 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12234
12235 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12236 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12237 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12238 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12239 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12240 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12241 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12242
12243 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12244 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12245 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12246 kwFmtU64(sz1, g_pFsCache->cObjects),
12247 kwFmtU64(sz2, g_pFsCache->cbObjects),
12248 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12249 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12250 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12251 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12252 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12253 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12254#ifdef KFSCACHE_CFG_UTF16
12255 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12256 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12257 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12258 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12259 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12260#endif
12261 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12262 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12263 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12264 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12265 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12266
12267 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12268 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12269 kwFmtU64(sz1, g_pFsCache->cLookups),
12270 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12271 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12272 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12273 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12274 kwFmtU64(sz4, cMisses),
12275 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12276
12277 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12278 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12279 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12280 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12281 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12282 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12283 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12284 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12285
12286
12287 /*
12288 * Process & Memory details.
12289 */
12290 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12291 cHandles = 0;
12292 MemInfo.cb = sizeof(MemInfo);
12293 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12294 memset(&MemInfo, 0, sizeof(MemInfo));
12295 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12296 kwFmtU64(sz1, cHandles),
12297 kwFmtU64(sz2, MemInfo.PageFaultCount),
12298 kwFmtU64(sz3, MemInfo.PagefileUsage),
12299 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12300 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12301 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12302 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12303 kwFmtU64(sz3, MemInfo.PrivateUsage));
12304 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12305 szPrf,
12306 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12307 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12308 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12309 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12310
12311 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12312 memset(&IoCounters, 0, sizeof(IoCounters));
12313 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12314 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12315 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12316 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12317 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12318 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12319 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12320 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12321 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12322
12323 MemStatus.dwLength = sizeof(MemStatus);
12324 if (!GlobalMemoryStatusEx(&MemStatus))
12325 memset(&MemStatus, 0, sizeof(MemStatus));
12326 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12327 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12328 MemStatus.ullAvailVirtual);
12329 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12330
12331 maybe_con_fwrite(szBuf, off, 1, stdout);
12332 fflush(stdout);
12333}
12334
12335
12336/**
12337 * Handles what comes after --test.
12338 *
12339 * @returns Exit code.
12340 * @param argc Number of arguments after --test.
12341 * @param argv Arguments after --test.
12342 */
12343static int kwTestRun(int argc, char **argv)
12344{
12345 int i;
12346 int j;
12347 int rcExit;
12348 int cRepeats;
12349 char szCwd[MAX_PATH];
12350 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12351 KU32 cEnvVars;
12352 char **papszEnvVars;
12353 const char *pszSpecialEnv = "";
12354 const char *pszSpecialEnvFull = NULL;
12355 KBOOL fWatcomBrainDamange = K_FALSE;
12356 KBOOL fNoPchCaching = K_FALSE;
12357
12358 /*
12359 * Parse arguments.
12360 */
12361 /* Repeat count. */
12362 i = 0;
12363 if (i >= argc)
12364 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12365 if (strcmp(argv[i], "--") != 0)
12366 {
12367 cRepeats = atoi(argv[i]);
12368 if (cRepeats <= 0)
12369 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12370 i++;
12371
12372 /* Optional directory change. */
12373 if ( i < argc
12374 && ( strcmp(argv[i], "--chdir") == 0
12375 || strcmp(argv[i], "-C") == 0 ) )
12376 {
12377 i++;
12378 if (i >= argc)
12379 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12380 pszCwd = argv[i++];
12381 }
12382
12383 /* Optional watcom flag directory change. */
12384 if ( i < argc
12385 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12386 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12387 {
12388 fWatcomBrainDamange = K_TRUE;
12389 i++;
12390 }
12391
12392 /* Optional watcom flag directory change. */
12393 if ( i < argc
12394 && strcmp(argv[i], "--no-pch-caching") == 0)
12395 {
12396 fNoPchCaching = K_TRUE;
12397 i++;
12398 }
12399
12400 /* Optional directory change. */
12401 if ( i < argc
12402 && ( strcmp(argv[i], "--set-special") == 0
12403 || strcmp(argv[i], "-s") == 0 ) )
12404 {
12405 i++;
12406 if (i >= argc)
12407 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12408 pszSpecialEnvFull = argv[i++];
12409 putenv(pszSpecialEnvFull);
12410 pszSpecialEnv = strdup(pszSpecialEnvFull);
12411 *strchr(pszSpecialEnv, '=') = '\0';
12412 }
12413
12414 /* Trigger breakpoint */
12415 if ( i < argc
12416 && strcmp(argv[i], "--breakpoint") == 0)
12417 {
12418 __debugbreak();
12419 i++;
12420 }
12421
12422 /* Check for '--'. */
12423 if (i >= argc)
12424 return kwErrPrintfRc(2, "Missing '--'\n");
12425 if (strcmp(argv[i], "--") != 0)
12426 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
12427 i++;
12428 }
12429 else
12430 {
12431 cRepeats = 1;
12432 i++;
12433 }
12434 if (i >= argc)
12435 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
12436
12437 /*
12438 * Duplicate the environment.
12439 */
12440 cEnvVars = 0;
12441 while (environ[cEnvVars] != NULL)
12442 cEnvVars++;
12443 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
12444
12445 /*
12446 * Do the job.
12447 */
12448 rcExit = 0;
12449 for (j = 0; j < cRepeats; j++)
12450 {
12451 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
12452 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
12453 argc - i, &argv[i], fWatcomBrainDamange,
12454 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
12455 0, NULL);
12456 KW_LOG(("rcExit=%d\n", rcExit));
12457 kwSandboxCleanupLate(&g_Sandbox);
12458 }
12459
12460 if (getenv("KWORKER_STATS") != NULL)
12461 kwPrintStats();
12462
12463# ifdef WITH_LOG_FILE
12464 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12465 CloseHandle(g_hLogFile);
12466# endif
12467 return rcExit;
12468}
12469
12470
12471/**
12472 * Reads @a pszFile into memory and chops it up into an argument vector.
12473 *
12474 * @returns Pointer to the argument vector on success, NULL on failure.
12475 * @param pszFile The file to load.
12476 * @param pcArgs Where to return the number of arguments.
12477 * @param ppszFileContent Where to return the allocation.
12478 */
12479static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
12480{
12481 char **papszArgs = NULL;
12482 FILE *pFile = fopen(pszFile, "r");
12483 if (pFile)
12484 {
12485 long cbFile;
12486 if ( fseek(pFile, 0, SEEK_END) == 0
12487 && (cbFile = ftell(pFile)) >= 0
12488 && fseek(pFile, 0, SEEK_SET) == 0)
12489 {
12490 char *pszFile = kHlpAllocZ(cbFile + 3);
12491 if (pszFile)
12492 {
12493 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
12494 if ( feof(pFile)
12495 && !ferror(pFile))
12496 {
12497 size_t off = 0;
12498 int cArgs = 0;
12499 int cAllocated = 0;
12500 char ch;
12501
12502 pszFile[cbRead] = '\0';
12503 pszFile[cbRead + 1] = '\0';
12504 pszFile[cbRead + 2] = '\0';
12505
12506 while ((ch = pszFile[off]) != '\0')
12507 {
12508 char *pszArg;
12509 switch (ch)
12510 {
12511 case ' ':
12512 case '\t':
12513 case '\n':
12514 case '\r':
12515 off++;
12516 continue;
12517
12518 case '\\':
12519 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
12520 {
12521 off += 2;
12522 continue;
12523 }
12524 /* fall thru */
12525 default:
12526 pszArg = &pszFile[off];
12527 do
12528 ch = pszFile[++off];
12529 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
12530 pszFile[off++] = '\0';
12531 break;
12532
12533 case '\'':
12534 pszArg = &pszFile[++off];
12535 while ((ch = pszFile[off]) != '\0' && ch != '\'')
12536 off++;
12537 pszFile[off++] = '\0';
12538 break;
12539
12540 case '\"': /** @todo escape sequences */
12541 pszArg = &pszFile[++off];
12542 while ((ch = pszFile[off]) != '\0' && ch != '"')
12543 off++;
12544 pszFile[off++] = '\0';
12545 break;
12546 }
12547 if (cArgs + 1 >= cAllocated)
12548 {
12549 void *pvNew;
12550 cAllocated = cAllocated ? cAllocated * 2 : 16;
12551 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
12552 if (pvNew)
12553 papszArgs = (char **)pvNew;
12554 else
12555 {
12556 kHlpFree(papszArgs);
12557 papszArgs = NULL;
12558 break;
12559 }
12560 }
12561 papszArgs[cArgs] = pszArg;
12562 papszArgs[++cArgs] = NULL;
12563 }
12564 *pcArgs = cArgs;
12565 }
12566 else
12567 kwErrPrintf("Error reading '%s'!\n", pszFile);
12568 }
12569 else
12570 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
12571 }
12572 else
12573 kwErrPrintf("Error seeking '%s'!\n", pszFile);
12574 fclose(pFile);
12575 }
12576 else
12577 kwErrPrintf("Error opening '%s'!\n", pszFile);
12578 return papszArgs;
12579}
12580
12581/**
12582 * Appends a string to an string vector (arguments or enviornment).
12583 *
12584 * @returns 0 on success, non-zero on failure (exit code).
12585 * @param ppapszVector Pointer to the string pointer array.
12586 * @param pcEntries Pointer to the array size.
12587 * @param pszAppend The string to append.
12588 */
12589static int kwFullTestVectorAppend(const char ***ppapszVector, KU32 *pcEntries, char const *pszAppend)
12590{
12591 KU32 cEntries = *pcEntries;
12592 if (!(cEntries & 15))
12593 {
12594 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
12595 if (pvNew)
12596 *ppapszVector = (const char **)pvNew;
12597 else
12598 return kwErrPrintfRc(2, "Out of memory!\n");
12599 }
12600 (*ppapszVector)[cEntries] = pszAppend;
12601 (*ppapszVector)[++cEntries] = NULL;
12602 *pcEntries = cEntries;
12603 return 0;
12604}
12605
12606
12607/**
12608 * Parses arguments for --full-test.
12609 *
12610 * @returns 0 on success, non-zero on failure (exit code).
12611 */
12612static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
12613 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
12614{
12615 PKWONETEST pCur = *ppHead;
12616 int i;
12617 for (i = 0; i < argc; i++)
12618 {
12619 int rc = 0;
12620 const char *pszArg = argv[i];
12621 if (*pszArg == 'k' && kHlpStrComp(pszArg, "kSubmit") == 0)
12622 {
12623 if (*piState != 0)
12624 {
12625 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
12626 if (!pCur)
12627 return kwErrPrintfRc(2, "Out of memory!\n");
12628 pCur->fVirgin = K_TRUE;
12629 pCur->pszCwd = pszDefaultCwd;
12630 pCur->cRuns = 1;
12631 pCur->pNext = *ppHead;
12632 *ppHead = pCur;
12633 *piState = 0;
12634 }
12635 else if (!pCur->fVirgin)
12636 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
12637 pCur->pszJobSrc = pszJobSrc;
12638 pCur->iJobSrc = i;
12639 continue; /* (to stay virgin) */
12640 }
12641 else if (*pszArg == '-' && *piState == 0)
12642 {
12643 const char *pszValue = NULL;
12644 char ch = *++pszArg;
12645 pszArg++;
12646 if (ch == '-')
12647 {
12648 ch = '\0';
12649 if (*pszArg == '\0') /* -- */
12650 *piState = 2;
12651 /* Translate or handle long options: */
12652 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
12653 ch = 'E';
12654 else if (kHlpStrComp(pszArg, "special-env") == 0)
12655 ch = 's';
12656 else if (kHlpStrComp(pszArg, "default-env") == 0)
12657 {
12658 unsigned i;
12659 pCur->cEnvVars = 0;
12660 for (i = 0; environ[i] && rc == 0; i++)
12661 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
12662 }
12663 else if (kHlpStrComp(pszArg, "chdir") == 0)
12664 ch = 'C';
12665 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
12666 ch = 'P';
12667 else if (kHlpStrComp(pszArg, "response-file") == 0)
12668 ch = '@';
12669 else if (kHlpStrComp(pszArg, "runs") == 0)
12670 ch = 'R';
12671 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
12672 pCur->fWatcomBrainDamange = K_TRUE;
12673 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
12674 pCur->fNoPchCaching = K_TRUE;
12675 else if (kHlpStrComp(pszArg, "executable") == 0)
12676 ch = 'e';
12677 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12678 {
12679 __debugbreak();
12680 continue; /* (to stay virgin) */
12681 }
12682 else
12683 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12684 pszArg = "";
12685 }
12686
12687 while (ch != '\0' && rc == 0)
12688 {
12689 /* Fetch value if needed: */
12690 switch (ch)
12691 {
12692 case '@':
12693 case 'e':
12694 case 'E':
12695 case 's':
12696 case 'C':
12697 case 'R':
12698 if (*pszArg == ':' || *pszArg == '=')
12699 pszValue = &pszArg[1];
12700 else if (*pszArg)
12701 pszValue = pszArg;
12702 else if (i + 1 < argc)
12703 pszValue = argv[++i];
12704 else
12705 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
12706 pszArg = "";
12707 break;
12708 }
12709
12710 /* Handle the option: */
12711 switch (ch)
12712 {
12713 case 'E':
12714 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
12715 break;
12716 case 'C':
12717 pCur->pszCwd = pszValue;
12718 break;
12719 case 's':
12720 pCur->pszSpecialEnv = pszValue;
12721 break;
12722 case 'e':
12723 pCur->pszExecutable = pszValue;
12724 break;
12725 case 'P':
12726 *piState = 1;
12727 if (*pszArg)
12728 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
12729 break;
12730 case 'R':
12731 pCur->cRuns = atoi(pszValue);
12732 if ((int)pCur->cRuns < 0)
12733 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
12734 break;
12735 case '@':
12736 if (cRecursions < 5)
12737 {
12738 char *pszLeaked = NULL;
12739 int cArgs = 0;
12740 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
12741 if (papszArgsLeaked)
12742 {
12743 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
12744 cRecursions + 1, pszValue);
12745 pCur = *ppHead;
12746 }
12747 else
12748 return 2;
12749 }
12750 else
12751 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
12752 break;
12753 }
12754
12755 /* next */
12756 ch = *pszArg++;
12757 }
12758 }
12759 else if (*piState == 2)
12760 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
12761 else if (*piState == 1)
12762 {
12763 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
12764 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
12765 else
12766 *piState = 2;
12767 }
12768 else
12769 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
12770 if (rc)
12771 return rc;
12772 pCur->fVirgin = K_FALSE;
12773 }
12774 return 0;
12775}
12776
12777
12778/**
12779 * Handles what comes after --full-test.
12780 *
12781 * @returns Exit code.
12782 * @param argc Number of arguments after --full-test.
12783 * @param argv Arguments after --full-test.
12784 */
12785static int kwFullTestRun(int argc, char **argv)
12786{
12787 char szDefaultCwd[MAX_PATH];
12788 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
12789 KWONETEST FirstTest;
12790 PKWONETEST pHead = &FirstTest;
12791 PKWONETEST pCur;
12792 int iState = 0;
12793 int rcExit;
12794
12795 /*
12796 * Parse arguments.
12797 */
12798 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
12799 FirstTest.pszJobSrc = "command-line";
12800 FirstTest.iJobSrc = 1;
12801 FirstTest.fVirgin = K_TRUE;
12802 FirstTest.pszCwd = pszDefaultCwd;
12803 FirstTest.cRuns = 1;
12804
12805 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
12806 if (rcExit)
12807 return rcExit;
12808
12809 /*
12810 * Do the job. LIFO ordering (see kSubmit).
12811 */
12812 for (pCur = pHead; pCur; pCur = pCur->pNext)
12813 {
12814 if (!pCur->pszExecutable && pCur->papszArgs)
12815 pCur->pszExecutable = pCur->papszArgs[0];
12816 if ( pCur->pszExecutable
12817 && pCur->cArgs > 0
12818 && pCur->cEnvVars > 0)
12819 {
12820 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
12821 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
12822 unsigned iRun;
12823
12824 for (iRun = 0; iRun < pCur->cRuns; iRun++)
12825 {
12826 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
12827 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
12828 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
12829 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
12830
12831 KW_LOG(("rcExit=%d\n", rcExit));
12832 kwSandboxCleanupLate(&g_Sandbox);
12833
12834 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
12835 }
12836 kHlpFree(papszEnvVarsCopy);
12837 }
12838 else
12839 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
12840 pCur->pszExecutable ? "" : " No executable!",
12841 pCur->cArgs < 1 ? " No arguments!" : "",
12842 pCur->cEnvVars < 1 ? " No environment!" : "",
12843 pCur->iJobSrc, pCur->pszJobSrc);
12844 }
12845
12846 if (getenv("KWORKER_STATS") != NULL)
12847 kwPrintStats();
12848
12849# ifdef WITH_LOG_FILE
12850 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12851 CloseHandle(g_hLogFile);
12852# endif
12853 return rcExit;
12854}
12855
12856
12857/**
12858 * Helper for main() argument handling that sets the processor group if
12859 * possible.
12860 */
12861static void kwSetProcessorGroup(unsigned long uGroup)
12862{
12863 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
12864 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
12865 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
12866
12867 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
12868 if (pfnSetThreadGroupAffinity)
12869 {
12870 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
12871 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
12872 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
12873 if (NewAff.Mask && (WORD)uGroup == uGroup)
12874 {
12875 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
12876 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
12877 }
12878 else if (GetLastError() == NO_ERROR)
12879 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
12880 else
12881 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
12882 uGroup, GetLastError());
12883 }
12884 else
12885 {
12886 OSVERSIONINFOA VerInfo = {0};
12887 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
12888 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
12889 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
12890 }
12891}
12892
12893
12894int main(int argc, char **argv)
12895{
12896 KSIZE cbMsgBuf = 0;
12897 KU8 *pbMsgBuf = NULL;
12898 int i;
12899 HANDLE hPipe = INVALID_HANDLE_VALUE;
12900 const char *pszTmp;
12901 KFSLOOKUPERROR enmIgnored;
12902 DWORD dwType;
12903#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
12904 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
12905 kwSandboxVecXcptEmulateChained);
12906#endif
12907#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12908 HANDLE hCurProc = GetCurrentProcess();
12909 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12910 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12911#endif
12912
12913
12914#ifdef WITH_FIXED_VIRTUAL_ALLOCS
12915 /*
12916 * Reserve memory for cl.exe
12917 */
12918 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
12919 {
12920 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
12921 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
12922 g_aFixedVirtualAllocs[i].cbFixed,
12923 MEM_RESERVE, PAGE_READWRITE);
12924 if ( !g_aFixedVirtualAllocs[i].pvReserved
12925 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
12926 {
12927 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
12928 GetLastError());
12929 if (g_aFixedVirtualAllocs[i].pvReserved)
12930 {
12931 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
12932 g_aFixedVirtualAllocs[i].pvReserved = NULL;
12933 }
12934 }
12935 }
12936#endif
12937
12938 /*
12939 * Register our Control-C and Control-Break handlers.
12940 */
12941 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
12942 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
12943
12944 /*
12945 * Create the cache and mark the temporary directory as using the custom revision.
12946 */
12947 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
12948 if (!g_pFsCache)
12949 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
12950
12951 pszTmp = getenv("TEMP");
12952 if (pszTmp && *pszTmp != '\0')
12953 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12954 pszTmp = getenv("TMP");
12955 if (pszTmp && *pszTmp != '\0')
12956 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12957 pszTmp = getenv("TMPDIR");
12958 if (pszTmp && *pszTmp != '\0')
12959 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12960
12961 /*
12962 * Make g_abDefLdBuf executable.
12963 */
12964 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
12965 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
12966 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
12967 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
12968
12969#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12970 /*
12971 * Get and duplicate the console handles.
12972 */
12973 /* Standard output. */
12974 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
12975 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
12976 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12977 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
12978 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
12979 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
12980 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12981 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12982 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
12983 g_Sandbox.HandleStdOut.cRefs = 0x10001;
12984 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
12985 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
12986 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
12987 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
12988 {
12989 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
12990 g_Sandbox.cFixedHandles++;
12991 else
12992 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
12993 }
12994 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12995 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
12996
12997 /* Standard error. */
12998 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
12999 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
13000 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13001 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
13002 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
13003 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
13004 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13005 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13006 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
13007 g_Sandbox.HandleStdErr.cRefs = 0x10001;
13008 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
13009 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
13010 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
13011 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
13012 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
13013 {
13014 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
13015 g_Sandbox.cFixedHandles++;
13016 else
13017 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
13018 }
13019 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13020 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
13021
13022 /* Combined console buffer. */
13023 if (g_Sandbox.StdErr.fIsConsole)
13024 {
13025 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
13026 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13027 }
13028 else if (g_Sandbox.StdOut.fIsConsole)
13029 {
13030 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
13031 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13032 }
13033 else
13034 {
13035 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
13036 g_Sandbox.Combined.uCodepage = CP_ACP;
13037 }
13038 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
13039#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
13040
13041
13042 /*
13043 * Parse arguments.
13044 */
13045 for (i = 1; i < argc; i++)
13046 {
13047 if (strcmp(argv[i], "--pipe") == 0)
13048 {
13049 i++;
13050 if (i < argc)
13051 {
13052 char *pszEnd = NULL;
13053 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13054 if ( *argv[i]
13055 && pszEnd != NULL
13056 && *pszEnd == '\0'
13057 && u64Value != 0
13058 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13059 && (uintptr_t)u64Value == u64Value)
13060 hPipe = (HANDLE)(uintptr_t)u64Value;
13061 else
13062 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13063 }
13064 else
13065 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13066 }
13067 else if (strcmp(argv[i], "--volatile") == 0)
13068 {
13069 i++;
13070 if (i < argc)
13071 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13072 else
13073 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13074 }
13075 else if (strcmp(argv[i], "--test") == 0)
13076 return kwTestRun(argc - i - 1, &argv[i + 1]);
13077 else if (strcmp(argv[i], "--full-test") == 0)
13078 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13079 else if (strcmp(argv[i], "--priority") == 0)
13080 {
13081 i++;
13082 if (i < argc)
13083 {
13084 char *pszEnd = NULL;
13085 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13086 if ( *argv[i]
13087 && pszEnd != NULL
13088 && *pszEnd == '\0'
13089 && uValue >= 1
13090 && uValue <= 5)
13091 {
13092 DWORD dwClass;
13093 int dwPriority;
13094 switch (uValue)
13095 {
13096 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13097 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13098 default:
13099 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13100 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13101 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13102 }
13103 SetPriorityClass(GetCurrentProcess(), dwClass);
13104 if (dwPriority != INT_MAX)
13105 SetThreadPriority(GetCurrentThread(), dwPriority);
13106 }
13107 else
13108 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13109 }
13110 else
13111 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13112 }
13113 else if (strcmp(argv[i], "--group") == 0)
13114 {
13115 i++;
13116 if (i < argc)
13117 {
13118 char *pszEnd = NULL;
13119 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13120 if ( *argv[i]
13121 && pszEnd != NULL
13122 && *pszEnd == '\0'
13123 && uValue == (WORD)uValue)
13124 kwSetProcessorGroup(uValue);
13125 else
13126 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13127 }
13128 else
13129 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13130 }
13131 else if ( strcmp(argv[i], "--help") == 0
13132 || strcmp(argv[i], "-h") == 0
13133 || strcmp(argv[i], "-?") == 0)
13134 {
13135 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13136 "usage: kWorker <--help|-h>\n"
13137 "usage: kWorker <--version|-V>\n"
13138 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13139 "\n"
13140 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13141 return 0;
13142 }
13143 else if ( strcmp(argv[i], "--version") == 0
13144 || strcmp(argv[i], "-V") == 0)
13145 return kbuild_version(argv[0]);
13146 else
13147 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13148 }
13149
13150 /*
13151 * If no --pipe argument, then assume its standard input.
13152 * We need to carefully replace the CRT stdin with a handle to "nul".
13153 */
13154 if (hPipe == INVALID_HANDLE_VALUE)
13155 {
13156 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13157 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13158 {
13159 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13160 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13161 {
13162 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13163 if (fdNul >= 0)
13164 {
13165 if (_dup2(fdNul, 0) >= 0)
13166 {
13167 close(fdNul);
13168 hPipe = hDuplicate;
13169 }
13170 else
13171 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13172 }
13173 else
13174 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13175 }
13176 else
13177 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13178 }
13179 else
13180 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13181 GetFileType(hPipe), GetLastError());
13182 }
13183 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13184 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13185 GetFileType(hPipe), GetLastError());
13186 g_hPipe = hPipe;
13187
13188 /*
13189 * Serve the pipe.
13190 */
13191 for (;;)
13192 {
13193 KU32 cbMsg = 0;
13194 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13195 if (rc == 0)
13196 {
13197 /* Make sure the message length is within sane bounds. */
13198 if ( cbMsg > 4
13199 && cbMsg <= 256*1024*1024)
13200 {
13201 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13202 if (cbMsg + 4 <= cbMsgBuf)
13203 { /* likely */ }
13204 else
13205 {
13206 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13207 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13208 if (!pbMsgBuf)
13209 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13210 }
13211
13212 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13213 *(KU32 *)pbMsgBuf = cbMsg;
13214 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13215 if (rc == 0)
13216 {
13217 const char *psz;
13218
13219 pbMsgBuf[cbMsg] = '\0';
13220 pbMsgBuf[cbMsg + 1] = '\0';
13221 pbMsgBuf[cbMsg + 2] = '\0';
13222 pbMsgBuf[cbMsg + 3] = '\0';
13223
13224 /* The first string after the header is the command. */
13225 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13226 if ( strcmp(psz, "JOB") == 0
13227 && g_rcCtrlC == 0)
13228 {
13229 struct
13230 {
13231 KI32 rcExitCode;
13232 KU8 bExiting;
13233 KU8 abZero[3];
13234 } Reply;
13235 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13236 Reply.bExiting = g_fRestart;
13237 Reply.abZero[0] = 0;
13238 Reply.abZero[1] = 0;
13239 Reply.abZero[2] = 0;
13240 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13241 if ( rc == 0
13242 && !g_fRestart)
13243 {
13244 kwSandboxCleanupLate(&g_Sandbox);
13245 if (g_rcCtrlC == 0)
13246 continue;
13247 }
13248 }
13249 else
13250 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13251 }
13252 }
13253 else
13254 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13255 }
13256
13257 /*
13258 * If we're exitting because we're restarting, we need to delay till
13259 * kmk/kSubmit has read the result. Windows documentation says it
13260 * immediately discards pipe buffers once the pipe is broken by the
13261 * server (us). So, We flush the buffer and queues a 1 byte read
13262 * waiting for kSubmit to close the pipe when it receives the
13263 * bExiting = K_TRUE result.
13264 */
13265 if (g_fRestart)
13266 {
13267 DWORD cbIgnored = 1;
13268 KU8 b;
13269 FlushFileBuffers(hPipe);
13270 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13271 }
13272
13273 CloseHandle(hPipe);
13274#ifdef WITH_LOG_FILE
13275 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13276 CloseHandle(g_hLogFile);
13277#endif
13278 if (getenv("KWORKER_STATS") != NULL)
13279 kwPrintStats();
13280 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13281 }
13282}
13283
13284
13285/** @page pg_kWorker kSubmit / kWorker
13286 *
13287 * @section sec_kWorker_Motivation Motivation / Inspiration
13288 *
13289 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13290 * builds on machines "infested" by Anti Virus protection and disk encryption
13291 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13292 * got "infected".
13293 *
13294 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13295 * mainly a bunch of tiny assembly and C files being compiler a million times.
13296 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13297 * own toolchain from within the same process, saving a lot of process creation
13298 * and teardown overhead.
13299 *
13300 *
13301 * @section sec_kWorker_kSubmit About kSubmit
13302 *
13303 * When wanting to execute a job in a kWorker instance, it must be submitted
13304 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13305 * built into kmk and does not exist as an external program. The reason for
13306 * this is that it keep track of the kWorker instances.
13307 *
13308 * The kSubmit command has the --32-bit and --64-bit options for selecting
13309 * between 32-bit and 64-bit worker instance. We generally assume the user of
13310 * the command knows which bit count the executable has, so kSubmit is spared
13311 * the extra work of finding out.
13312 *
13313 * The kSubmit command shares a environment and current directory manipulation
13314 * with the kRedirect command, but not the file redirection. So long no file
13315 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13316 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13317 * and/or current directory changes to work.
13318 *
13319 * Unlike the kRedirect command, the kSubmit command can also specify an
13320 * internall post command to be executed after the main command succeeds.
13321 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13322 * information from Microsoft COFF object files and Watcom OMF object files and
13323 * is scheduled to replace kDepIDB.
13324 *
13325 *
13326 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13327 *
13328 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13329 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13330 * it. When the job is completed, kWorker writes a short reply with the exit
13331 * code and an internal status indicating whether it is going to restart.
13332 *
13333 * The kWorker intance will reply to kSubmit before completing all the internal
13334 * cleanup work, so as not to delay the next job execution unnecessarily. This
13335 * includes checking its own memory consumption and checking whether it needs
13336 * restarting. So, a decision to restart unfortunately have to wait till after
13337 * the next job has completed. This is a little bit unfortunate if the next job
13338 * requires a lot of memory and kWorker has already leaked/used a lot.
13339 *
13340 *
13341 * @section sec_kWorker_How_Works How kWorker Works
13342 *
13343 * kWorker will load the executable specified by kSubmit into memory and call
13344 * it's entrypoint in a lightly sandbox'ed environment.
13345 *
13346 *
13347 * @subsection ssec_kWorker_Loaing Image loading
13348 *
13349 * kWorker will manually load all the executable images into memory, fix them
13350 * up, and make a copy of the virgin image so it can be restored using memcpy
13351 * the next time it is used.
13352 *
13353 * Imported functions are monitored and replacements used for a few of them.
13354 * These replacements are serve the following purposes:
13355 * - Provide a different command line.
13356 * - Provide a different environment.
13357 * - Intercept process termination.
13358 * - Intercept thread creation (only linker is allowed to create threads).
13359 * - Intercept file reading for caching (header files, ++) as file system
13360 * access is made even slower by anti-virus software.
13361 * - Intercept crypto hash APIs to cache MD5 digests of header files
13362 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13363 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13364 * in memory as writing files grows expensive with encryption and
13365 * anti-virus software active.
13366 * - Intercept some file system queries to use the kFsCache instead of
13367 * going to the kernel and slowly worm thru the AV filter driver.
13368 * - Intercept standard output/error and console writes to aggressivly
13369 * buffer the output. The MS CRT does not buffer either when it goes to
13370 * the console, resulting in terrible performance and mixing up output
13371 * with other compile jobs.
13372 * This also allows us to filter out the annoying source file announcements
13373 * by cl.exe.
13374 * - Intercept VirtualAlloc and VirtualFree to prevent
13375 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13376 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13377 * the callbacks run after each job.
13378 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13379 * executables and tools using custom heaps (like the microsoft linker).
13380 * [exectuable images only]
13381 * - Intercept atexit and _onexit registration to be able run them after
13382 * each job instead of crashing as kWorker exits. This also helps avoid
13383 * some leaks. [executable image only]
13384 *
13385 * DLLs falls into two categories, system DLLs which we always load using the
13386 * native loader, and tool DLLs which can be handled like the executable or
13387 * optionally using the native loader. We maintain a hardcoded white listing of
13388 * tool DLLs we trust to load using the native loader.
13389 *
13390 * Imports of natively loaded DLLs are processed too, but we only replace a
13391 * subset of the functions compared to natively loaded excutable and DLL images.
13392 *
13393 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13394 * This is to speed up job execution.
13395 *
13396 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13397 * for each job run, but so far this hasn't been necessary.
13398 *
13399 *
13400 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
13401 *
13402 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
13403 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
13404 * intermediate representation between the first (c1/c1xx.dll) and second pass
13405 * (c2.dll).
13406 *
13407 * kWorker helps the compiler as best as it can. Given a little knowledge about
13408 * stable and volatile file system areas, it can do a lot of caching that a
13409 * normal compiler driver cannot easily do when given a single file.
13410 *
13411 *
13412 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
13413 *
13414 * The preprocessor part will open and process header files exactly as they are
13415 * encountered in the source files. If string.h is included by the main source
13416 * and five other header files, it will be searched for (include path), opened,
13417 * read, MD5-summed, and pre-processed six times. The last five times is just a
13418 * waste of time because of the guards or \#pragma once. A smart compiler would
13419 * make a little extra effort and realize this.
13420 *
13421 * kWorker will cache help the preprocessor by remembering places where the
13422 * header was not found with help of kFsCache, and cache the file in memory when
13423 * found. The first part is taken care of by intercepting GetFileAttributesW,
13424 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
13425 * cached, the file is kept open and the CreateFileW call returns a duplicate of
13426 * that handle. An internal handle table is used by ReadFile and CloseFile to
13427 * keep track of intercepted handles (also used for temporary file, temporary
13428 * file mappings, console buffering, and standard out/err buffering).
13429 *
13430 * PS. The header search optimization also comes in handy when cl.exe goes on
13431 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
13432 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
13433 * optionally compile the three pass DLLs as executables during development
13434 * and problem analysis.
13435 *
13436 *
13437 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
13438 *
13439 * The issues of the temporary files is pretty severe on the Dell machine used
13440 * for benchmarking with full AV and encryption. The synthetic benchmark
13441 * improved by 30% when kWorker implemented measures to keep them entirely in
13442 * memory.
13443 *
13444 * kWorker implement these by recognizing the filename pattern in CreateFileW
13445 * and creating/opening the given file as needed. The handle returned is a
13446 * duplicate of the current process, thus giving us a good chance of catching
13447 * API calls we're not intercepting.
13448 *
13449 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
13450 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
13451 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
13452 * UnmapViewOfFile.
13453 *
13454 *
13455 * @section sec_kWorker_Numbers Some measurements.
13456 *
13457 * - r2881 building src/VBox/Runtime:
13458 * - without: 2m01.016388s = 120.016388 s
13459 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
13460 * - r2884 building vbox/debug (r110512):
13461 * - without: 11m14.446609s = 674.446609 s
13462 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
13463 * - r2896 building vbox/debug (r110577):
13464 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
13465 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
13466 * MS Defender as AV):
13467 * - without: 10m24.990389s = 624.990389s
13468 * - with: 08m04.738184s = 484.738184s
13469 * - delta: 624.99s - 484.74s = 140.25s
13470 * - saved: 140.25/624.99 = 22% faster
13471 *
13472 *
13473 * @subsection subsec_kWorker_Early_Numbers Early Experiments
13474 *
13475 * These are some early experiments doing 1024 compilations of
13476 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
13477 * main function:
13478 *
13479 * Skylake (W10/amd64, only stdandard MS defender):
13480 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
13481 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
13482 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
13483 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
13484 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
13485 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
13486 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
13487 *
13488 * Dell (W7/amd64, infected by mcafee):
13489 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
13490 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
13491 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
13492 *
13493 * The command line:
13494 * @code{.cpp}
13495 "\"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"
13496 * @endcode
13497 */
13498
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