VirtualBox

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

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

kWorker: Generalized the hash caching to support SHA-1, SHA-256 and SHA-512 in addition to MD5. We need SHA-256 for VC++ 14.x.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 531.5 KB
Line 
1/* $Id: kWorker.c 3374 2020-06-10 18:01:15Z 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_CACHE
73 * Enables caching of MD5, SHA-1, SHA-256 and SHA-512 hashes for cl.exe.
74 * This prevents wasting time on rehashing common headers each time
75 * they are included. */
76#define WITH_HASH_CACHE
77
78/** @def WITH_CRYPT_CTX_REUSE
79 * Enables reusing crypt contexts. The Visual C++ compiler always creates a
80 * context which is only used for MD5 and maybe some random bytes (VS 2010).
81 * So, only create it once and add a reference to it instead of creating new
82 * ones. Saves registry access among other things. */
83#define WITH_CRYPT_CTX_REUSE
84
85/** @def WITH_CONSOLE_OUTPUT_BUFFERING
86 * Enables buffering of all console output as well as removal of annoying
87 * source file echo by cl.exe. */
88#define WITH_CONSOLE_OUTPUT_BUFFERING
89
90/** @def WITH_STD_OUT_ERR_BUFFERING
91 * Enables buffering of standard output and standard error buffer as well as
92 * removal of annoying source file echo by cl.exe. */
93#define WITH_STD_OUT_ERR_BUFFERING
94
95/** @def WITH_LOG_FILE
96 * Log to file instead of stderr. */
97#define WITH_LOG_FILE
98
99/** @def WITH_HISTORY
100 * Keep history of the last jobs. For debugging. */
101#define WITH_HISTORY
102
103/** @def WITH_FIXED_VIRTUAL_ALLOCS
104 * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
105 * there is only one, but an important one, from cl.exe).
106 */
107#if K_ARCH == K_ARCH_X86_32
108# define WITH_FIXED_VIRTUAL_ALLOCS
109#endif
110
111/** @def WITH_PCH_CACHING
112 * Enables read caching of precompiled header files. */
113#if K_ARCH_BITS >= 64
114# define WITH_PCH_CACHING
115#endif
116
117
118#ifndef NDEBUG
119# define KW_LOG_ENABLED
120#endif
121
122/** @def KW_LOG
123 * Generic logging.
124 * @param a Argument list for kwDbgPrintf */
125#ifdef KW_LOG_ENABLED
126# define KW_LOG(a) kwDbgPrintf a
127#else
128# define KW_LOG(a) do { } while (0)
129#endif
130
131/** @def KWLDR_LOG
132 * Loader related logging.
133 * @param a Argument list for kwDbgPrintf */
134#ifdef KW_LOG_ENABLED
135# define KWLDR_LOG(a) kwDbgPrintf a
136#else
137# define KWLDR_LOG(a) do { } while (0)
138#endif
139
140
141/** @def KWFS_LOG
142 * FS cache logging.
143 * @param a Argument list for kwDbgPrintf */
144#ifdef KW_LOG_ENABLED
145# define KWFS_LOG(a) kwDbgPrintf a
146#else
147# define KWFS_LOG(a) do { } while (0)
148#endif
149
150/** @def KWOUT_LOG
151 * Output related logging.
152 * @param a Argument list for kwDbgPrintf */
153#ifdef KW_LOG_ENABLED
154# define KWOUT_LOG(a) kwDbgPrintf a
155#else
156# define KWOUT_LOG(a) do { } while (0)
157#endif
158
159/** @def KWCRYPT_LOG
160 * FS cache logging.
161 * @param a Argument list for kwDbgPrintf */
162#ifdef KW_LOG_ENABLED
163# define KWCRYPT_LOG(a) kwDbgPrintf a
164#else
165# define KWCRYPT_LOG(a) do { } while (0)
166#endif
167
168/** Converts a windows handle to a handle table index.
169 * @note We currently just mask off the 31th bit, and do no shifting or anything
170 * else to create an index of the handle.
171 * @todo consider shifting by 2 or 3. */
172#define KW_HANDLE_TO_INDEX(a_hHandle) ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
173/** Maximum handle value we can deal with. */
174#define KW_HANDLE_MAX 0x20000
175
176/** Max temporary file size (memory backed). */
177#if K_ARCH_BITS >= 64
178# define KWFS_TEMP_FILE_MAX (256*1024*1024)
179#else
180# define KWFS_TEMP_FILE_MAX (64*1024*1024)
181#endif
182
183/** Marks unfinished code. */
184#if 1
185# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
186#else
187# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
188#endif
189
190/** User data key for tools. */
191#define KW_DATA_KEY_TOOL (~(KUPTR)16381)
192/** User data key for a cached file. */
193#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
194
195/** String constant comma length. */
196#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
197
198
199/**
200 * Generate CRT slot wrapper functions.
201 */
202#define CRT_SLOT_FUNCTION_WRAPPER(a_RetTypeAndCallConv, a_FnName, a_aArgsDecl, a_aArgCall) \
203 static a_RetTypeAndCallConv a_FnName##00 a_aArgsDecl { const unsigned iCrtSlot = 0; return a_FnName##_wrapped a_aArgCall; } \
204 static a_RetTypeAndCallConv a_FnName##01 a_aArgsDecl { const unsigned iCrtSlot = 1; return a_FnName##_wrapped a_aArgCall; } \
205 static a_RetTypeAndCallConv a_FnName##02 a_aArgsDecl { const unsigned iCrtSlot = 2; return a_FnName##_wrapped a_aArgCall; } \
206 static a_RetTypeAndCallConv a_FnName##03 a_aArgsDecl { const unsigned iCrtSlot = 3; return a_FnName##_wrapped a_aArgCall; } \
207 static a_RetTypeAndCallConv a_FnName##04 a_aArgsDecl { const unsigned iCrtSlot = 4; return a_FnName##_wrapped a_aArgCall; } \
208 static a_RetTypeAndCallConv a_FnName##05 a_aArgsDecl { const unsigned iCrtSlot = 5; return a_FnName##_wrapped a_aArgCall; } \
209 static a_RetTypeAndCallConv a_FnName##06 a_aArgsDecl { const unsigned iCrtSlot = 6; return a_FnName##_wrapped a_aArgCall; } \
210 static a_RetTypeAndCallConv a_FnName##07 a_aArgsDecl { const unsigned iCrtSlot = 7; return a_FnName##_wrapped a_aArgCall; } \
211 static a_RetTypeAndCallConv a_FnName##08 a_aArgsDecl { const unsigned iCrtSlot = 8; return a_FnName##_wrapped a_aArgCall; } \
212 static a_RetTypeAndCallConv a_FnName##09 a_aArgsDecl { const unsigned iCrtSlot = 9; return a_FnName##_wrapped a_aArgCall; } \
213 static a_RetTypeAndCallConv a_FnName##10 a_aArgsDecl { const unsigned iCrtSlot = 10; return a_FnName##_wrapped a_aArgCall; } \
214 static a_RetTypeAndCallConv a_FnName##11 a_aArgsDecl { const unsigned iCrtSlot = 11; return a_FnName##_wrapped a_aArgCall; } \
215 static a_RetTypeAndCallConv a_FnName##12 a_aArgsDecl { const unsigned iCrtSlot = 12; return a_FnName##_wrapped a_aArgCall; } \
216 static a_RetTypeAndCallConv a_FnName##13 a_aArgsDecl { const unsigned iCrtSlot = 13; return a_FnName##_wrapped a_aArgCall; } \
217 static a_RetTypeAndCallConv a_FnName##14 a_aArgsDecl { const unsigned iCrtSlot = 14; return a_FnName##_wrapped a_aArgCall; } \
218 static a_RetTypeAndCallConv a_FnName##15 a_aArgsDecl { const unsigned iCrtSlot = 15; return a_FnName##_wrapped a_aArgCall; } \
219 static a_RetTypeAndCallConv a_FnName##16 a_aArgsDecl { const unsigned iCrtSlot = 16; return a_FnName##_wrapped a_aArgCall; } \
220 static a_RetTypeAndCallConv a_FnName##17 a_aArgsDecl { const unsigned iCrtSlot = 17; return a_FnName##_wrapped a_aArgCall; } \
221 static a_RetTypeAndCallConv a_FnName##18 a_aArgsDecl { const unsigned iCrtSlot = 18; return a_FnName##_wrapped a_aArgCall; } \
222 static a_RetTypeAndCallConv a_FnName##19 a_aArgsDecl { const unsigned iCrtSlot = 19; return a_FnName##_wrapped a_aArgCall; } \
223 static a_RetTypeAndCallConv a_FnName##20 a_aArgsDecl { const unsigned iCrtSlot = 20; return a_FnName##_wrapped a_aArgCall; } \
224 static a_RetTypeAndCallConv a_FnName##21 a_aArgsDecl { const unsigned iCrtSlot = 21; return a_FnName##_wrapped a_aArgCall; } \
225 static a_RetTypeAndCallConv a_FnName##22 a_aArgsDecl { const unsigned iCrtSlot = 22; return a_FnName##_wrapped a_aArgCall; } \
226 static a_RetTypeAndCallConv a_FnName##23 a_aArgsDecl { const unsigned iCrtSlot = 23; return a_FnName##_wrapped a_aArgCall; } \
227 static a_RetTypeAndCallConv a_FnName##24 a_aArgsDecl { const unsigned iCrtSlot = 24; return a_FnName##_wrapped a_aArgCall; } \
228 static a_RetTypeAndCallConv a_FnName##25 a_aArgsDecl { const unsigned iCrtSlot = 25; return a_FnName##_wrapped a_aArgCall; } \
229 static a_RetTypeAndCallConv a_FnName##26 a_aArgsDecl { const unsigned iCrtSlot = 26; return a_FnName##_wrapped a_aArgCall; } \
230 static a_RetTypeAndCallConv a_FnName##27 a_aArgsDecl { const unsigned iCrtSlot = 27; return a_FnName##_wrapped a_aArgCall; } \
231 static a_RetTypeAndCallConv a_FnName##28 a_aArgsDecl { const unsigned iCrtSlot = 28; return a_FnName##_wrapped a_aArgCall; } \
232 static a_RetTypeAndCallConv a_FnName##29 a_aArgsDecl { const unsigned iCrtSlot = 29; return a_FnName##_wrapped a_aArgCall; } \
233 static a_RetTypeAndCallConv a_FnName##30 a_aArgsDecl { const unsigned iCrtSlot = 30; return a_FnName##_wrapped a_aArgCall; } \
234 static a_RetTypeAndCallConv a_FnName##31 a_aArgsDecl { const unsigned iCrtSlot = 31; return a_FnName##_wrapped a_aArgCall; } \
235 static const KUPTR a_FnName[] = \
236 { \
237 (KUPTR)a_FnName##00, \
238 (KUPTR)a_FnName##01, \
239 (KUPTR)a_FnName##02, \
240 (KUPTR)a_FnName##03, \
241 (KUPTR)a_FnName##04, \
242 (KUPTR)a_FnName##05, \
243 (KUPTR)a_FnName##06, \
244 (KUPTR)a_FnName##07, \
245 (KUPTR)a_FnName##08, \
246 (KUPTR)a_FnName##09, \
247 (KUPTR)a_FnName##10, \
248 (KUPTR)a_FnName##11, \
249 (KUPTR)a_FnName##12, \
250 (KUPTR)a_FnName##13, \
251 (KUPTR)a_FnName##14, \
252 (KUPTR)a_FnName##15, \
253 (KUPTR)a_FnName##16, \
254 (KUPTR)a_FnName##17, \
255 (KUPTR)a_FnName##18, \
256 (KUPTR)a_FnName##19, \
257 (KUPTR)a_FnName##20, \
258 (KUPTR)a_FnName##21, \
259 (KUPTR)a_FnName##22, \
260 (KUPTR)a_FnName##23, \
261 (KUPTR)a_FnName##24, \
262 (KUPTR)a_FnName##25, \
263 (KUPTR)a_FnName##26, \
264 (KUPTR)a_FnName##27, \
265 (KUPTR)a_FnName##28, \
266 (KUPTR)a_FnName##29, \
267 (KUPTR)a_FnName##30, \
268 (KUPTR)a_FnName##31, \
269 }
270
271
272/*********************************************************************************************************************************
273* Structures and Typedefs *
274*********************************************************************************************************************************/
275typedef enum KWLOCATION
276{
277 KWLOCATION_INVALID = 0,
278 KWLOCATION_EXE_DIR,
279 KWLOCATION_IMPORTER_DIR,
280 KWLOCATION_SYSTEM32,
281 KWLOCATION_UNKNOWN_NATIVE,
282 KWLOCATION_UNKNOWN,
283} KWLOCATION;
284
285typedef enum KWMODSTATE
286{
287 KWMODSTATE_INVALID = 0,
288 KWMODSTATE_NEEDS_BITS,
289 KWMODSTATE_NEEDS_INIT,
290 KWMODSTATE_BEING_INITED,
291 KWMODSTATE_INIT_FAILED,
292 KWMODSTATE_READY,
293} KWMODSTATE;
294
295typedef struct KWMODULE *PKWMODULE;
296typedef struct KWMODULE
297{
298 /** Pointer to the next image withe the same hash. */
299 PKWMODULE pNextHash;
300 /** Pointer to the next image in the global list. */
301 PKWMODULE pNextList;
302 /** The normalized path to the image. */
303 const char *pszPath;
304 /** The hash of the program path. */
305 KU32 uHashPath;
306 /** Number of references. */
307 KU32 cRefs;
308 /** UTF-16 version of pszPath. */
309 const wchar_t *pwszPath;
310 /** The offset of the filename in pszPath. */
311 KU16 offFilename;
312 /** The offset of the filename in pwszPath. */
313 KU16 offFilenameW;
314 /** Set if executable. */
315 KBOOL fExe;
316 /** Set if native module entry. */
317 KBOOL fNative;
318 /** Loader module handle. */
319 PKLDRMOD pLdrMod;
320 /** The windows module handle. */
321 HMODULE hOurMod;
322 /** Parent (real) module if this is a virtual API module (api-ms-*.dll or
323 * ext-ms-*.dll). Referenced. */
324 PKWMODULE pVirtualApiMod;
325 /** The of the loaded image bits. */
326 KSIZE cbImage;
327 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
328 KU8 iCrtSlot;
329 /** Loop prevention when working the tree. */
330 KBOOL fVisited;
331 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
332 KBOOL fNeedReInit;
333 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
334 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
335 KU8 fReInitOnMsPdbSrvEndpointChange;
336 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
337 char *pszMsPdbSrvEndpoint;
338
339 union
340 {
341 /** Data for a manually loaded image. */
342 struct
343 {
344 /** Where we load the image. */
345 KU8 *pbLoad;
346 /** Virgin copy of the image. */
347 KU8 *pbCopy;
348 /** Ldr pvBits argument. This is NULL till we've successfully resolved
349 * the imports. */
350 void *pvBits;
351 /** The state. */
352 KWMODSTATE enmState;
353 /** The re-init state. */
354 KWMODSTATE enmReInitState;
355#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
356 /** The number of entries in the table. */
357 KU32 cFunctions;
358 /** The function table address (in the copy). */
359 PRUNTIME_FUNCTION paFunctions;
360 /** Set if we've already registered a function table already. */
361 KBOOL fRegisteredFunctionTable;
362#endif
363 /** Set if we share memory with other executables. */
364 KBOOL fUseLdBuf;
365 /** Set after the first whole image copy is done. */
366 KBOOL fCanDoQuick;
367 /** Number of quick copy chunks. */
368 KU8 cQuickCopyChunks;
369 /** Number of quick zero chunks. */
370 KU8 cQuickZeroChunks;
371 /** Quicker image copy instructions that skips non-writable parts when
372 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
373 * image. */
374 struct
375 {
376 /** The copy destination. */
377 KU8 *pbDst;
378 /** The copy source. */
379 KU8 const *pbSrc;
380 /** How much to copy. */
381 KSIZE cbToCopy;
382 } aQuickCopyChunks[3];
383 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
384 struct
385 {
386 /** Where to start zeroing. */
387 KU8 *pbDst;
388 /** How much to zero. */
389 KSIZE cbToZero;
390 } aQuickZeroChunks[3];
391
392 /** Pointer to g_abInitData of the kWorkerTlsXxxK.c instance.
393 * This member is set by kwLdrTlsAllocationHook. */
394 KU8 *pabTlsInitData;
395 /** Pointer to the g_pvWorkerModule variable in kWorkerTlsXxxK.c (our instance
396 * of it). This member is set by kwLdrTlsAllocationHook. Used by our
397 * destructor to prevent after-free references. */
398 PKWMODULE *ppTlsWorkerModuleVar;
399 /** TLS index if one was allocated, otherwise KU32_MAX.
400 * This member is set by kwLdrTlsAllocationHook. */
401 KU32 idxTls;
402 /** Offset (RVA) of the TLS initialization data. */
403 KU32 offTlsInitData;
404 /** Number of bytes of TLS initialization data. */
405 KU32 cbTlsInitData;
406 /** Number of allocated bytes for TLS. */
407 KU32 cbTlsAlloc;
408 /** Number of TLS callbacks. */
409 KU32 cTlsCallbacks;
410 /** Offset (RVA) of the TLS callback table. */
411 KU32 offTlsCallbacks;
412
413 /** Number of imported modules. */
414 KSIZE cImpMods;
415 /** Import array (variable size). */
416 PKWMODULE apImpMods[1];
417 } Manual;
418 } u;
419} KWMODULE;
420
421
422typedef struct KWDYNLOAD *PKWDYNLOAD;
423typedef struct KWDYNLOAD
424{
425 /** Pointer to the next in the list. */
426 PKWDYNLOAD pNext;
427
428 /** The module handle we present to the application.
429 * This is the LoadLibraryEx return value for special modules and the
430 * KWMODULE.hOurMod value for the others. */
431 HMODULE hmod;
432
433 /** The module for non-special resource stuff, NULL if special. */
434 PKWMODULE pMod;
435
436 /** The length of the LoadLibary filename. */
437 KSIZE cchRequest;
438 /** The LoadLibrary filename. */
439 char szRequest[1];
440} KWDYNLOAD;
441
442
443/**
444 * GetModuleHandle cache for system modules frequently queried.
445 */
446typedef struct KWGETMODULEHANDLECACHE
447{
448 const char *pszName;
449 const wchar_t *pwszName;
450 KU8 cchName;
451 KU8 cwcName;
452 KBOOL fAlwaysPresent;
453 HANDLE hmod;
454} KWGETMODULEHANDLECACHE;
455typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
456
457
458/** One TLS DLL. */
459typedef struct KWTLSDLL
460{
461 const wchar_t *pwszName; /**< The DLL name. */
462 KBOOL fUsed; /**< Set if used, clear if not. */
463} KWTLSDLL;
464typedef KWTLSDLL *PKWTLSDLL;
465
466/**
467 * TLS DLL tracker.
468 */
469typedef struct KWTLSDLLENTRY
470{
471 KU32 cbTls; /**< Max TLS size. */
472 KU32 cDlls; /**< Number of DLLs we ship (paDlls). */
473 PKWTLSDLL paDlls; /**< Array of DLLs we ship. */
474} KWTLSDLLENTRY;
475typedef KWTLSDLLENTRY *PKWTLSDLLENTRY;
476
477
478/**
479 * A cached file.
480 */
481typedef struct KFSWCACHEDFILE
482{
483 /** The user data core. */
484 KFSUSERDATA Core;
485
486 /** Cached file handle. */
487 HANDLE hCached;
488 /** Cached file section handle. */
489 HANDLE hSection;
490 /** Cached file content. */
491 KU8 *pbCached;
492 /** The file size. */
493 KU32 cbCached;
494#ifdef WITH_HASH_CACHE
495 /** Set if we've got a valid MD5 hash in abMd5Digest. */
496 KBOOL fValidMd5;
497 /** Set if we've got a valid SHA-1 hash in abMd5Digest. */
498 KBOOL fValidSha1;
499 /** Set if we've got a valid SHA-256 hash in abMd5Digest. */
500 KBOOL fValidSha256;
501 /** Set if we've got a valid SHA-512 hash in abMd5Digest. */
502 KBOOL fValidSha512;
503 /** The MD5 digest if fValidMd5 is set. */
504 KU8 abMd5Digest[16];
505 /** The SHA-1 digest if fValidSha1 is set. */
506 KU8 abSha1Digest[20];
507 /** The SHA-256 digest if fValidSha256 is set. */
508 KU8 abSha256Digest[32];
509 /** The SHA-512 digest if fValidSha256 is set. */
510 KU8 abSha512Digest[64];
511#endif
512
513 /** Circular self reference. Prevents the object from ever going away and
514 * keeps it handy for debugging. */
515 PKFSOBJ pFsObj;
516 /** The file path (for debugging). */
517 char szPath[1];
518} KFSWCACHEDFILE;
519/** Pointer to a cached filed. */
520typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
521
522#ifdef WITH_HASH_CACHE
523
524/** Pointer to a MD5 hash instance. */
525typedef struct KWCRYPTHASH *PKWCRYPTHASH;
526/**
527 * A MD5 hash instance.
528 */
529typedef struct KWCRYPTHASH
530{
531 /** The magic value. */
532 KUPTR uMagic;
533 /** Pointer to the next hash handle. */
534 PKWCRYPTHASH pNext;
535 /** The cached file we've associated this handle with. */
536 PKFSWCACHEDFILE pCachedFile;
537 /** The number of bytes we've hashed. */
538 KU32 cbHashed;
539 /** Set if this has gone wrong. */
540 KBOOL fGoneBad;
541 /** Set if we've already finalized the digest. */
542 KBOOL fFinal;
543 /** If in fallback mode. */
544 HCRYPTHASH hFallback;
545 /** The algorithm. */
546 ALG_ID idAlg;
547 /** The hash name. */
548 const char *pszAlgName;
549 /** The digest size. */
550 KU32 cbDigest;
551 /** The finalized digest. */
552 KU8 abDigest[64];
553} KWCRYPTHASH;
554/** Magic value for KWCRYPTHASH::uMagic (Les McCann). */
555# define KWCRYPTHASH_MAGIC KUPTR_C(0x19350923)
556
557#endif /* WITH_HASH_CACHE */
558#ifdef WITH_TEMP_MEMORY_FILES
559
560typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
561typedef struct KWFSTEMPFILESEG
562{
563 /** File offset of data. */
564 KU32 offData;
565 /** The size of the buffer pbData points to. */
566 KU32 cbDataAlloc;
567 /** The segment data. */
568 KU8 *pbData;
569} KWFSTEMPFILESEG;
570
571typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
572typedef struct KWFSTEMPFILE
573{
574 /** Pointer to the next temporary file for this run. */
575 PKWFSTEMPFILE pNext;
576 /** The UTF-16 path. (Allocated after this structure.) */
577 const wchar_t *pwszPath;
578 /** The path length. */
579 KU16 cwcPath;
580 /** Number of active handles using this file/mapping (<= 2). */
581 KU8 cActiveHandles;
582 /** Number of active mappings (mapped views) (0 or 1). */
583 KU8 cMappings;
584 /** The amount of space allocated in the segments. */
585 KU32 cbFileAllocated;
586 /** The current file size. */
587 KU32 cbFile;
588 /** The number of segments. */
589 KU32 cSegs;
590 /** Segments making up the file. */
591 PKWFSTEMPFILESEG paSegs;
592} KWFSTEMPFILE;
593
594#endif /* WITH_TEMP_MEMORY_FILES */
595#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
596
597/**
598 * Console line buffer or output full buffer.
599 */
600typedef struct KWOUTPUTSTREAMBUF
601{
602 /** The main output handle. */
603 HANDLE hOutput;
604 /** Our backup handle. */
605 HANDLE hBackup;
606 /** Set if this is a console handle and we're in line buffered mode.
607 * When clear, we may buffer multiple lines, though try flush on line
608 * boundraries when ever possible. */
609 KBOOL fIsConsole;
610 /** Compressed GetFileType result. */
611 KU8 fFileType;
612 KU8 abPadding[2];
613 union
614 {
615 /** Line buffer mode (fIsConsole == K_TRUE). */
616 struct
617 {
618 /** Amount of pending console output in wchar_t's. */
619 KU32 cwcBuf;
620 /** The allocated buffer size. */
621 KU32 cwcBufAlloc;
622 /** Pending console output. */
623 wchar_t *pwcBuf;
624 } Con;
625 /** Fully buffered mode (fIsConsole == K_FALSE). */
626 struct
627 {
628 /** Amount of pending output (in chars). */
629 KU32 cchBuf;
630#ifdef WITH_STD_OUT_ERR_BUFFERING
631 /** The allocated buffer size (in chars). */
632 KU32 cchBufAlloc;
633 /** Pending output. */
634 char *pchBuf;
635#endif
636 } Fully;
637 } u;
638} KWOUTPUTSTREAMBUF;
639/** Pointer to a console line buffer. */
640typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
641
642/**
643 * Combined console buffer of complete lines.
644 */
645typedef struct KWCONSOLEOUTPUT
646{
647 /** The console output handle.
648 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
649 * combined output buffering. */
650 HANDLE hOutput;
651 /** The current code page for the console. */
652 KU32 uCodepage;
653 /** Amount of pending console output in wchar_t's. */
654 KU32 cwcBuf;
655 /** Number of times we've flushed it in any way (for cl.exe hack). */
656 KU32 cFlushes;
657 /** Pending console output. */
658 wchar_t wszBuf[8192];
659} KWCONSOLEOUTPUT;
660/** Pointer to a combined console buffer. */
661typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
662
663#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
664
665/** Handle type. */
666typedef enum KWHANDLETYPE
667{
668 KWHANDLETYPE_INVALID = 0,
669 KWHANDLETYPE_FSOBJ_READ_CACHE,
670 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
671#ifdef WITH_TEMP_MEMORY_FILES
672 KWHANDLETYPE_TEMP_FILE,
673 KWHANDLETYPE_TEMP_FILE_MAPPING,
674#endif
675 KWHANDLETYPE_OUTPUT_BUF
676} KWHANDLETYPE;
677
678/** Handle data. */
679typedef struct KWHANDLE
680{
681 KWHANDLETYPE enmType;
682 /** Number of references */
683 KU32 cRefs;
684 /** The current file offset. */
685 KU32 offFile;
686 /** Handle access. */
687 KU32 dwDesiredAccess;
688 /** The handle. */
689 HANDLE hHandle;
690 /** The current owner (GetCurrentThreadId). */
691 KU32 tidOwner;
692
693 /** Type specific data. */
694 union
695 {
696 /** The file system object. */
697 PKFSWCACHEDFILE pCachedFile;
698#ifdef WITH_TEMP_MEMORY_FILES
699 /** Temporary file handle or mapping handle. */
700 PKWFSTEMPFILE pTempFile;
701#endif
702#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
703 /** Buffered output stream. */
704 PKWOUTPUTSTREAMBUF pOutBuf;
705#endif
706 } u;
707} KWHANDLE;
708typedef KWHANDLE *PKWHANDLE;
709
710/**
711 * Tracking one of our memory mappings.
712 */
713typedef struct KWMEMMAPPING
714{
715 /** Number of references. */
716 KU32 cRefs;
717 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
718 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
719 KWHANDLETYPE enmType;
720 /** The mapping address. */
721 PVOID pvMapping;
722 /** Type specific data. */
723 union
724 {
725 /** The file system object. */
726 PKFSWCACHEDFILE pCachedFile;
727#ifdef WITH_TEMP_MEMORY_FILES
728 /** Temporary file handle or mapping handle. */
729 PKWFSTEMPFILE pTempFile;
730#endif
731 } u;
732} KWMEMMAPPING;
733/** Pointer to a memory mapping tracker. */
734typedef KWMEMMAPPING *PKWMEMMAPPING;
735
736
737/** Pointer to a VirtualAlloc tracker entry. */
738typedef struct KWVIRTALLOC *PKWVIRTALLOC;
739/**
740 * Tracking an VirtualAlloc allocation.
741 */
742typedef struct KWVIRTALLOC
743{
744 PKWVIRTALLOC pNext;
745 void *pvAlloc;
746 KSIZE cbAlloc;
747 /** This is KU32_MAX if not a preallocated chunk. */
748 KU32 idxPreAllocated;
749} KWVIRTALLOC;
750
751
752/** Pointer to a heap (HeapCreate) tracker entry. */
753typedef struct KWHEAP *PKWHEAP;
754/**
755 * Tracking an heap (HeapCreate)
756 */
757typedef struct KWHEAP
758{
759 PKWHEAP pNext;
760 HANDLE hHeap;
761} KWHEAP;
762
763
764/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
765typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
766/**
767 * Tracking an FlsAlloc/TlsAlloc index.
768 */
769typedef struct KWLOCALSTORAGE
770{
771 PKWLOCALSTORAGE pNext;
772 KU32 idx;
773} KWLOCALSTORAGE;
774
775
776/** Pointer to an at exit callback record */
777typedef struct KWEXITCALLACK *PKWEXITCALLACK;
778/**
779 * At exit callback record.
780 */
781typedef struct KWEXITCALLACK
782{
783 PKWEXITCALLACK pNext;
784 _onexit_t pfnCallback;
785 /** At exit doesn't have an exit code. */
786 KBOOL fAtExit;
787} KWEXITCALLACK;
788
789
790typedef enum KWTOOLTYPE
791{
792 KWTOOLTYPE_INVALID = 0,
793 KWTOOLTYPE_SANDBOXED,
794 KWTOOLTYPE_WATCOM,
795 KWTOOLTYPE_EXEC,
796 KWTOOLTYPE_END
797} KWTOOLTYPE;
798
799typedef enum KWTOOLHINT
800{
801 KWTOOLHINT_INVALID = 0,
802 KWTOOLHINT_NONE,
803 KWTOOLHINT_VISUAL_CPP_CL,
804 KWTOOLHINT_VISUAL_CPP_LINK,
805 KWTOOLHINT_END
806} KWTOOLHINT;
807
808
809/**
810 * A kWorker tool.
811 */
812typedef struct KWTOOL
813{
814 /** The user data core structure. */
815 KFSUSERDATA Core;
816
817 /** The normalized path to the program. */
818 const char *pszPath;
819 /** UTF-16 version of pszPath. */
820 wchar_t const *pwszPath;
821 /** The kind of tool. */
822 KWTOOLTYPE enmType;
823
824 union
825 {
826 struct
827 {
828 /** The main entry point. */
829 KUPTR uMainAddr;
830 /** The executable. */
831 PKWMODULE pExe;
832 /** List of dynamically loaded modules.
833 * These will be kept loaded till the tool is destroyed (if we ever do that). */
834 PKWDYNLOAD pDynLoadHead;
835 /** Module array sorted by hOurMod. */
836 PKWMODULE *papModules;
837 /** Number of entries in papModules. */
838 KU32 cModules;
839
840 /** Tool hint (for hacks and such). */
841 KWTOOLHINT enmHint;
842 } Sandboxed;
843 } u;
844} KWTOOL;
845/** Pointer to a tool. */
846typedef struct KWTOOL *PKWTOOL;
847
848
849typedef struct KWSANDBOX *PKWSANDBOX;
850typedef struct KWSANDBOX
851{
852 /** Jump buffer (first for alignment reasons). */
853 jmp_buf JmpBuf;
854 /** The tool currently running in the sandbox. */
855 PKWTOOL pTool;
856 /** The thread ID of the main thread (owner of JmpBuf). */
857 DWORD idMainThread;
858 /** Copy of the NT TIB of the main thread. */
859 NT_TIB TibMainThread;
860 /** The NT_TIB::ExceptionList value inside the try case.
861 * We restore this prior to the longjmp. */
862 void *pOutXcptListHead;
863 /** The exit code in case of longjmp. */
864 int rcExitCode;
865 /** Set if we're running. */
866 KBOOL fRunning;
867 /** Whether to disable caching of ".pch" files. */
868 KBOOL fNoPchCaching;
869
870 /** The command line. */
871 char *pszCmdLine;
872 /** The UTF-16 command line. */
873 wchar_t *pwszCmdLine;
874 /** Number of arguments in papszArgs. */
875 int cArgs;
876 /** The argument vector. */
877 char **papszArgs;
878 /** The argument vector. */
879 wchar_t **papwszArgs;
880
881 /** The _pgmptr msvcrt variable. */
882 char *pgmptr;
883 /** The _wpgmptr msvcrt variable. */
884 wchar_t *wpgmptr;
885
886 /** The _initenv msvcrt variable. */
887 char **initenv;
888 /** The _winitenv msvcrt variable. */
889 wchar_t **winitenv;
890
891 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
892 KSIZE cEnvVarsAllocated;
893 /** The _environ msvcrt variable. */
894 char **environ;
895 /** The _wenviron msvcrt variable. */
896 wchar_t **wenviron;
897 /** The shadow _environ msvcrt variable. */
898 char **papszEnvVars;
899 /** The shadow _wenviron msvcrt variable. */
900 wchar_t **papwszEnvVars;
901
902
903 /** Critical section protecting the below handle members below.
904 * @note Does not protect the individual handles. */
905 CRITICAL_SECTION HandlesLock;
906 /** Handle table. */
907 PKWHANDLE *papHandles;
908 /** Size of the handle table. */
909 KU32 cHandles;
910 /** Number of active handles in the table. */
911 KU32 cActiveHandles;
912 /** Number of handles in the handle table that will not be freed. */
913 KU32 cFixedHandles;
914 /** Total number of leaked handles. */
915 KU32 cLeakedHandles;
916
917 /** Number of active memory mappings in paMemMappings. */
918 KU32 cMemMappings;
919 /** The allocated size of paMemMappings. */
920 KU32 cMemMappingsAlloc;
921 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
922 PKWMEMMAPPING paMemMappings;
923
924#ifdef WITH_TEMP_MEMORY_FILES
925 /** Head of the list of temporary file. */
926 PKWFSTEMPFILE pTempFileHead;
927#endif
928
929 /** Critical section protecting pVirtualAllocHead. */
930 CRITICAL_SECTION VirtualAllocLock;
931 /** Head of the virtual alloc allocations. */
932 PKWVIRTALLOC pVirtualAllocHead;
933 /** Head of the heap list (HeapCreate).
934 * This is only done from images we forcibly restore. */
935 PKWHEAP pHeapHead;
936 /** Head of the FlsAlloc indexes. */
937 PKWLOCALSTORAGE pFlsAllocHead;
938 /** Head of the TlsAlloc indexes. */
939 PKWLOCALSTORAGE pTlsAllocHead;
940
941 /** The at exit callback head.
942 * This is only done from images we forcibly restore. */
943 PKWEXITCALLACK pExitCallbackHead;
944
945 MY_UNICODE_STRING SavedCommandLine;
946
947#ifdef WITH_HASH_CACHE
948 /** The crypto provider instance we use for hashes. */
949 HCRYPTPROV hCryptProvRsa;
950 /** The crypto provider instance we use for hashes. */
951 HCRYPTPROV hCryptProvAes;
952 /** List of crypto hash instances. */
953 PKWCRYPTHASH pHashHead;
954 /** ReadFile sets these while CryptHashData claims and clears them.
955 *
956 * This is part of the heuristics we use for MD5/SHA1/SHA256 caching for header
957 * files. The observed pattern is that c1.dll/c1xx.dll first reads a chunk of a
958 * source or header, then passes the same buffer and read byte count to
959 * CryptHashData.
960 */
961 struct
962 {
963 /** The cached file last read from. */
964 PKFSWCACHEDFILE pCachedFile;
965 /** The file offset of the last cached read. */
966 KU32 offRead;
967 /** The number of bytes read last. */
968 KU32 cbRead;
969 /** The buffer pointer of the last read. */
970 void *pvRead;
971 } LastHashRead;
972#endif
973
974#ifdef WITH_CRYPT_CTX_REUSE
975 /** Reusable crypt contexts. */
976 struct
977 {
978 /** The creation provider type. */
979 KU32 dwProvType;
980 /** The creation flags. */
981 KU32 dwFlags;
982 /** The length of the container name. */
983 KU32 cwcContainer;
984 /** The length of the provider name. */
985 KU32 cwcProvider;
986 /** The container name string. */
987 wchar_t *pwszContainer;
988 /** The provider name string. */
989 wchar_t *pwszProvider;
990 /** The context handle. */
991 HCRYPTPROV hProv;
992 } aCryptCtxs[4];
993 /** Number of reusable crypt conexts in aCryptCtxs. */
994 KU32 cCryptCtxs;
995#endif
996
997
998#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
999 /** The internal standard output handle. */
1000 KWHANDLE HandleStdOut;
1001 /** The internal standard error handle. */
1002 KWHANDLE HandleStdErr;
1003 /** Standard output (and whatever else) buffer. */
1004 KWOUTPUTSTREAMBUF StdOut;
1005 /** Standard error buffer. */
1006 KWOUTPUTSTREAMBUF StdErr;
1007 /** Combined buffer of completed lines. */
1008 KWCONSOLEOUTPUT Combined;
1009#endif
1010} KWSANDBOX;
1011
1012
1013/** A CRT slot. */
1014typedef struct KWCRTSLOT
1015{
1016 KU32 iSlot;
1017
1018 /** The CRT module data. */
1019 PKWMODULE pModule;
1020 /** Pointer to the malloc function. */
1021 void * (__cdecl *pfnMalloc)(size_t);
1022 /** Pointer to the beginthreadex function. */
1023 uintptr_t (__cdecl *pfnBeginThreadEx)(void *, unsigned, unsigned (__stdcall *)(void *), void *, unsigned, unsigned *);
1024
1025} KWCRTSLOT;
1026typedef KWCRTSLOT *PKWCRTSLOT;
1027
1028
1029/** Replacement function entry. */
1030typedef struct KWREPLACEMENTFUNCTION
1031{
1032 /** The function name. */
1033 const char *pszFunction;
1034 /** The length of the function name. */
1035 KSIZE cchFunction;
1036 /** The module name (optional). */
1037 const char *pszModule;
1038 /** The replacement function, data address or CRT slot function array. */
1039 KUPTR pfnReplacement;
1040 /** Only replace in the executable.
1041 * @todo fix the reinitialization of non-native DLLs! */
1042 KBOOL fOnlyExe;
1043 /** Set if pfnReplacement points to a CRT slot function array. */
1044 KBOOL fCrtSlotArray;
1045} KWREPLACEMENTFUNCTION;
1046typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
1047
1048#if 0
1049/** Replacement function entry. */
1050typedef struct KWREPLACEMENTDATA
1051{
1052 /** The function name. */
1053 const char *pszFunction;
1054 /** The length of the function name. */
1055 KSIZE cchFunction;
1056 /** The module name (optional). */
1057 const char *pszModule;
1058 /** Function providing the replacement. */
1059 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
1060} KWREPLACEMENTDATA;
1061typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
1062#endif
1063
1064/**
1065 * One test job (--full-test).
1066 */
1067typedef struct KWONETEST
1068{
1069 /** Where this job originated. */
1070 const char *pszJobSrc;
1071 /** The argument number it started with. */
1072 unsigned iJobSrc;
1073 /** Set if virgin, clear if modified. */
1074 KBOOL fVirgin;
1075
1076 /** Number of runs to give it. */
1077 unsigned cRuns;
1078
1079 /** @name kSubmitHandleJobUnpacked arguments
1080 * @{ */
1081 const char *pszExecutable;
1082 const char *pszCwd;
1083 KU32 cArgs;
1084 const char **papszArgs;
1085 KU32 cEnvVars;
1086 const char **papszEnvVars;
1087 const char *pszSpecialEnv;
1088 KBOOL fWatcomBrainDamange;
1089 KBOOL fNoPchCaching;
1090 KU32 cPostCmdArgs;
1091 const char **papszPostCmdArgs;
1092 /** @} */
1093
1094 /** Pointer to the next one. */
1095 struct KWONETEST *pNext;
1096} KWONETEST;
1097/** Pointer to one test job. */
1098typedef KWONETEST *PKWONETEST;
1099
1100
1101/*********************************************************************************************************************************
1102* Global Variables *
1103*********************************************************************************************************************************/
1104/** The sandbox data. */
1105static KWSANDBOX g_Sandbox;
1106
1107/** The module currently occupying g_abDefLdBuf. */
1108static PKWMODULE g_pModInLdBuf = NULL;
1109
1110/** The module that previuosly occupied g_abDefLdBuf. */
1111static PKWMODULE g_pModPrevInLdBuf = NULL;
1112
1113/** Module list head. */
1114static PKWMODULE g_pModuleHead = NULL;
1115/** Where to insert the next module. */
1116static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1117
1118/** Module hash table. */
1119static PKWMODULE g_apModules[127];
1120
1121/** GetModuleHandle cache. */
1122static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1123{
1124#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1125 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1126#if 1
1127 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1128 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1129#endif
1130 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1131};
1132
1133/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1134static PKWMODULE g_pModPendingTlsAlloc = NULL;
1135
1136/** The 1KB TLS DLLs. */
1137static KWTLSDLL g_aTls1KDlls[] =
1138{
1139 { L"kWorkerTls1K.dll", K_FALSE },
1140 { L"kWorkerTls1K01.dll", K_FALSE },
1141 { L"kWorkerTls1K02.dll", K_FALSE },
1142 { L"kWorkerTls1K03.dll", K_FALSE },
1143 { L"kWorkerTls1K04.dll", K_FALSE },
1144 { L"kWorkerTls1K05.dll", K_FALSE },
1145 { L"kWorkerTls1K06.dll", K_FALSE },
1146 { L"kWorkerTls1K07.dll", K_FALSE },
1147 { L"kWorkerTls1K08.dll", K_FALSE },
1148 { L"kWorkerTls1K09.dll", K_FALSE },
1149 { L"kWorkerTls1K10.dll", K_FALSE },
1150 { L"kWorkerTls1K11.dll", K_FALSE },
1151 { L"kWorkerTls1K12.dll", K_FALSE },
1152 { L"kWorkerTls1K13.dll", K_FALSE },
1153 { L"kWorkerTls1K14.dll", K_FALSE },
1154 { L"kWorkerTls1K15.dll", K_FALSE },
1155};
1156
1157/** The 64KB TLS DLLs. */
1158static KWTLSDLL g_aTls64KDlls[] =
1159{
1160 { L"kWorkerTls64K.dll", K_FALSE },
1161 { L"kWorkerTls64K01.dll", K_FALSE },
1162 { L"kWorkerTls64K02.dll", K_FALSE },
1163 { L"kWorkerTls64K03.dll", K_FALSE },
1164 { L"kWorkerTls64K04.dll", K_FALSE },
1165 { L"kWorkerTls64K05.dll", K_FALSE },
1166 { L"kWorkerTls64K06.dll", K_FALSE },
1167 { L"kWorkerTls64K07.dll", K_FALSE },
1168};
1169
1170/** The 128KB TLS DLLs. */
1171static KWTLSDLL g_aTls128KDlls[] =
1172{
1173 { L"kWorkerTls128K.dll", K_FALSE },
1174 { L"kWorkerTls128K01.dll", K_FALSE },
1175 { L"kWorkerTls128K02.dll", K_FALSE },
1176 { L"kWorkerTls128K03.dll", K_FALSE },
1177 { L"kWorkerTls128K04.dll", K_FALSE },
1178 { L"kWorkerTls128K05.dll", K_FALSE },
1179 { L"kWorkerTls128K06.dll", K_FALSE },
1180 { L"kWorkerTls128K07.dll", K_FALSE },
1181};
1182
1183/** The 512KB TLS DLLs. */
1184static KWTLSDLL g_aTls512KDlls[] =
1185{
1186 { L"kWorkerTls512K.dll", K_FALSE },
1187 { L"kWorkerTls512K01.dll", K_FALSE },
1188 { L"kWorkerTls512K02.dll", K_FALSE },
1189 { L"kWorkerTls512K03.dll", K_FALSE },
1190 { L"kWorkerTls512K04.dll", K_FALSE },
1191 { L"kWorkerTls512K05.dll", K_FALSE },
1192 { L"kWorkerTls512K06.dll", K_FALSE },
1193 { L"kWorkerTls512K07.dll", K_FALSE },
1194};
1195
1196/** The TLS DLLs grouped by size. */
1197static KWTLSDLLENTRY const g_aTlsDlls[] =
1198{
1199 { 1024, K_ELEMENTS(g_aTls1KDlls), g_aTls1KDlls },
1200 { 64*1024, K_ELEMENTS(g_aTls64KDlls), g_aTls64KDlls },
1201 { 128*1024, K_ELEMENTS(g_aTls128KDlls), g_aTls128KDlls },
1202 { 512*1024, K_ELEMENTS(g_aTls512KDlls), g_aTls512KDlls },
1203};
1204
1205/** CRT slots.
1206 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1207static KWCRTSLOT g_aCrtSlots[32];
1208
1209/** windbg .reload statements. vs */
1210char g_szReloads[4096];
1211/** Current offset into g_szReloads. */
1212KU32 volatile g_cchReloads;
1213
1214/** The file system cache. */
1215static PKFSCACHE g_pFsCache;
1216/** The current directory (referenced). */
1217static PKFSOBJ g_pCurDirObj = NULL;
1218#ifdef KBUILD_OS_WINDOWS
1219/** The windows system32 directory (referenced). */
1220static PKFSDIR g_pWinSys32 = NULL;
1221#endif
1222
1223/** Verbosity level. */
1224static int g_cVerbose = 2;
1225
1226/** Whether we should restart the worker. */
1227static KBOOL g_fRestart = K_FALSE;
1228
1229/** The process group this worker is tied to (--group option), -1 if none. */
1230static KI32 g_iProcessGroup = -1;
1231
1232/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1233static int volatile g_rcCtrlC = 0;
1234
1235/** The communication pipe handle. We break this when we see Ctrl-C such. */
1236#ifdef KBUILD_OS_WINDOWS
1237static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1238#else
1239static int g_hPipe = -1;
1240#endif
1241
1242
1243/* Further down. */
1244extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1245extern KU32 const g_cSandboxReplacements;
1246
1247extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1248extern KU32 const g_cSandboxNativeReplacements;
1249
1250extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1251extern KU32 const g_cSandboxGetProcReplacements;
1252
1253
1254/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1255 * cover the default executable link address of 0x400000.
1256 * @remarks Early main() makes it read+write+executable. Attempts as having
1257 * it as a separate section failed because the linker insists on
1258 * writing out every zero in the uninitialized section, resulting in
1259 * really big binaries. */
1260__declspec(align(0x1000))
1261static KU8 g_abDefLdBuf[16*1024*1024];
1262
1263#ifdef WITH_LOG_FILE
1264/** Log file handle. */
1265static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1266#endif
1267
1268
1269#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1270/** Virtual address space reserved for CL.EXE heap manager.
1271 *
1272 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1273 * address. It's among other things used for precompiled headers, which
1274 * seemingly have addresses hardcoded into them and won't work if mapped
1275 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1276 * for it. (The /Zm option may affect this allocation.)
1277 */
1278static struct
1279{
1280 /** The memory address we need. */
1281 KUPTR const uFixed;
1282 /** How much we need to fix. */
1283 KSIZE const cbFixed;
1284 /** What we actually got, NULL if given back. */
1285 void *pvReserved;
1286 /** Whether it is in use or not. */
1287 KBOOL fInUse;
1288} g_aFixedVirtualAllocs[] =
1289{
1290# if K_ARCH == K_ARCH_X86_32
1291 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1292 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1293 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1294# else
1295 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1296# endif
1297};
1298#endif
1299
1300
1301#ifdef WITH_HISTORY
1302/** The job history. */
1303static char *g_apszHistory[32];
1304/** Index of the next history entry. */
1305static unsigned g_iHistoryNext = 0;
1306#endif
1307
1308
1309/** Number of jobs executed. */
1310static KU32 g_cJobs;
1311/** Number of tools. */
1312static KU32 g_cTools;
1313/** Number of modules. */
1314static KU32 g_cModules;
1315/** Number of non-native modules. */
1316static KU32 g_cNonNativeModules;
1317/** Number of read-cached files. */
1318static KU32 g_cReadCachedFiles;
1319/** Total size of read-cached files. */
1320static KSIZE g_cbReadCachedFiles;
1321
1322/** Total number of ReadFile calls. */
1323static KSIZE g_cReadFileCalls;
1324/** Total bytes read via ReadFile. */
1325static KSIZE g_cbReadFileTotal;
1326/** Total number of read from read-cached files. */
1327static KSIZE g_cReadFileFromReadCached;
1328/** Total bytes read from read-cached files. */
1329static KSIZE g_cbReadFileFromReadCached;
1330/** Total number of read from in-memory temporary files. */
1331static KSIZE g_cReadFileFromInMemTemp;
1332/** Total bytes read from in-memory temporary files. */
1333static KSIZE g_cbReadFileFromInMemTemp;
1334
1335/** Total number of WriteFile calls. */
1336static KSIZE g_cWriteFileCalls;
1337/** Total bytes written via WriteFile. */
1338static KSIZE g_cbWriteFileTotal;
1339/** Total number of written to from in-memory temporary files. */
1340static KSIZE g_cWriteFileToInMemTemp;
1341/** Total bytes written to in-memory temporary files. */
1342static KSIZE g_cbWriteFileToInMemTemp;
1343
1344#ifdef WITH_HASH_CACHE
1345/** Total number of hashes. */
1346static KSIZE g_cHashes;
1347/** Number of cached hash hits. */
1348static KSIZE g_cHashesCached;
1349/** Number of fallbacks. */
1350static KSIZE g_cHashesFallbacks;
1351/** Number of partial cached file hashes. */
1352static KSIZE g_cHashesPartial;
1353/** Total number of MD5 hashes. */
1354static KSIZE g_cHashesMd5;
1355/** Total number of SHA-1 hashes. */
1356static KSIZE g_cHashesSha1;
1357/** Total number of SHA-256 hashes. */
1358static KSIZE g_cHashesSha256;
1359/** Total number of SHA-512 hashes. */
1360static KSIZE g_cHashesSha512;
1361#endif
1362
1363
1364/*********************************************************************************************************************************
1365* Internal Functions *
1366*********************************************************************************************************************************/
1367static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1368static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1369 const char *pszSearchPath, PKWMODULE *ppMod);
1370static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent);
1371static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName);
1372static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule);
1373static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod);
1374static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1375static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1376static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile);
1377static PKWHANDLE kwSandboxHandleGet(HANDLE hFile);
1378K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle);
1379#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1380static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1381#endif
1382static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1383
1384
1385
1386
1387/**
1388 * Debug printing.
1389 * @param pszFormat Debug format string.
1390 * @param ... Format argument.
1391 */
1392static void kwDbgPrintfV(const char *pszFormat, va_list va)
1393{
1394 if (g_cVerbose >= 2)
1395 {
1396 DWORD const dwSavedErr = GetLastError();
1397#ifdef WITH_LOG_FILE
1398 DWORD dwIgnored;
1399 char szTmp[2048];
1400 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1401 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1402 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1403 cch += cchPrefix;
1404 else
1405 {
1406 cch = sizeof(szTmp) - 1;
1407 szTmp[cch] = '\0';
1408 }
1409
1410 if (g_hLogFile == INVALID_HANDLE_VALUE)
1411 {
1412 wchar_t wszFilename[128];
1413 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1414 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1415 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1416 }
1417
1418 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1419#else
1420 fprintf(stderr, "debug: ");
1421 vfprintf(stderr, pszFormat, va);
1422#endif
1423
1424 SetLastError(dwSavedErr);
1425 }
1426}
1427
1428
1429/**
1430 * Debug printing.
1431 * @param pszFormat Debug format string.
1432 * @param ... Format argument.
1433 */
1434static void kwDbgPrintf(const char *pszFormat, ...)
1435{
1436 if (g_cVerbose >= 2)
1437 {
1438 va_list va;
1439 va_start(va, pszFormat);
1440 kwDbgPrintfV(pszFormat, va);
1441 va_end(va);
1442 }
1443}
1444
1445
1446/**
1447 * Debugger printing.
1448 * @param pszFormat Debug format string.
1449 * @param ... Format argument.
1450 */
1451static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1452{
1453 if (IsDebuggerPresent())
1454 {
1455 DWORD const dwSavedErr = GetLastError();
1456 char szTmp[2048];
1457
1458 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1459 OutputDebugStringA(szTmp);
1460
1461 SetLastError(dwSavedErr);
1462 }
1463}
1464
1465
1466/**
1467 * Debugger printing.
1468 * @param pszFormat Debug format string.
1469 * @param ... Format argument.
1470 */
1471static void kwDebuggerPrintf(const char *pszFormat, ...)
1472{
1473 va_list va;
1474 va_start(va, pszFormat);
1475 kwDebuggerPrintfV(pszFormat, va);
1476 va_end(va);
1477}
1478
1479
1480
1481/**
1482 * Error printing.
1483 * @param pszFormat Message format string.
1484 * @param ... Format argument.
1485 */
1486static void kwErrPrintfV(const char *pszFormat, va_list va)
1487{
1488 DWORD const dwSavedErr = GetLastError();
1489
1490#if defined(KW_LOG_ENABLED) && defined(WITH_LOG_FILE)
1491 va_list vaCopy;
1492# if defined(va_copy) || !defined(_MSC_VER) || _MSC_VER >= 1700 /*??*/
1493 va_copy(vaCopy, va);
1494# else
1495 vaCopy = va;
1496# endif
1497 kwDebuggerPrintf("kWorker: error: ");
1498 kwDebuggerPrintfV(pszFormat, vaCopy);
1499#endif
1500
1501 fprintf(stderr, "kWorker: error: ");
1502 vfprintf(stderr, pszFormat, va);
1503 fflush(stderr); /* In case it's a pipe. */
1504
1505 SetLastError(dwSavedErr);
1506}
1507
1508
1509/**
1510 * Error printing.
1511 * @param pszFormat Message format string.
1512 * @param ... Format argument.
1513 */
1514static void kwErrPrintf(const char *pszFormat, ...)
1515{
1516 va_list va;
1517 va_start(va, pszFormat);
1518 kwErrPrintfV(pszFormat, va);
1519 va_end(va);
1520}
1521
1522
1523/**
1524 * Error printing.
1525 * @return rc;
1526 * @param rc Return value
1527 * @param pszFormat Message format string.
1528 * @param ... Format argument.
1529 */
1530static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1531{
1532 va_list va;
1533 va_start(va, pszFormat);
1534 kwErrPrintfV(pszFormat, va);
1535 va_end(va);
1536 return rc;
1537}
1538
1539
1540#ifdef K_STRICT
1541
1542KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1543{
1544 DWORD const dwSavedErr = GetLastError();
1545
1546 fprintf(stderr,
1547 "\n"
1548 "!!Assertion failed!!\n"
1549 "Expression: %s\n"
1550 "Function : %s\n"
1551 "File: %s\n"
1552 "Line: %d\n"
1553 , pszExpr, pszFunction, pszFile, iLine);
1554
1555 SetLastError(dwSavedErr);
1556}
1557
1558
1559KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1560{
1561 DWORD const dwSavedErr = GetLastError();
1562 va_list va;
1563
1564 va_start(va, pszFormat);
1565 fprintf(stderr, pszFormat, va);
1566 va_end(va);
1567
1568 SetLastError(dwSavedErr);
1569}
1570
1571#endif /* K_STRICT */
1572
1573
1574/**
1575 * Hashes a string.
1576 *
1577 * @returns 32-bit string hash.
1578 * @param pszString String to hash.
1579 */
1580static KU32 kwStrHash(const char *pszString)
1581{
1582 /* This algorithm was created for sdbm (a public-domain reimplementation of
1583 ndbm) database library. it was found to do well in scrambling bits,
1584 causing better distribution of the keys and fewer splits. it also happens
1585 to be a good general hashing function with good distribution. the actual
1586 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1587 is the faster version used in gawk. [there is even a faster, duff-device
1588 version] the magic constant 65599 was picked out of thin air while
1589 experimenting with different constants, and turns out to be a prime.
1590 this is one of the algorithms used in berkeley db (see sleepycat) and
1591 elsewhere. */
1592 KU32 uHash = 0;
1593 KU32 uChar;
1594 while ((uChar = (unsigned char)*pszString++) != 0)
1595 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1596 return uHash;
1597}
1598
1599
1600/**
1601 * Hashes a string.
1602 *
1603 * @returns The string length.
1604 * @param pszString String to hash.
1605 * @param puHash Where to return the 32-bit string hash.
1606 */
1607static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1608{
1609 const char * const pszStart = pszString;
1610 KU32 uHash = 0;
1611 KU32 uChar;
1612 while ((uChar = (unsigned char)*pszString) != 0)
1613 {
1614 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1615 pszString++;
1616 }
1617 *puHash = uHash;
1618 return pszString - pszStart;
1619}
1620
1621
1622/**
1623 * Hashes a string.
1624 *
1625 * @returns The string length in wchar_t units.
1626 * @param pwszString String to hash.
1627 * @param puHash Where to return the 32-bit string hash.
1628 */
1629static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1630{
1631 const wchar_t * const pwszStart = pwszString;
1632 KU32 uHash = 0;
1633 KU32 uChar;
1634 while ((uChar = *pwszString) != 0)
1635 {
1636 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1637 pwszString++;
1638 }
1639 *puHash = uHash;
1640 return pwszString - pwszStart;
1641}
1642
1643
1644/**
1645 * Converts the given string to unicode.
1646 *
1647 * @returns Length of the resulting string in wchar_t's.
1648 * @param pszSrc The source string.
1649 * @param pwszDst The destination buffer.
1650 * @param cwcDst The size of the destination buffer in wchar_t's.
1651 */
1652static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1653{
1654 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1655 KSIZE offDst = 0;
1656 while (offDst < cwcDst)
1657 {
1658 char ch = *pszSrc++;
1659 pwszDst[offDst++] = ch;
1660 if (!ch)
1661 return offDst - 1;
1662 kHlpAssert((unsigned)ch < 127);
1663 }
1664
1665 pwszDst[offDst - 1] = '\0';
1666 return offDst;
1667}
1668
1669
1670/**
1671 * Converts the given string to UTF-16, allocating the buffer.
1672 *
1673 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1674 * the source string.
1675 * @param pchSrc The source string.
1676 * @param cchSrc The length of the source string.
1677 */
1678static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1679{
1680 DWORD const dwErrSaved = GetLastError();
1681 KSIZE cwcBuf = cchSrc + 1;
1682 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1683 if (pwszBuf)
1684 {
1685 if (cchSrc > 0)
1686 {
1687 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1688 if (cwcRet > 0)
1689 {
1690 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1691 pwszBuf[cwcRet] = '\0';
1692 }
1693 else
1694 {
1695 kHlpFree(pwszBuf);
1696
1697 /* Figure the length and allocate the right buffer size. */
1698 SetLastError(NO_ERROR);
1699 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1700 if (cwcRet)
1701 {
1702 cwcBuf = cwcRet + 2;
1703 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1704 if (pwszBuf)
1705 {
1706 SetLastError(NO_ERROR);
1707 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1708 if (cwcRet)
1709 {
1710 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1711 pwszBuf[cwcRet] = '\0';
1712 }
1713 else
1714 {
1715 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1716 kHlpFree(pwszBuf);
1717 pwszBuf = NULL;
1718 }
1719 }
1720 }
1721 else
1722 {
1723 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1724 pwszBuf = NULL;
1725 }
1726 }
1727 }
1728 else
1729 pwszBuf[0] = '\0';
1730 }
1731 SetLastError(dwErrSaved);
1732 return pwszBuf;
1733}
1734
1735
1736/**
1737 * Converts the given UTF-16 to a normal string.
1738 *
1739 * @returns Length of the resulting string.
1740 * @param pwszSrc The source UTF-16 string.
1741 * @param pszDst The destination buffer.
1742 * @param cbDst The size of the destination buffer in bytes.
1743 */
1744static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1745{
1746 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1747 KSIZE offDst = 0;
1748 while (offDst < cbDst)
1749 {
1750 wchar_t wc = *pwszSrc++;
1751 pszDst[offDst++] = (char)wc;
1752 if (!wc)
1753 return offDst - 1;
1754 kHlpAssert((unsigned)wc < 127);
1755 }
1756
1757 pszDst[offDst - 1] = '\0';
1758 return offDst;
1759}
1760
1761
1762/**
1763 * Converts the given UTF-16 to ASSI, allocating the buffer.
1764 *
1765 * @returns Pointer to the new heap allocation containing the ANSI version of
1766 * the source string.
1767 * @param pwcSrc The source string.
1768 * @param cwcSrc The length of the source string.
1769 */
1770static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1771{
1772 DWORD const dwErrSaved = GetLastError();
1773 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1774 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1775 if (pszBuf)
1776 {
1777 if (cwcSrc > 0)
1778 {
1779 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1780 if (cchRet > 0)
1781 {
1782 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1783 pszBuf[cchRet] = '\0';
1784 }
1785 else
1786 {
1787 kHlpFree(pszBuf);
1788
1789 /* Figure the length and allocate the right buffer size. */
1790 SetLastError(NO_ERROR);
1791 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1792 if (cchRet)
1793 {
1794 cbBuf = cchRet + 2;
1795 pszBuf = (char *)kHlpAlloc(cbBuf);
1796 if (pszBuf)
1797 {
1798 SetLastError(NO_ERROR);
1799 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1800 if (cchRet)
1801 {
1802 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1803 pszBuf[cchRet] = '\0';
1804 }
1805 else
1806 {
1807 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1808 kHlpFree(pszBuf);
1809 pszBuf = NULL;
1810 }
1811 }
1812 }
1813 else
1814 {
1815 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1816 pszBuf = NULL;
1817 }
1818 }
1819 }
1820 else
1821 pszBuf[0] = '\0';
1822 }
1823 SetLastError(dwErrSaved);
1824 return pszBuf;
1825}
1826
1827
1828
1829/** UTF-16 string length. */
1830static KSIZE kwUtf16Len(wchar_t const *pwsz)
1831{
1832 KSIZE cwc = 0;
1833 while (*pwsz != '\0')
1834 cwc++, pwsz++;
1835 return cwc;
1836}
1837
1838/**
1839 * Copy out the UTF-16 string following the convension of GetModuleFileName
1840 */
1841static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1842{
1843 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1844 if (cwcSrc + 1 <= cwcDst)
1845 {
1846 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1847 return (DWORD)cwcSrc;
1848 }
1849 if (cwcDst > 0)
1850 {
1851 KSIZE cwcDstTmp = cwcDst - 1;
1852 pwszDst[cwcDstTmp] = '\0';
1853 if (cwcDstTmp > 0)
1854 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1855 }
1856 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1857 return (DWORD)cwcDst;
1858}
1859
1860
1861/**
1862 * Copy out the ANSI string following the convension of GetModuleFileName
1863 */
1864static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1865{
1866 KSIZE cchSrc = kHlpStrLen(pszSrc);
1867 if (cchSrc + 1 <= cbDst)
1868 {
1869 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1870 return (DWORD)cchSrc;
1871 }
1872 if (cbDst > 0)
1873 {
1874 KSIZE cbDstTmp = cbDst - 1;
1875 pszDst[cbDstTmp] = '\0';
1876 if (cbDstTmp > 0)
1877 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1878 }
1879 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1880 return (DWORD)cbDst;
1881}
1882
1883
1884/**
1885 * Normalizes the path so we get a consistent hash.
1886 *
1887 * @returns status code.
1888 * @param pszPath The path.
1889 * @param pszNormPath The output buffer.
1890 * @param cbNormPath The size of the output buffer.
1891 */
1892static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1893{
1894 KFSLOOKUPERROR enmError;
1895 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1896 if (pFsObj)
1897 {
1898 KBOOL fRc;
1899 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1900 kFsCacheObjRelease(g_pFsCache, pFsObj);
1901 if (fRc)
1902 return 0;
1903 return KERR_BUFFER_OVERFLOW;
1904 }
1905 return KERR_FILE_NOT_FOUND;
1906}
1907
1908
1909/**
1910 * Get the pointer to the filename part of the path.
1911 *
1912 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1913 * @returns Pointer to the terminator char if no filename.
1914 * @param pszPath The path to parse.
1915 */
1916static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1917{
1918 const wchar_t *pwszLast = NULL;
1919 for (;;)
1920 {
1921 wchar_t wc = *pwszPath;
1922#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1923 if (wc == '/' || wc == '\\' || wc == ':')
1924 {
1925 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1926 /* nothing */;
1927 pwszLast = pwszPath;
1928 }
1929#else
1930 if (wc == '/')
1931 {
1932 while ((wc = *++pszFilename) == '/')
1933 /* betsuni */;
1934 pwszLast = pwszPath;
1935 }
1936#endif
1937 if (!wc)
1938 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1939 pwszPath++;
1940 }
1941}
1942
1943
1944
1945/**
1946 * Retains a new reference to the given module
1947 * @returns pMod
1948 * @param pMod The module to retain.
1949 */
1950static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1951{
1952 kHlpAssert(pMod->cRefs > 0);
1953 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
1954 pMod->cRefs++;
1955 return pMod;
1956}
1957
1958
1959/**
1960 * Releases a module reference.
1961 *
1962 * @param pMod The module to release.
1963 */
1964static void kwLdrModuleRelease(PKWMODULE pMod)
1965{
1966 if (--pMod->cRefs == 0)
1967 {
1968 /* Make sure it doesn't receive any more native TLS callbacks.if non-native. */
1969 if (!pMod->fNative && pMod->u.Manual.ppTlsWorkerModuleVar)
1970 {
1971 *pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1972 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
1973 }
1974
1975 /* Unlink it from the hash table. */
1976 if (!pMod->fExe)
1977 {
1978 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1979 if (g_apModules[idx] == pMod)
1980 g_apModules[idx] = pMod->pNextHash;
1981 else
1982 {
1983 PKWMODULE pPrev = g_apModules[idx];
1984 kHlpAssert(pPrev != NULL);
1985 while (pPrev->pNextHash != pMod)
1986 {
1987 pPrev = pPrev->pNextHash;
1988 kHlpAssert(pPrev != NULL);
1989 }
1990 pPrev->pNextHash = pMod->pNextHash;
1991 }
1992 }
1993
1994 /* Unlink it from the list. */
1995 if (pMod != g_pModuleHead)
1996 {
1997 PKWMODULE pPrev = g_pModuleHead;
1998 while (pPrev)
1999 {
2000 if (pPrev->pNextList == pMod)
2001 {
2002 pPrev->pNextList = pMod->pNextList;
2003 if (!pMod->pNextList)
2004 g_ppModuleNext = &pPrev->pNextList;
2005 break;
2006 }
2007 pPrev = pPrev->pNextList;
2008 }
2009 kHlpAssert(pPrev != NULL);
2010 }
2011 else
2012 {
2013 g_pModuleHead = pMod->pNextList;
2014 if (!pMod->pNextList)
2015 g_ppModuleNext = &g_pModuleHead;
2016 }
2017
2018 /* Release import modules. */
2019 if (!pMod->fNative)
2020 {
2021 KSIZE idx = pMod->u.Manual.cImpMods;
2022 while (idx-- > 0)
2023 if (pMod->u.Manual.apImpMods[idx])
2024 {
2025 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
2026 pMod->u.Manual.apImpMods[idx] = NULL;
2027 }
2028 }
2029
2030 /* Free our resources. */
2031 kLdrModClose(pMod->pLdrMod);
2032 pMod->pLdrMod = NULL;
2033
2034 if (!pMod->fNative)
2035 {
2036 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
2037 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2038 }
2039
2040 if (pMod->iCrtSlot != KU8_MAX)
2041 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
2042
2043 if (pMod->pszMsPdbSrvEndpoint)
2044 {
2045 kHlpFree(pMod->pszMsPdbSrvEndpoint);
2046 pMod->pszMsPdbSrvEndpoint = NULL;
2047 }
2048
2049 kHlpFree(pMod);
2050 }
2051 else
2052 kHlpAssert(pMod->cRefs < 64 || pMod->fNative /* kernelbase.dll and VC++ 14.2 */);
2053}
2054
2055
2056/**
2057 * Links the module into the module hash table.
2058 *
2059 * @returns pMod
2060 * @param pMod The module to link.
2061 */
2062static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
2063{
2064 if (!pMod->fExe)
2065 {
2066 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
2067 pMod->pNextHash = g_apModules[idx];
2068 g_apModules[idx] = pMod;
2069 }
2070
2071 pMod->pNextList = NULL;
2072 *g_ppModuleNext = pMod;
2073 g_ppModuleNext = &pMod->pNextList;
2074
2075 return pMod;
2076}
2077
2078
2079/**
2080 * Replaces imports for this module according to g_aSandboxNativeReplacements.
2081 *
2082 * @param pMod The natively loaded module to process.
2083 */
2084static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
2085{
2086 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
2087 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
2088 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
2089 IMAGE_NT_HEADERS const *pNtHdrs;
2090 IMAGE_DATA_DIRECTORY const *pDirEnt;
2091
2092 kHlpAssert(pMod->fNative);
2093
2094 /*
2095 * Locate the export descriptors.
2096 */
2097 /* MZ header. */
2098 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
2099 {
2100 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
2101 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
2102 }
2103 else
2104 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
2105
2106 /* Check PE header. */
2107 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2108 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
2109
2110 /* Locate the import descriptor array. */
2111 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
2112 if ( pDirEnt->Size > 0
2113 && pDirEnt->VirtualAddress != 0)
2114 {
2115 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
2116 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
2117 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
2118 KU8 *pbProtRange = NULL;
2119 SIZE_T cbProtRange = 0;
2120 DWORD fOldProt = 0;
2121 KU32 const cbPage = 0x1000;
2122 BOOL fRc;
2123
2124
2125 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
2126 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
2127 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
2128
2129 /*
2130 * Walk the import descriptor array.
2131 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
2132 */
2133 while ( cLeft-- > 0
2134 && pImpDesc->Name > 0
2135 && pImpDesc->FirstThunk > 0)
2136 {
2137 KU32 iThunk;
2138 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
2139 PKWMODULE pImportMod = NULL;
2140 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
2141 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
2142 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
2143 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
2144 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
2145 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
2146 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
2147
2148 /* Iterate the thunks. */
2149 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
2150 {
2151 KUPTR const off = paOrgThunks[iThunk].u1.Function;
2152 kHlpAssertReturnVoid(off < cbImage);
2153 if (!IMAGE_SNAP_BY_ORDINAL(off))
2154 {
2155 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
2156 KSIZE const cchSymbol = kHlpStrLen((const char *)&pName->Name[0]);
2157 KU32 i = g_cSandboxNativeReplacements;
2158 while (i-- > 0)
2159 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
2160 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
2161 {
2162 if ( !g_aSandboxNativeReplacements[i].pszModule
2163 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
2164 {
2165 KWLDR_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
2166
2167 /* The .rdata section is normally read-only, so we need to make it writable first. */
2168 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
2169 {
2170 /* Restore previous .rdata page. */
2171 if (fOldProt)
2172 {
2173 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
2174 kHlpAssert(fRc);
2175 fOldProt = 0;
2176 }
2177
2178 /* Query attributes for the current .rdata page. */
2179 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
2180 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
2181 kHlpAssert(cbProtRange);
2182 if (cbProtRange)
2183 {
2184 switch (ProtInfo.Protect)
2185 {
2186 case PAGE_READWRITE:
2187 case PAGE_WRITECOPY:
2188 case PAGE_EXECUTE_READWRITE:
2189 case PAGE_EXECUTE_WRITECOPY:
2190 /* Already writable, nothing to do. */
2191 fRc = TRUE;
2192 break;
2193
2194 default:
2195 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2196 case PAGE_READONLY:
2197 cbProtRange = cbPage;
2198 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2199 break;
2200
2201 case PAGE_EXECUTE:
2202 case PAGE_EXECUTE_READ:
2203 cbProtRange = cbPage;
2204 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2205 break;
2206 }
2207 kHlpAssertStmt(fRc, fOldProt = 0);
2208 }
2209 }
2210
2211 /*
2212 * Unslotted replacements are simple.
2213 */
2214 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2215 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2216 else
2217 {
2218 /*
2219 * Must find our module entry for this module, possibly creating one.
2220 */
2221 if (!pImportMod)
2222 {
2223 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/,
2224 K_TRUE /*fAlwaysPresent*/);
2225 if (!pImportMod)
2226 {
2227 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2228 pszImport, pMod->pszPath);
2229 break;
2230 }
2231 }
2232 paThunks[iThunk].u1.AddressOfData
2233 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2234 }
2235 break;
2236 }
2237 }
2238 }
2239 }
2240
2241
2242 /* Next import descriptor. */
2243 pImpDesc++;
2244 }
2245
2246
2247 if (fOldProt)
2248 {
2249 DWORD fIgnore = 0;
2250 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2251 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2252 }
2253 }
2254
2255}
2256
2257
2258/**
2259 * Creates a module from a native kLdr module handle.
2260 *
2261 * @returns Module w/ 1 reference on success, NULL on failure.
2262 * @param pLdrMod The native kLdr module.
2263 * @param pszPath The normalized path to the module.
2264 * @param cbPath The module path length with terminator.
2265 * @param uHashPath The module path hash.
2266 * @param fDoReplacements Whether to do import replacements on this
2267 * module.
2268 */
2269static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2270 KBOOL fDoReplacements, PKWMODULE pVirtualApiMod)
2271{
2272 /*
2273 * Create the entry.
2274 */
2275 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2276 if (pMod)
2277 {
2278 pMod->pwszPath = (wchar_t *)(pMod + 1);
2279 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2280 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2281 pMod->uHashPath = uHashPath;
2282 pMod->cRefs = 1;
2283 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2284 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2285 pMod->fExe = K_FALSE;
2286 pMod->fNative = K_TRUE;
2287 pMod->pLdrMod = pLdrMod;
2288 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2289 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2290 pMod->iCrtSlot = KU8_MAX;
2291 pMod->fNeedReInit = K_FALSE;
2292 pMod->pszMsPdbSrvEndpoint = NULL;
2293 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2294 pMod->pVirtualApiMod = pVirtualApiMod;
2295 if (pVirtualApiMod)
2296 kwLdrModuleRetain(pVirtualApiMod);
2297
2298 if (fDoReplacements)
2299 {
2300 DWORD const dwSavedErr = GetLastError();
2301 kwLdrModuleDoNativeImportReplacements(pMod);
2302 SetLastError(dwSavedErr);
2303 }
2304
2305 KWLDR_LOG(("New module: %p LB %#010x %s (native%s%s)\n",
2306 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath,
2307 pVirtualApiMod ? ", virtual api => " : "", pVirtualApiMod ? pVirtualApiMod->pszPath : ""));
2308 g_cModules++;
2309 return kwLdrModuleLink(pMod);
2310 }
2311 return NULL;
2312}
2313
2314
2315
2316/**
2317 * Creates a module using the native loader.
2318 *
2319 * @returns Module w/ 1 reference on success, NULL on failure.
2320 * @param pszPath The normalized path to the module.
2321 * @param uHashPath The module path hash.
2322 * @param fDoReplacements Whether to do import replacements on this
2323 * module.
2324 */
2325static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2326{
2327 PKLDRMOD pLdrMod;
2328 int rc;
2329
2330 /*
2331 * HACK ALERT! Make sure the application path is searched when looking for
2332 * imports in the module we're loading.
2333 */
2334 /** @todo improve on this hack! */
2335 PKWMODULE pExe = g_Sandbox.pTool ? g_Sandbox.pTool->u.Sandboxed.pExe : NULL;
2336 if (pExe)
2337 {
2338 /* HACK ALERT! */
2339 wchar_t *pwzFilename = (wchar_t *)&pExe->pwszPath[pExe->offFilenameW];
2340 wchar_t wcSaved = pExe->pwszPath[pExe->offFilenameW];
2341 *pwzFilename = '\0';
2342 if (!SetDllDirectoryW(pExe->pwszPath))
2343 kwErrPrintf("SetDllDirectoryW failed: %u\n", GetLastError());
2344 KW_LOG(("kwLdrModuleCreateNative: Applied SetDllDirectoryW hack (%ls)\n", pExe->pwszPath));
2345 *pwzFilename = wcSaved;
2346 }
2347 else
2348 KW_LOG(("kwLdrModuleCreateNative: Warning! Too early for SetDllDirectoryW hack\n"));
2349
2350
2351 /*
2352 * Load the library and create a module structure for it.
2353 */
2354 rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2355 if (rc == 0)
2356 {
2357 KSIZE cchPath = kHlpStrLen(pszPath);
2358 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, cchPath + 1, uHashPath,
2359 fDoReplacements, NULL /*pVirtualApiMod*/);
2360 if (pMod)
2361 return pMod;
2362 kLdrModClose(pLdrMod);
2363 }
2364 return NULL;
2365}
2366
2367
2368/**
2369 * Checks if the given name could be a virtual API module or not.
2370 */
2371static KBOOL kwLdrIsVirtualApiModule(const char *pszName, KSIZE cchName)
2372{
2373 if (cchName <= 7)
2374 return K_FALSE;
2375 switch (*pszName)
2376 {
2377 default:
2378 return K_FALSE;
2379 case 'a':
2380 case 'A':
2381 if (pszName[1] != 'p' && pszName[1] != 'P')
2382 return K_FALSE;
2383 if (pszName[2] != 'i' && pszName[2] != 'I')
2384 return K_FALSE;
2385 break;
2386 case 'e':
2387 case 'E':
2388 if (pszName[1] != 'x' && pszName[1] != 'X')
2389 return K_FALSE;
2390 if (pszName[2] != 't' && pszName[2] != 'T')
2391 return K_FALSE;
2392 break;
2393 }
2394 if (pszName[3] != '-')
2395 return K_FALSE;
2396 if (pszName[4] != 'm' && pszName[4] != 'M')
2397 return K_FALSE;
2398 if (pszName[5] != 's' && pszName[5] != 'S')
2399 return K_FALSE;
2400 if (pszName[6] != '-')
2401 return K_FALSE;
2402 return K_TRUE;
2403}
2404
2405
2406/**
2407 * Try load what seems to be a virtual API DLL.
2408 *
2409 * This is a worker for kwLdrModuleResolveAndLookup and
2410 * kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule.
2411 *
2412 * @returns Pointer to module on success, NULL on failure.
2413 * @param pszName The name of the module. This must be
2414 * normalized already!
2415 * @param cchName The length of the name.
2416 */
2417static PKWMODULE kwLdrModuleTryLoadVirtualDll(const char *pszName, KSIZE cchName)
2418{
2419 HMODULE hModule;
2420
2421 /*
2422 * Look it up in the hash table.
2423 */
2424 KU32 const uHashPath = kwStrHash(pszName);
2425 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2426 PKWMODULE pMod = g_apModules[idxHash];
2427 if (pMod)
2428 {
2429 do
2430 {
2431 if ( pMod->uHashPath == uHashPath
2432 && kHlpStrComp(pMod->pszPath, pszName) == 0)
2433 return kwLdrModuleRetain(pMod);
2434 pMod = pMod->pNextHash;
2435 } while (pMod);
2436 }
2437
2438 /*
2439 * Not found. Try load it.
2440 */
2441 hModule = LoadLibraryA(pszName);
2442 if (!hModule)
2443 {
2444 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s failed (%u)\n", pszName, GetLastError()));
2445 return NULL;
2446 }
2447
2448 /*
2449 * Loaded successfully. Create a module for the real module.
2450 */
2451 pMod = kwLdrModuleForLoadedNativeByHandle(hModule, K_FALSE /*fEnsureCrtSlot*/, pszName);
2452 if (pMod)
2453 {
2454 /* Create a module for the virtual API name too, unless it is actually a real DLL. */
2455 if (stricmp(&pMod->pszPath[pMod->offFilename], pszName) != 0)
2456 {
2457 PKLDRMOD pLdrMod;
2458 int rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2459 if (rc == 0)
2460 {
2461 PKWMODULE pVirtMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszName, cchName + 1, kwStrHash(pszName),
2462 K_FALSE /*fDoReplacements*/, pMod /*pVirtualApiMod*/);
2463 if (pVirtMod)
2464 {
2465 kwLdrModuleRelease(pMod);
2466 pMod = pVirtMod;
2467 }
2468 else
2469 {
2470 kLdrModClose(pLdrMod);
2471 kwErrPrintf("out of memory\n");
2472 }
2473 }
2474 else
2475 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
2476 }
2477 else
2478 KWLDR_LOG(("kwLdrModuleTryLoadVirtualDll: %s -> %s - A real DLL!\n", pszName, pMod->pszPath));
2479 }
2480
2481 return pMod;
2482}
2483
2484
2485/**
2486 * Sets up the quick zero & copy tables for the non-native module.
2487 *
2488 * This is a worker for kwLdrModuleCreateNonNative.
2489 *
2490 * @param pMod The module.
2491 */
2492static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2493{
2494 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2495 KU32 cSegs = pMod->pLdrMod->cSegments;
2496 KU32 iSeg;
2497
2498 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2499 pMod->u.Manual.cQuickCopyChunks = 0;
2500 pMod->u.Manual.cQuickZeroChunks = 0;
2501
2502 for (iSeg = 0; iSeg < cSegs; iSeg++)
2503 switch (paSegs[iSeg].enmProt)
2504 {
2505 case KPROT_READWRITE:
2506 case KPROT_WRITECOPY:
2507 case KPROT_EXECUTE_READWRITE:
2508 case KPROT_EXECUTE_WRITECOPY:
2509 if (paSegs[iSeg].cbMapped)
2510 {
2511 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2512 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2513 {
2514 /*
2515 * Check for trailing zero words.
2516 */
2517 KSIZE cbTrailingZeros;
2518 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2519 && (paSegs[iSeg].cbMapped & 7) == 0
2520 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2521 {
2522 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2523 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2524 KSIZE idxFirstZero = cNatural;
2525 while (idxFirstZero > 0)
2526 if (pauNatural[--idxFirstZero] == 0)
2527 { /* likely */ }
2528 else
2529 {
2530 idxFirstZero++;
2531 break;
2532 }
2533 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2534 if (cbTrailingZeros < 128)
2535 cbTrailingZeros = 0;
2536 }
2537 else
2538 cbTrailingZeros = 0;
2539
2540 /*
2541 * Add quick copy entry.
2542 */
2543 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2544 {
2545 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2546 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2547 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2548 pMod->u.Manual.cQuickCopyChunks = (KU8)(iChunk + 1);
2549 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2550 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2551 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2552 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2553 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2554 }
2555
2556 /*
2557 * Add quick zero entry.
2558 */
2559 if (cbTrailingZeros)
2560 {
2561 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2562 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2563 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2564 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2565 pMod->u.Manual.cQuickZeroChunks = (KU8)(iZero + 1);
2566 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2567 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2568 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2569 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2570 }
2571 }
2572 else
2573 {
2574 /*
2575 * We're out of quick copy table entries, so just copy the whole darn thing.
2576 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2577 */
2578 kHlpAssertFailed();
2579 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2580 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2581 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2582 pMod->u.Manual.cQuickCopyChunks = 1;
2583 KWLDR_LOG(("Quick copy not possible!\n"));
2584 return;
2585 }
2586 }
2587 break;
2588
2589 default:
2590 break;
2591 }
2592}
2593
2594
2595/**
2596 * Called from the TLS allocation DLL when ever the native loader wants to issue
2597 * a TLS callback after the initial kwLdrTlsAllocationHook callout.
2598 *
2599 * @param hDll The DLL handle.
2600 * @param dwReason The callback reason.
2601 * @param pvContext Some context value that seems to always be NULL.
2602 * @param pMod Out internal module.
2603 */
2604static void kwLdrTlsNativeLoaderCallback(void *hDll, DWORD dwReason, void *pvContext, PKWMODULE pMod)
2605{
2606 if ( pMod
2607 && pMod->u.Manual.enmState == KWMODSTATE_READY)
2608 {
2609 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p\n",
2610 hDll, dwReason, pvContext, pMod));
2611 if (pMod->u.Manual.cTlsCallbacks)
2612 {
2613 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
2614 do
2615 {
2616 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: Calling TLS callback %p(%p, %#x, %p) - %s\n",
2617 *ppfnCallback, pMod->hOurMod, dwReason, pvContext, pMod->pszPath));
2618 (*ppfnCallback)(pMod->hOurMod, dwReason, pvContext);
2619 ppfnCallback++;
2620 } while (*ppfnCallback);
2621 }
2622 }
2623 else
2624 KWLDR_LOG(("kwLdrTlsNativeLoaderCallback: hDll=%p dwReason=%#x pvContext=%p pMod=%p - skipped\n",
2625 hDll, dwReason, pvContext, pMod));
2626}
2627
2628
2629/**
2630 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2631 *
2632 * @returns Address of the callback function (kwLdrTlsNativeLoaderCallback).
2633 * @param hDll The DLL handle.
2634 * @param idxTls The allocated TLS index.
2635 * @param pabInitData The init data in the TLS allocation DLL
2636 * (g_abInitData).
2637 * @param ppWorkerModuleVar Pointer to the variable holding the pMod
2638 * callback parameter value (g_pvWorkerModule).
2639 *
2640 * @see KWLDRTLSALLOCATIONHOOK in kWorkerTlsXxxxK.c
2641 */
2642__declspec(dllexport) KUPTR kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, KU8 *pabInitData, PKWMODULE *ppWorkerModuleVar)
2643{
2644 /*
2645 * Do the module initialization thing first.
2646 */
2647 PKWMODULE pMod = g_pModPendingTlsAlloc;
2648 if (pMod)
2649 {
2650 if ( pMod->u.Manual.idxTls == KU32_MAX
2651 && pMod->u.Manual.pabTlsInitData == NULL)
2652 {
2653 pMod->u.Manual.idxTls = idxTls;
2654 pMod->u.Manual.pabTlsInitData = pabInitData;
2655 pMod->u.Manual.ppTlsWorkerModuleVar = ppWorkerModuleVar;
2656 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2657
2658#if 0 /** @todo this doesn't work W10 18363 */
2659 {
2660 /*
2661 * Try sabotage the DLL name so we can load this module again.
2662 */
2663 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2664 LIST_ENTRY *pHead;
2665 LIST_ENTRY *pCur;
2666
2667 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2668 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2669 {
2670 LDR_DATA_TABLE_ENTRY *pMte;
2671 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2672 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2673 {
2674 PUNICODE_STRING pStr = &pMte->FullDllName;
2675 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2676 pStr->Buffer[--off]++;
2677 pStr->Buffer[--off]++;
2678 pStr->Buffer[--off]++;
2679 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2680 break;
2681 }
2682 }
2683 }
2684#endif
2685
2686 /*
2687 * Don't return a callback function unless the module has callbacks to service.
2688 */
2689 if (pMod->u.Manual.cTlsCallbacks > 0)
2690 {
2691 *ppWorkerModuleVar = pMod;
2692 return (KUPTR)kwLdrTlsNativeLoaderCallback;
2693 }
2694 return 0;
2695 }
2696 KWLDR_LOG(("kwLdrTlsAllocationHook: WTF? pMod=%p: idxTls=%#x pabTlsInitData=%p\n",
2697 pMod, pMod->u.Manual.idxTls, pMod->u.Manual.pabTlsInitData));
2698 }
2699 return 0;
2700}
2701
2702
2703/**
2704 * Allocates and initializes TLS variables.
2705 *
2706 * @returns 0 on success, non-zero failure.
2707 * @param pMod The module.
2708 */
2709static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2710{
2711 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2712 IMAGE_NT_HEADERS const *pNtHdrs;
2713 IMAGE_DATA_DIRECTORY const *pTlsDir;
2714
2715 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2716 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2717 else
2718 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2719 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2720
2721 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2722 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2723 {
2724 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2725 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2726 KU32 iEntry;
2727 KU32 iTlsDll;
2728 KU32 iTlsDllSub;
2729 KUPTR offIndex;
2730 KUPTR offCallbacks;
2731 KUPTR const *puCallbacks;
2732 KSIZE cbData;
2733 const wchar_t *pwszTlsDll;
2734 HMODULE hmodTlsDll;
2735
2736 /*
2737 * Check and log.
2738 */
2739 for (iEntry = 0; iEntry < cEntries; iEntry++)
2740 {
2741 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2742 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2743 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2744 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2745 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2746 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2747 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2748
2749 if (offIndex >= pMod->cbImage)
2750 {
2751 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2752 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2753 return -1;
2754 }
2755 if (offCallbacks >= pMod->cbImage)
2756 {
2757 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2758 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2759 return -1;
2760 }
2761 while (*puCallbacks != 0)
2762 {
2763 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2764 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2765 puCallbacks++;
2766 }
2767 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2768 {
2769 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2770 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2771 return -1;
2772 }
2773 }
2774
2775 if (cEntries > 1)
2776 {
2777 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2778 return -1;
2779 }
2780
2781 /*
2782 * Make the allocation by loading a new instance of one of the TLS dlls.
2783 * The DLL will make a call to kwLdrTlsAllocationHook.
2784 */
2785 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2786 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2787 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2788 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2789
2790 /** @todo find better strategy here. Like temporary copy or whatever when
2791 * there is more than a single user. */
2792 for (iTlsDll = 0; cbData > g_aTlsDlls[iTlsDll].cbTls;)
2793 if (++iTlsDll >= K_ELEMENTS(g_aTlsDlls))
2794 {
2795 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2796 return -1;
2797 }
2798 for (iTlsDllSub = 0; g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed;)
2799 if (++iTlsDllSub >= g_aTlsDlls[iTlsDll].cDlls)
2800 {
2801 kwErrPrintf("No unused TLS DLLs for %s of size %u!\n", pMod->pszPath, (unsigned)cbData);
2802 return -1;
2803 }
2804
2805 g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].fUsed = K_TRUE;
2806 pwszTlsDll = g_aTlsDlls[iTlsDll].paDlls[iTlsDllSub].pwszName;
2807
2808 pMod->u.Manual.pabTlsInitData = NULL;
2809 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2810 pMod->u.Manual.idxTls = KU32_MAX;
2811
2812 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2813 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2814 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2815 pMod->u.Manual.cTlsCallbacks = 0;
2816 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2817 pMod->u.Manual.cTlsCallbacks++;
2818 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2819
2820 g_pModPendingTlsAlloc = pMod;
2821 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2822 g_pModPendingTlsAlloc = NULL;
2823 if (hmodTlsDll == NULL)
2824 {
2825 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2826 return -1;
2827 }
2828 if (pMod->u.Manual.idxTls == KU32_MAX)
2829 {
2830 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2831 return -1;
2832 }
2833
2834 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2835 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x pabTlsInitData=%p\n",
2836 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData, pMod->u.Manual.pabTlsInitData));
2837
2838 kHlpAssert(pMod->u.Manual.pabTlsInitData);
2839 if (pMod->u.Manual.pabTlsInitData && pMod->u.Manual.cbTlsInitData)
2840 kHlpMemCopy(pMod->u.Manual.pabTlsInitData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData],
2841 pMod->u.Manual.cbTlsInitData);
2842 }
2843 return 0;
2844}
2845
2846
2847/**
2848 * Creates a module using the our own loader.
2849 *
2850 * @returns Module w/ 1 reference on success, NULL on failure.
2851 * @param pszPath The normalized path to the module.
2852 * @param uHashPath The module path hash.
2853 * @param fExe K_TRUE if this is an executable image, K_FALSE
2854 * if not. Executable images does not get entered
2855 * into the global module table.
2856 * @param pExeMod The executable module of the process (for
2857 * resolving imports). NULL if fExe is set.
2858 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2859 */
2860static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2861 PKWMODULE pExeMod, const char *pszSearchPath)
2862{
2863 /*
2864 * Open the module and check the type.
2865 */
2866 PKLDRMOD pLdrMod;
2867 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2868 if (rc == 0)
2869 {
2870 switch (pLdrMod->enmType)
2871 {
2872 case KLDRTYPE_EXECUTABLE_FIXED:
2873 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2874 case KLDRTYPE_EXECUTABLE_PIC:
2875 if (!fExe)
2876 rc = KERR_GENERAL_FAILURE;
2877 break;
2878
2879 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2880 case KLDRTYPE_SHARED_LIBRARY_PIC:
2881 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2882 if (fExe)
2883 rc = KERR_GENERAL_FAILURE;
2884 break;
2885
2886 default:
2887 rc = KERR_GENERAL_FAILURE;
2888 break;
2889 }
2890 if (rc == 0)
2891 {
2892 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2893 if (cImports >= 0)
2894 {
2895 /*
2896 * Create the entry.
2897 */
2898 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2899 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2900 + sizeof(pMod) * cImports
2901 + cbPath
2902 + cbPath * 2 * sizeof(wchar_t));
2903 if (pMod)
2904 {
2905 KBOOL fFixed;
2906
2907 pMod->cRefs = 1;
2908 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2909 pMod->uHashPath = uHashPath;
2910 pMod->fExe = fExe;
2911 pMod->fNative = K_FALSE;
2912 pMod->pLdrMod = pLdrMod;
2913 pMod->iCrtSlot = KU8_MAX;
2914 pMod->fNeedReInit = K_FALSE;
2915 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2916 pMod->pszMsPdbSrvEndpoint = NULL;
2917 pMod->u.Manual.cImpMods = (KU32)cImports;
2918#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2919 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2920#endif
2921 pMod->u.Manual.fUseLdBuf = K_FALSE;
2922 pMod->u.Manual.fCanDoQuick = K_FALSE;
2923 pMod->u.Manual.cQuickZeroChunks = 0;
2924 pMod->u.Manual.cQuickCopyChunks = 0;
2925 pMod->u.Manual.pabTlsInitData = NULL;
2926 pMod->u.Manual.ppTlsWorkerModuleVar = NULL;
2927 pMod->u.Manual.idxTls = KU32_MAX;
2928 pMod->u.Manual.offTlsInitData = KU32_MAX;
2929 pMod->u.Manual.cbTlsInitData = 0;
2930 pMod->u.Manual.cbTlsAlloc = 0;
2931 pMod->u.Manual.cTlsCallbacks = 0;
2932 pMod->u.Manual.offTlsCallbacks = 0;
2933 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2934 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2935 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2936 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2937
2938 /*
2939 * Figure out where to load it and get memory there.
2940 */
2941 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2942 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2943 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2944 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2945 if ( !fFixed
2946 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2947 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2948 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2949 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2950 else
2951 pMod->u.Manual.fUseLdBuf = K_TRUE;
2952 if (rc == 0)
2953 {
2954 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2955 if (rc == 0)
2956 {
2957 KI32 iImp;
2958 KU32 cchReloads;
2959
2960 /*
2961 * Link the module (unless it's an executable image) and process the imports.
2962 */
2963 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2964 kwLdrModuleLink(pMod);
2965 KWLDR_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2966 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2967 KWLDR_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2968 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2969 cchReloads = g_cchReloads;
2970 if (cchReloads + 80 < sizeof(g_szReloads))
2971 {
2972 cchReloads += _snprintf(&g_szReloads[cchReloads], sizeof(g_szReloads) - cchReloads,
2973 "%s.reload /f %s=%p\n", cchReloads ? "; " : "",
2974 pMod->pszPath, pMod->u.Manual.pbLoad);
2975 g_cchReloads = cchReloads;
2976 }
2977
2978 for (iImp = 0; iImp < cImports; iImp++)
2979 {
2980 char szName[1024];
2981 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2982 if (rc == 0)
2983 {
2984 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2985 &pMod->u.Manual.apImpMods[iImp]);
2986 if (rc == 0)
2987 continue;
2988 }
2989 break;
2990 }
2991
2992 if (rc == 0)
2993 {
2994 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2995 kwLdrModuleGetImportCallback, pMod);
2996 if (rc == 0)
2997 {
2998#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2999 /*
3000 * Find the function table. No validation here because the
3001 * loader did that already, right...
3002 */
3003 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
3004 IMAGE_NT_HEADERS const *pNtHdrs;
3005 IMAGE_DATA_DIRECTORY const *pXcptDir;
3006 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
3007 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
3008 else
3009 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
3010 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
3011 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
3012 if (pXcptDir->Size > 0)
3013 {
3014 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
3015 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
3016 == pXcptDir->Size);
3017 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
3018 }
3019 else
3020 {
3021 pMod->u.Manual.cFunctions = 0;
3022 pMod->u.Manual.paFunctions = NULL;
3023 }
3024#endif
3025
3026 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
3027
3028 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
3029 if (rc == 0)
3030 {
3031 /*
3032 * Final finish.
3033 */
3034 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
3035 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
3036 pMod->u.Manual.enmReInitState = KWMODSTATE_NEEDS_BITS;
3037 if ( g_Sandbox.pTool
3038 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
3039 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3040 && !pMod->fExe)
3041 pMod->u.Manual.enmReInitState = KWMODSTATE_READY;
3042 g_cModules++;
3043 g_cNonNativeModules++;
3044 return pMod;
3045 }
3046 }
3047 else
3048 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
3049 }
3050
3051 kwLdrModuleRelease(pMod);
3052 return NULL;
3053 }
3054
3055 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
3056 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3057 }
3058 else if (fFixed)
3059 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
3060 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
3061 else
3062 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
3063 }
3064 }
3065 }
3066 kLdrModClose(pLdrMod);
3067 }
3068 else
3069 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
3070 return NULL;
3071}
3072
3073
3074/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
3075static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
3076 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
3077{
3078 PKWMODULE pCurMod = (PKWMODULE)pvUser;
3079 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
3080 int rc;
3081 K_NOREF(pMod);
3082
3083 if (pImpMod->fNative)
3084 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
3085 iSymbol, pchSymbol, cchSymbol, pszVersion,
3086 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3087 puValue, pfKind);
3088 else
3089 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
3090 iSymbol, pchSymbol, cchSymbol, pszVersion,
3091 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
3092 puValue, pfKind);
3093 if (rc == 0)
3094 {
3095 KU32 i = g_cSandboxReplacements;
3096 while (i-- > 0)
3097 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
3098 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
3099 {
3100 if ( !g_aSandboxReplacements[i].pszModule
3101 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
3102 {
3103 if ( pCurMod->fExe
3104 || !g_aSandboxReplacements[i].fOnlyExe)
3105 {
3106 KWLDR_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
3107 if (!g_aSandboxReplacements[i].fCrtSlotArray)
3108 *puValue = g_aSandboxReplacements[i].pfnReplacement;
3109 else
3110 {
3111 if (pImpMod->iCrtSlot == KU8_MAX)
3112 {
3113 rc = kwLdrModuleCreateCrtSlot(pImpMod);
3114 if (rc)
3115 KWLDR_LOG(("kwLdrModuleGetImportCallback: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
3116 }
3117 *puValue = ((KUPTR *)g_aSandboxReplacements[i].pfnReplacement)[pImpMod->iCrtSlot];
3118 }
3119 }
3120 break;
3121 }
3122 }
3123 }
3124
3125 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
3126 KWLDR_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
3127 return rc;
3128
3129}
3130
3131
3132/**
3133 * Gets the main entrypoint for a module.
3134 *
3135 * @returns 0 on success, KERR on failure
3136 * @param pMod The module.
3137 * @param puAddrMain Where to return the address.
3138 */
3139static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
3140{
3141 KLDRADDR uLdrAddrMain;
3142 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
3143 if (rc == 0)
3144 {
3145 *puAddrMain = (KUPTR)uLdrAddrMain;
3146 return 0;
3147 }
3148 return rc;
3149}
3150
3151
3152/**
3153 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
3154 *
3155 * @returns K_TRUE/K_FALSE.
3156 * @param pszFilename The filename (no path).
3157 * @param enmLocation The location.
3158 */
3159static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
3160{
3161 if (enmLocation != KWLOCATION_SYSTEM32)
3162 return K_TRUE;
3163 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3164 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3165 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3166 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3167 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3168 || kHlpStrNICompAscii(pszFilename, TUPLE("api-ms-win-crt-")) == 0
3169#if 0 /* for debugging, only for debugging. */
3170 || kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3171 || kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3172 || kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3173#endif
3174 ;
3175}
3176
3177
3178/**
3179 * Lazily initializes the g_pWinSys32 variable.
3180 */
3181static PKFSDIR kwLdrResolveWinSys32(void)
3182{
3183 KFSLOOKUPERROR enmError;
3184 PKFSDIR pWinSys32;
3185
3186 /* Get the path first. */
3187 char szSystem32[MAX_PATH];
3188 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
3189 {
3190 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
3191 strcpy(szSystem32, "C:\\Windows\\System32");
3192 }
3193
3194 /* Look it up and verify it. */
3195 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
3196 if (pWinSys32)
3197 {
3198 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
3199 {
3200 g_pWinSys32 = pWinSys32;
3201 return pWinSys32;
3202 }
3203
3204 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
3205 }
3206 else
3207 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
3208 return NULL;
3209}
3210
3211
3212/**
3213 * Whether we can load this DLL natively or not.
3214 *
3215 * @returns K_TRUE/K_FALSE.
3216 * @param pszFilename The filename (no path).
3217 * @param enmLocation The location.
3218 * @param pszFullPath The full filename and path.
3219 */
3220static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
3221{
3222 if (enmLocation == KWLOCATION_SYSTEM32)
3223 return K_TRUE;
3224 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
3225 return K_TRUE;
3226 if ( enmLocation == KWLOCATION_UNKNOWN
3227 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3228 return K_TRUE;
3229
3230 /* If the location is unknown, we must check if it's some dynamic loading
3231 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
3232 if (enmLocation == KWLOCATION_UNKNOWN)
3233 {
3234 PKFSDIR pWinSys32 = g_pWinSys32;
3235 if (!pWinSys32)
3236 pWinSys32 = kwLdrResolveWinSys32();
3237 if (pWinSys32)
3238 {
3239 KFSLOOKUPERROR enmError;
3240 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
3241 if (pFsObj)
3242 {
3243 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
3244 kFsCacheObjRelease(g_pFsCache, pFsObj);
3245 if (fInWinSys32)
3246 return K_TRUE;
3247 }
3248 }
3249 }
3250
3251 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
3252 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
3253 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0
3254 || kHlpStrNICompAscii(pszFilename, TUPLE("tbbmalloc")) == 0
3255 || kHlpStrNICompAscii(pszFilename, TUPLE("ucrtbase")) == 0
3256 || kHlpStrNICompAscii(pszFilename, TUPLE("vcruntime")) == 0
3257 || ( enmLocation != KWLOCATION_UNKNOWN
3258 && kwLdrIsVirtualApiModule(pszFilename, kHlpStrLen(pszFilename)))
3259#if 0 /* for debugging, only for debugging. */
3260 //|| kHlpStrICompAscii(pszFilename, "c1.dll") == 0
3261 //|| kHlpStrICompAscii(pszFilename, "c1xx.dll") == 0
3262 //|| kHlpStrICompAscii(pszFilename, "c2.dll") == 0
3263#endif
3264 ;
3265}
3266
3267
3268/**
3269 * Check if the path leads to a regular file (that exists).
3270 *
3271 * @returns K_TRUE / K_FALSE
3272 * @param pszPath Path to the file to check out.
3273 */
3274static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
3275{
3276 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
3277 KSIZE cchPath = kHlpStrLen(pszPath);
3278 if ( cchPath > 3
3279 && pszPath[cchPath - 4] == '.'
3280 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
3281 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
3282 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
3283 {
3284 KFSLOOKUPERROR enmError;
3285 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3286 if (pFsObj)
3287 {
3288 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
3289 kFsCacheObjRelease(g_pFsCache, pFsObj);
3290 return fRc;
3291 }
3292 }
3293 else
3294 {
3295 BirdStat_T Stat;
3296 int rc = birdStatFollowLink(pszPath, &Stat);
3297 if (rc == 0)
3298 {
3299 if (S_ISREG(Stat.st_mode))
3300 return K_TRUE;
3301 }
3302 }
3303 return K_FALSE;
3304}
3305
3306
3307/**
3308 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
3309 *
3310 * If the file exists, we consult the module hash table before trying to load it
3311 * off the disk.
3312 *
3313 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
3314 * failure.
3315 * @param pszPath The name of the import module.
3316 * @param enmLocation The location we're searching. This is used in
3317 * the heuristics for determining if we can use the
3318 * native loader or need to sandbox the DLL.
3319 * @param pExe The executable (optional).
3320 * @param pszSearchPath The PATH to search (optional).
3321 */
3322static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
3323{
3324 /*
3325 * Does the file exists and is it a regular file?
3326 */
3327 if (kwLdrModuleIsRegularFile(pszPath))
3328 {
3329 /*
3330 * Yes! Normalize it and look it up in the hash table.
3331 */
3332 char szNormPath[1024];
3333 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
3334 if (rc == 0)
3335 {
3336 const char *pszName;
3337 KU32 const uHashPath = kwStrHash(szNormPath);
3338 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3339 PKWMODULE pMod = g_apModules[idxHash];
3340 if (pMod)
3341 {
3342 do
3343 {
3344 if ( pMod->uHashPath == uHashPath
3345 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3346 return kwLdrModuleRetain(pMod);
3347 pMod = pMod->pNextHash;
3348 } while (pMod);
3349 }
3350
3351 /*
3352 * Not in the hash table, so we have to load it from scratch.
3353 */
3354 pszName = kHlpGetFilename(szNormPath);
3355 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
3356 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
3357 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
3358 else
3359 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
3360 if (pMod)
3361 return pMod;
3362 return (PKWMODULE)~(KUPTR)0;
3363 }
3364 }
3365 return NULL;
3366}
3367
3368
3369/**
3370 * Gets a reference to the module by the given name.
3371 *
3372 * We must do the search path thing, as our hash table may multiple DLLs with
3373 * the same base name due to different tools version and similar. We'll use a
3374 * modified search sequence, though. No point in searching the current
3375 * directory for instance.
3376 *
3377 * @returns 0 on success, KERR on failure.
3378 * @param pszName The name of the import module.
3379 * @param pExe The executable (optional).
3380 * @param pImporter The module doing the importing (optional).
3381 * @param pszSearchPath The PATH to search (optional).
3382 * @param ppMod Where to return the module pointer w/ reference.
3383 */
3384static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
3385 const char *pszSearchPath, PKWMODULE *ppMod)
3386{
3387 KSIZE const cchName = kHlpStrLen(pszName);
3388 char szPath[1024];
3389 char *psz;
3390 PKWMODULE pMod = NULL;
3391 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
3392 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
3393
3394 /* Virtual API module. Normalize and try load it. */
3395 if (pMod == NULL && cchName > 7 && kwLdrIsVirtualApiModule(pszName, cchName))
3396 {
3397 if (cchName + cchSuffix >= sizeof(szPath))
3398 return KERR_BUFFER_OVERFLOW;
3399 kHlpMemCopy(szPath, pszName, cchName);
3400 if (fNeedSuffix)
3401 kHlpMemCopy(&szPath[cchName], ".dll", sizeof(".dll"));
3402 szPath[cchName + cchSuffix] = '\0';
3403 _strlwr(szPath);
3404 pMod = kwLdrModuleTryLoadVirtualDll(szPath, cchName + cchSuffix);
3405 }
3406
3407 /* The import path. */
3408 if (pMod == NULL && pImporter != NULL)
3409 {
3410 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
3411 return KERR_BUFFER_OVERFLOW;
3412
3413 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
3414 if (fNeedSuffix)
3415 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3416 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
3417 }
3418
3419 /* Application directory first. */
3420 if (pMod == NULL && pExe != NULL && pExe != pImporter)
3421 {
3422 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
3423 return KERR_BUFFER_OVERFLOW;
3424 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
3425 if (fNeedSuffix)
3426 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3427 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
3428 }
3429
3430 /* The windows directory. */
3431 if (pMod == NULL)
3432 {
3433 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
3434 if ( cchDir <= 2
3435 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
3436 return KERR_BUFFER_OVERFLOW;
3437 szPath[cchDir++] = '\\';
3438 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
3439 if (fNeedSuffix)
3440 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
3441 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3442 }
3443
3444 /* The path. */
3445 if ( pMod == NULL
3446 && pszSearchPath)
3447 {
3448 const char *pszCur = pszSearchPath;
3449 while (*pszCur != '\0')
3450 {
3451 /* Find the end of the component */
3452 KSIZE cch = 0;
3453 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
3454 cch++;
3455
3456 if ( cch > 0 /* wrong, but whatever */
3457 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
3458 {
3459 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
3460 if ( szPath[cch - 1] != ':'
3461 && szPath[cch - 1] != '/'
3462 && szPath[cch - 1] != '\\')
3463 *pszDst++ = '\\';
3464 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
3465 if (fNeedSuffix)
3466 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
3467 *pszDst = '\0';
3468
3469 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
3470 if (pMod)
3471 break;
3472 }
3473
3474 /* Advance */
3475 pszCur += cch;
3476 while (*pszCur == ';')
3477 pszCur++;
3478 }
3479 }
3480
3481 /* Return. */
3482 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3483 {
3484 *ppMod = pMod;
3485 return 0;
3486 }
3487 *ppMod = NULL;
3488 return KERR_GENERAL_FAILURE;
3489}
3490
3491
3492/**
3493 * Creates a CRT slot for the given module.
3494 *
3495 * @returns 0 on success, non-zero on failure.
3496 * @param pModule The module.
3497 */
3498static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3499{
3500 KSIZE iSlot;
3501 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3502 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3503 if (g_aCrtSlots[iSlot].pModule == NULL)
3504 {
3505 KLDRADDR uAddr;
3506 int rc;
3507
3508 /* Do the linking: */
3509 g_aCrtSlots[iSlot].pModule = pModule;
3510 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3511 pModule->iCrtSlot = (KU8)iSlot;
3512
3513 /* resolve symbols: */
3514 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3515 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3516 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3517 if (rc != 0)
3518 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3519
3520 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "_beginthreadex", 14,
3521 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3522 *(KUPTR *)&g_aCrtSlots[iSlot].pfnBeginThreadEx = rc == 0 ? (KUPTR)uAddr : 0;
3523 //if (rc != 0)
3524 // kwErrPrintf("Failed to resolved '_beginthreadex' in '%s': %d\n", pModule->pszPath, rc);
3525
3526 return 0;
3527 }
3528 kwErrPrintf("Out of CRT slots!\n");
3529 return KERR_NO_MEMORY;
3530}
3531
3532
3533/**
3534 * Locates the module structure for an already loaded native module.
3535 *
3536 * This will create a module structure if needed.
3537 *
3538 * @returns Pointer to the module structure on success, NULL on failure.
3539 * @param hModule The native module handle.
3540 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3541 * @param pszLogName The name to use for logging/errors.
3542 */
3543static PKWMODULE kwLdrModuleForLoadedNativeByHandle(HMODULE hModule, KBOOL fEnsureCrtSlot, const char *pszLogName)
3544{
3545 /*
3546 * Get a normalized path for it.
3547 */
3548 char szModPath[1024];
3549 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3550 {
3551 char szNormPath[1024];
3552 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3553 if (rc == 0)
3554 {
3555 /*
3556 * Hash the path and look it up.
3557 */
3558 KU32 uHashPath;
3559 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3560 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3561 PKWMODULE pMod = g_apModules[idxHash];
3562 if (pMod)
3563 {
3564 do
3565 {
3566 if ( pMod->uHashPath == uHashPath
3567 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3568 {
3569 kwLdrModuleRetain(pMod);
3570 break;
3571 }
3572 pMod = pMod->pNextHash;
3573 } while (pMod);
3574 }
3575
3576 /*
3577 * If not in the hash table, so create a module entry.
3578 */
3579 if (!pMod)
3580 {
3581 PKLDRMOD pLdrMod;
3582 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3583 if (rc == 0)
3584 {
3585 /** @todo more accurately determine location */
3586 const char *pszFilename = kHlpGetFilename(szNormPath);
3587 KBOOL fDoReplacements = kwLdrModuleShouldDoNativeReplacements(pszFilename, KWLOCATION_SYSTEM32);
3588 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3589 fDoReplacements, NULL /*pVirtualApiMod*/);
3590 if (!pMod)
3591 {
3592 kLdrModClose(pLdrMod);
3593 kwErrPrintf("out of memory\n");
3594 }
3595 }
3596 else
3597 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszLogName, rc);
3598 }
3599 if (pMod)
3600 {
3601 /*
3602 * Create a CRT slot for the module if necessary.
3603 */
3604 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3605 return pMod;
3606 rc = kwLdrModuleCreateCrtSlot(pMod);
3607 if (rc == 0)
3608 return pMod;
3609 kwLdrModuleRelease(pMod);
3610 }
3611 }
3612 else
3613 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszLogName, GetLastError());
3614 }
3615 else
3616 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszLogName, GetLastError());
3617 return NULL;
3618}
3619
3620
3621/**
3622 * Locates the module structure for an already loaded native module.
3623 *
3624 * This will create a module structure if needed.
3625 *
3626 * @returns Pointer to the module structure on success, NULL on failure.
3627 * @param pszName The name of the module.
3628 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3629 * @param fAlwaysPresent Whether the module is expected to always be present,
3630 * or not. If not, complain less.
3631 */
3632static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot, KBOOL fAlwaysPresent)
3633{
3634 /*
3635 * Locate the module handle and pass it to kwLdrModuleForLoadedNativeByHandle.
3636 */
3637 HANDLE hModule = GetModuleHandleA(pszName);
3638 if (hModule)
3639 return kwLdrModuleForLoadedNativeByHandle(hModule, fEnsureCrtSlot, pszName);
3640 if (fAlwaysPresent)
3641 kwErrPrintf("Module '%s' was not found by GetModuleHandleA/W!\n", pszName);
3642 return NULL;
3643}
3644
3645
3646/**
3647 * Does the TLS memory initialization for a module on the current thread.
3648 *
3649 * @returns 0 on success, error on failure.
3650 * @param pMod The module.
3651 */
3652static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3653{
3654 if (pMod->u.Manual.idxTls != KU32_MAX)
3655 {
3656 PTEB pTeb = NtCurrentTeb();
3657 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3658 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3659 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3660 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3661 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3662 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3663 if (pMod->u.Manual.cbTlsInitData)
3664 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3665 }
3666 return 0;
3667}
3668
3669
3670/**
3671 * Does the TLS callbacks for a module.
3672 *
3673 * @param pMod The module.
3674 * @param dwReason The callback reason.
3675 */
3676static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3677{
3678 if (pMod->u.Manual.cTlsCallbacks)
3679 {
3680 PIMAGE_TLS_CALLBACK *ppfnCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3681 do
3682 {
3683 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *ppfnCallback, pMod->hOurMod, dwReason));
3684 (*ppfnCallback)(pMod->hOurMod, dwReason, 0);
3685 } while (*++ppfnCallback);
3686 }
3687}
3688
3689
3690/**
3691 * Does module initialization starting at @a pMod.
3692 *
3693 * This is initially used on the executable. Later it is used by the
3694 * LoadLibrary interceptor.
3695 *
3696 * @returns 0 on success, error on failure.
3697 * @param pMod The module to initialize.
3698 */
3699static int kwLdrModuleInitTree(PKWMODULE pMod)
3700{
3701 int rc = 0;
3702 if (!pMod->fNative)
3703 {
3704 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3705 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3706
3707 /*
3708 * Need to copy bits?
3709 */
3710 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3711 {
3712 if (pMod->u.Manual.fUseLdBuf)
3713 {
3714#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3715 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3716 {
3717 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3718 kHlpAssert(fRc); K_NOREF(fRc);
3719 }
3720#endif
3721 g_pModPrevInLdBuf = g_pModInLdBuf;
3722 g_pModInLdBuf = pMod;
3723 }
3724
3725 /* Do quick zeroing and copying when we can. */
3726 pMod->u.Manual.fCanDoQuick = K_FALSE;
3727 if ( pMod->u.Manual.fCanDoQuick
3728 && ( !pMod->u.Manual.fUseLdBuf
3729 || g_pModPrevInLdBuf == pMod))
3730 {
3731 /* Zero first. */
3732 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3733 switch (pMod->u.Manual.cQuickZeroChunks)
3734 {
3735 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3736 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3737 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3738 case 0: break;
3739 }
3740
3741 /* Then copy. */
3742 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3743 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3744 switch (pMod->u.Manual.cQuickCopyChunks)
3745 {
3746 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3747 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3748 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3749 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3750 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3751 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3752 case 0: break;
3753 }
3754 }
3755 /* Must copy the whole image. */
3756 else
3757 {
3758 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3759 pMod->u.Manual.fCanDoQuick = K_TRUE;
3760 }
3761 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3762 }
3763
3764#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3765 /*
3766 * Need to register function table?
3767 */
3768 if ( !pMod->u.Manual.fRegisteredFunctionTable
3769 && pMod->u.Manual.cFunctions > 0)
3770 {
3771 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3772 pMod->u.Manual.cFunctions,
3773 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3774 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3775 }
3776#endif
3777
3778
3779 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3780 {
3781 /*
3782 * Must do imports first, but mark our module as being initialized to avoid
3783 * endless recursion should there be a dependency loop.
3784 */
3785 KSIZE iImp;
3786 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3787
3788 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3789 {
3790 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3791 if (rc != 0)
3792 return rc;
3793 }
3794
3795 /* Do TLS allocations for module init? */
3796 rc = kwLdrCallTlsAllocateAndInit(pMod);
3797 if (rc != 0)
3798 return rc;
3799 if (pMod->u.Manual.cTlsCallbacks > 0)
3800 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3801
3802 /* Finally call the entry point. */
3803 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3804 if (rc == 0)
3805 pMod->u.Manual.enmState = KWMODSTATE_READY;
3806 else
3807 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3808 }
3809 }
3810 /*
3811 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3812 * _MSPDBSRV_ENDPOINT_ changes value.
3813 */
3814 else if (pMod->fNeedReInit)
3815 {
3816 int rc2;
3817 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3818 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3819 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3820 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3821 if (!rc && !rc2)
3822 { /* likely */ }
3823 else
3824 {
3825 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3826 if (rc2 && !rc)
3827 rc = rc2;
3828 }
3829 pMod->fNeedReInit = K_FALSE;
3830 }
3831 return rc;
3832}
3833
3834
3835/**
3836 * Looks up a module handle for a tool.
3837 *
3838 * @returns Referenced loader module on success, NULL on if not found.
3839 * @param pTool The tool.
3840 * @param hmod The module handle.
3841 */
3842static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3843{
3844 KUPTR const uHMod = (KUPTR)hmod;
3845 PKWMODULE *papMods;
3846 KU32 iEnd;
3847 KU32 i;
3848 PKWDYNLOAD pDynLoad;
3849
3850 if (pTool)
3851 { /* likely */ }
3852 else
3853 return NULL;
3854
3855 /* The executable. */
3856 if ( hmod == NULL
3857 || (pTool->u.Sandboxed.pExe && pTool->u.Sandboxed.pExe->hOurMod == hmod))
3858 {
3859 if (pTool->u.Sandboxed.pExe)
3860 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3861 return NULL;
3862 }
3863
3864 /*
3865 * Binary lookup using the module table.
3866 */
3867 papMods = pTool->u.Sandboxed.papModules;
3868 iEnd = pTool->u.Sandboxed.cModules;
3869 if (iEnd)
3870 {
3871 KU32 iStart = 0;
3872 i = iEnd / 2;
3873 for (;;)
3874 {
3875 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
3876 if (uHMod < uHModCur)
3877 {
3878 iEnd = i--;
3879 if (iStart <= i)
3880 { }
3881 else
3882 break;
3883 }
3884 else if (uHMod != uHModCur)
3885 {
3886 iStart = ++i;
3887 if (i < iEnd)
3888 { }
3889 else
3890 break;
3891 }
3892 /* We've got a match. Always return the non-virtual module (first) when there is one. */
3893 else if (!papMods[i]->pVirtualApiMod)
3894 return kwLdrModuleRetain(papMods[i]);
3895 else
3896 {
3897 while (i > 0 && papMods[i - 1]->pVirtualApiMod && papMods[i - 1]->hOurMod == hmod)
3898 i--;
3899 return kwLdrModuleRetain(papMods[i]);
3900 }
3901
3902 i = iStart + (iEnd - iStart) / 2;
3903 }
3904
3905#ifndef NDEBUG
3906 iStart = pTool->u.Sandboxed.cModules;
3907 while (--iStart > 0)
3908 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3909 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3910#endif
3911 }
3912
3913 /*
3914 * Dynamically loaded images.
3915 */
3916 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3917 if (pDynLoad->hmod == hmod)
3918 {
3919 if (pDynLoad->pMod)
3920 return kwLdrModuleRetain(pDynLoad->pMod);
3921 KWFS_TODO();
3922 return NULL;
3923 }
3924
3925 return NULL;
3926}
3927
3928/**
3929 * Adds the given module to the tool import table.
3930 *
3931 * @returns 0 on success, non-zero on failure.
3932 * @param pTool The tool.
3933 * @param pMod The module.
3934 */
3935static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3936{
3937 /*
3938 * Binary lookup. Locating the right slot for it, return if already there.
3939 */
3940 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3941 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3942 KU32 iEnd = pTool->u.Sandboxed.cModules;
3943 KU32 i;
3944 if (iEnd)
3945 {
3946 KU32 iStart = 0;
3947 i = iEnd / 2;
3948 for (;;)
3949 {
3950 PKWMODULE pCurMod = papMods[i];
3951 KUPTR const uHModCur = (KUPTR)pCurMod->hOurMod;
3952 if (uHMod < uHModCur)
3953 {
3954 iEnd = i;
3955 if (iStart < i)
3956 { }
3957 else
3958 break;
3959 }
3960 else if (uHMod != uHModCur)
3961 {
3962 iStart = ++i;
3963 if (i < iEnd)
3964 { }
3965 else
3966 break;
3967 }
3968 else
3969 {
3970 /* Already there in the table. The non-virtual module must be the first
3971 entry if we've got duplicate hmod values because of virtual modules. */
3972 if (pMod != pCurMod)
3973 {
3974 /* Skip to the last module with the same hmod. */
3975 while (i + 1 < iEnd && (KUPTR)(pCurMod = papMods[i + 1])->hOurMod == uHMod)
3976 {
3977 if (pMod == pCurMod)
3978 return 0;
3979 i++;
3980 }
3981
3982 /* Then scan backwards till the first one. */
3983 while (i > 0 && (KUPTR)(pCurMod = papMods[i - 1])->hOurMod == uHMod)
3984 {
3985 if (pMod == pCurMod)
3986 return 0;
3987 i--;
3988 }
3989 pCurMod = papMods[i];
3990 if (pMod != pCurMod)
3991 {
3992 if (pMod->pVirtualApiMod && !pCurMod->pVirtualApiMod)
3993 i++;
3994 break;
3995 }
3996 }
3997 return 0;
3998 }
3999
4000 i = iStart + (iEnd - iStart) / 2;
4001 }
4002#ifndef NDEBUG
4003 iStart = pTool->u.Sandboxed.cModules;
4004 while (--iStart > 0)
4005 {
4006 kHlpAssert(papMods[iStart] != pMod);
4007 kHlpAssert( (KUPTR)papMods[iStart]->hOurMod != uHMod
4008 || pMod->pVirtualApiMod
4009 || papMods[iStart]->pVirtualApiMod);
4010 }
4011 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod <= uHMod);
4012 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod >= uHMod);
4013#endif
4014 }
4015 else
4016 i = 0;
4017
4018 /*
4019 * Grow the table?
4020 */
4021 if ((pTool->u.Sandboxed.cModules % 16) == 0)
4022 {
4023 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
4024 if (!pvNew)
4025 return KERR_NO_MEMORY;
4026 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
4027 }
4028
4029 /* Insert it. */
4030 if (i != pTool->u.Sandboxed.cModules)
4031 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
4032 papMods[i] = kwLdrModuleRetain(pMod);
4033 pTool->u.Sandboxed.cModules++;
4034 KWLDR_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
4035 return 0;
4036}
4037
4038
4039/**
4040 * Adds the given module and all its imports to the
4041 *
4042 * @returns 0 on success, non-zero on failure.
4043 * @param pTool The tool.
4044 * @param pMod The module.
4045 */
4046static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
4047{
4048 int rc = kwToolAddModule(pTool, pMod);
4049 if (pMod->pVirtualApiMod && rc == 0)
4050 rc = kwToolAddModule(pTool, pMod->pVirtualApiMod);
4051 if (!pMod->fNative && rc == 0)
4052 {
4053 KSIZE iImp = pMod->u.Manual.cImpMods;
4054 while (iImp-- > 0)
4055 {
4056 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
4057 if (rc == 0)
4058 { }
4059 else
4060 break;
4061 }
4062 }
4063
4064 return 0;
4065}
4066
4067
4068/**
4069 * Creates a tool entry and inserts it.
4070 *
4071 * @returns Pointer to the tool entry. NULL on failure.
4072 * @param pToolFsObj The file object of the tool. The created tool
4073 * will be associated with it.
4074 *
4075 * A reference is donated by the caller and must be
4076 * released.
4077 * @param pszSearchPath The PATH environment variable value, or NULL.
4078 */
4079static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
4080{
4081 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
4082 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
4083 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
4084 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
4085 if (pTool)
4086 {
4087 KBOOL fRc;
4088 pTool->pwszPath = (wchar_t const *)(pTool + 1);
4089 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
4090 kHlpAssert(fRc); K_NOREF(fRc);
4091
4092 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
4093 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
4094 kHlpAssert(fRc);
4095
4096 pTool->enmType = KWTOOLTYPE_SANDBOXED;
4097 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
4098 NULL /*pEexeMod*/, pszSearchPath);
4099 if (pTool->u.Sandboxed.pExe)
4100 {
4101 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
4102 if (rc == 0)
4103 {
4104 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
4105 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
4106 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
4107 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
4108 else
4109 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
4110 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
4111 }
4112 else
4113 {
4114 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
4115 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
4116 pTool->u.Sandboxed.pExe = NULL;
4117 pTool->enmType = KWTOOLTYPE_EXEC;
4118 }
4119 }
4120 else
4121 pTool->enmType = KWTOOLTYPE_EXEC;
4122
4123 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4124 g_cTools++;
4125 return pTool;
4126 }
4127 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4128 return NULL;
4129}
4130
4131
4132/**
4133 * Looks up the given tool, creating a new tool table entry if necessary.
4134 *
4135 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
4136 * @param pszExe The executable for the tool (not normalized).
4137 * @param cEnvVars Number of environment varibles.
4138 * @param papszEnvVars Environment variables. For getting the PATH.
4139 */
4140static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
4141{
4142 /*
4143 * We associate the tools instances with the file system objects.
4144 *
4145 * We'd like to do the lookup without invaliding the volatile parts of the
4146 * cache, thus the double lookup here. The cache gets invalidate later on.
4147 */
4148 KFSLOOKUPERROR enmError;
4149 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4150 if ( !pToolFsObj
4151 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
4152 {
4153 kFsCacheInvalidateCustomBoth(g_pFsCache);
4154 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
4155 }
4156 if (pToolFsObj)
4157 {
4158 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
4159 {
4160 const char *pszSearchPath;
4161 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
4162 if (pTool)
4163 {
4164 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4165 return pTool;
4166 }
4167
4168 /*
4169 * Need to create a new tool.
4170 */
4171 pszSearchPath = NULL;
4172 while (cEnvVars-- > 0)
4173 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
4174 {
4175 pszSearchPath = &papszEnvVars[cEnvVars][5];
4176 break;
4177 }
4178
4179 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
4180 if (pTool)
4181 return pTool;
4182
4183 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
4184 }
4185 else
4186 {
4187 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
4188 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
4189 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
4190 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
4191 }
4192 }
4193 else
4194 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
4195 return NULL;
4196}
4197
4198
4199
4200/*
4201 *
4202 * File system cache.
4203 * File system cache.
4204 * File system cache.
4205 *
4206 */
4207
4208
4209/**
4210 * This is for kDep.
4211 */
4212int kwFsPathExists(const char *pszPath)
4213{
4214 BirdTimeSpec_T TsIgnored;
4215 KFSLOOKUPERROR enmError;
4216 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
4217 if (pFsObj)
4218 {
4219 kFsCacheObjRelease(g_pFsCache, pFsObj);
4220 return 1;
4221 }
4222 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
4223}
4224
4225
4226/* duplicated in dir-nt-bird.c */
4227void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
4228{
4229 KFSLOOKUPERROR enmError;
4230 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
4231 if (pPathObj)
4232 {
4233 KSIZE off = pPathObj->cchParent;
4234 if (off > 0)
4235 {
4236 KSIZE offEnd = off + pPathObj->cchName;
4237 if (offEnd < cbFull)
4238 {
4239 PKFSDIR pAncestor;
4240
4241 pszFull[off + pPathObj->cchName] = '\0';
4242 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
4243
4244 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
4245 {
4246 kHlpAssert(off > 1);
4247 kHlpAssert(pAncestor != NULL);
4248 kHlpAssert(pAncestor->Obj.cchName > 0);
4249 pszFull[--off] = '/';
4250 off -= pAncestor->Obj.cchName;
4251 kHlpAssert(pAncestor->Obj.cchParent == off);
4252 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
4253 }
4254 kFsCacheObjRelease(g_pFsCache, pPathObj);
4255 return;
4256 }
4257 }
4258 else
4259 {
4260 if ((size_t)pPathObj->cchName + 1 < cbFull)
4261 {
4262 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
4263 pszFull[pPathObj->cchName] = '/';
4264 pszFull[pPathObj->cchName + 1] = '\0';
4265
4266 kFsCacheObjRelease(g_pFsCache, pPathObj);
4267 return;
4268 }
4269 }
4270
4271 /* do fallback. */
4272 kHlpAssertFailed();
4273 kFsCacheObjRelease(g_pFsCache, pPathObj);
4274 }
4275
4276 nt_fullpath(pszPath, pszFull, cbFull);
4277}
4278
4279
4280/**
4281 * Helper for getting the extension of a UTF-16 path.
4282 *
4283 * @returns Pointer to the extension or the terminator.
4284 * @param pwszPath The path.
4285 * @param pcwcExt Where to return the length of the extension.
4286 */
4287static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
4288{
4289 wchar_t const *pwszName = pwszPath;
4290 wchar_t const *pwszExt = NULL;
4291 for (;;)
4292 {
4293 wchar_t const wc = *pwszPath++;
4294 if (wc == '.')
4295 pwszExt = pwszPath;
4296 else if (wc == '/' || wc == '\\' || wc == ':')
4297 {
4298 pwszName = pwszPath;
4299 pwszExt = NULL;
4300 }
4301 else if (wc == '\0')
4302 {
4303 if (pwszExt)
4304 {
4305 *pcwcExt = pwszPath - pwszExt - 1;
4306 return pwszExt;
4307 }
4308 *pcwcExt = 0;
4309 return pwszPath - 1;
4310 }
4311 }
4312}
4313
4314
4315
4316/**
4317 * Parses the argument string passed in as pszSrc.
4318 *
4319 * @returns size of the processed arguments.
4320 * @param pszSrc Pointer to the commandline that's to be parsed.
4321 * @param pcArgs Where to return the number of arguments.
4322 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
4323 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
4324 *
4325 * @remarks Lifted from startuphacks-win.c
4326 */
4327static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
4328{
4329 int bs;
4330 char chQuote;
4331 char *pfFlags;
4332 int cbArgs;
4333 int cArgs;
4334
4335#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
4336#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
4337#define WHITE(c) ((c) == ' ' || (c) == '\t')
4338
4339#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
4340#define _ARG_RESPONSE 0x02 /* Argument read from response file */
4341#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
4342#define _ARG_ENV 0x08 /* Argument from environment */
4343#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
4344
4345 cArgs = 0;
4346 cbArgs = 0;
4347
4348#if 0
4349 /* argv[0] */
4350 PUTC((char)_ARG_NONZERO);
4351 PUTV;
4352 for (;;)
4353 {
4354 PUTC(*pszSrc);
4355 if (*pszSrc == 0)
4356 break;
4357 ++pszSrc;
4358 }
4359 ++pszSrc;
4360#endif
4361
4362 for (;;)
4363 {
4364 while (WHITE(*pszSrc))
4365 ++pszSrc;
4366 if (*pszSrc == 0)
4367 break;
4368 pfFlags = pchPool;
4369 PUTC((unsigned char)_ARG_NONZERO);
4370 PUTV;
4371 bs = 0; chQuote = 0;
4372 for (;;)
4373 {
4374 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
4375 {
4376 while (bs >= 2)
4377 {
4378 PUTC('\\');
4379 bs -= 2;
4380 }
4381 if (bs & 1)
4382 PUTC(*pszSrc);
4383 else
4384 {
4385 chQuote = chQuote ? 0 : *pszSrc;
4386 if (pfFlags != NULL)
4387 *pfFlags |= _ARG_DQUOTE;
4388 }
4389 bs = 0;
4390 }
4391 else if (*pszSrc == '\\')
4392 ++bs;
4393 else
4394 {
4395 while (bs != 0)
4396 {
4397 PUTC('\\');
4398 --bs;
4399 }
4400 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
4401 break;
4402 PUTC(*pszSrc);
4403 }
4404 ++pszSrc;
4405 }
4406 PUTC(0);
4407 }
4408
4409 *pcArgs = cArgs;
4410 return cbArgs;
4411}
4412
4413
4414
4415
4416/*
4417 *
4418 * Process and thread related APIs.
4419 * Process and thread related APIs.
4420 * Process and thread related APIs.
4421 *
4422 */
4423
4424/** Common worker for ExitProcess(), exit() and friends. */
4425static void WINAPI kwSandboxDoExit(int uExitCode)
4426{
4427 if (g_Sandbox.idMainThread == GetCurrentThreadId())
4428 {
4429 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
4430
4431 g_Sandbox.rcExitCode = (int)uExitCode;
4432
4433 /* Before we jump, restore the TIB as we're not interested in any
4434 exception chain stuff installed by the sandboxed executable. */
4435 *pTib = g_Sandbox.TibMainThread;
4436 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
4437
4438 longjmp(g_Sandbox.JmpBuf, 1);
4439 }
4440 KWFS_TODO();
4441}
4442
4443
4444/** ExitProcess replacement. */
4445static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
4446{
4447 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
4448 kwSandboxDoExit((int)uExitCode);
4449}
4450
4451
4452/** ExitProcess replacement. */
4453static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
4454{
4455 if (hProcess == GetCurrentProcess())
4456 kwSandboxDoExit(uExitCode);
4457 KWFS_TODO();
4458 return TerminateProcess(hProcess, uExitCode);
4459}
4460
4461
4462/** Normal CRT exit(). */
4463static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
4464{
4465 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
4466 kwSandboxDoExit(rcExitCode);
4467}
4468
4469
4470/** Quick CRT _exit(). */
4471static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
4472{
4473 /* Quick. */
4474 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
4475 kwSandboxDoExit(rcExitCode);
4476}
4477
4478
4479/** Return to caller CRT _cexit(). */
4480static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
4481{
4482 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
4483 kwSandboxDoExit(rcExitCode);
4484}
4485
4486
4487/** Quick return to caller CRT _c_exit(). */
4488static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
4489{
4490 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
4491 kwSandboxDoExit(rcExitCode);
4492}
4493
4494
4495/** Runtime error and exit _amsg_exit(). */
4496static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
4497{
4498 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
4499 kwSandboxDoExit(255);
4500}
4501
4502
4503/** CRT - terminate(). */
4504static void __cdecl kwSandbox_msvcrt_terminate(void)
4505{
4506 KW_LOG(("\nRuntime - terminate!\n"));
4507 kwSandboxDoExit(254);
4508}
4509
4510
4511/** CRT - _onexit */
4512static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
4513{
4514 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4515 {
4516 PKWEXITCALLACK pCallback;
4517 KW_LOG(("_onexit(%p)\n", pfnFunc));
4518 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4519
4520 pCallback = kHlpAlloc(sizeof(*pCallback));
4521 if (pCallback)
4522 {
4523 pCallback->pfnCallback = pfnFunc;
4524 pCallback->fAtExit = K_FALSE;
4525 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4526 g_Sandbox.pExitCallbackHead = pCallback;
4527 return pfnFunc;
4528 }
4529 return NULL;
4530 }
4531 //KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
4532 //return pfnFunc;
4533}
4534
4535
4536/** CRT - atexit */
4537static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
4538{
4539 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4540 {
4541 PKWEXITCALLACK pCallback;
4542 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4543 KW_LOG(("atexit(%p)\n", pfnFunc));
4544
4545 pCallback = kHlpAlloc(sizeof(*pCallback));
4546 if (pCallback)
4547 {
4548 pCallback->pfnCallback = (_onexit_t)pfnFunc;
4549 pCallback->fAtExit = K_TRUE;
4550 pCallback->pNext = g_Sandbox.pExitCallbackHead;
4551 g_Sandbox.pExitCallbackHead = pCallback;
4552 return 0;
4553 }
4554 return -1;
4555 }
4556 //KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4557 //return 0;
4558}
4559
4560
4561/** Kernel32 - SetConsoleCtrlHandler(). */
4562static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4563{
4564 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4565 return TRUE;
4566}
4567
4568
4569/** The CRT internal __getmainargs() API. */
4570static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4571 int dowildcard, int const *piNewMode)
4572{
4573 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4574 *pargc = g_Sandbox.cArgs;
4575 *pargv = g_Sandbox.papszArgs;
4576 *penvp = g_Sandbox.environ;
4577
4578 /** @todo startinfo points at a newmode (setmode) value. */
4579 return 0;
4580}
4581
4582
4583/** The CRT internal __wgetmainargs() API. */
4584static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4585 int dowildcard, int const *piNewMode)
4586{
4587 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4588 *pargc = g_Sandbox.cArgs;
4589 *pargv = g_Sandbox.papwszArgs;
4590 *penvp = g_Sandbox.wenviron;
4591
4592 /** @todo startinfo points at a newmode (setmode) value. */
4593 return 0;
4594}
4595
4596
4597
4598/** Kernel32 - GetCommandLineA() */
4599static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4600{
4601 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4602 return g_Sandbox.pszCmdLine;
4603}
4604
4605
4606/** Kernel32 - GetCommandLineW() */
4607static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4608{
4609 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4610 return g_Sandbox.pwszCmdLine;
4611}
4612
4613
4614/** Kernel32 - GetStartupInfoA() */
4615static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4616{
4617 KW_LOG(("GetStartupInfoA\n"));
4618 GetStartupInfoA(pStartupInfo);
4619 pStartupInfo->lpReserved = NULL;
4620 pStartupInfo->lpTitle = NULL;
4621 pStartupInfo->lpReserved2 = NULL;
4622 pStartupInfo->cbReserved2 = 0;
4623}
4624
4625
4626/** Kernel32 - GetStartupInfoW() */
4627static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4628{
4629 KW_LOG(("GetStartupInfoW\n"));
4630 GetStartupInfoW(pStartupInfo);
4631 pStartupInfo->lpReserved = NULL;
4632 pStartupInfo->lpTitle = NULL;
4633 pStartupInfo->lpReserved2 = NULL;
4634 pStartupInfo->cbReserved2 = 0;
4635}
4636
4637
4638/** CRT - __p___argc(). */
4639static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4640{
4641 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4642 return &g_Sandbox.cArgs;
4643}
4644
4645
4646/** CRT - __p___argv(). */
4647static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4648{
4649 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4650 return &g_Sandbox.papszArgs;
4651}
4652
4653
4654/** CRT - __p___sargv(). */
4655static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4656{
4657 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4658 return &g_Sandbox.papwszArgs;
4659}
4660
4661
4662/** CRT - __p__acmdln(). */
4663static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4664{
4665 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4666 return (char **)&g_Sandbox.pszCmdLine;
4667}
4668
4669
4670/** CRT - __p__acmdln(). */
4671static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4672{
4673 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4674 return &g_Sandbox.pwszCmdLine;
4675}
4676
4677
4678/** CRT - __p__pgmptr(). */
4679static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4680{
4681 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4682 return &g_Sandbox.pgmptr;
4683}
4684
4685
4686/** CRT - __p__wpgmptr(). */
4687static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4688{
4689 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4690 return &g_Sandbox.wpgmptr;
4691}
4692
4693
4694/** CRT - _get_pgmptr(). */
4695static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4696{
4697 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4698 *ppszValue = g_Sandbox.pgmptr;
4699 return 0;
4700}
4701
4702
4703/** CRT - _get_wpgmptr(). */
4704static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4705{
4706 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4707 *ppwszValue = g_Sandbox.wpgmptr;
4708 return 0;
4709}
4710
4711/** Just in case. */
4712static void kwSandbox_msvcrt__wincmdln(void)
4713{
4714 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4715 KWFS_TODO();
4716}
4717
4718
4719/** Just in case. */
4720static void kwSandbox_msvcrt__wwincmdln(void)
4721{
4722 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4723 KWFS_TODO();
4724}
4725
4726/** CreateThread interceptor. */
4727static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4728 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4729 DWORD fFlags, PDWORD pidThread)
4730{
4731 HANDLE hThread = NULL;
4732 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4733 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4734 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4735 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4736 {
4737 /* Allow link::DbgThread. */
4738 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4739 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4740 }
4741 else
4742 KWFS_TODO();
4743 return hThread;
4744}
4745
4746
4747/** _beginthread - create a new thread. */
4748static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4749{
4750 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4751 KWFS_TODO();
4752 return 0;
4753}
4754
4755
4756/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4757static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4758 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4759 unsigned fCreate, unsigned *pidThread)
4760{
4761 /*
4762 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4763 * whatever it needs to.
4764 */
4765 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4766 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4767 pfnThreadProc, pvUser, fCreate, pidThread));
4768 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4769 {
4770 uintptr_t rcRet;
4771 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4772 if (!s_pfnReal)
4773 {
4774 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4775 if (!s_pfnReal)
4776 {
4777 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4778 __debugbreak();
4779 }
4780 }
4781 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4782 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4783 return rcRet;
4784 }
4785
4786 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4787 KWFS_TODO();
4788 return 0;
4789}
4790
4791
4792/** _beginthreadex - create a new thread. */
4793static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex_wrapped(void *pvSecAttr, unsigned cbStack,
4794 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4795 unsigned fCreate, unsigned *pidThread, PKWCRTSLOT pSlot)
4796{
4797 /*
4798 * Since the VC++ 12 (VS 2013) compiler, the 2nd pass is now threaded.
4799 * Let it do whatever it needs to.
4800 */
4801 KW_LOG(("kwSandbox_msvcrt__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4802 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4803 pfnThreadProc, pvUser, fCreate, pidThread));
4804 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
4805 && pSlot->pfnBeginThreadEx)
4806 {
4807 uintptr_t rcRet = pSlot->pfnBeginThreadEx(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4808 KW_LOG(("kwSandbox_msvcrt__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4809 return rcRet;
4810 }
4811
4812 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4813 KWFS_TODO();
4814 return 0;
4815}
4816
4817CRT_SLOT_FUNCTION_WRAPPER(uintptr_t __cdecl, kwSandbox_msvcrt__beginthreadex,
4818 (void *pvSecAttr, unsigned cbStack, unsigned (__stdcall *pfnThreadProc)(void *),
4819 void *pvUser, unsigned fCreate, unsigned *pidThread),
4820 (pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread, &g_aCrtSlots[iCrtSlot]));
4821
4822
4823
4824/*
4825 *
4826 * Environment related APIs.
4827 * Environment related APIs.
4828 * Environment related APIs.
4829 *
4830 */
4831
4832/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4833static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4834{
4835 char *pszzEnv;
4836 char *pszCur;
4837 KSIZE cbNeeded = 1;
4838 KSIZE iVar = 0;
4839
4840 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4841
4842 /* Figure how space much we need first. */
4843 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4844 cbNeeded += kHlpStrLen(pszCur) + 1;
4845
4846 /* Allocate it. */
4847 pszzEnv = kHlpAlloc(cbNeeded);
4848 if (pszzEnv)
4849 {
4850 char *psz = pszzEnv;
4851 iVar = 0;
4852 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4853 {
4854 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4855 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4856 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4857 }
4858 *psz++ = '\0';
4859 kHlpAssert((KUPTR)(psz - pszzEnv) == cbNeeded);
4860 }
4861
4862 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4863#if 0
4864 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4865 pszCur = pszzEnv;
4866 iVar = 0;
4867 while (*pszCur)
4868 {
4869 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4870 iVar++;
4871 pszCur += kHlpStrLen(pszCur) + 1;
4872 }
4873 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4874 pszCur++;
4875 fprintf(stderr, "ended at %p, after %u bytes (expected %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4876#endif
4877 return pszzEnv;
4878}
4879
4880
4881/** Kernel32 - GetEnvironmentStrings */
4882static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4883{
4884 KW_LOG(("GetEnvironmentStrings!\n"));
4885 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4886}
4887
4888
4889/** Kernel32 - GetEnvironmentStringsW */
4890static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4891{
4892 wchar_t *pwszzEnv;
4893 wchar_t *pwszCur;
4894 KSIZE cwcNeeded = 1;
4895 KSIZE iVar = 0;
4896
4897 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4898
4899 /* Figure how space much we need first. */
4900 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4901 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4902
4903 /* Allocate it. */
4904 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4905 if (pwszzEnv)
4906 {
4907 wchar_t *pwsz = pwszzEnv;
4908 iVar = 0;
4909 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4910 {
4911 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4912 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4913 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4914 }
4915 *pwsz++ = '\0';
4916 kHlpAssert((KUPTR)(pwsz - pwszzEnv) == cwcNeeded);
4917 }
4918
4919 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4920 return pwszzEnv;
4921}
4922
4923
4924/** Kernel32 - FreeEnvironmentStringsA */
4925static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4926{
4927 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4928 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4929 kHlpFree(pszzEnv);
4930 return TRUE;
4931}
4932
4933
4934/** Kernel32 - FreeEnvironmentStringsW */
4935static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4936{
4937 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4938 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4939 kHlpFree(pwszzEnv);
4940 return TRUE;
4941}
4942
4943
4944/**
4945 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4946 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4947 *
4948 * @returns 0 on success, non-zero on failure.
4949 * @param pSandbox The sandbox.
4950 * @param cMin Minimum size, including terminator.
4951 */
4952static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4953{
4954 void *pvNew;
4955 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4956 KSIZE cNew = cOld + 256;
4957 while (cNew < cMin)
4958 cNew += 256;
4959
4960 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4961 if (pvNew)
4962 {
4963 pSandbox->environ = (char **)pvNew;
4964 pSandbox->environ[cOld] = NULL;
4965
4966 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4967 if (pvNew)
4968 {
4969 pSandbox->papszEnvVars = (char **)pvNew;
4970 pSandbox->papszEnvVars[cOld] = NULL;
4971
4972 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4973 if (pvNew)
4974 {
4975 pSandbox->wenviron = (wchar_t **)pvNew;
4976 pSandbox->wenviron[cOld] = NULL;
4977
4978 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4979 if (pvNew)
4980 {
4981 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4982 pSandbox->papwszEnvVars[cOld] = NULL;
4983
4984 pSandbox->cEnvVarsAllocated = cNew;
4985 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4986 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4987 return 0;
4988 }
4989 }
4990 }
4991 }
4992 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4993 return KERR_NO_MEMORY;
4994}
4995
4996
4997/**
4998 * Sets an environment variable, ANSI style.
4999 *
5000 * @returns 0 on success, non-zero on failure.
5001 * @param pSandbox The sandbox.
5002 * @param pchVar The variable name.
5003 * @param cchVar The length of the name.
5004 * @param pszValue The value.
5005 */
5006static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
5007{
5008 /* Allocate and construct the new strings. */
5009 KSIZE cchTmp = kHlpStrLen(pszValue);
5010 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
5011 if (pszNew)
5012 {
5013 wchar_t *pwszNew;
5014 kHlpMemCopy(pszNew, pchVar, cchVar);
5015 pszNew[cchVar] = '=';
5016 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
5017 cchTmp += cchVar + 1;
5018 pszNew[cchTmp] = '\0';
5019
5020 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
5021 if (pwszNew)
5022 {
5023 /* Look it up. */
5024 KSIZE iVar = 0;
5025 char *pszEnv;
5026 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5027 {
5028 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5029 && pszEnv[cchVar] == '=')
5030 {
5031 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5032 " iVar=%d: %p='%s' and %p='%ls'\n",
5033 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5034 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5035 iVar, pszNew, pszNew, pwszNew, pwszNew));
5036
5037 kHlpFree(pSandbox->papszEnvVars[iVar]);
5038 pSandbox->papszEnvVars[iVar] = pszNew;
5039 pSandbox->environ[iVar] = pszNew;
5040
5041 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5042 pSandbox->papwszEnvVars[iVar] = pwszNew;
5043 pSandbox->wenviron[iVar] = pwszNew;
5044 return 0;
5045 }
5046 iVar++;
5047 }
5048
5049 /* Not found, do we need to grow the table first? */
5050 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5051 kwSandboxGrowEnv(pSandbox, iVar + 2);
5052 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5053 {
5054 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5055
5056 pSandbox->papszEnvVars[iVar + 1] = NULL;
5057 pSandbox->papszEnvVars[iVar] = pszNew;
5058 pSandbox->environ[iVar + 1] = NULL;
5059 pSandbox->environ[iVar] = pszNew;
5060
5061 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5062 pSandbox->papwszEnvVars[iVar] = pwszNew;
5063 pSandbox->wenviron[iVar + 1] = NULL;
5064 pSandbox->wenviron[iVar] = pwszNew;
5065 return 0;
5066 }
5067
5068 kHlpFree(pwszNew);
5069 }
5070 kHlpFree(pszNew);
5071 }
5072 KW_LOG(("Out of memory!\n"));
5073 return 0;
5074}
5075
5076
5077/**
5078 * Sets an environment variable, UTF-16 style.
5079 *
5080 * @returns 0 on success, non-zero on failure.
5081 * @param pSandbox The sandbox.
5082 * @param pwcVar The variable name.
5083 * @param cwcVar The length of the name.
5084 * @param pwszValue The value.
5085 */
5086static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
5087{
5088 /* Allocate and construct the new strings. */
5089 KSIZE cwcTmp = kwUtf16Len(pwszValue);
5090 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
5091 if (pwszNew)
5092 {
5093 char *pszNew;
5094 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
5095 pwszNew[cwcVar] = '=';
5096 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
5097 cwcTmp += cwcVar + 1;
5098 pwszNew[cwcVar] = '\0';
5099
5100 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
5101 if (pszNew)
5102 {
5103 /* Look it up. */
5104 KSIZE iVar = 0;
5105 wchar_t *pwszEnv;
5106 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5107 {
5108 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
5109 && pwszEnv[cwcVar] == '=')
5110 {
5111 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
5112 " iVar=%d: %p='%s' and %p='%ls'\n",
5113 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5114 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
5115 iVar, pszNew, pszNew, pwszNew, pwszNew));
5116
5117 kHlpFree(pSandbox->papszEnvVars[iVar]);
5118 pSandbox->papszEnvVars[iVar] = pszNew;
5119 pSandbox->environ[iVar] = pszNew;
5120
5121 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5122 pSandbox->papwszEnvVars[iVar] = pwszNew;
5123 pSandbox->wenviron[iVar] = pwszNew;
5124 return 0;
5125 }
5126 iVar++;
5127 }
5128
5129 /* Not found, do we need to grow the table first? */
5130 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
5131 kwSandboxGrowEnv(pSandbox, iVar + 2);
5132 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
5133 {
5134 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
5135
5136 pSandbox->papszEnvVars[iVar + 1] = NULL;
5137 pSandbox->papszEnvVars[iVar] = pszNew;
5138 pSandbox->environ[iVar + 1] = NULL;
5139 pSandbox->environ[iVar] = pszNew;
5140
5141 pSandbox->papwszEnvVars[iVar + 1] = NULL;
5142 pSandbox->papwszEnvVars[iVar] = pwszNew;
5143 pSandbox->wenviron[iVar + 1] = NULL;
5144 pSandbox->wenviron[iVar] = pwszNew;
5145 return 0;
5146 }
5147
5148 kHlpFree(pwszNew);
5149 }
5150 kHlpFree(pszNew);
5151 }
5152 KW_LOG(("Out of memory!\n"));
5153 return 0;
5154}
5155
5156
5157/** ANSI unsetenv worker. */
5158static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5159{
5160 KSIZE iVar = 0;
5161 char *pszEnv;
5162 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
5163 {
5164 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5165 && pszEnv[cchVar] == '=')
5166 {
5167 KSIZE cVars = iVar;
5168 while (pSandbox->papszEnvVars[cVars])
5169 cVars++;
5170 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
5171 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
5172
5173 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5174 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5175 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5176
5177 kHlpFree(pSandbox->papszEnvVars[iVar]);
5178 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5179 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5180 pSandbox->papszEnvVars[cVars] = NULL;
5181 pSandbox->environ[cVars] = NULL;
5182
5183 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5184 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5185 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5186 pSandbox->papwszEnvVars[cVars] = NULL;
5187 pSandbox->wenviron[cVars] = NULL;
5188 return 0;
5189 }
5190 iVar++;
5191 }
5192 return KERR_ENVVAR_NOT_FOUND;
5193}
5194
5195
5196/** UTF-16 unsetenv worker. */
5197static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5198{
5199 KSIZE iVar = 0;
5200 wchar_t *pwszEnv;
5201 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
5202 {
5203 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5204 && pwszEnv[cwcVar] == '=')
5205 {
5206 KSIZE cVars = iVar;
5207 while (pSandbox->papwszEnvVars[cVars])
5208 cVars++;
5209 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
5210 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
5211
5212 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
5213 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
5214 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
5215
5216 kHlpFree(pSandbox->papszEnvVars[iVar]);
5217 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
5218 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
5219 pSandbox->papszEnvVars[cVars] = NULL;
5220 pSandbox->environ[cVars] = NULL;
5221
5222 kHlpFree(pSandbox->papwszEnvVars[iVar]);
5223 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
5224 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
5225 pSandbox->papwszEnvVars[cVars] = NULL;
5226 pSandbox->wenviron[cVars] = NULL;
5227 return 0;
5228 }
5229 iVar++;
5230 }
5231 return KERR_ENVVAR_NOT_FOUND;
5232}
5233
5234
5235
5236/** ANSI getenv worker. */
5237static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
5238{
5239 KSIZE iVar = 0;
5240 char *pszEnv;
5241 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
5242 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
5243 && pszEnv[cchVar] == '=')
5244 return &pszEnv[cchVar + 1];
5245 return NULL;
5246}
5247
5248
5249/** UTF-16 getenv worker. */
5250static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
5251{
5252 KSIZE iVar = 0;
5253 wchar_t *pwszEnv;
5254 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
5255 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
5256 && pwszEnv[cwcVar] == '=')
5257 return &pwszEnv[cwcVar + 1];
5258 return NULL;
5259}
5260
5261
5262/** Kernel32 - GetEnvironmentVariableA() */
5263static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
5264{
5265 char *pszFoundValue;
5266 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5267
5268 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5269 if (pszFoundValue)
5270 {
5271 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
5272 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
5273 return cchRet;
5274 }
5275 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
5276 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5277 return 0;
5278}
5279
5280
5281/** Kernel32 - GetEnvironmentVariableW() */
5282static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
5283{
5284 wchar_t *pwszFoundValue;
5285 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5286
5287 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5288 if (pwszFoundValue)
5289 {
5290 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
5291 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
5292 return cchRet;
5293 }
5294 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
5295 SetLastError(ERROR_ENVVAR_NOT_FOUND);
5296 return 0;
5297}
5298
5299
5300/** Kernel32 - SetEnvironmentVariableA() */
5301static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
5302{
5303 int rc;
5304 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5305
5306 if (pszValue)
5307 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5308 else
5309 {
5310 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5311 rc = 0; //??
5312 }
5313 if (rc == 0)
5314 {
5315 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
5316 return TRUE;
5317 }
5318 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5319 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
5320 return FALSE;
5321}
5322
5323
5324/** Kernel32 - SetEnvironmentVariableW() */
5325static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
5326{
5327 int rc;
5328 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5329
5330 if (pwszValue)
5331 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5332 else
5333 {
5334 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5335 rc = 0; //??
5336 }
5337 if (rc == 0)
5338 {
5339 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
5340 return TRUE;
5341 }
5342 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5343 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
5344 return FALSE;
5345}
5346
5347
5348/** Kernel32 - ExpandEnvironmentStringsA() */
5349static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
5350{
5351 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5352 KWFS_TODO();
5353 return 0;
5354}
5355
5356
5357/** Kernel32 - ExpandEnvironmentStringsW() */
5358static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
5359{
5360 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5361 KWFS_TODO();
5362 return 0;
5363}
5364
5365
5366/** CRT - _putenv(). */
5367static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
5368{
5369 int rc;
5370 char const *pszEqual;
5371 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5372
5373 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
5374 if (pszEqual)
5375 {
5376 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
5377 if (rc == 0)
5378 { }
5379 else
5380 rc = -1;
5381 }
5382 else
5383 {
5384 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
5385 rc = 0;
5386 }
5387 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
5388 return rc;
5389}
5390
5391
5392/** CRT - _wputenv(). */
5393static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
5394{
5395 int rc;
5396 wchar_t const *pwszEqual;
5397 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5398
5399 pwszEqual = wcschr(pwszVarEqualValue, '=');
5400 if (pwszEqual)
5401 {
5402 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
5403 if (rc == 0)
5404 { }
5405 else
5406 rc = -1;
5407 }
5408 else
5409 {
5410 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
5411 rc = 0;
5412 }
5413 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
5414 return rc;
5415}
5416
5417
5418/** CRT - _putenv_s(). */
5419static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
5420{
5421 char const *pszEqual;
5422 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5423
5424 pszEqual = kHlpStrChr(pszVar, '=');
5425 if (pszEqual == NULL)
5426 {
5427 if (pszValue)
5428 {
5429 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
5430 if (rc == 0)
5431 {
5432 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
5433 return 0;
5434 }
5435 }
5436 else
5437 {
5438 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
5439 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
5440 return 0;
5441 }
5442 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
5443 return ENOMEM;
5444 }
5445 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
5446 return EINVAL;
5447}
5448
5449
5450/** CRT - _wputenv_s(). */
5451static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
5452{
5453 wchar_t const *pwszEqual;
5454 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5455
5456 pwszEqual = wcschr(pwszVar, '=');
5457 if (pwszEqual == NULL)
5458 {
5459 if (pwszValue)
5460 {
5461 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
5462 if (rc == 0)
5463 {
5464 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
5465 return 0;
5466 }
5467 }
5468 else
5469 {
5470 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
5471 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
5472 return 0;
5473 }
5474 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
5475 return ENOMEM;
5476 }
5477 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
5478 return EINVAL;
5479}
5480
5481
5482/** CRT - get pointer to the __initenv variable (initial environment). */
5483static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
5484{
5485 KW_LOG(("__p___initenv\n"));
5486 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5487 KWFS_TODO();
5488 return &g_Sandbox.initenv;
5489}
5490
5491
5492/** CRT - get pointer to the __winitenv variable (initial environment). */
5493static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
5494{
5495 KW_LOG(("__p___winitenv\n"));
5496 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5497 KWFS_TODO();
5498 return &g_Sandbox.winitenv;
5499}
5500
5501
5502/** CRT - get pointer to the _environ variable (current environment). */
5503static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
5504{
5505 KW_LOG(("__p__environ\n"));
5506 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5507 return &g_Sandbox.environ;
5508}
5509
5510
5511/** CRT - get pointer to the _wenviron variable (current environment). */
5512static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
5513{
5514 KW_LOG(("__p__wenviron\n"));
5515 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5516 return &g_Sandbox.wenviron;
5517}
5518
5519
5520/** CRT - get the _environ variable (current environment).
5521 * @remarks Not documented or prototyped? */
5522static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
5523{
5524 KWFS_TODO(); /** @todo check the callers expectations! */
5525 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5526 *ppapszEnviron = g_Sandbox.environ;
5527 return 0;
5528}
5529
5530
5531/** CRT - get the _wenviron variable (current environment).
5532 * @remarks Not documented or prototyped? */
5533static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
5534{
5535 KWFS_TODO(); /** @todo check the callers expectations! */
5536 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5537 *ppapwszEnviron = g_Sandbox.wenviron;
5538 return 0;
5539}
5540
5541
5542/** CRT - _wdupenv_s() (see _tdupenv_s(). */
5543static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
5544 PKWCRTSLOT pSlot)
5545{
5546 errno_t rc;
5547 wchar_t *pwszValue;
5548 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5549
5550 if (ppwszValue)
5551 {
5552 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
5553 if (pwszValue)
5554 {
5555 size_t cwcValue = wcslen(pwszValue);
5556 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
5557 if (pwszDst)
5558 {
5559 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
5560 pwszDst[cwcValue] = '\0';
5561 *ppwszValue = pwszDst;
5562 if (pcwcValue)
5563 *pcwcValue = cwcValue;
5564 rc = 0;
5565 }
5566 else
5567 {
5568 *ppwszValue = NULL;
5569 if (pcwcValue)
5570 *pcwcValue = 0;
5571 rc = ENOMEM;
5572 }
5573 }
5574 else
5575 {
5576 *ppwszValue = NULL;
5577 if (pcwcValue)
5578 *pcwcValue = 0;
5579 rc = 0;
5580 }
5581 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5582 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5583 }
5584 else
5585 {
5586 /*
5587 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5588 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5589 * was set to a value.
5590 */
5591 if (pcwcValue)
5592 *pcwcValue = 0;
5593 rc = EINVAL;
5594 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5595 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5596 }
5597 return rc;
5598}
5599CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5600 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5601 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5602
5603
5604
5605/*
5606 *
5607 * Loader related APIs
5608 * Loader related APIs
5609 * Loader related APIs
5610 *
5611 */
5612
5613/**
5614 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5615 */
5616static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5617{
5618 /* Load it first. */
5619 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5620 if (hmod)
5621 {
5622 pDynLoad->hmod = hmod;
5623 pDynLoad->pMod = NULL; /* indicates special */
5624
5625 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5626 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5627 KWLDR_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5628 }
5629 else
5630 kHlpFree(pDynLoad);
5631 return hmod;
5632}
5633
5634
5635/**
5636 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5637 */
5638static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5639{
5640 static const char s_szDll[] = ".dll";
5641 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5642 PKWMODULE pMod;
5643 char szNormPath[256];
5644
5645 /*
5646 * Lower case it and make sure it ends with .dll.
5647 */
5648 if (cbFilename > sizeof(szNormPath))
5649 {
5650 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5651 return NULL;
5652 }
5653 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5654 _strlwr(szNormPath);
5655 kHlpAssert(cbFilename > 7 /* api-ms- */ );
5656 if (strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5657 {
5658 if (cbFilename + sizeof(s_szDll) - 1 > sizeof(szNormPath))
5659 {
5660 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5661 return NULL;
5662 }
5663
5664 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5665 cbFilename += sizeof(s_szDll) - 1;
5666 }
5667
5668 /*
5669 * Try load it.
5670 */
5671 pMod = kwLdrModuleTryLoadVirtualDll(szNormPath, cbFilename - 1);
5672 if (pMod)
5673 {
5674 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5675
5676 pDynLoad->pMod = pMod;
5677 pDynLoad->hmod = pMod->hOurMod;
5678
5679 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5680 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5681 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5682 return pDynLoad->hmod;
5683 }
5684 kHlpFree(pDynLoad);
5685 return NULL;
5686}
5687
5688
5689/** Kernel32 - LoadLibraryExA() */
5690static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5691{
5692 KSIZE cchFilename = kHlpStrLen(pszFilename);
5693 const char *pszSearchPath;
5694 PKWDYNLOAD pDynLoad;
5695 PKWMODULE pMod;
5696 int rc;
5697 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5698 //fprintf(stderr, "LoadLibraryExA: %s, %#x\n", pszFilename, fFlags);
5699
5700 /*
5701 * Deal with a couple of extremely unlikely special cases right away.
5702 */
5703 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5704 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5705 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5706 { /* likely */ }
5707 else
5708 {
5709 KWFS_TODO();
5710 return LoadLibraryExA(pszFilename, hFile, fFlags);
5711 }
5712
5713 /*
5714 * Check if we've already got a dynload entry for this one.
5715 */
5716 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5717 if ( pDynLoad->cchRequest == cchFilename
5718 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5719 {
5720 if (pDynLoad->pMod)
5721 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5722 else
5723 rc = 0;
5724 if (rc == 0)
5725 {
5726 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5727 return pDynLoad->hmod;
5728 }
5729 SetLastError(ERROR_DLL_INIT_FAILED);
5730 return NULL;
5731 }
5732
5733 /*
5734 * Allocate a dynload entry for the request.
5735 */
5736 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5737 if (pDynLoad)
5738 {
5739 pDynLoad->cchRequest = cchFilename;
5740 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5741 }
5742 else
5743 {
5744 KWLDR_LOG(("LoadLibraryExA: Out of memory!\n"));
5745 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5746 return NULL;
5747 }
5748
5749 /*
5750 * Deal with resource / data DLLs.
5751 */
5752 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5753 | LOAD_LIBRARY_AS_DATAFILE
5754 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5755 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5756 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5757
5758 /*
5759 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5760 */
5761 if ( kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5762 && kHlpIsFilenameOnly(pszFilename))
5763 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5764
5765 /*
5766 * Normal library loading.
5767 * We start by being very lazy and reusing the code for resolving imports.
5768 */
5769 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5770 if (!kHlpIsFilenameOnly(pszFilename))
5771 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5772#if 1 /* HACK ALERT! We run into trouble with a 2nd mspdb140.dll instance (x64 + x86), so use the one already loaded. A call
5773 * to NdrClientCall2 at ConnectToServer+0x426 fails with E_INVALIDARG. Problems with multiple connections from same PID? */
5774 else if ( strcmp(pszFilename, "mspdb140.dll") == 0
5775 && GetModuleHandleA(pszFilename) != NULL)
5776 {
5777 pMod = kwLdrModuleForLoadedNativeByHandle(GetModuleHandleA(pszFilename), K_FALSE, pszFilename);
5778 KWLDR_LOG(("LoadLibraryExA: mspdb140 hack: pMod=%p\n", pMod));
5779 }
5780#endif
5781 else
5782 {
5783 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5784 if (rc != 0)
5785 pMod = NULL;
5786 }
5787 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5788 {
5789 /* Enter it into the tool module table and dynamic link request cache. */
5790 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5791
5792 pDynLoad->pMod = pMod;
5793 pDynLoad->hmod = pMod->hOurMod;
5794
5795 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5796 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5797
5798 /*
5799 * Make sure it's initialized (need to link it first since DllMain may
5800 * use loader APIs).
5801 */
5802 rc = kwLdrModuleInitTree(pMod);
5803 if (rc == 0)
5804 {
5805 KWLDR_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5806 return pDynLoad->hmod;
5807 }
5808
5809 SetLastError(ERROR_DLL_INIT_FAILED);
5810 }
5811 else
5812 {
5813 KWFS_TODO();
5814 kHlpFree(pDynLoad);
5815 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5816 }
5817 return NULL;
5818}
5819
5820
5821/** Kernel32 - LoadLibraryExA() for native overloads */
5822static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5823{
5824 char szPath[1024];
5825 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5826
5827 /*
5828 * We may have to help resolved unqualified DLLs living in the executable directory.
5829 */
5830 if ( kHlpIsFilenameOnly(pszFilename)
5831 && g_Sandbox.pTool
5832 && g_Sandbox.pTool->u.Sandboxed.pExe)
5833 {
5834 KSIZE const cchFilename = kHlpStrLen(pszFilename);
5835#define MY_IMATCH(a_szName) (cchFilename == sizeof(a_szName) - 1 && kHlpStrICompAscii(pszFilename, a_szName) == 0)
5836 if ( !kwLdrIsVirtualApiModule(pszFilename, cchFilename)
5837 && !MY_IMATCH("ntdll")
5838 && !MY_IMATCH("kernel32")
5839 && !MY_IMATCH("ntdll.dll")
5840 && !MY_IMATCH("kernelbase")
5841 && !MY_IMATCH("kernel32.dll")
5842 && !MY_IMATCH("kernelbase.dll")
5843 )
5844#undef MY_IMATCH
5845 {
5846 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5847 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5848 {
5849 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5850 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5851 if (kwFsPathExists(szPath))
5852 {
5853 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5854 pszFilename = szPath;
5855 }
5856 }
5857
5858 if (pszFilename != szPath)
5859 {
5860 KSIZE cchSuffix = 0;
5861 KBOOL fNeedSuffix = K_FALSE;
5862 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5863 kHlpAssert(pszCur);
5864 if (pszCur)
5865 {
5866 while (*pszCur != '\0')
5867 {
5868 /* Find the end of the component */
5869 KSIZE cch = 0;
5870 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5871 cch++;
5872
5873 if ( cch > 0 /* wrong, but whatever */
5874 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5875 {
5876 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5877 if ( szPath[cch - 1] != ':'
5878 && szPath[cch - 1] != '/'
5879 && szPath[cch - 1] != '\\')
5880 *pszDst++ = '\\';
5881 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5882 if (fNeedSuffix)
5883 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5884 *pszDst = '\0';
5885
5886 if (kwFsPathExists(szPath))
5887 {
5888 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5889 pszFilename = szPath;
5890 break;
5891 }
5892 }
5893
5894 /* Advance */
5895 pszCur += cch;
5896 while (*pszCur == ';')
5897 pszCur++;
5898 }
5899 }
5900 }
5901 }
5902 }
5903
5904 return LoadLibraryExA(pszFilename, hFile, fFlags);
5905}
5906
5907
5908/** Kernel32 - LoadLibraryExW() for native overloads */
5909static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5910{
5911 char szTmp[4096];
5912 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5913 if (cchTmp < sizeof(szTmp))
5914 return kwSandbox_Kernel32_Native_LoadLibraryExA(szTmp, hFile, fFlags);
5915
5916 KWFS_TODO();
5917 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5918 return NULL;
5919}
5920
5921
5922/** Kernel32 - LoadLibraryExW() */
5923static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5924{
5925 char szTmp[4096];
5926 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5927 if (cchTmp < sizeof(szTmp))
5928 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5929
5930 KWFS_TODO();
5931 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5932 return NULL;
5933}
5934
5935/** Kernel32 - LoadLibraryA() */
5936static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5937{
5938 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5939}
5940
5941
5942/** Kernel32 - LoadLibraryW() */
5943static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5944{
5945 char szTmp[4096];
5946 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5947 if (cchTmp < sizeof(szTmp))
5948 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5949 KWFS_TODO();
5950 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5951 return NULL;
5952}
5953
5954
5955/** Kernel32 - FreeLibrary() */
5956static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5957{
5958 /* Ignored, we like to keep everything loaded. */
5959 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5960 return TRUE;
5961}
5962
5963
5964/** Worker for GetModuleHandleA/W for handling cached modules. */
5965static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5966{
5967 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5968 if (hmod)
5969 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5970 hmod, g_aGetModuleHandleCache[i].pszName));
5971 else
5972 {
5973 /*
5974 * The first time around we have to make sure we have a module table
5975 * entry for it, if not we add one. We need to add it to the tools
5976 * module list to for it to work.
5977 */
5978 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE,
5979 g_aGetModuleHandleCache[i].fAlwaysPresent);
5980 if (pMod)
5981 {
5982 hmod = pMod->hOurMod;
5983 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5984 {
5985 kwToolAddModule(g_Sandbox.pTool, pMod);
5986 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5987 hmod, g_aGetModuleHandleCache[i].pszName));
5988 }
5989 else
5990 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5991 hmod, g_aGetModuleHandleCache[i].pszName));
5992
5993 }
5994 }
5995 return hmod;
5996}
5997
5998
5999/** Kernel32 - GetModuleHandleA() */
6000static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
6001{
6002 KSIZE i;
6003 KSIZE cchModule;
6004 PKWDYNLOAD pDynLoad;
6005 KSIZE cchSuffix;
6006 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6007 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6008
6009 /*
6010 * The executable.
6011 */
6012 if (pszModule == NULL)
6013 {
6014 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
6015 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
6016 }
6017
6018 /*
6019 * If no path of suffix, pretend it ends with .DLL.
6020 */
6021 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
6022
6023 /*
6024 * Cache of system modules we've seen queried.
6025 */
6026 cchModule = kHlpStrLen(pszModule);
6027 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6028 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
6029 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
6030 || ( cchSuffix > 0
6031 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
6032 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
6033 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
6034 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
6035
6036 /*
6037 * Modules we've dynamically loaded.
6038 */
6039 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6040 if (pDynLoad->pMod)
6041 {
6042 const char *pszPath = pDynLoad->pMod->pszPath;
6043 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
6044 if ( stricmp(pszPath, pszModule) == 0
6045 || stricmp(pszName, pszModule) == 0
6046 || ( cchSuffix > 0
6047 && strnicmp(pszName, pszModule, cchModule) == 0
6048 && stricmp(&pszName[cchModule], ".dll") == 0))
6049 {
6050 if ( pDynLoad->pMod->fNative
6051 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6052 {
6053 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
6054 return pDynLoad->hmod;
6055 }
6056 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
6057 SetLastError(ERROR_MOD_NOT_FOUND);
6058 return NULL;
6059 }
6060 }
6061
6062 /*
6063 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6064 * to and go via the g_aGetModuleHandleCache cache.
6065 */
6066/** @todo virtual api DLLs */
6067 if (kHlpStrNICompAscii(pszModule, "api-ms-win-", 11) == 0)
6068 {
6069 HMODULE hmod = GetModuleHandleA(pszModule);
6070 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
6071 if (hmod)
6072 {
6073 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6074 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
6075 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6076 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
6077 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6078 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
6079 if (hmod == GetModuleHandleW(L"UCRTBASE.DLL"))
6080 return kwSandbox_Kernel32_GetModuleHandleA("UCRTBASE.DLL");
6081 }
6082 else
6083 dwErr = GetLastError();
6084 }
6085
6086 kwErrPrintf("pszModule=%s\n", pszModule);
6087 KWFS_TODO();
6088 SetLastError(ERROR_MOD_NOT_FOUND);
6089 return NULL;
6090}
6091
6092
6093/** Kernel32 - GetModuleHandleW() */
6094static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
6095{
6096 KSIZE i;
6097 KSIZE cwcModule;
6098 PKWDYNLOAD pDynLoad;
6099 KSIZE cwcSuffix;
6100 DWORD dwErr = ERROR_MOD_NOT_FOUND;
6101 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6102
6103 /*
6104 * The executable.
6105 */
6106 if (pwszModule == NULL)
6107 {
6108 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
6109 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
6110 }
6111
6112 /*
6113 * If no path of suffix, pretend it ends with .DLL.
6114 */
6115 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
6116
6117 /*
6118 * Cache of system modules we've seen queried.
6119 */
6120 cwcModule = kwUtf16Len(pwszModule);
6121 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6122 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
6123 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
6124 || ( cwcSuffix > 0
6125 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
6126 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
6127 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
6128 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
6129
6130 /*
6131 * Modules we've dynamically loaded.
6132 */
6133 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
6134 if (pDynLoad->pMod)
6135 {
6136 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
6137 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
6138 if ( _wcsicmp(pwszPath, pwszModule) == 0
6139 || _wcsicmp(pwszName, pwszModule) == 0
6140 || ( cwcSuffix
6141 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
6142 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
6143 {
6144 if ( pDynLoad->pMod->fNative
6145 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
6146 {
6147 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
6148 return pDynLoad->hmod;
6149 }
6150 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
6151 SetLastError(ERROR_MOD_NOT_FOUND);
6152 return NULL;
6153 }
6154 }
6155
6156 /*
6157 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
6158 * to and go via the g_aGetModuleHandleCache cache.
6159 */
6160 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
6161 {
6162 HMODULE hmod = GetModuleHandleW(pwszModule);
6163 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
6164 if (hmod)
6165 {
6166 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
6167 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
6168 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
6169 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
6170 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
6171 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
6172 }
6173 else
6174 dwErr = GetLastError();
6175 }
6176
6177 kwErrPrintf("pwszModule=%ls\n", pwszModule);
6178 KWFS_TODO();
6179 SetLastError(dwErr);
6180 return NULL;
6181}
6182
6183
6184/** Used to debug dynamically resolved procedures. */
6185static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
6186{
6187#ifdef _MSC_VER
6188 __debugbreak();
6189#else
6190 KWFS_TODO();
6191#endif
6192 return ~(UINT)0;
6193}
6194
6195
6196#ifndef NDEBUG
6197/*
6198 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
6199 */
6200# if K_ARCH == K_ARCH_X86_32
6201static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
6202# else
6203static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
6204# endif
6205typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
6206typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
6207typedef struct KWCXINTERCEPTORENTRY
6208{
6209 PFNINVOKECOMPILERPASSW pfnOrg;
6210 PKWMODULE pModule;
6211 PFNINVOKECOMPILERPASSW pfnWrap;
6212} KWCXINTERCEPTORENTRY;
6213
6214static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
6215 KWCXINTERCEPTORENTRY *pEntry)
6216{
6217 int i;
6218 KIPTR rcExit;
6219 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
6220 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
6221 for (i = 0; i < cArgs; i++)
6222 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
6223
6224 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
6225
6226 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
6227 return rcExit;
6228}
6229
6230static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
6231static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
6232static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
6233
6234static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
6235{
6236 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
6237 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
6238 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
6239};
6240
6241static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6242{
6243 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
6244}
6245
6246static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6247{
6248 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
6249}
6250
6251static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
6252{
6253 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
6254}
6255
6256#endif /* !NDEBUG */
6257
6258
6259/** Kernel32 - GetProcAddress() */
6260static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6261{
6262 KSIZE i;
6263 PKWMODULE pMod;
6264 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6265
6266 /*
6267 * Try locate the module.
6268 */
6269 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6270 if (pMod)
6271 {
6272 KLDRADDR uValue;
6273 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
6274 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
6275 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
6276 KU32_MAX /*iSymbol*/,
6277 pszProc,
6278 kHlpStrLen(pszProc),
6279 NULL /*pszVersion*/,
6280 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
6281 &uValue,
6282 NULL /*pfKind*/);
6283 if (rc == 0)
6284 {
6285 //static int s_cDbgGets = 0;
6286 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
6287 KU32 i = g_cSandboxGetProcReplacements;
6288 while (i-- > 0)
6289 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
6290 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
6291 {
6292 if ( !g_aSandboxGetProcReplacements[i].pszModule
6293 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
6294 {
6295 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
6296 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
6297 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
6298 {
6299 if (!g_aSandboxReplacements[i].fCrtSlotArray)
6300 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
6301 else
6302 {
6303 if (pMod->iCrtSlot == KU8_MAX)
6304 {
6305 int rc = kwLdrModuleCreateCrtSlot(pMod);
6306 if (rc)
6307 {
6308 KW_LOG(("GetProcAddress: kwLdrModuleCreateCrtSlot failed: %d\n", rc));
6309 SetLastError(ERROR_INTERNAL_ERROR);
6310 return NULL;
6311 }
6312 }
6313 uValue = ((KUPTR *)g_aSandboxGetProcReplacements[i].pfnReplacement)[pMod->iCrtSlot];
6314 }
6315
6316 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6317 }
6318 kwLdrModuleRelease(pMod);
6319 return (FARPROC)(KUPTR)uValue;
6320 }
6321 }
6322
6323#ifndef NDEBUG
6324 /* Intercept the compiler pass method, dumping arguments. */
6325 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
6326 {
6327 KU32 i;
6328 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
6329 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
6330 {
6331 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6332 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
6333 break;
6334 }
6335 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
6336 while (i-- > 0)
6337 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
6338 {
6339 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
6340 g_aCxInterceptorEntries[i].pModule = pMod;
6341 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
6342 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
6343 break;
6344 }
6345 }
6346#endif
6347 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
6348 kwLdrModuleRelease(pMod);
6349 //s_cDbgGets++;
6350 //if (s_cGets >= 3)
6351 // return (FARPROC)kwSandbox_BreakIntoDebugger;
6352 return (FARPROC)(KUPTR)uValue;
6353 }
6354
6355 KWFS_TODO();
6356 SetLastError(ERROR_PROC_NOT_FOUND);
6357 kwLdrModuleRelease(pMod);
6358 return NULL;
6359 }
6360
6361 /*
6362 * Hmm... could be a cached module-by-name.
6363 */
6364 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
6365 if (g_aGetModuleHandleCache[i].hmod == hmod)
6366 return GetProcAddress(hmod, pszProc);
6367
6368 KWFS_TODO();
6369 return GetProcAddress(hmod, pszProc);
6370}
6371
6372
6373#ifndef NDEBUG
6374/** Kernel32 - GetProcAddress() - native replacement for debugging only. */
6375static FARPROC WINAPI kwSandbox_Kernel32_Native_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
6376{
6377 FARPROC pfnRet = GetProcAddress(hmod, pszProc);
6378 KWLDR_LOG(("kwSandbox_Kernel32_Native_GetProcAddress(%p, %s) -> %p\n", hmod, pszProc, pfnRet));
6379 return pfnRet;
6380}
6381#endif
6382
6383
6384/** Kernel32 - GetModuleFileNameA() */
6385static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
6386{
6387 PKWMODULE pMod;
6388 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6389
6390 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6391 if (pMod != NULL)
6392 {
6393 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
6394 kwLdrModuleRelease(pMod);
6395 return cbRet;
6396 }
6397 KWFS_TODO();
6398 return 0;
6399}
6400
6401
6402/** Kernel32 - GetModuleFileNameW() */
6403static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
6404{
6405 PKWMODULE pMod;
6406 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6407
6408 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
6409 if (pMod)
6410 {
6411 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
6412 kwLdrModuleRelease(pMod);
6413 return cwcRet;
6414 }
6415
6416 KWFS_TODO();
6417 return 0;
6418}
6419
6420
6421/** NtDll - RtlPcToFileHeader
6422 * This is necessary for msvcr100.dll!CxxThrowException. */
6423static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
6424{
6425 PVOID pvRet;
6426
6427 /*
6428 * Do a binary lookup of the module table for the current tool.
6429 * This will give us a
6430 */
6431 if (g_Sandbox.fRunning)
6432 {
6433 KUPTR const uPC = (KUPTR)pvPC;
6434 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
6435 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
6436 KU32 i;
6437 if (iEnd)
6438 {
6439 KU32 iStart = 0;
6440 i = iEnd / 2;
6441 for (;;)
6442 {
6443 KUPTR const uHModCur = (KUPTR)papMods[i]->hOurMod;
6444 if (uPC < uHModCur)
6445 {
6446 iEnd = i;
6447 if (iStart < i)
6448 { }
6449 else
6450 break;
6451 }
6452 else if (uPC != uHModCur)
6453 {
6454 iStart = ++i;
6455 if (i < iEnd)
6456 { }
6457 else
6458 break;
6459 }
6460 else
6461 {
6462 /* This isn't supposed to happen. */
6463 break;
6464 }
6465
6466 i = iStart + (iEnd - iStart) / 2;
6467 }
6468
6469 /* For reasons of simplicity (= copy & paste), we end up with the
6470 module after the one we're interested in here. */
6471 i--;
6472 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
6473 && papMods[i]->pLdrMod)
6474 {
6475 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
6476 if (uRvaPC < papMods[i]->cbImage)
6477 {
6478 *ppvImageBase = papMods[i]->hOurMod;
6479 pvRet = papMods[i]->hOurMod;
6480 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
6481 return pvRet;
6482 }
6483 }
6484 }
6485 else
6486 i = 0;
6487 }
6488
6489 /*
6490 * Call the regular API.
6491 */
6492 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
6493 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
6494 return pvRet;
6495}
6496
6497
6498/*
6499 *
6500 * File access APIs (for speeding them up).
6501 * File access APIs (for speeding them up).
6502 * File access APIs (for speeding them up).
6503 *
6504 */
6505
6506
6507/**
6508 * Converts a lookup error to a windows error code.
6509 *
6510 * @returns The windows error code.
6511 * @param enmError The lookup error.
6512 */
6513static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
6514{
6515 switch (enmError)
6516 {
6517 case KFSLOOKUPERROR_NOT_FOUND:
6518 case KFSLOOKUPERROR_NOT_DIR:
6519 return ERROR_FILE_NOT_FOUND;
6520
6521 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
6522 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
6523 case KFSLOOKUPERROR_PATH_TOO_SHORT:
6524 return ERROR_PATH_NOT_FOUND;
6525
6526 case KFSLOOKUPERROR_PATH_TOO_LONG:
6527 return ERROR_FILENAME_EXCED_RANGE;
6528
6529 case KFSLOOKUPERROR_OUT_OF_MEMORY:
6530 return ERROR_NOT_ENOUGH_MEMORY;
6531
6532 default:
6533 return ERROR_PATH_NOT_FOUND;
6534 }
6535}
6536
6537#ifdef WITH_TEMP_MEMORY_FILES
6538
6539/**
6540 * Checks for a cl.exe temporary file.
6541 *
6542 * There are quite a bunch of these. They seems to be passing data between the
6543 * first and second compiler pass. Since they're on disk, they get subjected to
6544 * AV software screening and normal file consistency rules. So, not necessarily
6545 * a very efficient way of handling reasonably small amounts of data.
6546 *
6547 * We make the files live in virtual memory by intercepting their opening,
6548 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
6549 *
6550 * @returns K_TRUE / K_FALSE
6551 * @param pwszFilename The file name being accessed.
6552 */
6553static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
6554{
6555 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
6556 if (pwszName)
6557 {
6558 /* The name starts with _CL_... */
6559 if ( pwszName[0] == '_'
6560 && pwszName[1] == 'C'
6561 && pwszName[2] == 'L'
6562 && pwszName[3] == '_' )
6563 {
6564 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
6565 this check by just checking that it's alpha numerical ascii from here on. */
6566 wchar_t wc;
6567 pwszName += 4;
6568 while ((wc = *pwszName++) != '\0')
6569 {
6570 if (wc < 127 && iswalnum(wc))
6571 { /* likely */ }
6572 else
6573 return K_FALSE;
6574 }
6575 return K_TRUE;
6576 }
6577
6578 /* In VC2019 there is also one {UUID} file in temp: */
6579 if (pwszName[0] == '{')
6580 {
6581 KSIZE cwcName = kwUtf16Len(pwszName);
6582 if ( cwcName == sizeof("{4465DDD9-E494-471B-996B-9B556E25AEF8}") - 1
6583 && pwszName[37] == '}'
6584 && iswalnum(pwszName[1]) // 4
6585 && iswalnum(pwszName[2]) // 4
6586 && iswalnum(pwszName[3]) // 6
6587 && iswalnum(pwszName[4]) // 5
6588 && iswalnum(pwszName[5]) // d
6589 && iswalnum(pwszName[6]) // d
6590 && iswalnum(pwszName[7]) // d
6591 && iswalnum(pwszName[8]) // 9
6592 && pwszName[9] == '-' // -
6593 && iswalnum(pwszName[10]) // e
6594 && iswalnum(pwszName[11]) // 4
6595 && iswalnum(pwszName[12]) // 9
6596 && iswalnum(pwszName[13]) // 4
6597 && pwszName[14] == '-' // -
6598 && iswalnum(pwszName[15]) // 4
6599 && iswalnum(pwszName[16]) // 7
6600 && iswalnum(pwszName[17]) // 1
6601 && iswalnum(pwszName[18]) // b
6602 && pwszName[19] == '-' // -
6603 && iswalnum(pwszName[20]) // 9
6604 && iswalnum(pwszName[21]) // 9
6605 && iswalnum(pwszName[22]) // 6
6606 && iswalnum(pwszName[23]) // b
6607 && pwszName[24] == '-' // -
6608 && iswalnum(pwszName[25]) // 9
6609 && iswalnum(pwszName[26]) // b
6610 && iswalnum(pwszName[27]) // 5
6611 && iswalnum(pwszName[28]) // 5
6612 && iswalnum(pwszName[29]) // 6
6613 && iswalnum(pwszName[30]) // e
6614 && iswalnum(pwszName[31]) // 2
6615 && iswalnum(pwszName[32]) // 5
6616 && iswalnum(pwszName[33]) // a
6617 && iswalnum(pwszName[34]) // 3
6618 && iswalnum(pwszName[35]) // f
6619 && iswalnum(pwszName[36])) // 8
6620 return K_TRUE;
6621 }
6622 }
6623 return K_FALSE;
6624}
6625
6626
6627/**
6628 * Creates a handle to a temporary file.
6629 *
6630 * @returns The handle on success.
6631 * INVALID_HANDLE_VALUE and SetLastError on failure.
6632 * @param pTempFile The temporary file.
6633 * @param dwDesiredAccess The desired access to the handle.
6634 * @param fMapping Whether this is a mapping (K_TRUE) or file
6635 * (K_FALSE) handle type.
6636 */
6637static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
6638{
6639 /*
6640 * Create a handle to the temporary file.
6641 */
6642 HANDLE hFile = INVALID_HANDLE_VALUE;
6643 HANDLE hProcSelf = GetCurrentProcess();
6644 if (DuplicateHandle(hProcSelf, hProcSelf,
6645 hProcSelf, &hFile,
6646 SYNCHRONIZE, FALSE,
6647 0 /*dwOptions*/))
6648 {
6649 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6650 if (pHandle)
6651 {
6652 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
6653 pHandle->cRefs = 1;
6654 pHandle->offFile = 0;
6655 pHandle->hHandle = hFile;
6656 pHandle->dwDesiredAccess = dwDesiredAccess;
6657 pHandle->tidOwner = KU32_MAX;
6658 pHandle->u.pTempFile = pTempFile;
6659 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6660 {
6661 pTempFile->cActiveHandles++;
6662 kHlpAssert(pTempFile->cActiveHandles >= 1);
6663 kHlpAssert(pTempFile->cActiveHandles <= 2);
6664 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6665 return hFile;
6666 }
6667
6668 kHlpFree(pHandle);
6669 }
6670 else
6671 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6672 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6673 }
6674 else
6675 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6676 return INVALID_HANDLE_VALUE;
6677}
6678
6679
6680static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition,
6681 KBOOL *pfFallback)
6682{
6683 HANDLE hFile;
6684 DWORD dwErr;
6685
6686 /*
6687 * Check if we've got an existing temp file.
6688 * ASSUME exact same path for now.
6689 */
6690 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6691 PKWFSTEMPFILE pTempFile;
6692 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6693 {
6694 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6695 if ( pTempFile->cwcPath == cwcFilename
6696 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6697 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6698 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6699 break;
6700 }
6701
6702 /*
6703 * Create a new temporary file instance if not found.
6704 */
6705 *pfFallback = K_FALSE;
6706 if (pTempFile == NULL)
6707 {
6708 KSIZE cbFilename;
6709
6710 switch (dwCreationDisposition)
6711 {
6712 case CREATE_ALWAYS:
6713 case OPEN_ALWAYS:
6714 case CREATE_NEW:
6715 dwErr = NO_ERROR;
6716 break;
6717
6718 case OPEN_EXISTING:
6719 case TRUNCATE_EXISTING:
6720 *pfFallback = K_TRUE;
6721 kHlpAssertFailed();
6722 SetLastError(ERROR_FILE_NOT_FOUND);
6723 return INVALID_HANDLE_VALUE;
6724
6725 default:
6726 kHlpAssertFailed();
6727 SetLastError(ERROR_INVALID_PARAMETER);
6728 return INVALID_HANDLE_VALUE;
6729 }
6730
6731 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6732 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6733 if (pTempFile)
6734 {
6735 pTempFile->cwcPath = (KU16)cwcFilename;
6736 pTempFile->cbFile = 0;
6737 pTempFile->cbFileAllocated = 0;
6738 pTempFile->cActiveHandles = 0;
6739 pTempFile->cMappings = 0;
6740 pTempFile->cSegs = 0;
6741 pTempFile->paSegs = NULL;
6742 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6743
6744 pTempFile->pNext = g_Sandbox.pTempFileHead;
6745 g_Sandbox.pTempFileHead = pTempFile;
6746 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6747 }
6748 else
6749 {
6750 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6751 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6752 return INVALID_HANDLE_VALUE;
6753 }
6754 }
6755 else
6756 {
6757 switch (dwCreationDisposition)
6758 {
6759 case OPEN_EXISTING:
6760 dwErr = NO_ERROR;
6761 break;
6762 case OPEN_ALWAYS:
6763 dwErr = ERROR_ALREADY_EXISTS;
6764 break;
6765
6766 case TRUNCATE_EXISTING:
6767 case CREATE_ALWAYS:
6768 kHlpAssertFailed();
6769 pTempFile->cbFile = 0;
6770 dwErr = ERROR_ALREADY_EXISTS;
6771 break;
6772
6773 case CREATE_NEW:
6774 kHlpAssertFailed();
6775 SetLastError(ERROR_FILE_EXISTS);
6776 return INVALID_HANDLE_VALUE;
6777
6778 default:
6779 kHlpAssertFailed();
6780 SetLastError(ERROR_INVALID_PARAMETER);
6781 return INVALID_HANDLE_VALUE;
6782 }
6783 }
6784
6785 /*
6786 * Create a handle to the temporary file.
6787 */
6788 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6789 if (hFile != INVALID_HANDLE_VALUE)
6790 SetLastError(dwErr);
6791 return hFile;
6792}
6793
6794#endif /* WITH_TEMP_MEMORY_FILES */
6795
6796/**
6797 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6798 *
6799 * @returns K_TRUE if cacheable, K_FALSE if not.
6800 * @param wcFirst The first extension character.
6801 * @param wcSecond The second extension character.
6802 * @param wcThird The third extension character.
6803 * @param fAttrQuery Set if it's for an attribute query, clear if for
6804 * file creation.
6805 */
6806static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6807{
6808 /* C++ header without an extension or a directory. */
6809 if (wcFirst == '\0')
6810 {
6811 /** @todo exclude temporary files... */
6812 return K_TRUE;
6813 }
6814
6815 /* C Header: .h */
6816 if (wcFirst == 'h' || wcFirst == 'H')
6817 {
6818 if (wcSecond == '\0')
6819 return K_TRUE;
6820
6821 /* C++ Header: .hpp, .hxx */
6822 if ( (wcSecond == 'p' || wcSecond == 'P')
6823 && (wcThird == 'p' || wcThird == 'P'))
6824 return K_TRUE;
6825 if ( (wcSecond == 'x' || wcSecond == 'X')
6826 && (wcThird == 'x' || wcThird == 'X'))
6827 return K_TRUE;
6828 }
6829 /* Misc starting with i. */
6830 else if (wcFirst == 'i' || wcFirst == 'I')
6831 {
6832 if (wcSecond != '\0')
6833 {
6834 if (wcSecond == 'n' || wcSecond == 'N')
6835 {
6836 /* C++ inline header: .inl */
6837 if (wcThird == 'l' || wcThird == 'L')
6838 return K_TRUE;
6839
6840 /* Assembly include file: .inc */
6841 if (wcThird == 'c' || wcThird == 'C')
6842 return K_TRUE;
6843 }
6844 }
6845 }
6846 /* Assembly header: .mac */
6847 else if (wcFirst == 'm' || wcFirst == 'M')
6848 {
6849 if (wcSecond == 'a' || wcSecond == 'A')
6850 {
6851 if (wcThird == 'c' || wcThird == 'C')
6852 return K_TRUE;
6853 }
6854 }
6855#ifdef WITH_PCH_CACHING
6856 /* Precompiled header: .pch */
6857 else if (wcFirst == 'p' || wcFirst == 'P')
6858 {
6859 if (wcSecond == 'c' || wcSecond == 'C')
6860 {
6861 if (wcThird == 'h' || wcThird == 'H')
6862 return !g_Sandbox.fNoPchCaching;
6863 }
6864 }
6865#endif
6866#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6867 /* Linker - Object file: .obj */
6868 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6869 {
6870 if (wcSecond == 'b' || wcSecond == 'B')
6871 {
6872 if (wcThird == 'j' || wcThird == 'J')
6873 return K_TRUE;
6874 }
6875 }
6876#endif
6877 else if (fAttrQuery)
6878 {
6879 /* Dynamic link library: .dll */
6880 if (wcFirst == 'd' || wcFirst == 'D')
6881 {
6882 if (wcSecond == 'l' || wcSecond == 'L')
6883 {
6884 if (wcThird == 'l' || wcThird == 'L')
6885 return K_TRUE;
6886 }
6887 }
6888 /* Executable file: .exe */
6889 else if (wcFirst == 'e' || wcFirst == 'E')
6890 {
6891 if (wcSecond == 'x' || wcSecond == 'X')
6892 {
6893 if (wcThird == 'e' || wcThird == 'E')
6894 return K_TRUE;
6895 }
6896 }
6897 /* Response file: .rsp */
6898 else if (wcFirst == 'r' || wcFirst == 'R')
6899 {
6900 if (wcSecond == 's' || wcSecond == 'S')
6901 {
6902 if (wcThird == 'p' || wcThird == 'P')
6903 return !g_Sandbox.fNoPchCaching;
6904 }
6905 }
6906 /* Linker: */
6907 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6908 {
6909 /* Object file: .obj */
6910 if (wcFirst == 'o' || wcFirst == 'O')
6911 {
6912 if (wcSecond == 'b' || wcSecond == 'B')
6913 {
6914 if (wcThird == 'j' || wcThird == 'J')
6915 return K_TRUE;
6916 }
6917 }
6918 /* Library file: .lib */
6919 else if (wcFirst == 'l' || wcFirst == 'L')
6920 {
6921 if (wcSecond == 'i' || wcSecond == 'I')
6922 {
6923 if (wcThird == 'b' || wcThird == 'B')
6924 return K_TRUE;
6925 }
6926 }
6927 /* Linker definition file: .def */
6928 else if (wcFirst == 'd' || wcFirst == 'D')
6929 {
6930 if (wcSecond == 'e' || wcSecond == 'E')
6931 {
6932 if (wcThird == 'f' || wcThird == 'F')
6933 return K_TRUE;
6934 }
6935 }
6936 }
6937 }
6938
6939 return K_FALSE;
6940}
6941
6942
6943/**
6944 * Checks if the file extension indicates that the file/dir is something we
6945 * ought to cache.
6946 *
6947 * @returns K_TRUE if cachable, K_FALSE if not.
6948 * @param pszExt The kHlpGetExt result.
6949 * @param fAttrQuery Set if it's for an attribute query, clear if for
6950 * file creation.
6951 */
6952static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6953{
6954 wchar_t const wcFirst = *pszExt;
6955 if (wcFirst)
6956 {
6957 wchar_t const wcSecond = pszExt[1];
6958 if (wcSecond)
6959 {
6960 wchar_t const wcThird = pszExt[2];
6961 if (pszExt[3] == '\0')
6962 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6963 return K_FALSE;
6964 }
6965 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6966 }
6967 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6968}
6969
6970
6971/**
6972 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6973 * should be cached.
6974 *
6975 * @returns K_TRUE if cachable, K_FALSE if not.
6976 * @param pwszPath The UTF-16 path to examine.
6977 * @param fAttrQuery Set if it's for an attribute query, clear if for
6978 * file creation.
6979 */
6980static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6981{
6982 KSIZE cwcExt;
6983 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6984 switch (cwcExt)
6985 {
6986 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6987 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6988 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6989 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6990 }
6991 return K_FALSE;
6992}
6993
6994
6995
6996/**
6997 * Creates a new
6998 *
6999 * @returns
7000 * @param pFsObj .
7001 * @param pwszFilename .
7002 */
7003static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
7004{
7005 HANDLE hFile;
7006 MY_IO_STATUS_BLOCK Ios;
7007 MY_OBJECT_ATTRIBUTES ObjAttr;
7008 MY_UNICODE_STRING UniStr;
7009 MY_NTSTATUS rcNt;
7010 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7011
7012 /*
7013 * Open the file relative to the parent directory.
7014 */
7015 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
7016 kHlpAssert(pFsObj->pParent);
7017 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
7018
7019 Ios.Information = ~(ULONG_PTR)0;
7020 Ios.u.Status = -1;
7021
7022 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
7023 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
7024 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
7025
7026 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
7027
7028 rcNt = g_pfnNtCreateFile(&hFile,
7029 GENERIC_READ | SYNCHRONIZE,
7030 &ObjAttr,
7031 &Ios,
7032 NULL, /*cbFileInitialAlloc */
7033 FILE_ATTRIBUTE_NORMAL,
7034 FILE_SHARE_READ,
7035 FILE_OPEN,
7036 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
7037 NULL, /*pEaBuffer*/
7038 0); /*cbEaBuffer*/
7039 if (MY_NT_SUCCESS(rcNt))
7040 {
7041 /*
7042 * Read the whole file into memory.
7043 */
7044 LARGE_INTEGER cbFile;
7045 if (GetFileSizeEx(hFile, &cbFile))
7046 {
7047 if ( cbFile.QuadPart >= 0
7048#ifdef WITH_PCH_CACHING
7049 && ( cbFile.QuadPart < 16*1024*1024
7050 || ( cbFile.QuadPart < 96*1024*1024
7051 && pFsObj->cchName > 4
7052 && !g_Sandbox.fNoPchCaching
7053 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
7054#endif
7055 )
7056 {
7057 KU32 cbCache = (KU32)cbFile.QuadPart;
7058 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
7059 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
7060 if (hMapping != NULL)
7061 {
7062 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
7063 if (pbCache)
7064 {
7065 /*
7066 * Create the cached file object.
7067 */
7068 PKFSWCACHEDFILE pCachedFile;
7069 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
7070 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
7071 sizeof(*pCachedFile) + cbPath);
7072 if (pCachedFile)
7073 {
7074 pCachedFile->hCached = hFile;
7075 pCachedFile->hSection = hMapping;
7076 pCachedFile->cbCached = cbCache;
7077 pCachedFile->pbCached = pbCache;
7078 pCachedFile->pFsObj = pFsObj;
7079 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
7080 kFsCacheObjRetain(pFsObj);
7081
7082 g_cReadCachedFiles++;
7083 g_cbReadCachedFiles += cbCache;
7084
7085 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
7086 return pCachedFile;
7087 }
7088
7089 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
7090 }
7091 else
7092 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
7093 CloseHandle(hMapping);
7094 }
7095 else
7096 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
7097 }
7098 else
7099 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
7100 }
7101 else
7102 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
7103 g_pfnNtClose(hFile);
7104 }
7105 else
7106 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
7107 return NULL;
7108}
7109
7110
7111/**
7112 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
7113 */
7114static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
7115 KBOOL fIsFileHandle, HANDLE *phFile)
7116{
7117 HANDLE hProcSelf = GetCurrentProcess();
7118 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
7119 hProcSelf, phFile,
7120 dwDesiredAccess, fInheritHandle,
7121 0 /*dwOptions*/))
7122 {
7123 /*
7124 * Create handle table entry for the duplicate handle.
7125 */
7126 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
7127 if (pHandle)
7128 {
7129 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
7130 pHandle->cRefs = 1;
7131 pHandle->offFile = 0;
7132 pHandle->hHandle = *phFile;
7133 pHandle->dwDesiredAccess = dwDesiredAccess;
7134 pHandle->tidOwner = KU32_MAX;
7135 pHandle->u.pCachedFile = pCachedFile;
7136 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
7137 return K_TRUE;
7138
7139 kHlpFree(pHandle);
7140 }
7141 else
7142 KWFS_LOG(("Out of memory for handle!\n"));
7143
7144 CloseHandle(*phFile);
7145 *phFile = INVALID_HANDLE_VALUE;
7146 }
7147 else
7148 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
7149 return K_FALSE;
7150}
7151
7152
7153/**
7154 * Kernel32 - Common code for CreateFileW and CreateFileA.
7155 */
7156static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
7157{
7158 *phFile = INVALID_HANDLE_VALUE;
7159
7160 /*
7161 * At the moment we only handle existing files.
7162 */
7163 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
7164 {
7165 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
7166 kHlpAssert(pFsObj->fHaveStats);
7167 if ( pCachedFile != NULL
7168 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
7169 {
7170 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
7171 return K_TRUE;
7172 }
7173 }
7174 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
7175
7176 /* Do fallback, please. */
7177 return K_FALSE;
7178}
7179
7180
7181/** Kernel32 - CreateFileA */
7182static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7183 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7184 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7185{
7186 HANDLE hFile;
7187
7188 /*
7189 * Check for include files and similar that we do read-only caching of.
7190 */
7191 if (dwCreationDisposition == OPEN_EXISTING)
7192 {
7193 if ( dwDesiredAccess == GENERIC_READ
7194 || dwDesiredAccess == FILE_GENERIC_READ)
7195 {
7196 if (dwShareMode & FILE_SHARE_READ)
7197 {
7198 if ( !pSecAttrs
7199 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7200 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7201 {
7202 const char *pszExt = kHlpGetExt(pszFilename);
7203 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
7204 {
7205 KFSLOOKUPERROR enmError;
7206 PKFSOBJ pFsObj;
7207 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7208
7209 pFsObj = kFsCacheLookupA(g_pFsCache, pszFilename, &enmError);
7210 if (pFsObj)
7211 {
7212 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7213 {
7214 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7215 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7216 kFsCacheObjRelease(g_pFsCache, pFsObj);
7217 if (fRc)
7218 {
7219 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
7220 return hFile;
7221 }
7222 }
7223 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7224 {
7225 KWFS_LOG(("CreateFileA(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7226 SetLastError(ERROR_FILE_NOT_FOUND);
7227 return INVALID_HANDLE_VALUE;
7228 }
7229 /* Always fall back on missing files in volatile areas. */
7230 }
7231 /* These are for nasm and yasm header searching. Cache will already
7232 have checked the directories for the file, no need to call
7233 CreateFile to do it again. */
7234 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7235 {
7236 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
7237 SetLastError(ERROR_FILE_NOT_FOUND);
7238 return INVALID_HANDLE_VALUE;
7239 }
7240 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7241 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7242 {
7243 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
7244 SetLastError(ERROR_PATH_NOT_FOUND);
7245 return INVALID_HANDLE_VALUE;
7246 }
7247
7248 /* fallback */
7249 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7250 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7251 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
7252 return hFile;
7253 }
7254 }
7255 }
7256 }
7257 }
7258
7259 /*
7260 * Okay, normal.
7261 */
7262 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7263 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7264 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7265
7266 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
7267 return hFile;
7268}
7269
7270
7271/** Kernel32 - CreateFileW */
7272static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
7273 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
7274 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
7275{
7276 HANDLE hFile;
7277
7278#ifdef WITH_TEMP_MEMORY_FILES
7279 /*
7280 * Check for temporary files (cl.exe only).
7281 */
7282 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
7283 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
7284 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
7285 && kwFsIsClTempFileW(pwszFilename))
7286 {
7287 KBOOL fFallback = K_FALSE;
7288 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition, &fFallback);
7289 if (!fFallback)
7290 {
7291 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
7292 return hFile;
7293 }
7294 }
7295#endif
7296
7297 /*
7298 * Check for include files and similar that we do read-only caching of.
7299 */
7300 if (dwCreationDisposition == OPEN_EXISTING)
7301 {
7302 if ( dwDesiredAccess == GENERIC_READ
7303 || dwDesiredAccess == FILE_GENERIC_READ)
7304 {
7305 if (dwShareMode & FILE_SHARE_READ)
7306 {
7307 if ( !pSecAttrs
7308 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
7309 && pSecAttrs->lpSecurityDescriptor == NULL ) )
7310 {
7311 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
7312 {
7313 KFSLOOKUPERROR enmError;
7314 PKFSOBJ pFsObj;
7315 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7316
7317 pFsObj = kFsCacheLookupW(g_pFsCache, pwszFilename, &enmError);
7318 if (pFsObj)
7319 {
7320 if (pFsObj->bObjType != KFSOBJ_TYPE_MISSING)
7321 {
7322 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess,
7323 pSecAttrs && pSecAttrs->bInheritHandle, &hFile);
7324 kFsCacheObjRelease(g_pFsCache, pFsObj);
7325 if (fRc)
7326 {
7327 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
7328 return hFile;
7329 }
7330 }
7331 else if (!(pFsObj->fFlags & KFSOBJ_F_USE_CUSTOM_GEN))
7332 {
7333 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7334 SetLastError(ERROR_FILE_NOT_FOUND);
7335#if 0
7336 if ( pFsObj->cchName > sizeof("generated.h")
7337 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - sizeof("generated.h") + 1], "generated.h") == 0)
7338 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; pFsObj->fFlags=%#x\n", pwszFilename, pFsObj->fFlags);
7339#endif
7340 return INVALID_HANDLE_VALUE;
7341 }
7342 /* Always fall back on missing files in volatile areas. */
7343 }
7344 /* These are for nasm and yasm style header searching. Cache will
7345 already have checked the directories for the file, no need to call
7346 CreateFile to do it again. */
7347 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
7348 {
7349#if 0
7350 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7351 if ( cwcFilename > sizeof("generated.h")
7352 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7353 L"generated.h", sizeof(L"generated.h")) == 0)
7354 kwErrPrintf("CreateFileW(%ls) -> ERROR_FILE_NOT_FOUND; (KFSLOOKUPERROR_NOT_FOUND)\n", pwszFilename, pFsObj->fFlags);
7355#endif
7356 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
7357 SetLastError(ERROR_FILE_NOT_FOUND);
7358 return INVALID_HANDLE_VALUE;
7359 }
7360 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
7361 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
7362 {
7363#if 0
7364 KSIZE cwcFilename = kwUtf16Len(pwszFilename);
7365 if ( cwcFilename > sizeof("generated.h")
7366 && memcmp(&pwszFilename[cwcFilename - sizeof("generated.h") + 1],
7367 L"generated.h", sizeof(L"generated.h")) == 0)
7368 kwErrPrintf("CreateFileW(%ls) -> ERROR_PATH_NOT_FOUND; (%d)\n", pwszFilename, enmError);
7369#endif
7370 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
7371 SetLastError(ERROR_PATH_NOT_FOUND);
7372 return INVALID_HANDLE_VALUE;
7373 }
7374
7375 /* fallback */
7376 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7377 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7378 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
7379 return hFile;
7380 }
7381 }
7382 else
7383 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
7384 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
7385 }
7386 else
7387 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
7388 }
7389 else
7390 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
7391 }
7392 else
7393 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
7394
7395 /*
7396 * Okay, normal.
7397 */
7398 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
7399 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
7400 kHlpAssert(hFile == INVALID_HANDLE_VALUE || kwSandboxHandleLookup(hFile) == NULL);
7401
7402 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
7403 return hFile;
7404}
7405
7406
7407
7408/** Kernel32 - SetFilePointer */
7409static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
7410{
7411 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7412 if (pHandle != NULL)
7413 {
7414 KU32 cbFile;
7415 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
7416 switch (pHandle->enmType)
7417 {
7418 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7419 cbFile = pHandle->u.pCachedFile->cbCached;
7420 break;
7421#ifdef WITH_TEMP_MEMORY_FILES
7422 case KWHANDLETYPE_TEMP_FILE:
7423 cbFile = pHandle->u.pTempFile->cbFile;
7424 break;
7425 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7426#endif
7427 case KWHANDLETYPE_OUTPUT_BUF:
7428 default:
7429 kHlpAssertFailed();
7430 kwSandboxHandlePut(pHandle);
7431 SetLastError(ERROR_INVALID_FUNCTION);
7432 return INVALID_SET_FILE_POINTER;
7433 }
7434
7435 switch (dwMoveMethod)
7436 {
7437 case FILE_BEGIN:
7438 break;
7439 case FILE_CURRENT:
7440 offMove += pHandle->offFile;
7441 break;
7442 case FILE_END:
7443 offMove += cbFile;
7444 break;
7445 default:
7446 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7447 kwSandboxHandlePut(pHandle);
7448 SetLastError(ERROR_INVALID_PARAMETER);
7449 return INVALID_SET_FILE_POINTER;
7450 }
7451 if (offMove >= 0)
7452 {
7453 if (offMove >= (KSSIZE)cbFile)
7454 {
7455#ifdef WITH_TEMP_MEMORY_FILES
7456 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7457 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7458#endif
7459 offMove = (KSSIZE)cbFile;
7460#ifdef WITH_TEMP_MEMORY_FILES
7461 /* For writable files, seeking beyond the end is fine, but check that we've got
7462 the type range for the request. */
7463 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
7464 {
7465 kHlpAssertMsgFailed(("%#llx\n", offMove));
7466 kwSandboxHandlePut(pHandle);
7467 SetLastError(ERROR_SEEK);
7468 return INVALID_SET_FILE_POINTER;
7469 }
7470#endif
7471 }
7472 pHandle->offFile = (KU32)offMove;
7473 }
7474 else
7475 {
7476 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
7477 kwSandboxHandlePut(pHandle);
7478 SetLastError(ERROR_NEGATIVE_SEEK);
7479 return INVALID_SET_FILE_POINTER;
7480 }
7481 if (pcbMoveHi)
7482 *pcbMoveHi = (KU64)offMove >> 32;
7483 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
7484 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7485 kwSandboxHandlePut(pHandle);
7486 SetLastError(NO_ERROR);
7487 return (KU32)offMove;
7488 }
7489
7490 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
7491 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
7492}
7493
7494
7495/** Kernel32 - SetFilePointerEx */
7496static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
7497 DWORD dwMoveMethod)
7498{
7499 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7500 if (pHandle != NULL)
7501 {
7502 KI64 offMyMove = offMove.QuadPart;
7503 KU32 cbFile;
7504 switch (pHandle->enmType)
7505 {
7506 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7507 cbFile = pHandle->u.pCachedFile->cbCached;
7508 break;
7509#ifdef WITH_TEMP_MEMORY_FILES
7510 case KWHANDLETYPE_TEMP_FILE:
7511 cbFile = pHandle->u.pTempFile->cbFile;
7512 break;
7513 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7514#endif
7515 case KWHANDLETYPE_OUTPUT_BUF:
7516 default:
7517 kHlpAssertFailed();
7518 kwSandboxHandlePut(pHandle);
7519 SetLastError(ERROR_INVALID_FUNCTION);
7520 return FALSE;
7521 }
7522
7523 switch (dwMoveMethod)
7524 {
7525 case FILE_BEGIN:
7526 break;
7527 case FILE_CURRENT:
7528 offMyMove += pHandle->offFile;
7529 break;
7530 case FILE_END:
7531 offMyMove += cbFile;
7532 break;
7533 default:
7534 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
7535 kwSandboxHandlePut(pHandle);
7536 SetLastError(ERROR_INVALID_PARAMETER);
7537 return FALSE;
7538 }
7539 if (offMyMove >= 0)
7540 {
7541 if (offMyMove >= (KSSIZE)cbFile)
7542 {
7543#ifdef WITH_TEMP_MEMORY_FILES
7544 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
7545 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
7546#endif
7547 offMyMove = (KSSIZE)cbFile;
7548#ifdef WITH_TEMP_MEMORY_FILES
7549 /* For writable files, seeking beyond the end is fine, but check that we've got
7550 the type range for the request. */
7551 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
7552 {
7553 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
7554 kwSandboxHandlePut(pHandle);
7555 SetLastError(ERROR_SEEK);
7556 return FALSE;
7557 }
7558#endif
7559 }
7560 pHandle->offFile = (KU32)offMyMove;
7561 }
7562 else
7563 {
7564 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
7565 kwSandboxHandlePut(pHandle);
7566 SetLastError(ERROR_NEGATIVE_SEEK);
7567 return FALSE;
7568 }
7569 if (poffNew)
7570 poffNew->QuadPart = offMyMove;
7571 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
7572 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
7573 kwSandboxHandlePut(pHandle);
7574 return TRUE;
7575 }
7576 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
7577 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
7578}
7579
7580
7581/** Kernel32 - ReadFile */
7582static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
7583 LPOVERLAPPED pOverlapped)
7584{
7585 BOOL fRet;
7586 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7587 g_cReadFileCalls++;
7588 if (pHandle != NULL)
7589 {
7590 switch (pHandle->enmType)
7591 {
7592 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7593 {
7594 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7595 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
7596 if (cbActually > cbToRead)
7597 cbActually = cbToRead;
7598
7599#ifdef WITH_HASH_CACHE
7600 if (g_Sandbox.pHashHead)
7601 {
7602 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
7603 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
7604 g_Sandbox.LastHashRead.cbRead = cbActually;
7605 g_Sandbox.LastHashRead.pvRead = pvBuffer;
7606 }
7607#endif
7608
7609 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
7610 pHandle->offFile += cbActually;
7611
7612 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7613 *pcbActuallyRead = cbActually;
7614
7615 g_cbReadFileFromReadCached += cbActually;
7616 g_cbReadFileTotal += cbActually;
7617 g_cReadFileFromReadCached++;
7618
7619 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
7620 kwSandboxHandlePut(pHandle);
7621 return TRUE;
7622 }
7623
7624#ifdef WITH_TEMP_MEMORY_FILES
7625 case KWHANDLETYPE_TEMP_FILE:
7626 {
7627 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7628 KU32 cbActually;
7629 if (pHandle->offFile < pTempFile->cbFile)
7630 {
7631 cbActually = pTempFile->cbFile - pHandle->offFile;
7632 if (cbActually > cbToRead)
7633 cbActually = cbToRead;
7634
7635 /* Copy the data. */
7636 if (cbActually > 0)
7637 {
7638 KU32 cbLeft;
7639 KU32 offSeg;
7640 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7641
7642 /* Locate the segment containing the byte at offFile. */
7643 KU32 iSeg = pTempFile->cSegs - 1;
7644 kHlpAssert(pTempFile->cSegs > 0);
7645 while (paSegs[iSeg].offData > pHandle->offFile)
7646 iSeg--;
7647
7648 /* Copy out the data. */
7649 cbLeft = cbActually;
7650 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7651 for (;;)
7652 {
7653 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7654 if (cbAvail >= cbLeft)
7655 {
7656 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
7657 break;
7658 }
7659
7660 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
7661 cbLeft -= cbAvail;
7662 offSeg = 0;
7663 iSeg++;
7664 kHlpAssert(iSeg < pTempFile->cSegs);
7665 }
7666
7667 /* Update the file offset. */
7668 pHandle->offFile += cbActually;
7669 }
7670 }
7671 /* Read does not commit file space, so return zero bytes. */
7672 else
7673 cbActually = 0;
7674
7675 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
7676 *pcbActuallyRead = cbActually;
7677
7678 g_cbReadFileTotal += cbActually;
7679 g_cbReadFileFromInMemTemp += cbActually;
7680 g_cReadFileFromInMemTemp++;
7681
7682 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
7683 kwSandboxHandlePut(pHandle);
7684 return TRUE;
7685 }
7686
7687 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7688#endif /* WITH_TEMP_MEMORY_FILES */
7689 case KWHANDLETYPE_OUTPUT_BUF:
7690 default:
7691 kHlpAssertFailed();
7692 kwSandboxHandlePut(pHandle);
7693 SetLastError(ERROR_INVALID_FUNCTION);
7694 *pcbActuallyRead = 0;
7695 return FALSE;
7696 }
7697 }
7698
7699 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
7700 if (fRet && pcbActuallyRead)
7701 g_cbReadFileTotal += *pcbActuallyRead;
7702 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
7703 return fRet;
7704}
7705
7706
7707/** Kernel32 - ReadFileEx */
7708static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7709 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7710{
7711 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
7712
7713 KWFS_LOG(("ReadFile(%p)\n", hFile));
7714 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7715}
7716
7717#ifdef WITH_STD_OUT_ERR_BUFFERING
7718
7719/**
7720 * Write something to a handle, making sure everything is actually written.
7721 *
7722 * @param hHandle Where to write it to.
7723 * @param pchBuf What to write
7724 * @param cchToWrite How much to write.
7725 */
7726static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7727{
7728 if (cchToWrite > 0)
7729 {
7730 DWORD cchWritten = 0;
7731 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7732 {
7733 if (cchWritten == cchToWrite)
7734 { /* likely */ }
7735 else
7736 {
7737 do
7738 {
7739 pchBuf += cchWritten;
7740 cchToWrite -= cchWritten;
7741 cchWritten = 0;
7742 } while ( cchToWrite > 0
7743 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7744 }
7745 }
7746 else
7747 kHlpAssertFailed();
7748 }
7749}
7750
7751
7752/**
7753 * Worker for WriteFile when the output isn't going to the console.
7754 *
7755 * @param pSandbox The sandbox.
7756 * @param pOutBuf The output buffer.
7757 * @param pchBuffer What to write.
7758 * @param cchToWrite How much to write.
7759 */
7760static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7761{
7762 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7763 { /* likely */ }
7764 else
7765 {
7766 /* No realloc, max size is 64KB. */
7767 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7768 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7769 if (!pOutBuf->u.Fully.pchBuf)
7770 {
7771 while ( !pOutBuf->u.Fully.pchBuf
7772 && pOutBuf->u.Fully.cchBufAlloc > 64)
7773 {
7774 pOutBuf->u.Fully.cchBufAlloc /= 2;
7775 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7776 }
7777 if (!pOutBuf->u.Fully.pchBuf)
7778 {
7779 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7780 pOutBuf->u.Fully.pchBuf = (char *)&pOutBuf->abPadding[0];
7781 }
7782 }
7783 }
7784
7785 /*
7786 * Special case: Output ends with newline and fits in the buffer.
7787 */
7788 if ( cchToWrite > 1
7789 && pchBuffer[cchToWrite - 1] == '\n'
7790 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7791 {
7792 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7793 pOutBuf->u.Fully.cchBuf += cchToWrite;
7794 }
7795 else
7796 {
7797 /*
7798 * Work thru the text line by line, flushing the buffer when
7799 * appropriate. The buffer is not a line buffer here, it's a
7800 * full buffer.
7801 */
7802 while (cchToWrite > 0)
7803 {
7804 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7805 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7806 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7807 {
7808 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7809 pOutBuf->u.Fully.cchBuf += cchLine;
7810 }
7811 /*
7812 * Option one: Flush the buffer and the current line.
7813 *
7814 * We choose this one when the line won't ever fit, or when we have
7815 * an incomplete line in the buffer.
7816 */
7817 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7818 || pOutBuf->u.Fully.cchBuf == 0
7819 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7820 {
7821 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7822 if (pOutBuf->u.Fully.cchBuf > 0)
7823 {
7824 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7825 pOutBuf->u.Fully.cchBuf = 0;
7826 }
7827 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7828 }
7829 /*
7830 * Option two: Only flush the lines in the buffer.
7831 */
7832 else
7833 {
7834 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7835 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7836 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7837 pOutBuf->u.Fully.cchBuf = cchLine;
7838 }
7839
7840 /* advance */
7841 pchBuffer += cchLine;
7842 cchToWrite -= cchLine;
7843 }
7844 }
7845}
7846
7847#endif /* WITH_STD_OUT_ERR_BUFFERING */
7848
7849#ifdef WITH_TEMP_MEMORY_FILES
7850static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7851{
7852 KU32 cbMinFile = offFile + cbNeeded;
7853 if (cbMinFile >= offFile)
7854 {
7855 /* Calc how much space we've already allocated and */
7856 if (cbMinFile <= pTempFile->cbFileAllocated)
7857 return K_TRUE;
7858
7859 /* Grow the file. */
7860 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7861 {
7862 int rc;
7863 KU32 cSegs = pTempFile->cSegs;
7864 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7865 do
7866 {
7867 /* grow the segment array? */
7868 if ((cSegs % 16) == 0)
7869 {
7870 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7871 if (!pvNew)
7872 return K_FALSE;
7873 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7874 }
7875
7876 /* Use page alloc here to simplify mapping later. */
7877 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7878 if (rc == 0)
7879 { /* likely */ }
7880 else
7881 {
7882 cbNewSeg = 64*1024;
7883 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7884 if (rc != 0)
7885 {
7886 kHlpAssertFailed();
7887 return K_FALSE;
7888 }
7889 }
7890 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7891 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7892 pTempFile->cbFileAllocated += cbNewSeg;
7893 pTempFile->cSegs = ++cSegs;
7894
7895 } while (pTempFile->cbFileAllocated < cbMinFile);
7896
7897 return K_TRUE;
7898 }
7899 }
7900
7901 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7902 return K_FALSE;
7903}
7904#endif /* WITH_TEMP_MEMORY_FILES */
7905
7906
7907#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7908/** Kernel32 - WriteFile */
7909static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7910 LPOVERLAPPED pOverlapped)
7911{
7912 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
7913 BOOL fRet;
7914 g_cWriteFileCalls++;
7915 if (pHandle != NULL)
7916 {
7917 switch (pHandle->enmType)
7918 {
7919# ifdef WITH_TEMP_MEMORY_FILES
7920 case KWHANDLETYPE_TEMP_FILE:
7921 {
7922 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7923
7924 kHlpAssert(!pOverlapped);
7925 kHlpAssert(pcbActuallyWritten);
7926
7927 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7928 {
7929 KU32 cbLeft;
7930 KU32 offSeg;
7931
7932 /* Locate the segment containing the byte at offFile. */
7933 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7934 KU32 iSeg = pTempFile->cSegs - 1;
7935 kHlpAssert(pTempFile->cSegs > 0);
7936 while (paSegs[iSeg].offData > pHandle->offFile)
7937 iSeg--;
7938
7939 /* Copy in the data. */
7940 cbLeft = cbToWrite;
7941 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7942 for (;;)
7943 {
7944 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7945 if (cbAvail >= cbLeft)
7946 {
7947 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7948 break;
7949 }
7950
7951 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7952 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7953 cbLeft -= cbAvail;
7954 offSeg = 0;
7955 iSeg++;
7956 kHlpAssert(iSeg < pTempFile->cSegs);
7957 }
7958
7959 /* Update the file offset. */
7960 pHandle->offFile += cbToWrite;
7961 if (pHandle->offFile > pTempFile->cbFile)
7962 pTempFile->cbFile = pHandle->offFile;
7963
7964 *pcbActuallyWritten = cbToWrite;
7965
7966 g_cbWriteFileTotal += cbToWrite;
7967 g_cbWriteFileToInMemTemp += cbToWrite;
7968 g_cWriteFileToInMemTemp++;
7969
7970 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7971 kwSandboxHandlePut(pHandle);
7972 return TRUE;
7973 }
7974
7975 kHlpAssertFailed();
7976 kwSandboxHandlePut(pHandle);
7977 *pcbActuallyWritten = 0;
7978 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7979 return FALSE;
7980 }
7981# endif
7982
7983 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7984 kHlpAssertFailed();
7985 kwSandboxHandlePut(pHandle);
7986 SetLastError(ERROR_ACCESS_DENIED);
7987 *pcbActuallyWritten = 0;
7988 return FALSE;
7989
7990# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7991 /*
7992 * Standard output & error.
7993 */
7994 case KWHANDLETYPE_OUTPUT_BUF:
7995 {
7996 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7997 if (pOutBuf->fIsConsole)
7998 {
7999 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
8000 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
8001 }
8002 else
8003 {
8004# ifdef WITH_STD_OUT_ERR_BUFFERING
8005 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
8006 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
8007 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
8008# else
8009 kHlpAssertFailed();
8010# endif
8011 }
8012 if (pcbActuallyWritten)
8013 *pcbActuallyWritten = cbToWrite;
8014 g_cbWriteFileTotal += cbToWrite;
8015 kwSandboxHandlePut(pHandle);
8016 return TRUE;
8017 }
8018# endif
8019
8020 default:
8021#ifdef WITH_TEMP_MEMORY_FILES
8022 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8023#endif
8024 kHlpAssertFailed();
8025 kwSandboxHandlePut(pHandle);
8026 SetLastError(ERROR_INVALID_FUNCTION);
8027 *pcbActuallyWritten = 0;
8028 return FALSE;
8029 }
8030 }
8031
8032 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
8033 if (fRet && pcbActuallyWritten)
8034 g_cbWriteFileTotal += *pcbActuallyWritten;
8035 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
8036 return fRet;
8037}
8038
8039
8040/** Kernel32 - WriteFileEx */
8041static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
8042 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
8043{
8044 kHlpAssert(kwSandboxHandleLookup(hFile) == NULL);
8045
8046 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
8047 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
8048}
8049
8050#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
8051
8052#ifdef WITH_TEMP_MEMORY_FILES
8053
8054/** Kernel32 - SetEndOfFile; */
8055static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
8056{
8057 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8058 if (pHandle != NULL)
8059 {
8060 switch (pHandle->enmType)
8061 {
8062 case KWHANDLETYPE_TEMP_FILE:
8063 {
8064 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8065 if ( pHandle->offFile > pTempFile->cbFile
8066 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
8067 {
8068 kHlpAssertFailed();
8069 kwSandboxHandlePut(pHandle);
8070 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8071 return FALSE;
8072 }
8073
8074 pTempFile->cbFile = pHandle->offFile;
8075 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
8076 kwSandboxHandlePut(pHandle);
8077 return TRUE;
8078 }
8079
8080 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8081 kHlpAssertFailed();
8082 kwSandboxHandlePut(pHandle);
8083 SetLastError(ERROR_ACCESS_DENIED);
8084 return FALSE;
8085
8086# ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8087 case KWHANDLETYPE_OUTPUT_BUF:
8088 kHlpAssertFailed();
8089 kwSandboxHandlePut(pHandle);
8090 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
8091 return FALSE;
8092# endif
8093
8094 default:
8095 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8096 kHlpAssertFailed();
8097 kwSandboxHandlePut(pHandle);
8098 SetLastError(ERROR_INVALID_FUNCTION);
8099 return FALSE;
8100 }
8101 }
8102
8103 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
8104 return SetEndOfFile(hFile);
8105}
8106
8107
8108/** Kernel32 - GetFileType */
8109static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
8110{
8111 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8112 if (pHandle != NULL)
8113 {
8114 switch (pHandle->enmType)
8115 {
8116 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8117 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
8118 kwSandboxHandlePut(pHandle);
8119 return FILE_TYPE_DISK;
8120
8121 case KWHANDLETYPE_TEMP_FILE:
8122 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
8123 kwSandboxHandlePut(pHandle);
8124 return FILE_TYPE_DISK;
8125
8126#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8127 case KWHANDLETYPE_OUTPUT_BUF:
8128 {
8129 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
8130 DWORD fRet;
8131 if (pOutBuf->fFileType != KU8_MAX)
8132 {
8133 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
8134 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
8135 }
8136 else
8137 {
8138 fRet = GetFileType(hFile);
8139 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
8140 }
8141 kwSandboxHandlePut(pHandle);
8142 return fRet;
8143 }
8144#endif
8145 }
8146 kwSandboxHandlePut(pHandle);
8147 }
8148
8149 KWFS_LOG(("GetFileType(%p)\n", hFile));
8150 return GetFileType(hFile);
8151}
8152
8153
8154/** Kernel32 - GetFileSize */
8155static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
8156{
8157 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8158 if (pHandle != NULL)
8159 {
8160 if (pcbHighDword)
8161 *pcbHighDword = 0;
8162 SetLastError(NO_ERROR);
8163 switch (pHandle->enmType)
8164 {
8165 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8166 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8167 kwSandboxHandlePut(pHandle);
8168 return pHandle->u.pCachedFile->cbCached;
8169
8170 case KWHANDLETYPE_TEMP_FILE:
8171 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8172 kwSandboxHandlePut(pHandle);
8173 return pHandle->u.pTempFile->cbFile;
8174
8175 case KWHANDLETYPE_OUTPUT_BUF:
8176 /* do default */
8177 break;
8178
8179 default:
8180 kHlpAssertFailed();
8181 kwSandboxHandlePut(pHandle);
8182 SetLastError(ERROR_INVALID_FUNCTION);
8183 return INVALID_FILE_SIZE;
8184 }
8185 kwSandboxHandlePut(pHandle);
8186 }
8187
8188 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
8189 return GetFileSize(hFile, pcbHighDword);
8190}
8191
8192
8193/** Kernel32 - GetFileSizeEx */
8194static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
8195{
8196 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8197 if (pHandle != NULL)
8198 {
8199 switch (pHandle->enmType)
8200 {
8201 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8202 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
8203 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
8204 kwSandboxHandlePut(pHandle);
8205 return TRUE;
8206
8207 case KWHANDLETYPE_TEMP_FILE:
8208 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
8209 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
8210 kwSandboxHandlePut(pHandle);
8211 return TRUE;
8212
8213 case KWHANDLETYPE_OUTPUT_BUF:
8214 /* do default */
8215 break;
8216
8217 default:
8218 kHlpAssertFailed();
8219 kwSandboxHandlePut(pHandle);
8220 SetLastError(ERROR_INVALID_FUNCTION);
8221 return INVALID_FILE_SIZE;
8222 }
8223 kwSandboxHandlePut(pHandle);
8224 }
8225
8226 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
8227 return GetFileSizeEx(hFile, pcbFile);
8228}
8229
8230
8231/** Kernel32 - CreateFileMappingW */
8232static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
8233 DWORD fProtect, DWORD dwMaximumSizeHigh,
8234 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
8235{
8236 HANDLE hMapping;
8237 PKWHANDLE pHandle = kwSandboxHandleGet(hFile);
8238 if (pHandle != NULL)
8239 {
8240 switch (pHandle->enmType)
8241 {
8242 case KWHANDLETYPE_TEMP_FILE:
8243 {
8244 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8245 if ( ( fProtect == PAGE_READONLY
8246 || fProtect == PAGE_EXECUTE_READ)
8247 && dwMaximumSizeHigh == 0
8248 && ( dwMaximumSizeLow == 0
8249 || dwMaximumSizeLow == pTempFile->cbFile)
8250 && pwszName == NULL)
8251 {
8252 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
8253 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
8254 kwSandboxHandlePut(pHandle);
8255 return hMapping;
8256 }
8257 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
8258 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8259 kwSandboxHandlePut(pHandle);
8260 SetLastError(ERROR_ACCESS_DENIED);
8261 return INVALID_HANDLE_VALUE;
8262 }
8263
8264 /* moc.exe benefits from this. */
8265 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8266 {
8267 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8268 if ( ( fProtect == PAGE_READONLY
8269 || fProtect == PAGE_EXECUTE_READ)
8270 && dwMaximumSizeHigh == 0
8271 && ( dwMaximumSizeLow == 0
8272 || dwMaximumSizeLow == pCachedFile->cbCached)
8273 && pwszName == NULL)
8274 {
8275 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
8276 K_FALSE /*fIsFileHandle*/, &hMapping))
8277 { /* likely */ }
8278 else
8279 hMapping = NULL;
8280 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
8281 kwSandboxHandlePut(pHandle);
8282 return hMapping;
8283 }
8284
8285 /* Do fallback (for .pch). */
8286 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
8287 ("fProtect=%#x cb=%#x'%08x name=%p\n",
8288 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
8289
8290 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8291 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
8292 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8293 kwSandboxHandlePut(pHandle);
8294 return hMapping;
8295 }
8296
8297 /** @todo read cached memory mapped files too for moc. */
8298 }
8299 kwSandboxHandlePut(pHandle);
8300 }
8301
8302 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
8303 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
8304 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
8305 return hMapping;
8306}
8307
8308
8309/** Kernel32 - MapViewOfFile */
8310static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
8311 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
8312{
8313 PVOID pvRet;
8314 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8315 if (pHandle != NULL)
8316 {
8317 KU32 idxMapping;
8318
8319 /*
8320 * Ensure one free entry in the mapping tracking table first,
8321 * since this is common to both temporary and cached files.
8322 */
8323 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
8324 { /* likely */ }
8325 else
8326 {
8327 void *pvNew;
8328 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
8329 if (cNew)
8330 cNew *= 2;
8331 else
8332 cNew = 32;
8333 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings[0]));
8334 if (pvNew)
8335 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
8336 else
8337 {
8338 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
8339 kwSandboxHandlePut(pHandle);
8340 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8341 return NULL;
8342 }
8343 g_Sandbox.cMemMappingsAlloc = cNew;
8344 }
8345
8346 /*
8347 * Type specific work.
8348 */
8349 switch (pHandle->enmType)
8350 {
8351 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8352 case KWHANDLETYPE_TEMP_FILE:
8353 case KWHANDLETYPE_OUTPUT_BUF:
8354 default:
8355 kHlpAssertFailed();
8356 kwSandboxHandlePut(pHandle);
8357 SetLastError(ERROR_INVALID_OPERATION);
8358 return NULL;
8359
8360 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8361 {
8362 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
8363 if ( dwDesiredAccess == FILE_MAP_READ
8364 && offFileHigh == 0
8365 && offFileLow == 0
8366 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
8367 {
8368 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
8369 if (pTempFile->cSegs != 1)
8370 {
8371 KU32 iSeg;
8372 KU32 cbLeft;
8373 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
8374 KU8 *pbAll = NULL;
8375 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
8376 if (rc != 0)
8377 {
8378 kHlpAssertFailed();
8379 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8380 return NULL;
8381 }
8382
8383 cbLeft = pTempFile->cbFile;
8384 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
8385 {
8386 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
8387 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
8388 cbLeft -= cbToCopy;
8389 }
8390
8391 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
8392 {
8393 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
8394 pTempFile->paSegs[iSeg].pbData = NULL;
8395 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
8396 }
8397
8398 pTempFile->cSegs = 1;
8399 pTempFile->cbFileAllocated = cbAll;
8400 pTempFile->paSegs[0].cbDataAlloc = cbAll;
8401 pTempFile->paSegs[0].pbData = pbAll;
8402 pTempFile->paSegs[0].offData = 0;
8403 }
8404
8405 pTempFile->cMappings++;
8406 kHlpAssert(pTempFile->cMappings == 1);
8407
8408 pvRet = pTempFile->paSegs[0].pbData;
8409 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
8410 break;
8411 }
8412
8413 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8414 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
8415 kwSandboxHandlePut(pHandle);
8416 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8417 return NULL;
8418 }
8419
8420 /*
8421 * This is simple in comparison to the above temporary file code.
8422 */
8423 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8424 {
8425 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
8426 if ( dwDesiredAccess == FILE_MAP_READ
8427 && offFileHigh == 0
8428 && offFileLow == 0
8429 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
8430 {
8431 pvRet = pCachedFile->pbCached;
8432 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
8433 break;
8434 }
8435 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
8436 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
8437 kwSandboxHandlePut(pHandle);
8438 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8439 return NULL;
8440 }
8441 }
8442
8443 /*
8444 * Insert into the mapping tracking table. This is common
8445 * and we should only get here with a non-NULL pvRet.
8446 *
8447 * Note! We could look for duplicates and do ref counting, but it's
8448 * easier to just append for now.
8449 */
8450 kHlpAssert(pvRet != NULL);
8451 idxMapping = g_Sandbox.cMemMappings;
8452 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
8453
8454 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
8455 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
8456 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
8457 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
8458 g_Sandbox.cMemMappings++;
8459
8460 kwSandboxHandlePut(pHandle);
8461 return pvRet;
8462 }
8463
8464 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8465 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
8466 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
8467 return pvRet;
8468}
8469
8470
8471/** Kernel32 - MapViewOfFileEx */
8472static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
8473 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
8474{
8475 PVOID pvRet;
8476 PKWHANDLE pHandle = kwSandboxHandleGet(hSection);
8477 if (pHandle != NULL)
8478 {
8479 switch (pHandle->enmType)
8480 {
8481 case KWHANDLETYPE_TEMP_FILE_MAPPING:
8482 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
8483 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8484 if (!pvMapAddr)
8485 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8486 else
8487 {
8488 kHlpAssertFailed();
8489 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8490 }
8491 kwSandboxHandlePut(pHandle);
8492 return NULL;
8493
8494 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
8495 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
8496 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
8497 if (!pvMapAddr)
8498 {
8499 pvRet = kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
8500 kwSandboxHandlePut(pHandle);
8501 return pvRet;
8502 }
8503 /* We can use fallback here as the handle is an actual section handle. */
8504 break;
8505
8506 case KWHANDLETYPE_FSOBJ_READ_CACHE:
8507 case KWHANDLETYPE_TEMP_FILE:
8508 case KWHANDLETYPE_OUTPUT_BUF:
8509 default:
8510 kHlpAssertFailed();
8511 kwSandboxHandlePut(pHandle);
8512 SetLastError(ERROR_INVALID_OPERATION);
8513 return NULL;
8514 }
8515 kwSandboxHandlePut(pHandle);
8516 }
8517
8518 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
8519 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
8520 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
8521 return pvRet;
8522
8523}
8524
8525/** Kernel32 - UnmapViewOfFile */
8526static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
8527{
8528 /*
8529 * Consult the memory mapping tracker.
8530 */
8531 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
8532 KU32 idxMapping = g_Sandbox.cMemMappings;
8533 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8534 while (idxMapping-- > 0)
8535 if (paMemMappings[idxMapping].pvMapping == pvBase)
8536 {
8537 /* Type specific stuff. */
8538 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
8539 {
8540 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
8541 paMemMappings[idxMapping].u.pTempFile->cMappings--;
8542 }
8543 else
8544 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
8545
8546 /* Deref and probably free it. */
8547 if (--paMemMappings[idxMapping].cRefs == 0)
8548 {
8549 g_Sandbox.cMemMappings--;
8550 if (idxMapping != g_Sandbox.cMemMappings)
8551 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
8552 }
8553 return TRUE;
8554 }
8555
8556 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
8557 return UnmapViewOfFile(pvBase);
8558}
8559
8560/** @todo UnmapViewOfFileEx */
8561
8562#endif /* WITH_TEMP_MEMORY_FILES */
8563
8564
8565/** Kernel32 - DuplicateHandle */
8566static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
8567 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
8568{
8569 BOOL fRet;
8570
8571 /*
8572 * We must catch our handles being duplicated.
8573 */
8574 if (hSrcProc == GetCurrentProcess())
8575 {
8576 PKWHANDLE pHandle = kwSandboxHandleGet(hSrc);
8577 if (pHandle)
8578 {
8579 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8580 if (fRet)
8581 {
8582 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
8583 {
8584 pHandle->cRefs++;
8585 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
8586 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
8587 pHandle->enmType, pHandle->cRefs));
8588 }
8589 else
8590 {
8591 fRet = FALSE;
8592 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
8593 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
8594 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
8595 }
8596 }
8597 else
8598 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
8599 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
8600 kwSandboxHandlePut(pHandle);
8601 return fRet;
8602 }
8603 }
8604
8605 /*
8606 * Not one of ours, just do what the caller asks and log it.
8607 */
8608 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
8609 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
8610 fInheritHandle, dwOptions, fRet, *phNew));
8611 return fRet;
8612}
8613
8614
8615/** Kernel32 - CloseHandle */
8616static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
8617{
8618 BOOL fRet;
8619 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
8620 PKWHANDLE pHandle = kwSandboxHandleGet(hObject);
8621 if (pHandle)
8622 {
8623 /* Prevent the closing of the standard output and error handles. */
8624 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
8625 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) /* why this?!? */)
8626 {
8627 fRet = CloseHandle(hObject);
8628 if (fRet)
8629 {
8630 EnterCriticalSection(&g_Sandbox.HandlesLock);
8631 pHandle = g_Sandbox.papHandles[idxHandle];
8632 g_Sandbox.papHandles[idxHandle] = NULL;
8633 g_Sandbox.cActiveHandles--;
8634 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
8635 if (--pHandle->cRefs == 0)
8636 {
8637#ifdef WITH_TEMP_MEMORY_FILES
8638 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
8639 {
8640 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
8641 pHandle->u.pTempFile->cActiveHandles--;
8642 }
8643#endif
8644 kHlpFree(pHandle);
8645 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
8646 }
8647 else
8648 {
8649 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
8650 kwSandboxHandlePut(pHandle);
8651 }
8652 LeaveCriticalSection(&g_Sandbox.HandlesLock);
8653 return fRet;
8654 }
8655 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
8656 }
8657 else
8658 {
8659#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8660 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
8661 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
8662#else
8663 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of stdXXX!\n", hObject));
8664#endif
8665 fRet = TRUE;
8666 }
8667 kwSandboxHandlePut(pHandle);
8668 return fRet;
8669 }
8670
8671 fRet = CloseHandle(hObject);
8672 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
8673 return fRet;
8674}
8675
8676
8677/** Kernel32 - GetFileAttributesA. */
8678static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
8679{
8680 DWORD fRet;
8681 const char *pszExt = kHlpGetExt(pszFilename);
8682 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8683 {
8684 KFSLOOKUPERROR enmError;
8685 PKFSOBJ pFsObj;
8686 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8687
8688 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8689 if (pFsObj)
8690 {
8691 kHlpAssert(pFsObj->fHaveStats);
8692 fRet = pFsObj->Stats.st_attribs;
8693 kFsCacheObjRelease(g_pFsCache, pFsObj);
8694 }
8695 else
8696 {
8697 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8698 fRet = INVALID_FILE_ATTRIBUTES;
8699 }
8700
8701 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8702 return fRet;
8703 }
8704
8705 fRet = GetFileAttributesA(pszFilename);
8706 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8707 return fRet;
8708}
8709
8710
8711/** Kernel32 - GetFileAttributesW. */
8712static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8713{
8714 DWORD fRet;
8715 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8716 {
8717 KFSLOOKUPERROR enmError;
8718 PKFSOBJ pFsObj;
8719 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8720
8721 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8722 if (pFsObj)
8723 {
8724 kHlpAssert(pFsObj->fHaveStats);
8725 fRet = pFsObj->Stats.st_attribs;
8726 kFsCacheObjRelease(g_pFsCache, pFsObj);
8727 }
8728 else
8729 {
8730 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8731 fRet = INVALID_FILE_ATTRIBUTES;
8732 }
8733#ifndef NDEBUG
8734 {
8735 DWORD fCheck = GetFileAttributesW(pwszFilename);
8736 kHlpAssertMsg(fCheck == fRet, ("fCheck=%x vs fRet=%#x diff=%#x; %ls\n", fCheck, fRet, fCheck ^ fRet, pwszFilename));
8737 }
8738#endif
8739 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8740 return fRet;
8741 }
8742
8743 fRet = GetFileAttributesW(pwszFilename);
8744 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8745 return fRet;
8746}
8747
8748
8749/** Kernel32 - GetFileAttributesExA. */
8750static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExA(LPCSTR pszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8751 WIN32_FILE_ATTRIBUTE_DATA *pData)
8752{
8753 BOOL fRet;
8754 const char *pszExt = kHlpGetExt(pszFilename);
8755 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
8756 {
8757 KFSLOOKUPERROR enmError;
8758 PKFSOBJ pFsObj;
8759 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8760
8761 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
8762 if (pFsObj)
8763 {
8764 kHlpAssert(pFsObj->fHaveStats);
8765 if (enmLevel == GetFileExInfoStandard)
8766 {
8767 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8768 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8769 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8770 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8771 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8772 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8773 kFsCacheObjRelease(g_pFsCache, pFsObj);
8774 fRet = TRUE;
8775 }
8776 else
8777 {
8778 kFsCacheObjRelease(g_pFsCache, pFsObj);
8779 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8780 }
8781 }
8782 else
8783 {
8784 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8785 fRet = FALSE;
8786 }
8787
8788#ifdef K_STRICT
8789 {
8790 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8791 DWORD const dwErrSaved = GetLastError();
8792 BOOL const fRetCheck = GetFileAttributesExA(pszFilename, enmLevel, &CheckData);
8793 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %s\n", fRet, fRetCheck, pszFilename));
8794 if (fRetCheck && fRet)
8795 {
8796# define ASSERT_FS_FIELD_EQUAL_A(pResult, pExpected, pszFilename, Field, szFmt) \
8797 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %s\n", (pResult)->Field, (pExpected)->Field, pszFilename))
8798 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, dwFileAttributes, "%#x");
8799 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeHigh, "%#x");
8800 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, nFileSizeLow, "%#x");
8801 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwHighDateTime, "%#x");
8802 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftCreationTime.dwLowDateTime, "%#x");
8803 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8804 ASSERT_FS_FIELD_EQUAL_A(pData, &CheckData, pszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8805 }
8806 else
8807 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %s\n", dwErrSaved, GetLastError(), pszFilename));
8808 SetLastError(dwErrSaved);
8809 }
8810#endif
8811 KWFS_LOG(("GetFileAttributesA(%s,%d,) -> %d [cached]\n", pszFilename, enmLevel, fRet));
8812 return fRet;
8813 }
8814
8815 fRet = GetFileAttributesExA(pszFilename, enmLevel, pData);
8816 KWFS_LOG(("GetFileAttributesExA(%s,%d,) -> %d\n", pszFilename, enmLevel, fRet));
8817 return fRet;
8818}
8819
8820
8821/** Kernel32 - GetFileAttributesExW. */
8822static BOOL WINAPI kwSandbox_Kernel32_GetFileAttributesExW(LPCWSTR pwszFilename, GET_FILEEX_INFO_LEVELS enmLevel,
8823 WIN32_FILE_ATTRIBUTE_DATA *pData)
8824{
8825 BOOL fRet;
8826 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8827 {
8828 KFSLOOKUPERROR enmError;
8829 PKFSOBJ pFsObj;
8830 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8831
8832 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8833 if (pFsObj)
8834 {
8835 kHlpAssert(pFsObj->fHaveStats);
8836 if (enmLevel == GetFileExInfoStandard)
8837 {
8838 pData->dwFileAttributes = pFsObj->Stats.st_attribs;
8839 pData->nFileSizeHigh = (KU64)pFsObj->Stats.st_size >> 32;
8840 pData->nFileSizeLow = (KU32)pFsObj->Stats.st_size;
8841 *(KU64 *)&pData->ftCreationTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_birthtim);
8842 *(KU64 *)&pData->ftLastAccessTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_atim);
8843 *(KU64 *)&pData->ftLastWriteTime = birdNtTimeFromTimeSpec(&pFsObj->Stats.st_mtim);
8844 kFsCacheObjRelease(g_pFsCache, pFsObj);
8845 fRet = TRUE;
8846 }
8847 else
8848 {
8849 kFsCacheObjRelease(g_pFsCache, pFsObj);
8850 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8851 }
8852 }
8853 else
8854 {
8855 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8856 fRet = FALSE;
8857 }
8858
8859#ifdef K_STRICT
8860 {
8861 WIN32_FILE_ATTRIBUTE_DATA CheckData = { 0 };
8862 DWORD const dwErrSaved = GetLastError();
8863 BOOL const fRetCheck = GetFileAttributesExW(pwszFilename, enmLevel, &CheckData);
8864 kHlpAssertMsg(fRet == fRetCheck, ("fRet=%d fRetCheck=%d; %ls\n", fRet, fRetCheck, pwszFilename));
8865 if (fRetCheck && fRet)
8866 {
8867# define ASSERT_FS_FIELD_EQUAL_W(pResult, pExpected, pszFilename, Field, szFmt) \
8868 kHlpAssertMsg((pResult)->Field == (pExpected)->Field, (#Field ": " szFmt " , expected " szFmt "; %ls\n", (pResult)->Field, (pExpected)->Field, pwszFilename))
8869 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, dwFileAttributes, "%#x");
8870 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeHigh, "%#x");
8871 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, nFileSizeLow, "%#x");
8872 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwHighDateTime, "%#x");
8873 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftCreationTime.dwLowDateTime, "%#x");
8874 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwHighDateTime, "%#x");
8875 ASSERT_FS_FIELD_EQUAL_W(pData, &CheckData, pwszFilename, ftLastWriteTime.dwLowDateTime, "%#x");
8876 }
8877 else
8878 kHlpAssertMsg(dwErrSaved == GetLastError(), ("%u, expected %u; %ls\n", dwErrSaved, GetLastError(), pwszFilename));
8879 SetLastError(dwErrSaved);
8880 }
8881#endif
8882 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d [cached]\n", pwszFilename, enmLevel, fRet));
8883 return fRet;
8884 }
8885
8886 fRet = GetFileAttributesExW(pwszFilename, enmLevel, pData);
8887 KWFS_LOG(("GetFileAttributesExW(%ls,%d,) -> %d\n", pwszFilename, enmLevel, fRet));
8888 return fRet;
8889}
8890
8891
8892/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8893 * directory containing each include file. We cache the result to speed
8894 * things up a little. */
8895static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8896{
8897 DWORD cwcRet;
8898 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8899 {
8900 KFSLOOKUPERROR enmError;
8901 PKFSOBJ pObj;
8902 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8903
8904 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8905 if (pObj)
8906 {
8907 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8908 {
8909 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8910 {
8911 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8912
8913 /* Should preserve trailing slash on directory paths. */
8914 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8915 {
8916 if ( cwcRet + 1 < cwcShortPath
8917 && pwszShortPath[cwcRet - 1] != '\\')
8918 {
8919 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8920 if ( cwcIn > 0
8921 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8922 {
8923 pwszShortPath[cwcRet++] = '\\';
8924 pwszShortPath[cwcRet] = '\0';
8925 }
8926 }
8927 }
8928
8929 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8930 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8931 kFsCacheObjRelease(g_pFsCache, pObj);
8932 return cwcRet;
8933 }
8934
8935 /* fall back for complicated cases. */
8936 }
8937 kFsCacheObjRelease(g_pFsCache, pObj);
8938 }
8939 }
8940 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8941 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8942 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8943 return cwcRet;
8944}
8945
8946
8947#ifdef WITH_TEMP_MEMORY_FILES
8948/** Kernel32 - DeleteFileW
8949 * Skip deleting the in-memory files. */
8950static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8951{
8952 BOOL fRc;
8953 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8954 && kwFsIsClTempFileW(pwszFilename))
8955 {
8956 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8957 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8958 fRc = TRUE;
8959 }
8960 else
8961 {
8962 fRc = DeleteFileW(pwszFilename);
8963 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8964 }
8965 return fRc;
8966}
8967#endif /* WITH_TEMP_MEMORY_FILES */
8968
8969
8970
8971#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8972
8973/*
8974 *
8975 * Console output buffering.
8976 * Console output buffering.
8977 * Console output buffering.
8978 *
8979 */
8980
8981
8982/**
8983 * Write a wide char string to the console.
8984 *
8985 * @param pSandbox The sandbox which output buffer to flush.
8986 */
8987static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8988{
8989 if (cwcToWrite > 0)
8990 {
8991 DWORD cwcWritten = 0;
8992 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8993 {
8994 if (cwcWritten == cwcToWrite)
8995 { /* likely */ }
8996 else
8997 {
8998 DWORD off = 0;
8999 do
9000 {
9001 off += cwcWritten;
9002 cwcWritten = 0;
9003 } while ( off < cwcToWrite
9004 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
9005 kHlpAssert(off == cwcWritten);
9006 }
9007 }
9008 else
9009 kHlpAssertFailed();
9010 pSandbox->Combined.cFlushes++;
9011 }
9012}
9013
9014
9015/**
9016 * Flushes the combined console output buffer.
9017 *
9018 * @param pSandbox The sandbox which output buffer to flush.
9019 */
9020static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
9021{
9022 if (pSandbox->Combined.cwcBuf > 0)
9023 {
9024 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
9025 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
9026 pSandbox->Combined.cwcBuf = 0;
9027 }
9028}
9029
9030
9031/**
9032 * For handling combined buffer overflow cases line by line.
9033 *
9034 * @param pSandbox The sandbox.
9035 * @param pwcBuf What to add to the combined buffer. Usually a
9036 * line, unless we're really low on buffer space.
9037 * @param cwcBuf The length of what to add.
9038 * @param fBrokenLine Whether this is a broken line.
9039 */
9040static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
9041{
9042 if (fBrokenLine)
9043 kwSandboxConsoleFlushCombined(pSandbox);
9044 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
9045 {
9046 kwSandboxConsoleFlushCombined(pSandbox);
9047 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
9048 }
9049 else
9050 {
9051 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
9052 pSandbox->Combined.cwcBuf += cwcBuf;
9053 }
9054}
9055
9056
9057/**
9058 * Called to final flush a line buffer via the combined buffer (if applicable).
9059 *
9060 * @param pSandbox The sandbox.
9061 * @param pLineBuf The line buffer.
9062 * @param pszName The line buffer name (for logging)
9063 */
9064static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
9065{
9066 if (pLineBuf->fIsConsole)
9067 {
9068 if (pLineBuf->u.Con.cwcBuf > 0)
9069 {
9070 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
9071
9072 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
9073 {
9074 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
9075 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
9076 }
9077 else
9078 {
9079 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
9080 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
9081 }
9082 pLineBuf->u.Con.cwcBuf = 0;
9083 }
9084 }
9085#ifdef WITH_STD_OUT_ERR_BUFFERING
9086 else if (pLineBuf->u.Fully.cchBuf > 0)
9087 {
9088 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
9089
9090 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
9091 pLineBuf->u.Fully.cchBuf = 0;
9092 }
9093#endif
9094}
9095
9096
9097/**
9098 * Called at the end of sandboxed execution to flush both stream buffers.
9099 *
9100 * @param pSandbox The sandbox.
9101 */
9102static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
9103{
9104 /*
9105 * First do the cl.exe source file supression trick, if applicable.
9106 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
9107 * handle.
9108 */
9109 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
9110 && pSandbox->Combined.cFlushes == 0)
9111 {
9112 if ( pSandbox->StdOut.fIsConsole
9113 || pSandbox->StdErr.fIsConsole)
9114 {
9115 if ( pSandbox->Combined.cwcBuf >= 3
9116 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
9117 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
9118 {
9119 KI32 off = pSandbox->Combined.cwcBuf - 1;
9120 if (pSandbox->Combined.wszBuf[off] == '\n')
9121 {
9122 KBOOL fOk = K_TRUE;
9123 while (off-- > 0)
9124 {
9125 wchar_t const wc = pSandbox->Combined.wszBuf[off];
9126 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
9127 { /* likely */ }
9128 else
9129 {
9130 fOk = K_FALSE;
9131 break;
9132 }
9133 }
9134 if (fOk)
9135 {
9136 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
9137 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9138 pSandbox->Combined.cwcBuf = 0;
9139 return;
9140 }
9141 }
9142 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
9143 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
9144 }
9145 }
9146#ifdef WITH_STD_OUT_ERR_BUFFERING
9147 /*
9148 * Otherwise, it goes to standard output (redirected).
9149 */
9150 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
9151 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
9152 {
9153 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
9154 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
9155 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
9156
9157 if (pchBuf[off] == '\n')
9158 {
9159 KBOOL fOk = K_TRUE;
9160 if (pchBuf[off - 1] == '\r')
9161 off--;
9162 while (off-- > 0)
9163 {
9164 char const ch = pchBuf[off];
9165 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
9166 { /* likely */ }
9167 else
9168 {
9169 fOk = K_FALSE;
9170 break;
9171 }
9172 }
9173 if (fOk)
9174 {
9175 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
9176 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9177 pSandbox->StdOut.u.Fully.cchBuf = 0;
9178 return;
9179 }
9180 }
9181 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
9182 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
9183 }
9184#endif
9185 }
9186
9187 /*
9188 * Flush the two line buffer, then the combined buffer.
9189 */
9190 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
9191 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
9192 kwSandboxConsoleFlushCombined(pSandbox);
9193}
9194
9195
9196/**
9197 * Writes a string to the given output stream.
9198 *
9199 * @param pSandbox The sandbox.
9200 * @param pLineBuf The line buffer for the output stream.
9201 * @param pwcBuffer The buffer to write.
9202 * @param cwcToWrite The number of wchar_t's in the buffer.
9203 */
9204static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
9205{
9206 kHlpAssert(pLineBuf->fIsConsole);
9207 if (cwcToWrite > 0)
9208 {
9209 /*
9210 * First, find the start of the last incomplete line so we can figure
9211 * out how much line buffering we need to do.
9212 */
9213 KU32 cchLastIncompleteLine;
9214 KU32 offLastIncompleteLine = cwcToWrite;
9215 while ( offLastIncompleteLine > 0
9216 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
9217 offLastIncompleteLine--;
9218 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
9219
9220 /* Was there anything to line buffer? */
9221 if (offLastIncompleteLine < cwcToWrite)
9222 {
9223 /* Need to grow the line buffer? */
9224 KU32 cwcNeeded = offLastIncompleteLine == 0
9225 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
9226 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
9227 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
9228 {
9229 void *pvNew;
9230 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
9231 while (cwcNew < cwcNeeded)
9232 cwcNew *= 2;
9233 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
9234 if (pvNew)
9235 {
9236 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9237 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
9238 }
9239 else
9240 {
9241 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
9242 if (pvNew)
9243 {
9244 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
9245 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
9246 }
9247 else
9248 {
9249 /* This isn't perfect, but it will have to do for now. */
9250 if (pLineBuf->u.Con.cwcBuf > 0)
9251 {
9252 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9253 K_TRUE /*fBrokenLine*/);
9254 pLineBuf->u.Con.cwcBuf = 0;
9255 }
9256 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
9257 return;
9258 }
9259 }
9260 }
9261
9262 /*
9263 * Handle the case where we only add to the line buffer.
9264 */
9265 if (offLastIncompleteLine == 0)
9266 {
9267 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
9268 pLineBuf->u.Con.cwcBuf += cwcToWrite;
9269 return;
9270 }
9271 }
9272
9273 /*
9274 * If there is sufficient combined buffer to handle this request, this is rather simple.
9275 */
9276 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
9277 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9278 {
9279 if (pLineBuf->u.Con.cwcBuf > 0)
9280 {
9281 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9282 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9283 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9284 pLineBuf->u.Con.cwcBuf = 0;
9285 }
9286
9287 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9288 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
9289 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
9290 }
9291 else
9292 {
9293 /*
9294 * Do line-by-line processing of the input, flusing the combined buffer
9295 * when it becomes necessary. We may have to write lines
9296 */
9297 KU32 off = 0;
9298 KU32 offNextLine = 0;
9299
9300 /* If there are buffered chars, we handle the first line outside the
9301 main loop. We must try our best outputting it as a complete line. */
9302 if (pLineBuf->u.Con.cwcBuf > 0)
9303 {
9304 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
9305 offNextLine++;
9306 offNextLine++;
9307 kHlpAssert(offNextLine <= offLastIncompleteLine);
9308
9309 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
9310 {
9311 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
9312 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
9313 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
9314 pLineBuf->u.Con.cwcBuf = 0;
9315
9316 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
9317 pSandbox->Combined.cwcBuf += offNextLine;
9318 }
9319 else
9320 {
9321 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
9322 if (cwcLeft > 0)
9323 {
9324 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
9325 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
9326 pLineBuf->u.Con.cwcBuf += cwcCopy;
9327 off += cwcCopy;
9328 }
9329 if (pLineBuf->u.Con.cwcBuf > 0)
9330 {
9331 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
9332 K_TRUE /*fBrokenLine*/);
9333 pLineBuf->u.Con.cwcBuf = 0;
9334 }
9335 if (off < offNextLine)
9336 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
9337 }
9338 off = offNextLine;
9339 }
9340
9341 /* Deal with the remaining lines */
9342 while (off < offLastIncompleteLine)
9343 {
9344 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
9345 offNextLine++;
9346 offNextLine++;
9347 kHlpAssert(offNextLine <= offLastIncompleteLine);
9348 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
9349 off = offNextLine;
9350 }
9351 }
9352
9353 /*
9354 * Buffer any remaining incomplete line chars.
9355 */
9356 if (cchLastIncompleteLine)
9357 {
9358 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
9359 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
9360 }
9361 }
9362}
9363
9364
9365/**
9366 * Worker for WriteConsoleA and WriteFile.
9367 *
9368 * @param pSandbox The sandbox.
9369 * @param pLineBuf The line buffer.
9370 * @param pchBuffer What to write.
9371 * @param cchToWrite How much to write.
9372 */
9373static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
9374{
9375 /*
9376 * Convert it to wide char and use the 'W' to do the work.
9377 */
9378 int cwcRet;
9379 KU32 cwcBuf = cchToWrite * 2 + 1;
9380 wchar_t *pwcBufFree = NULL;
9381 wchar_t *pwcBuf;
9382 kHlpAssert(pLineBuf->fIsConsole);
9383
9384 if (cwcBuf <= 4096)
9385 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
9386 else
9387 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
9388
9389 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
9390 if (cwcRet > 0)
9391 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
9392 else
9393 {
9394 DWORD cchWritten;
9395 kHlpAssertFailed();
9396
9397 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
9398 if (pLineBuf->u.Con.cwcBuf > 0)
9399 {
9400 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
9401 pLineBuf->u.Con.cwcBuf = 0;
9402 }
9403 kwSandboxConsoleFlushCombined(pSandbox);
9404
9405 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
9406 {
9407 if (cchWritten >= cchToWrite)
9408 { /* likely */ }
9409 else
9410 {
9411 KU32 off = 0;
9412 do
9413 {
9414 off += cchWritten;
9415 cchWritten = 0;
9416 } while ( off < cchToWrite
9417 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
9418 }
9419 }
9420 }
9421
9422 if (pwcBufFree)
9423 kHlpFree(pwcBufFree);
9424}
9425
9426
9427/** Kernel32 - WriteConsoleA */
9428BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
9429 PVOID pvReserved)
9430{
9431 BOOL fRc;
9432 PKWOUTPUTSTREAMBUF pLineBuf;
9433 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9434
9435 if (hConOutput == g_Sandbox.StdErr.hOutput)
9436 pLineBuf = &g_Sandbox.StdErr;
9437 else
9438 pLineBuf = &g_Sandbox.StdOut;
9439 if (pLineBuf->fIsConsole)
9440 {
9441 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
9442
9443 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
9444 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
9445 if (pcbWritten)
9446 *pcbWritten = cbToWrite;
9447 fRc = TRUE;
9448 }
9449 else
9450 {
9451 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
9452 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
9453 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
9454 }
9455 return fRc;
9456}
9457
9458
9459/** Kernel32 - WriteConsoleW */
9460BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
9461 PVOID pvReserved)
9462{
9463 BOOL fRc;
9464 PKWOUTPUTSTREAMBUF pLineBuf;
9465 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9466
9467 if (hConOutput == g_Sandbox.StdErr.hOutput)
9468 pLineBuf = &g_Sandbox.StdErr;
9469 else if (hConOutput == g_Sandbox.StdOut.hOutput)
9470 pLineBuf = &g_Sandbox.StdOut;
9471 else
9472 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
9473 if (pLineBuf->fIsConsole)
9474 {
9475 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
9476
9477 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
9478 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
9479 if (pcwcWritten)
9480 *pcwcWritten = cwcToWrite;
9481 fRc = TRUE;
9482 }
9483 else
9484 {
9485 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
9486 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
9487 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
9488 }
9489 return fRc;
9490}
9491
9492#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
9493
9494
9495
9496/*
9497 *
9498 * Virtual memory leak prevension.
9499 * Virtual memory leak prevension.
9500 * Virtual memory leak prevension.
9501 *
9502 */
9503
9504#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9505
9506/** For debug logging. */
9507# ifndef NDEBUG
9508static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
9509{
9510 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
9511 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
9512 kHlpAssert(cbMemInfo == sizeof(MemInfo));
9513 if (cbMemInfo != 0)
9514 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
9515 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
9516 MemInfo.BaseAddress,
9517 MemInfo.AllocationBase,
9518 MemInfo.RegionSize,
9519 MemInfo.State,
9520 MemInfo.Protect,
9521 MemInfo.Type));
9522}
9523# else
9524# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
9525# endif
9526
9527/**
9528 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
9529 *
9530 * @param idxFixed The fixed allocation index to "free".
9531 */
9532static void kwSandboxResetFixedAllocation(KU32 idxFixed)
9533{
9534 BOOL fRc;
9535 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
9536 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
9537 kHlpAssert(fRc); K_NOREF(fRc);
9538 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
9539 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
9540}
9541
9542#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
9543
9544
9545/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
9546 * location (~78MB in 32-bit 2010 compiler). */
9547static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
9548{
9549 PVOID pvMem;
9550 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9551 {
9552 KU32 idxPreAllocated = KU32_MAX;
9553
9554#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9555 /*
9556 * Look for a pre-reserved CL.exe heap allocation.
9557 */
9558 pvMem = NULL;
9559 if ( pvAddr != 0
9560 && (fAllocType & MEM_RESERVE))
9561 {
9562 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9563 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
9564 while (idxFixed-- > 0)
9565 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
9566 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
9567 {
9568 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
9569 {
9570 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
9571 {
9572 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
9573 pvMem = pvAddr;
9574 idxPreAllocated = idxFixed;
9575 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
9576 pvAddr, cb, fAllocType, fProt, pvMem));
9577 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
9578 SetLastError(NO_ERROR);
9579 break;
9580 }
9581 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
9582 }
9583 else
9584 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
9585 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
9586 }
9587 }
9588 if (!pvMem)
9589#endif
9590 {
9591 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9592 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9593 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9594 if ( pvAddr
9595 && pvAddr != pvMem
9596 && !( fAllocType == MEM_RESERVE /* After mapping the PCH, VS2019 ends up here (happens */
9597 && fProt == PAGE_READWRITE /* in real cl.exe runs too). Just shut it up to avoid confusion. */
9598#if K_ARCH_BITS >= 64
9599 && cb > 0x10000000 /* seen 67c00000, 33e00000, ++ */
9600#else
9601 && cb > 0x04000000 /* no idea */
9602#endif
9603 )
9604 )
9605 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
9606 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
9607 }
9608
9609 if (pvMem)
9610 {
9611 /*
9612 * Track it.
9613 */
9614 PKWVIRTALLOC pTracker;
9615
9616 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9617 pTracker = g_Sandbox.pVirtualAllocHead;
9618 while ( pTracker
9619 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
9620 pTracker = pTracker->pNext;
9621 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9622 if (!pTracker)
9623 {
9624 DWORD dwErr = GetLastError();
9625 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
9626 if (pTracker)
9627 {
9628 pTracker->pvAlloc = pvMem;
9629 pTracker->cbAlloc = cb;
9630 pTracker->idxPreAllocated = idxPreAllocated;
9631 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9632 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9633 g_Sandbox.pVirtualAllocHead = pTracker;
9634 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9635 }
9636 SetLastError(dwErr);
9637 }
9638 }
9639 }
9640 else
9641 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
9642 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
9643 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
9644 return pvMem;
9645}
9646
9647
9648/** Kernel32 - VirtualFree. */
9649static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
9650{
9651 BOOL fRc;
9652 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9653 {
9654 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9655 if (dwFreeType & MEM_RELEASE)
9656 {
9657 PKWVIRTALLOC pTracker;
9658 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
9659 pTracker = g_Sandbox.pVirtualAllocHead;
9660 if (pTracker)
9661 {
9662 if (pTracker->pvAlloc == pvAddr)
9663 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
9664 else
9665 {
9666 PKWVIRTALLOC pPrev;
9667 do
9668 {
9669 pPrev = pTracker;
9670 pTracker = pTracker->pNext;
9671 } while (pTracker && pTracker->pvAlloc != pvAddr);
9672 if (pTracker)
9673 pPrev->pNext = pTracker->pNext;
9674 }
9675 if (pTracker)
9676 {
9677#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9678 if (pTracker->idxPreAllocated != KU32_MAX)
9679 {
9680 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
9681 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9682 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
9683 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
9684 kHlpFree(pTracker);
9685 return TRUE;
9686 }
9687#endif
9688
9689 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9690 if (fRc)
9691 kHlpFree(pTracker);
9692 else
9693 {
9694 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
9695 g_Sandbox.pVirtualAllocHead = pTracker;
9696 }
9697 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9698 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9699 return fRc;
9700 }
9701
9702 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
9703 }
9704 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
9705 }
9706 }
9707
9708#ifdef WITH_FIXED_VIRTUAL_ALLOCS
9709 /*
9710 * Protect our fixed allocations (this isn't just paranoia, btw.).
9711 */
9712 if (dwFreeType & MEM_RELEASE)
9713 {
9714 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
9715 while (idxFixed-- > 0)
9716 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
9717 {
9718 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
9719 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
9720 return TRUE;
9721 }
9722 }
9723#endif
9724
9725 /*
9726 * Not tracker or not actually free the virtual range.
9727 */
9728 fRc = VirtualFree(pvAddr, cb, dwFreeType);
9729 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
9730 return fRc;
9731}
9732
9733
9734/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
9735HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
9736{
9737 HANDLE hHeap;
9738 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9739
9740 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
9741 if (hHeap != NULL)
9742 {
9743 DWORD dwErr = GetLastError();
9744 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
9745 if (pTracker)
9746 {
9747 pTracker->hHeap = hHeap;
9748 pTracker->pNext = g_Sandbox.pHeapHead;
9749 g_Sandbox.pHeapHead = pTracker;
9750 }
9751
9752 SetLastError(dwErr);
9753 }
9754 return hHeap;
9755
9756}
9757
9758
9759/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
9760BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
9761{
9762 BOOL fRc = HeapDestroy(hHeap);
9763 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
9764 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9765 if (fRc)
9766 {
9767 PKWHEAP pTracker = g_Sandbox.pHeapHead;
9768 if (pTracker)
9769 {
9770 if (pTracker->hHeap == hHeap)
9771 g_Sandbox.pHeapHead = pTracker->pNext;
9772 else
9773 {
9774 PKWHEAP pPrev;
9775 do
9776 {
9777 pPrev = pTracker;
9778 pTracker = pTracker->pNext;
9779 } while (pTracker && pTracker->hHeap == hHeap);
9780 if (pTracker)
9781 pPrev->pNext = pTracker->pNext;
9782 }
9783 if (pTracker)
9784 kHlpFree(pTracker);
9785 else
9786 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
9787 }
9788 }
9789
9790 return fRc;
9791}
9792
9793
9794
9795/*
9796 *
9797 * Thread/Fiber local storage leak prevention.
9798 * Thread/Fiber local storage leak prevention.
9799 * Thread/Fiber local storage leak prevention.
9800 *
9801 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
9802 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
9803 * we're leaking these indexes, but more importantely we crash during
9804 * worker exit since the callback is triggered multiple times.
9805 */
9806
9807
9808/** Kernel32 - FlsAlloc */
9809DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
9810{
9811 DWORD idxFls = FlsAlloc(pfnCallback);
9812 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
9813 if (idxFls != FLS_OUT_OF_INDEXES)
9814 {
9815 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9816 if (pTracker)
9817 {
9818 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9819 pTracker->idx = idxFls;
9820 pTracker->pNext = g_Sandbox.pFlsAllocHead;
9821 g_Sandbox.pFlsAllocHead = pTracker;
9822 }
9823 }
9824
9825 return idxFls;
9826}
9827
9828/** Kernel32 - FlsFree */
9829BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
9830{
9831 BOOL fRc = FlsFree(idxFls);
9832 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
9833 if (fRc)
9834 {
9835 PKWLOCALSTORAGE pTracker;
9836 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9837
9838 pTracker = g_Sandbox.pFlsAllocHead;
9839 if (pTracker)
9840 {
9841 if (pTracker->idx == idxFls)
9842 g_Sandbox.pFlsAllocHead = pTracker->pNext;
9843 else
9844 {
9845 PKWLOCALSTORAGE pPrev;
9846 do
9847 {
9848 pPrev = pTracker;
9849 pTracker = pTracker->pNext;
9850 } while (pTracker && pTracker->idx != idxFls);
9851 if (pTracker)
9852 pPrev->pNext = pTracker->pNext;
9853 }
9854 if (pTracker)
9855 {
9856 pTracker->idx = FLS_OUT_OF_INDEXES;
9857 pTracker->pNext = NULL;
9858 kHlpFree(pTracker);
9859 }
9860 }
9861 }
9862 return fRc;
9863}
9864
9865
9866/** Kernel32 - TlsAlloc */
9867DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9868{
9869 DWORD idxTls = TlsAlloc();
9870 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9871 if (idxTls != TLS_OUT_OF_INDEXES)
9872 {
9873 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9874 if (pTracker)
9875 {
9876 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9877 pTracker->idx = idxTls;
9878 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9879 g_Sandbox.pTlsAllocHead = pTracker;
9880 }
9881 }
9882
9883 return idxTls;
9884}
9885
9886/** Kernel32 - TlsFree */
9887BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9888{
9889 BOOL fRc = TlsFree(idxTls);
9890 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9891 if (fRc)
9892 {
9893 PKWLOCALSTORAGE pTracker;
9894 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9895
9896 pTracker = g_Sandbox.pTlsAllocHead;
9897 if (pTracker)
9898 {
9899 if (pTracker->idx == idxTls)
9900 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9901 else
9902 {
9903 PKWLOCALSTORAGE pPrev;
9904 do
9905 {
9906 pPrev = pTracker;
9907 pTracker = pTracker->pNext;
9908 } while (pTracker && pTracker->idx != idxTls);
9909 if (pTracker)
9910 pPrev->pNext = pTracker->pNext;
9911 }
9912 if (pTracker)
9913 {
9914 pTracker->idx = TLS_OUT_OF_INDEXES;
9915 pTracker->pNext = NULL;
9916 kHlpFree(pTracker);
9917 }
9918 }
9919 }
9920 return fRc;
9921}
9922
9923
9924
9925/*
9926 *
9927 * Header file hashing.
9928 * Header file hashing.
9929 * Header file hashing.
9930 *
9931 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9932 * indicated that ~12% of the time was spent doing MD5 caluclation when
9933 * rebuiling openssl. The hashing it done right after reading the source
9934 * via ReadFile, same buffers and sizes.
9935 */
9936
9937#ifdef WITH_HASH_CACHE
9938
9939/**
9940 * Gets our crypto provider context/instance, creating it if needed.
9941 */
9942static HCRYPTPROV kwSandboxGetCryptoProvider(ALG_ID idAlg)
9943{
9944 DWORD dwProvider;
9945 HCRYPTPROV *phCryptProv;
9946 HCRYPTPROV hCryptProv;
9947 if ( idAlg == CALG_SHA_256
9948 || idAlg == CALG_SHA_512)
9949 {
9950 phCryptProv = &g_Sandbox.hCryptProvAes;
9951 dwProvider = PROV_RSA_AES;
9952 }
9953 else
9954 {
9955 phCryptProv = &g_Sandbox.hCryptProvRsa;
9956 dwProvider = PROV_RSA_FULL;
9957 }
9958 hCryptProv = *phCryptProv;
9959 if (hCryptProv)
9960 return hCryptProv;
9961
9962 /* Create it. */
9963 if (CryptAcquireContextW(&hCryptProv, NULL, NULL, dwProvider, CRYPT_VERIFYCONTEXT))
9964 {
9965 kHlpAssert(hCryptProv != 0);
9966 kHlpAssert(hCryptProv != KUPTR_MAX);
9967 *phCryptProv = hCryptProv;
9968 return hCryptProv;
9969 }
9970
9971 kwErrPrintf("kwSandboxGetCryptoProvider: CryptAcquireContext(,,,%#x, CRYPT_VERIFYCONTEXT) failed! %u\n",
9972 dwProvider, GetLastError());
9973 return (HCRYPTPROV)NULL;
9974}
9975
9976/** AdvApi32 - CryptCreateHash */
9977static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9978 HCRYPTHASH *phHash)
9979{
9980 BOOL fRc;
9981
9982 /*
9983 * Only do this for cl.exe when it request normal MD5.
9984 */
9985 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9986 {
9987 KU32 cbDigest;
9988 const char *pszName;
9989 switch (idAlg)
9990 {
9991 case CALG_MD5:
9992 cbDigest = 128/8;
9993 pszName = "MD5";
9994 g_cHashesMd5++;
9995 break;
9996 case CALG_SHA1:
9997 cbDigest = 160/8;
9998 pszName = "SHA1";
9999 g_cHashesSha1++;
10000 break;
10001 case CALG_SHA_256:
10002 cbDigest = 256/8;
10003 pszName = "SHA-256";
10004 g_cHashesSha256++;
10005 break;
10006 case CALG_SHA_512:
10007 cbDigest = 512/8;
10008 pszName = "SHA-512";
10009 g_cHashesSha512++;
10010 break;
10011 default:
10012 cbDigest = 0;
10013 pszName = NULL;
10014 break;
10015 }
10016
10017 if (cbDigest)
10018 {
10019 if (hKey == 0)
10020 {
10021 if (dwFlags == 0)
10022 {
10023 PKWCRYPTHASH pHash = (PKWCRYPTHASH)kHlpAllocZ(sizeof(*pHash));
10024 if (pHash)
10025 {
10026 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10027 pHash->uMagic = KWCRYPTHASH_MAGIC;
10028 pHash->cbHashed = 0;
10029 pHash->fGoneBad = K_FALSE;
10030 pHash->fFinal = K_FALSE;
10031 pHash->hFallback = KUPTR_MAX;
10032 pHash->idAlg = idAlg;
10033 pHash->pszAlgName = pszName;
10034 pHash->cbDigest = cbDigest;
10035
10036 /* link it. */
10037 pHash->pNext = g_Sandbox.pHashHead;
10038 g_Sandbox.pHashHead = pHash;
10039
10040 *phHash = (KUPTR)pHash;
10041 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%s, 0, 0, *phHash=%p) -> %d [cached]\n",
10042 hProv, pszName, *phHash, TRUE));
10043 return TRUE;
10044 }
10045
10046 kwErrPrintf("CryptCreateHash: out of memory!\n");
10047 }
10048 else
10049 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with %s\n", hKey, pszName);
10050 }
10051 else
10052 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with %s\n", hKey, pszName);
10053 }
10054 else
10055 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
10056 }
10057
10058 /*
10059 * Fallback.
10060 */
10061 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
10062 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
10063 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
10064 return fRc;
10065}
10066
10067
10068/** AdvApi32 - CryptHashData */
10069static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
10070{
10071 BOOL fRc;
10072 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10073 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10074 while (pHash && (KUPTR)pHash != hHash)
10075 pHash = pHash->pNext;
10076 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
10077 hHash, pHash, pbData, cbData, dwFlags));
10078 if (pHash)
10079 {
10080 /*
10081 * Validate the state.
10082 */
10083 if ( pHash->uMagic == KWCRYPTHASH_MAGIC
10084 && !pHash->fFinal
10085 && !pHash->fGoneBad)
10086 {
10087 if (pHash->hFallback == KUPTR_MAX)
10088 {
10089 /*
10090 * Does this match the previous ReadFile call to a cached file?
10091 * If it doesn't, try falling back.
10092 */
10093 if ( g_Sandbox.LastHashRead.cbRead == cbData
10094 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
10095 {
10096 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
10097 if ( pCachedFile
10098 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
10099 {
10100
10101 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
10102 {
10103 if ( pHash->pCachedFile == NULL
10104 && pHash->cbHashed == 0)
10105 pHash->pCachedFile = pCachedFile;
10106 if (pHash->pCachedFile == pCachedFile)
10107 {
10108 pHash->cbHashed += cbData;
10109 g_Sandbox.LastHashRead.pCachedFile = NULL;
10110 g_Sandbox.LastHashRead.pvRead = NULL;
10111 g_Sandbox.LastHashRead.cbRead = 0;
10112 g_Sandbox.LastHashRead.offRead = 0;
10113 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
10114 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
10115 return TRUE;
10116 }
10117
10118 /* Note! it's possible to fall back here too, if necessary. */
10119 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
10120 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
10121 }
10122 else
10123 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
10124 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
10125 }
10126 else if (!pCachedFile)
10127 KWCRYPT_LOG(("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n"));
10128 else
10129 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
10130 }
10131 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
10132 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
10133 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
10134 if (pHash->cbHashed == 0)
10135 {
10136 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
10137 HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(pHash->idAlg);
10138 if (hCryptProv)
10139 {
10140 HCRYPTHASH hCryptHash = KUPTR_MAX;
10141 if (CryptCreateHash(hCryptProv, pHash->idAlg, 0, 0, &hCryptHash))
10142 {
10143 kHlpAssert(hCryptHash != KUPTR_MAX);
10144 pHash->hFallback = hCryptHash;
10145 fRc = CryptHashData(hCryptHash, pbData, cbData, dwFlags);
10146 if (fRc)
10147 pHash->cbHashed = cbData;
10148 g_cHashesFallbacks++;
10149 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d (%u) [fallback!]\n",
10150 hHash, pbData, cbData, dwFlags, fRc, GetLastError()));
10151 }
10152 else
10153 {
10154 kwErrPrintf("kwSandbox_Advapi32_CryptHashData: Fallback CryptCreateHash(%u) failed: %u\n",
10155 pHash->idAlg, GetLastError());
10156 fRc = FALSE;
10157 }
10158 return fRc;
10159 }
10160 }
10161 pHash->fGoneBad = K_TRUE;
10162 SetLastError(ERROR_INVALID_PARAMETER);
10163 fRc = FALSE;
10164 }
10165 else
10166 {
10167 /* fallback. */
10168 fRc = CryptHashData(pHash->hFallback, pbData, cbData, dwFlags);
10169 if (fRc)
10170 pHash->cbHashed += cbData;
10171 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d [fallback]\n",
10172 hHash, pbData, cbData, dwFlags, fRc));
10173 }
10174 }
10175 /*
10176 * Bad handle state.
10177 */
10178 else
10179 {
10180 if (pHash->uMagic != KWCRYPTHASH_MAGIC)
10181 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
10182 else
10183 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
10184 SetLastError((DWORD)NTE_BAD_HASH);
10185 fRc = FALSE;
10186 }
10187 }
10188 else
10189 {
10190
10191 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
10192 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
10193 }
10194 return fRc;
10195}
10196
10197
10198/** Helper for simpe data hashing. */
10199static BOOL kwSandboxCalcHash(ALG_ID idAlg, void const *pvData, KSIZE cbData, KU8 *pbDigest, KSIZE cbDigest)
10200{
10201 BOOL fRet = FALSE;
10202 if (idAlg == CALG_MD5)
10203 {
10204 struct MD5Context Ctx;
10205 MD5Init(&Ctx);
10206 MD5Update(&Ctx, (unsigned char const *)pvData, (unsigned)cbData);
10207 MD5Final(pbDigest, &Ctx);
10208 fRet = TRUE;
10209 }
10210 else
10211 {
10212 HCRYPTPROV hCryptProv = kwSandboxGetCryptoProvider(idAlg);
10213 if (hCryptProv)
10214 {
10215 HCRYPTHASH hCryptHash = KUPTR_MAX;
10216 if (CryptCreateHash(hCryptProv, idAlg, 0, 0, &hCryptHash))
10217 {
10218 if (CryptHashData(hCryptHash, (const BYTE *)pvData, (DWORD)cbData, 0))
10219 {
10220 DWORD cbActual = (DWORD)cbDigest;
10221 if (CryptGetHashParam(hCryptHash, HP_HASHVAL, pbDigest, &cbActual, 0))
10222 {
10223 fRet = TRUE;
10224 kHlpAssert(cbActual == cbDigest);
10225 }
10226 else
10227 kwErrPrintf("CryptGetHashParam([%#x],HP_HASHVAL,%p,%#x,0) failed: %u\n",
10228 idAlg, pbDigest, cbDigest, GetLastError());
10229 }
10230 else
10231 kwErrPrintf("CryptHashData([%#x],%p,%#x,0) failed: %u\n", idAlg, pvData, cbData, GetLastError());
10232 CryptDestroyHash(hCryptHash);
10233 }
10234 else
10235 kwErrPrintf("CryptCreateHash(%#x) failed: %u\n", idAlg, GetLastError());
10236 }
10237 }
10238 return fRet;
10239}
10240
10241
10242/** AdvApi32 - CryptGetHashParam */
10243static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
10244 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
10245{
10246 BOOL fRc;
10247 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10248 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10249 while (pHash && (KUPTR)pHash != hHash)
10250 pHash = pHash->pNext;
10251 if (pHash)
10252 {
10253 if (pHash->uMagic == KWCRYPTHASH_MAGIC)
10254 {
10255 if (dwFlags == 0)
10256 {
10257 DWORD cbRet;
10258 void *pvRet;
10259 union
10260 {
10261 DWORD dw;
10262 } uBuf;
10263
10264 switch (dwParam)
10265 {
10266 case HP_HASHVAL:
10267 {
10268 /* Check the hash progress. */
10269 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
10270 g_cHashes++;
10271 if (pCachedFile)
10272 {
10273 if ( pCachedFile->cbCached == pHash->cbHashed
10274 && !pHash->fGoneBad)
10275 {
10276 KBOOL *pfValid;
10277 switch (pHash->idAlg)
10278 {
10279 case CALG_MD5:
10280 pfValid = &pCachedFile->fValidMd5;
10281 pvRet = pCachedFile->abMd5Digest;
10282 break;
10283 case CALG_SHA1:
10284 pfValid = &pCachedFile->fValidSha1;
10285 pvRet = pCachedFile->abSha1Digest;
10286 break;
10287 case CALG_SHA_256:
10288 pfValid = &pCachedFile->fValidSha256;
10289 pvRet = pCachedFile->abSha256Digest;
10290 break;
10291 case CALG_SHA_512:
10292 pfValid = &pCachedFile->fValidSha512;
10293 pvRet = pCachedFile->abSha512Digest;
10294 break;
10295 default:
10296 kwErrPrintf("Invalid idAlg value: %#x\n", pHash->idAlg);
10297 SetLastError(ERROR_INVALID_SERVER_STATE);
10298 return FALSE;
10299 }
10300
10301 if (*pfValid)
10302 {
10303 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
10304 g_cHashesCached++;
10305 }
10306 else
10307 {
10308 fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pCachedFile->cbCached,
10309 pvRet, pHash->cbDigest);
10310 if (!fRc)
10311 return FALSE;
10312 *pfValid = K_TRUE;
10313 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
10314 }
10315 }
10316 else
10317 {
10318 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
10319 from what I can tell, so just deal with it. */
10320 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
10321 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
10322 pHash, pCachedFile, pCachedFile->szPath));
10323 g_cHashesPartial++;
10324 pHash->pCachedFile = NULL;
10325 pvRet = pHash->abDigest;
10326 fRc = kwSandboxCalcHash(pHash->idAlg, pCachedFile->pbCached, pHash->cbHashed,
10327 pvRet, pHash->cbDigest);
10328 if (!fRc)
10329 {
10330 pHash->fGoneBad = K_TRUE;
10331 return FALSE;
10332 }
10333 }
10334 pHash->fFinal = K_TRUE;
10335 cbRet = pHash->cbDigest;
10336 break;
10337 }
10338
10339 pvRet = pHash->abDigest;
10340 cbRet = pHash->cbDigest;
10341 if (pHash->fFinal)
10342 break;
10343 if (pHash->hFallback != KUPTR_MAX)
10344 {
10345 DWORD cbActual = (DWORD)pHash->cbDigest;
10346 if (CryptGetHashParam(pHash->hFallback, HP_HASHVAL, pHash->abDigest, &cbActual, 0))
10347 {
10348 kHlpAssert(cbActual == pHash->cbDigest);
10349 pHash->fFinal = K_TRUE;
10350 break;
10351 }
10352 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: Fallback CryptGetHashParam failed: %u!!\n", GetLastError());
10353 }
10354 else
10355 {
10356 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
10357 SetLastError(ERROR_INVALID_SERVER_STATE);
10358 }
10359 return FALSE;
10360 }
10361
10362 case HP_HASHSIZE:
10363 uBuf.dw = pHash->cbDigest;
10364 pvRet = &uBuf;
10365 cbRet = sizeof(DWORD);
10366 break;
10367
10368 case HP_ALGID:
10369 uBuf.dw = pHash->idAlg;
10370 pvRet = &uBuf;
10371 cbRet = sizeof(DWORD);
10372 break;
10373
10374 default:
10375 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
10376 SetLastError((DWORD)NTE_BAD_TYPE);
10377 return FALSE;
10378 }
10379
10380 /*
10381 * Copy out cbRet from pvRet.
10382 */
10383 if (pbData)
10384 {
10385 if (*pcbData >= cbRet)
10386 {
10387 *pcbData = cbRet;
10388 kHlpMemCopy(pbData, pvRet, cbRet);
10389 if (cbRet == 4)
10390 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
10391 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
10392 else if (cbRet == 16)
10393 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",
10394 dwParam, pHash, pHash->pCachedFile, cbRet,
10395 pbData[0], pbData[1], pbData[2], pbData[3],
10396 pbData[4], pbData[5], pbData[6], pbData[7],
10397 pbData[8], pbData[9], pbData[10], pbData[11],
10398 pbData[12], pbData[13], pbData[14], pbData[15]));
10399 else if (cbRet == 20)
10400 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x [cached]\n",
10401 dwParam, pHash, pHash->pCachedFile, cbRet,
10402 pbData[0], pbData[1], pbData[2], pbData[3],
10403 pbData[4], pbData[5], pbData[6], pbData[7],
10404 pbData[8], pbData[9], pbData[10], pbData[11],
10405 pbData[12], pbData[13], pbData[14], pbData[15],
10406 pbData[16], pbData[17], pbData[18], pbData[19] ));
10407 else if (cbRet >= 32)
10408 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%s [cached]\n",
10409 dwParam, pHash, pHash->pCachedFile, cbRet,
10410 pbData[0], pbData[1], pbData[2], pbData[3],
10411 pbData[4], pbData[5], pbData[6], pbData[7],
10412 pbData[8], pbData[9], pbData[10], pbData[11],
10413 pbData[12], pbData[13], pbData[14], pbData[15],
10414 pbData[16], pbData[17], pbData[18], pbData[19],
10415 pbData[20], pbData[21], pbData[22], pbData[23],
10416 pbData[24], pbData[25], pbData[26], pbData[27],
10417 pbData[28], pbData[29], pbData[30], pbData[31], cbRet > 32 ? "..." : ""));
10418 else
10419 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
10420 dwParam, pHash, pHash->pCachedFile, cbRet));
10421 return TRUE;
10422 }
10423
10424 kHlpMemCopy(pbData, pvRet, *pcbData);
10425 }
10426 SetLastError(ERROR_MORE_DATA);
10427 *pcbData = cbRet;
10428 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
10429 }
10430 else
10431 {
10432 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
10433 SetLastError((DWORD)NTE_BAD_FLAGS);
10434 }
10435 }
10436 else
10437 {
10438 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
10439 SetLastError((DWORD)NTE_BAD_HASH);
10440 }
10441 fRc = FALSE;
10442 }
10443 /*
10444 * Regular handle.
10445 */
10446 else
10447 {
10448 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
10449 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
10450 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
10451 }
10452
10453 return fRc;
10454}
10455
10456
10457/** AdvApi32 - CryptDestroyHash */
10458static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
10459{
10460 BOOL fRc;
10461 PKWCRYPTHASH pPrev = NULL;
10462 PKWCRYPTHASH pHash = g_Sandbox.pHashHead;
10463 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
10464 while (pHash && (KUPTR)pHash != hHash)
10465 {
10466 pPrev = pHash;
10467 pHash = pHash->pNext;
10468 }
10469 if (pHash)
10470 {
10471 if (pHash->uMagic == KWCRYPTHASH_MAGIC)
10472 {
10473 pHash->uMagic = 0;
10474 if (!pPrev)
10475 g_Sandbox.pHashHead = pHash->pNext;
10476 else
10477 pPrev->pNext = pHash->pNext;
10478 kHlpFree(pHash);
10479 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
10480 fRc = TRUE;
10481 }
10482 else
10483 {
10484 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
10485 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
10486 SetLastError(ERROR_INVALID_HANDLE);
10487 fRc = FALSE;
10488 }
10489 }
10490 /*
10491 * Regular handle.
10492 */
10493 else
10494 {
10495 fRc = CryptDestroyHash(hHash);
10496 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
10497 }
10498 return fRc;
10499}
10500
10501#endif /* WITH_HASH_CACHE */
10502
10503
10504/*
10505 *
10506 * Reuse crypt context.
10507 * Reuse crypt context.
10508 * Reuse crypt context.
10509 *
10510 *
10511 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
10512 *
10513 */
10514
10515#ifdef WITH_CRYPT_CTX_REUSE
10516
10517/** AdvApi32 - CryptAcquireContextW. */
10518static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
10519 DWORD dwProvType, DWORD dwFlags)
10520{
10521 BOOL fRet;
10522
10523 /*
10524 * Lookup reusable context based on the input.
10525 */
10526 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
10527 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
10528 KU32 iCtx = g_Sandbox.cCryptCtxs;
10529 while (iCtx-- > 0)
10530 {
10531 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
10532 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
10533 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
10534 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
10535 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
10536 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
10537 {
10538 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
10539 {
10540 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
10541 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
10542 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10543 return TRUE;
10544 }
10545 }
10546 }
10547
10548 /*
10549 * Create it and enter it into the reused array if possible.
10550 */
10551 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
10552 if (fRet)
10553 {
10554 iCtx = g_Sandbox.cCryptCtxs;
10555 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
10556 {
10557 /* Try duplicate the input strings. */
10558 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
10559 (cwcContainer + 1) * sizeof(wchar_t));
10560 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
10561 {
10562 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
10563 (cwcProvider + 1) * sizeof(wchar_t));
10564 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
10565 {
10566 /* Add a couple of references just to be on the safe side and all that. */
10567 HCRYPTPROV hProv = *phProv;
10568 if (CryptContextAddRef(hProv, NULL, 0))
10569 {
10570 if (CryptContextAddRef(hProv, NULL, 0))
10571 {
10572 /* Okay, finish the entry and return success */
10573 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
10574 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
10575 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
10576 g_Sandbox.cCryptCtxs = iCtx + 1;
10577
10578 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
10579 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10580 return TRUE;
10581 }
10582 CryptReleaseContext(hProv, 0);
10583 }
10584 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
10585
10586 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
10587 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
10588 }
10589 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
10590 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
10591 }
10592 }
10593 else
10594 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
10595 }
10596
10597 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
10598 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
10599 return fRet;
10600}
10601
10602
10603/** AdvApi32 - CryptReleaseContext */
10604static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
10605{
10606 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
10607 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
10608 return fRet;
10609}
10610
10611
10612/** AdvApi32 - CryptContextAddRef */
10613static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
10614{
10615 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
10616 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
10617 return fRet;
10618}
10619
10620#endif /* WITH_CRYPT_CTX_REUSE */
10621
10622/*
10623 *
10624 * Structured exception handling.
10625 * Structured exception handling.
10626 * Structured exception handling.
10627 *
10628 */
10629#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10630
10631# define EH_NONCONTINUABLE KU32_C(0x00000001)
10632# define EH_UNWINDING KU32_C(0x00000002)
10633# define EH_EXIT_UNWIND KU32_C(0x00000004)
10634# define EH_STACK_INVALID KU32_C(0x00000008)
10635# define EH_NESTED_CALL KU32_C(0x00000010)
10636
10637typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
10638 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
10639typedef struct _EXCEPTION_REGISTRATION_RECORD
10640{
10641 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
10642 PFNXCPTHANDLER pfnXcptHandler;
10643};
10644
10645
10646/**
10647 * Calls @a pfnHandler.
10648 */
10649static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
10650 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
10651 PFNXCPTHANDLER pfnHandler)
10652{
10653# if 1
10654 /* This is a more robust version that isn't subject to calling
10655 convension cleanup disputes and such. */
10656 KU32 uSavedEdi;
10657 KU32 uSavedEsi;
10658 KU32 uSavedEbx;
10659 KU32 rcHandler;
10660
10661 __asm
10662 {
10663 mov [uSavedEdi], edi
10664 mov [uSavedEsi], esi
10665 mov [uSavedEbx], ebx
10666 mov esi, esp
10667 mov edi, esp
10668 mov edi, [pXcptRec]
10669 mov edx, [pRegRec]
10670 mov eax, [pXcptCtx]
10671 mov ebx, [ppRegRec]
10672 mov ecx, [pfnHandler]
10673 sub esp, 16
10674 and esp, 0fffffff0h
10675 mov [esp ], edi
10676 mov [esp + 4], edx
10677 mov [esp + 8], eax
10678 mov [esp + 12], ebx
10679 mov edi, esi
10680 call ecx
10681 mov esp, esi
10682 cmp esp, edi
10683 je stack_ok
10684 int 3
10685 stack_ok:
10686 mov edi, [uSavedEdi]
10687 mov esi, [uSavedEsi]
10688 mov ebx, [uSavedEbx]
10689 mov [rcHandler], eax
10690 }
10691 return rcHandler;
10692# else
10693 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
10694# endif
10695}
10696
10697
10698/**
10699 * Vectored exception handler that emulates x86 chained exception handler.
10700 *
10701 * This is necessary because the RtlIsValidHandler check fails for self loaded
10702 * code and prevents cl.exe from working. (On AMD64 we can register function
10703 * tables, but on X86 cooking your own handling seems to be the only viabke
10704 * alternative.)
10705 *
10706 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
10707 * @param pXcptPtrs The exception details.
10708 */
10709static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
10710{
10711 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10712 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
10713 if (g_Sandbox.fRunning)
10714 {
10715 HANDLE const hCurProc = GetCurrentProcess();
10716 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
10717 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
10718 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10719 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
10720 {
10721 /* Read the exception record in a safe manner. */
10722 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10723 DWORD cbActuallyRead = 0;
10724 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10725 && cbActuallyRead == sizeof(RegRec))
10726 {
10727 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10728 KU32 rcHandler;
10729 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10730 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10731 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10732 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10733 if (rcHandler == ExceptionContinueExecution)
10734 {
10735 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
10736 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
10737 return EXCEPTION_CONTINUE_EXECUTION;
10738 }
10739
10740 if (rcHandler == ExceptionContinueSearch)
10741 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10742 else if (rcHandler == ExceptionNestedException)
10743 kHlpAssertMsgFailed(("Nested exceptions.\n"));
10744 else
10745 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10746 }
10747 else
10748 {
10749 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10750 break;
10751 }
10752
10753 /*
10754 * Next.
10755 */
10756 pRegRec = RegRec.pPrevRegRec;
10757 }
10758 }
10759 return EXCEPTION_CONTINUE_SEARCH;
10760}
10761
10762
10763/** NtDll,Kernel32 - RtlUnwind */
10764static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
10765 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
10766{
10767 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10768 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
10769 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
10770 if (g_Sandbox.fRunning)
10771 {
10772 HANDLE const hCurProc = GetCurrentProcess();
10773 PCONTEXT pXcptCtx = NULL;
10774 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
10775
10776 /*
10777 * Update / create an exception record.
10778 */
10779 if (pXcptRec)
10780 pXcptRec->ExceptionFlags |= EH_UNWINDING;
10781 else
10782 {
10783 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
10784 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
10785 pXcptRec->ExceptionCode = (DWORD)STATUS_UNWIND;
10786 pXcptRec->ExceptionFlags = EH_UNWINDING;
10787 }
10788 if (!pStopXcptRec)
10789 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
10790
10791 /*
10792 * Walk the chain till we find pStopXctpRec.
10793 */
10794 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
10795 && pRegRec != NULL
10796 && pRegRec != pStopXcptRec)
10797 {
10798 /* Read the exception record in a safe manner. */
10799 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
10800 DWORD cbActuallyRead = 0;
10801 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
10802 && cbActuallyRead == sizeof(RegRec))
10803 {
10804 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
10805 KU32 rcHandler;
10806 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
10807 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
10808 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
10809 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
10810
10811 if (rcHandler == ExceptionContinueSearch)
10812 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
10813 else if (rcHandler == ExceptionCollidedUnwind)
10814 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
10815 else
10816 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
10817 }
10818 else
10819 {
10820 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
10821 break;
10822 }
10823
10824 /*
10825 * Pop next.
10826 */
10827 pTib->ExceptionList = RegRec.pPrevRegRec;
10828 pRegRec = RegRec.pPrevRegRec;
10829 }
10830 return;
10831 }
10832
10833 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
10834}
10835
10836#endif /* WINDOWS + X86 */
10837
10838
10839/*
10840 *
10841 * Misc function only intercepted while debugging.
10842 * Misc function only intercepted while debugging.
10843 * Misc function only intercepted while debugging.
10844 *
10845 */
10846
10847#ifndef NDEBUG
10848
10849/** CRT - memcpy */
10850static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
10851{
10852 KU8 const *pbSrc = (KU8 const *)pvSrc;
10853 KU8 *pbDst = (KU8 *)pvDst;
10854 KSIZE cbLeft = cb;
10855 while (cbLeft-- > 0)
10856 *pbDst++ = *pbSrc++;
10857 return pvDst;
10858}
10859
10860
10861/** CRT - memset */
10862static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
10863{
10864 KU8 *pbDst = (KU8 *)pvDst;
10865 KSIZE cbLeft = cb;
10866 while (cbLeft-- > 0)
10867 *pbDst++ = (KU8)bFiller;
10868 return pvDst;
10869}
10870
10871#endif /* NDEBUG */
10872
10873
10874/** @todo consider hooking NtQueryDirectoryFile as c1xx.dll/c1.dll in 2019
10875 * uses it directly to read the content of include directories, however
10876 * they do it one file at the time. We already have the info in the
10877 * cache (where we do bulk reads). There are a lot of calls for the
10878 * SDK include directories, as one can imagine. */
10879
10880/**
10881 * Functions that needs replacing for sandboxed execution.
10882 */
10883KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
10884{
10885 /*
10886 * Kernel32.dll and friends.
10887 */
10888 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10889 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10890
10891 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
10892 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
10893 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
10894 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
10895 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
10896 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
10897 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
10898 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
10899 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
10900 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
10901 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10902
10903 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
10904 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
10905 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
10906 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
10907
10908 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10909
10910 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
10911 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
10912 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
10913 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
10914 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
10915 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
10916 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
10917 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
10918 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
10919 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
10920 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
10921
10922 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10923 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10924 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10925 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10926#ifdef WITH_TEMP_MEMORY_FILES
10927 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10928 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10929 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10930 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10931 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10932 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10933 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10934 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10935 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10936 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10937#endif
10938 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10939 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10940 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10941 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10942 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10943 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10944 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
10945 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
10946 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10947#ifdef WITH_TEMP_MEMORY_FILES
10948 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10949#endif
10950
10951#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10952 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10953 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10954#endif
10955
10956 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
10957 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
10958
10959 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
10960 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
10961
10962 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10963 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10964 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10965 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10966
10967 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10968
10969#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10970 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10971#endif
10972
10973#ifdef WITH_HASH_CACHE
10974 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10975 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10976 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10977 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10978#endif
10979
10980#ifdef WITH_CRYPT_CTX_REUSE
10981 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
10982 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
10983 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
10984#endif
10985
10986 /*
10987 * MS Visual C++ CRTs.
10988 */
10989 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10990 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10991 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10992 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10993 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10994 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10995
10996 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10997 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
10998 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
10999
11000 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
11001 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
11002 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
11003
11004 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
11005 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
11006 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
11007 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
11008 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
11009 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
11010 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
11011 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
11012 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
11013 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
11014 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
11015 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
11016 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
11017 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
11018 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
11019 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
11020 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
11021 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
11022 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
11023 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
11024
11025 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
11026 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
11027 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
11028 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
11029 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
11030 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
11031 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
11032 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
11033 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
11034 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
11035 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
11036 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
11037 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
11038 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
11039
11040#ifndef NDEBUG
11041 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
11042 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
11043#endif
11044};
11045/** Number of entries in g_aReplacements. */
11046KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
11047
11048
11049/**
11050 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
11051 * execution.
11052 */
11053KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
11054{
11055 /*
11056 * Kernel32.dll and friends.
11057 */
11058 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
11059 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
11060
11061 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
11062 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
11063
11064#if 0
11065 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
11066#endif
11067
11068 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
11069 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
11070 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
11071 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
11072#ifdef WITH_TEMP_MEMORY_FILES
11073 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
11074 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
11075 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
11076 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
11077 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
11078 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
11079 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
11080 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
11081 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
11082 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
11083#endif
11084 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
11085 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
11086 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
11087 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
11088 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
11089 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
11090 { TUPLE("GetFileAttributesExA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExA },
11091 { TUPLE("GetFileAttributesExW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesExW },
11092 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
11093#ifdef WITH_TEMP_MEMORY_FILES
11094 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
11095#endif
11096 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
11097 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
11098 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExW },
11099#ifndef NDEBUG
11100 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_Native_GetProcAddress },
11101#endif
11102
11103#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11104 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
11105 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
11106#endif
11107
11108#ifdef WITH_HASH_CACHE
11109 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
11110 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
11111 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
11112 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
11113#endif
11114
11115 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
11116
11117#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
11118 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
11119#endif
11120
11121 /*
11122 * MS Visual C++ CRTs.
11123 */
11124 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
11125 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
11126 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
11127 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
11128 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
11129 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
11130 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
11131
11132#if 0 /* used by mspdbXXX.dll */
11133 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
11134 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
11135#endif
11136};
11137/** Number of entries in g_aSandboxNativeReplacements. */
11138KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
11139
11140
11141/**
11142 * Functions that needs replacing when queried by GetProcAddress.
11143 */
11144KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
11145{
11146 /*
11147 * Kernel32.dll and friends.
11148 */
11149 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
11150 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
11151 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
11152 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
11153};
11154/** Number of entries in g_aSandboxGetProcReplacements. */
11155KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
11156
11157
11158/**
11159 * Control handler.
11160 *
11161 * @returns TRUE if handled, FALSE if not.
11162 * @param dwCtrlType The signal.
11163 */
11164static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
11165{
11166 DWORD cbIgn;
11167 int volatile rc; /* volatile for debugging */
11168 int volatile rcPrev;
11169 const char *pszMsg;
11170 switch (dwCtrlType)
11171 {
11172 case CTRL_C_EVENT:
11173 rc = 9;
11174 pszMsg = "kWorker: Ctrl-C\r\n";
11175 break;
11176
11177 case CTRL_BREAK_EVENT:
11178 rc = 10;
11179 pszMsg = "kWorker: Ctrl-Break\r\n";
11180 break;
11181
11182 case CTRL_CLOSE_EVENT:
11183 rc = 11;
11184 pszMsg = "kWorker: console closed\r\n";
11185 break;
11186
11187 case CTRL_LOGOFF_EVENT:
11188 rc = 11;
11189 pszMsg = "kWorker: logoff event\r\n";
11190 break;
11191
11192 case CTRL_SHUTDOWN_EVENT:
11193 rc = 11;
11194 pszMsg = "kWorker: shutdown event\r\n";
11195 break;
11196
11197 default:
11198 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
11199 return TRUE;
11200 }
11201
11202 /*
11203 * Terminate the process after 5 seconds.
11204 * If we get here a second time we just terminate the process ourselves.
11205 *
11206 * Note! We do no try call exit() here as it turned out to deadlock a lot
11207 * flusing file descriptors (stderr back when we first wrote to it).
11208 */
11209 rcPrev = g_rcCtrlC;
11210 g_rcCtrlC = rc;
11211 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
11212 if (rcPrev == 0)
11213 {
11214 int i;
11215 for (i = 0; i < 10; i++)
11216 {
11217 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
11218 Sleep(500);
11219 }
11220 }
11221 TerminateProcess(GetCurrentProcess(), rc);
11222 return TRUE;
11223}
11224
11225
11226#if 0
11227/**
11228 * Resets the KWMODULE::fVisited flag for _all_ known modules.
11229 */
11230static void kwSandboxResetModuleVisited(void)
11231{
11232 PKWMODULE pMod = g_pModuleHead;
11233 while (pMod)
11234 {
11235 pMod->fVisited = K_FALSE;
11236 pMod = pMod->pNextList;
11237 }
11238}
11239
11240
11241/**
11242 * Used by kwSandboxExec to reset the state of the module tree.
11243 *
11244 * This is done recursively.
11245 *
11246 * @param pMod The root of the tree to consider.
11247 */
11248static void kwSandboxResetModuleState(PKWMODULE pMod)
11249{
11250 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
11251 if (!pMod->fNative)
11252 {
11253 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
11254 if (!pMod->fVisited) /* Avoid loops. */
11255 {
11256 KSIZE iImp;
11257 pMod->fVisited = K_TRUE;
11258 iImp = pMod->u.Manual.cImpMods;
11259 while (iImp-- > 0)
11260 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
11261 }
11262 }
11263 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11264 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
11265 {
11266 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11267 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11268 {
11269 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11270 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11271 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11272 pMod->pszPath, pszValue ? pszValue : "<null>"));
11273 }
11274 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11275 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11276 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11277 pMod->pszPath, pszValue ? pszValue : "<null>"));
11278 else
11279 {
11280 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11281 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11282 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11283 if (pszValue != NULL)
11284 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11285 else
11286 pMod->pszMsPdbSrvEndpoint = NULL;
11287 pMod->fNeedReInit = K_TRUE;
11288 }
11289 }
11290}
11291#else
11292/**
11293 * Used by kwSandboxExec to reset the state of the module tree.
11294 */
11295static void kwSandboxResetModuleState(void)
11296{
11297 PKWMODULE pMod = g_pModuleHead;
11298 while (pMod)
11299 {
11300 if (!pMod->fNative)
11301 pMod->u.Manual.enmState = K_MIN(pMod->u.Manual.enmReInitState, pMod->u.Manual.enmState);
11302 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
11303 else if ( pMod->fReInitOnMsPdbSrvEndpointChange
11304 && ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
11305 || g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK))
11306 {
11307 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
11308 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
11309 {
11310 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
11311 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
11312 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
11313 pMod->pszPath, pszValue ? pszValue : "<null>"));
11314 }
11315 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
11316 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
11317 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
11318 pMod->pszPath, pszValue ? pszValue : "<null>"));
11319 else
11320 {
11321 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
11322 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
11323 kHlpFree(pMod->pszMsPdbSrvEndpoint);
11324 if (pszValue != NULL)
11325 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
11326 else
11327 pMod->pszMsPdbSrvEndpoint = NULL;
11328 pMod->fNeedReInit = K_TRUE;
11329 }
11330 }
11331
11332 pMod = pMod->pNextList;
11333 }
11334}
11335#endif
11336
11337static PPEB kwSandboxGetProcessEnvironmentBlock(void)
11338{
11339#if K_ARCH == K_ARCH_X86_32
11340 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
11341#elif K_ARCH == K_ARCH_AMD64
11342 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
11343#else
11344# error "Port me!"
11345#endif
11346}
11347
11348
11349/**
11350 * Enters the given handle into the handle table.
11351 *
11352 * @returns K_TRUE on success, K_FALSE on failure.
11353 * @param pSandbox The sandbox.
11354 * @param pHandle The handle.
11355 * @param hHandle The handle value to enter it under (for the
11356 * duplicate handle API).
11357 */
11358static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
11359{
11360 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
11361 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
11362
11363 EnterCriticalSection(&g_Sandbox.HandlesLock);
11364
11365 /*
11366 * Grow handle table.
11367 */
11368 if (idxHandle >= pSandbox->cHandles)
11369 {
11370 void *pvNew;
11371 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
11372 while (cHandles <= idxHandle)
11373 cHandles *= 2;
11374 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
11375 if (!pvNew)
11376 {
11377 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11378 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
11379 return K_FALSE;
11380 }
11381 pSandbox->papHandles = (PKWHANDLE *)pvNew;
11382 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
11383 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
11384 pSandbox->cHandles = cHandles;
11385 }
11386
11387 /*
11388 * Check that the entry is unused then insert it.
11389 */
11390 kHlpAssertStmtReturn(pSandbox->papHandles[idxHandle] == NULL, LeaveCriticalSection(&g_Sandbox.HandlesLock), K_FALSE);
11391 pSandbox->papHandles[idxHandle] = pHandle;
11392 pSandbox->cActiveHandles++;
11393 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11394 return K_TRUE;
11395}
11396
11397
11398/**
11399 * Safely looks up a handle, does not get it and it must not be 'put'.
11400 *
11401 * @returns Pointer to the handle structure if found, otherwise NULL.
11402 * @param hFile The handle to resolve.
11403 */
11404static PKWHANDLE kwSandboxHandleLookup(HANDLE hFile)
11405{
11406 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11407 EnterCriticalSection(&g_Sandbox.HandlesLock);
11408 if (idxHandle < g_Sandbox.cHandles)
11409 {
11410 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11411 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11412 return pHandle;
11413 }
11414 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11415 return NULL;
11416}
11417
11418
11419/**
11420 * Safely gets a handle, must be "put" when done with it.
11421 *
11422 * @returns Pointer to the handle structure if found, otherwise NULL.
11423 * @param hFile The handle to resolve.
11424 */
11425static PKWHANDLE kwSandboxHandleGet(HANDLE hFile)
11426{
11427 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
11428 EnterCriticalSection(&g_Sandbox.HandlesLock);
11429 if (idxHandle < g_Sandbox.cHandles)
11430 {
11431 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
11432 if (pHandle)
11433 {
11434 kHlpAssertMsg(pHandle->tidOwner == KU32_MAX, ("hFile=%p tidOwner=%#x\n", hFile, pHandle->tidOwner));
11435 pHandle->tidOwner = GetCurrentThreadId();
11436 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11437 return pHandle;
11438 }
11439 }
11440 LeaveCriticalSection(&g_Sandbox.HandlesLock);
11441 return NULL;
11442}
11443
11444
11445/**
11446 * Puts a handle returned by kwSandboxHandleGet.
11447 *
11448 * @param pHandle The handle to "put".
11449 */
11450K_INLINE void kwSandboxHandlePut(PKWHANDLE pHandle)
11451{
11452 kHlpAssertMsg(pHandle->tidOwner == GetCurrentThreadId(),
11453 ("hFile tidOwner=%#x tidMe=%#x\n", pHandle->hHandle, pHandle->tidOwner, GetCurrentThreadId()));
11454 pHandle->tidOwner = KU32_MAX;
11455}
11456
11457
11458/**
11459 * Creates a correctly quoted ANSI command line string from the given argv.
11460 *
11461 * @returns Pointer to the command line.
11462 * @param cArgs Number of arguments.
11463 * @param papszArgs The argument vector.
11464 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11465 * @param pcbCmdLine Where to return the command line length,
11466 * including one terminator.
11467 */
11468static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
11469{
11470 KU32 i;
11471 KSIZE cbCmdLine;
11472 char *pszCmdLine;
11473
11474 /* Make a copy of the argument vector that we'll be quoting. */
11475 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
11476 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
11477
11478 /* Quote the arguments that need it. */
11479 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
11480
11481 /* figure out cmd line length. */
11482 cbCmdLine = 0;
11483 for (i = 0; i < cArgs; i++)
11484 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
11485 *pcbCmdLine = cbCmdLine;
11486
11487 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
11488 if (pszCmdLine)
11489 {
11490 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
11491 if (papszQuotedArgs[0] != papszArgs[0])
11492 free(papszQuotedArgs[0]);
11493
11494 for (i = 1; i < cArgs; i++)
11495 {
11496 *psz++ = ' ';
11497 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
11498 if (papszQuotedArgs[i] != papszArgs[i])
11499 free(papszQuotedArgs[i]);
11500 }
11501 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
11502
11503 *psz++ = '\0';
11504 *psz++ = '\0';
11505 }
11506
11507 return pszCmdLine;
11508}
11509
11510
11511
11512static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
11513 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11514 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11515{
11516 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11517 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11518 wchar_t *pwcPool;
11519 KSIZE cbStrings;
11520 KSIZE cwc;
11521 KSIZE cbCmdLine;
11522 KU32 i;
11523
11524 /* Simple stuff. */
11525 pSandbox->rcExitCode = 256;
11526 pSandbox->pTool = pTool;
11527 pSandbox->idMainThread = GetCurrentThreadId();
11528 pSandbox->pgmptr = (char *)pTool->pszPath;
11529 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
11530#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11531 if (pSandbox->StdOut.fIsConsole)
11532 pSandbox->StdOut.u.Con.cwcBuf = 0;
11533 else
11534 pSandbox->StdOut.u.Fully.cchBuf = 0;
11535 if (pSandbox->StdErr.fIsConsole)
11536 pSandbox->StdErr.u.Con.cwcBuf = 0;
11537 else
11538 pSandbox->StdErr.u.Fully.cchBuf = 0;
11539 pSandbox->Combined.cwcBuf = 0;
11540 pSandbox->Combined.cFlushes = 0;
11541#endif
11542 pSandbox->fNoPchCaching = fNoPchCaching;
11543 pSandbox->cArgs = cArgs;
11544 pSandbox->papszArgs = (char **)papszArgs;
11545 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
11546 if (!pSandbox->pszCmdLine)
11547 return KERR_NO_MEMORY;
11548
11549 /*
11550 * Convert command line and argv to UTF-16.
11551 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
11552 */
11553 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
11554 if (!pSandbox->papwszArgs)
11555 return KERR_NO_MEMORY;
11556 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
11557 for (i = 0; i < cArgs; i++)
11558 {
11559 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
11560 pSandbox->papwszArgs[i] = pwcPool;
11561 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
11562 pwcPool++;
11563 }
11564 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
11565 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
11566
11567 /*
11568 * Convert the commandline string to UTF-16, same pessimistic approach as above.
11569 */
11570 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
11571 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
11572 if (!pSandbox->pwszCmdLine)
11573 return KERR_NO_MEMORY;
11574 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
11575
11576 pSandbox->SavedCommandLine = pProcParams->CommandLine;
11577 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
11578 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
11579
11580 /*
11581 * Setup the environment.
11582 */
11583 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
11584 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
11585 {
11586 KU32 iDst = 0;
11587 for (i = 0; i < cEnvVars; i++)
11588 {
11589 const char *pszVar = papszEnvVars[i];
11590 KSIZE cchVar = kHlpStrLen(pszVar);
11591 const char *pszEqual;
11592 if ( cchVar > 0
11593 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
11594 {
11595 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
11596 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
11597 if (pszCopy && pwszCopy)
11598 {
11599 pSandbox->papszEnvVars[iDst] = pszCopy;
11600 pSandbox->environ[iDst] = pszCopy;
11601 pSandbox->papwszEnvVars[iDst] = pwszCopy;
11602 pSandbox->wenviron[iDst] = pwszCopy;
11603
11604 /* When we see the path, we must tell the system or native exec and module loading won't work . */
11605 if ( (pszEqual - pszVar) == 4
11606 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
11607 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
11608 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
11609 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
11610 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
11611 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
11612
11613 iDst++;
11614 }
11615 else
11616 {
11617 kHlpFree(pszCopy);
11618 kHlpFree(pwszCopy);
11619 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
11620 }
11621 }
11622 else
11623 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
11624 }
11625 pSandbox->papszEnvVars[iDst] = NULL;
11626 pSandbox->environ[iDst] = NULL;
11627 pSandbox->papwszEnvVars[iDst] = NULL;
11628 pSandbox->wenviron[iDst] = NULL;
11629 }
11630 else
11631 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
11632
11633 /*
11634 * Invalidate the volatile parts of cache (kBuild output directory,
11635 * temporary directory, whatever).
11636 */
11637 kFsCacheInvalidateCustomBoth(g_pFsCache);
11638
11639#ifdef WITH_HISTORY
11640 /*
11641 * Record command line in debug history.
11642 */
11643 kHlpFree(g_apszHistory[g_iHistoryNext]);
11644 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
11645 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
11646#endif
11647
11648 return 0;
11649}
11650
11651
11652/**
11653 * Does sandbox cleanup between jobs.
11654 *
11655 * We postpone whatever isn't externally visible (i.e. files) and doesn't
11656 * influence the result, so that kmk can get on with things ASAP.
11657 *
11658 * @param pSandbox The sandbox.
11659 */
11660static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
11661{
11662 PROCESS_MEMORY_COUNTERS MemInfo;
11663 PKWVIRTALLOC pTracker;
11664 PKWHEAP pHeap;
11665 PKWLOCALSTORAGE pLocalStorage;
11666#ifdef WITH_HASH_CACHE
11667 PKWCRYPTHASH pHash;
11668#endif
11669#ifdef WITH_TEMP_MEMORY_FILES
11670 PKWFSTEMPFILE pTempFile;
11671#endif
11672 PKWEXITCALLACK pExitCallback;
11673
11674 /*
11675 * First stuff that may cause code to run.
11676 */
11677
11678 /* Do exit callback first. */
11679 pExitCallback = g_Sandbox.pExitCallbackHead;
11680 g_Sandbox.pExitCallbackHead = NULL;
11681 while (pExitCallback)
11682 {
11683 PKWEXITCALLACK pNext = pExitCallback->pNext;
11684 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
11685 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
11686 __try
11687 {
11688 pExitCallback->pfnCallback();
11689 }
11690 __except (EXCEPTION_EXECUTE_HANDLER)
11691 {
11692 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
11693 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
11694 kHlpAssertFailed();
11695 }
11696 kHlpFree(pExitCallback);
11697 pExitCallback = pNext;
11698 }
11699
11700 /* Free left behind FlsAlloc leaks. */
11701 pLocalStorage = g_Sandbox.pFlsAllocHead;
11702 g_Sandbox.pFlsAllocHead = NULL;
11703 while (pLocalStorage)
11704 {
11705 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11706 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
11707 FlsFree(pLocalStorage->idx);
11708 kHlpFree(pLocalStorage);
11709 pLocalStorage = pNext;
11710 }
11711
11712 /* Free left behind TlsAlloc leaks. */
11713 pLocalStorage = g_Sandbox.pTlsAllocHead;
11714 g_Sandbox.pTlsAllocHead = NULL;
11715 while (pLocalStorage)
11716 {
11717 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
11718 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
11719 TlsFree(pLocalStorage->idx);
11720 kHlpFree(pLocalStorage);
11721 pLocalStorage = pNext;
11722 }
11723
11724
11725 /*
11726 * Then free resources associated with the sandbox run.
11727 */
11728
11729 /* Open handles, except fixed handles (stdout and stderr). */
11730 EnterCriticalSection(&pSandbox->HandlesLock);
11731 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
11732 {
11733 KU32 idxHandle = pSandbox->cHandles;
11734 while (idxHandle-- > 0)
11735 if (pSandbox->papHandles[idxHandle] == NULL)
11736 { /* likely */ }
11737 else
11738 {
11739 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
11740 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
11741 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
11742 {
11743 pSandbox->papHandles[idxHandle] = NULL;
11744 pSandbox->cLeakedHandles++;
11745
11746 switch (pHandle->enmType)
11747 {
11748 case KWHANDLETYPE_FSOBJ_READ_CACHE:
11749 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
11750 idxHandle, pHandle->hHandle, pHandle->cRefs));
11751 break;
11752 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
11753 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
11754 idxHandle, pHandle->hHandle, pHandle->cRefs));
11755 break;
11756 case KWHANDLETYPE_OUTPUT_BUF:
11757 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
11758 idxHandle, pHandle->hHandle, pHandle->cRefs));
11759 break;
11760#ifdef WITH_TEMP_MEMORY_FILES
11761 case KWHANDLETYPE_TEMP_FILE:
11762 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
11763 idxHandle, pHandle->hHandle, pHandle->cRefs));
11764 pHandle->u.pTempFile->cActiveHandles--;
11765 break;
11766 case KWHANDLETYPE_TEMP_FILE_MAPPING:
11767 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
11768 idxHandle, pHandle->hHandle, pHandle->cRefs));
11769 pHandle->u.pTempFile->cActiveHandles--;
11770 break;
11771#endif
11772 default:
11773 kHlpAssertFailed();
11774 }
11775 if (--pHandle->cRefs == 0)
11776 kHlpFree(pHandle);
11777 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
11778 break;
11779 }
11780 }
11781 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
11782 }
11783 LeaveCriticalSection(&pSandbox->HandlesLock);
11784
11785 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
11786 g_Sandbox.cMemMappings = 0;
11787
11788#ifdef WITH_TEMP_MEMORY_FILES
11789 /* The temporary files aren't externally visible, they're all in memory. */
11790 pTempFile = pSandbox->pTempFileHead;
11791 pSandbox->pTempFileHead = NULL;
11792 while (pTempFile)
11793 {
11794 PKWFSTEMPFILE pNext = pTempFile->pNext;
11795 KU32 iSeg = pTempFile->cSegs;
11796 while (iSeg-- > 0)
11797 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
11798 kHlpFree(pTempFile->paSegs);
11799 pTempFile->pNext = NULL;
11800 kHlpFree(pTempFile);
11801
11802 pTempFile = pNext;
11803 }
11804#endif
11805
11806 /* Free left behind HeapCreate leaks. */
11807 pHeap = g_Sandbox.pHeapHead;
11808 g_Sandbox.pHeapHead = NULL;
11809 while (pHeap != NULL)
11810 {
11811 PKWHEAP pNext = pHeap->pNext;
11812 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
11813 HeapDestroy(pHeap->hHeap);
11814 pHeap = pNext;
11815 }
11816
11817 /* Free left behind VirtualAlloc leaks. */
11818 EnterCriticalSection(&g_Sandbox.VirtualAllocLock);
11819 pTracker = g_Sandbox.pVirtualAllocHead;
11820 g_Sandbox.pVirtualAllocHead = NULL;
11821 LeaveCriticalSection(&g_Sandbox.VirtualAllocLock);
11822 while (pTracker)
11823 {
11824 PKWVIRTALLOC pNext = pTracker->pNext;
11825 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
11826
11827#ifdef WITH_FIXED_VIRTUAL_ALLOCS
11828 if (pTracker->idxPreAllocated != KU32_MAX)
11829 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
11830 else
11831#endif
11832 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
11833 kHlpFree(pTracker);
11834 pTracker = pNext;
11835 }
11836
11837 /* Free the environment. */
11838 if (pSandbox->papszEnvVars)
11839 {
11840 KU32 i;
11841 for (i = 0; pSandbox->papszEnvVars[i]; i++)
11842 kHlpFree(pSandbox->papszEnvVars[i]);
11843 pSandbox->environ[0] = NULL;
11844 pSandbox->papszEnvVars[0] = NULL;
11845
11846 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
11847 kHlpFree(pSandbox->papwszEnvVars[i]);
11848 pSandbox->wenviron[0] = NULL;
11849 pSandbox->papwszEnvVars[0] = NULL;
11850 }
11851
11852#ifdef WITH_HASH_CACHE
11853 /*
11854 * Hash handles.
11855 */
11856 pHash = pSandbox->pHashHead;
11857 pSandbox->pHashHead = NULL;
11858 while (pHash)
11859 {
11860 PKWCRYPTHASH pNext = pHash->pNext;
11861 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
11862 if (pHash->hFallback != KUPTR_MAX)
11863 CryptDestroyHash(pHash->hFallback);
11864 kHlpFree(pHash);
11865 pHash = pNext;
11866 }
11867#endif
11868
11869 /*
11870 * Check the memory usage. If it's getting high, trigger a respawn
11871 * after the next job.
11872 */
11873 MemInfo.WorkingSetSize = 0;
11874 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
11875 {
11876 /* The first time thru, we figure out approximately when to restart
11877 based on installed RAM and CPU threads. */
11878 static KU64 s_cbMaxWorkingSet = 0;
11879 if (s_cbMaxWorkingSet != 0)
11880 { /* likely */ }
11881 else
11882 {
11883 SYSTEM_INFO SysInfo;
11884 MEMORYSTATUSEX GlobalMemInfo;
11885 const char *pszValue;
11886
11887 /* Calculate a reasonable estimate. */
11888 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
11889 GetNativeSystemInfo(&SysInfo);
11890
11891 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
11892 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
11893 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
11894#if K_ARCH_BITS >= 64
11895 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
11896#else
11897 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
11898#endif
11899 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
11900 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11901
11902 /* User limit. */
11903 pszValue = getenv("KWORKER_MEMORY_LIMIT");
11904 if (pszValue != NULL)
11905 {
11906 char *pszNext;
11907 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
11908 if (*pszNext == '\0' || *pszNext == 'M')
11909 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
11910 else if (*pszNext == 'K')
11911 s_cbMaxWorkingSet = ulValue * (KU64)1024;
11912 else if (*pszNext == 'G')
11913 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
11914 else
11915 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
11916 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11917 }
11918
11919 /* Clamp it a little. */
11920 if (s_cbMaxWorkingSet < 168*1024*1024)
11921 s_cbMaxWorkingSet = 168*1024*1024;
11922#if K_ARCH_BITS < 64
11923 else
11924 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
11925 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
11926 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
11927 : 1536*1024*1024 /* got 4GB VA */);
11928#endif
11929 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
11930 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
11931 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
11932 }
11933
11934 /* Finally the check. */
11935 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
11936 {
11937 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
11938 g_fRestart = K_TRUE;
11939 }
11940 }
11941
11942 /*
11943 * The CRT has a max of 8192 handles, so we better restart after a while if
11944 * someone is leaking handles or we risk running out of descriptors.
11945 *
11946 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
11947 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
11948 */
11949 if (pSandbox->cLeakedHandles > 6000)
11950 {
11951 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
11952 g_fRestart = K_TRUE;
11953 }
11954}
11955
11956
11957/**
11958 * Does essential cleanups and restoring, anything externally visible.
11959 *
11960 * All cleanups that aren't externally visible are postponed till after we've
11961 * informed kmk of the result, so it can be done in the dead time between jobs.
11962 *
11963 * @param pSandbox The sandbox.
11964 */
11965static void kwSandboxCleanup(PKWSANDBOX pSandbox)
11966{
11967 /*
11968 * Restore the parent command line string.
11969 */
11970 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
11971 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
11972 pProcParams->CommandLine = pSandbox->SavedCommandLine;
11973#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
11974 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
11975 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
11976#endif
11977}
11978
11979
11980static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11981 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
11982{
11983 int rcExit = 42;
11984 int rc;
11985
11986 /*
11987 * Initialize the sandbox environment.
11988 */
11989 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
11990 if (rc == 0)
11991 {
11992 if (g_cVerbose > 2)
11993 fprintf(stderr, "kWorker: info: Executing (sandboxed): %s\n", g_Sandbox.pszCmdLine);
11994
11995 /*
11996 * Do module initialization.
11997 */
11998#if 0
11999 //kwSandboxResetModuleVisited();
12000 //kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
12001#else
12002 kwSandboxResetModuleState();
12003#endif
12004 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
12005 if (rc == 0)
12006 {
12007 /*
12008 * Call the main function.
12009 */
12010#if K_ARCH == K_ARCH_AMD64
12011 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
12012#elif K_ARCH == K_ARCH_X86_32
12013 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
12014#else
12015# error "Port me!"
12016#endif
12017
12018 /* Save the NT TIB first (should do that here, not in some other function). */
12019 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
12020 pSandbox->TibMainThread = *pTib;
12021
12022 /* Make the call in a guarded fashion. */
12023#if K_ARCH == K_ARCH_AMD64
12024 /* AMD64 */
12025 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
12026 __try
12027 {
12028 pSandbox->pOutXcptListHead = pTib->ExceptionList;
12029 if (setjmp(pSandbox->JmpBuf) == 0)
12030 {
12031 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
12032 pSandbox->fRunning = K_TRUE;
12033 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
12034 pSandbox->fRunning = K_FALSE;
12035 }
12036 else
12037 rcExit = pSandbox->rcExitCode;
12038 }
12039#elif K_ARCH == K_ARCH_X86_32
12040 /* x86 (see _tmainCRTStartup) */
12041 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
12042 __try
12043 {
12044 pSandbox->pOutXcptListHead = pTib->ExceptionList;
12045 if (setjmp(pSandbox->JmpBuf) == 0)
12046 {
12047 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
12048 pSandbox->fRunning = K_TRUE;
12049 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
12050 pSandbox->fRunning = K_FALSE;
12051 }
12052 else
12053 rcExit = pSandbox->rcExitCode;
12054 }
12055#endif
12056 __except (EXCEPTION_EXECUTE_HANDLER)
12057 {
12058 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
12059#ifdef WITH_HISTORY
12060 {
12061 KU32 cPrinted = 0;
12062 while (cPrinted++ < 5)
12063 {
12064 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
12065 if (g_apszHistory[idx])
12066 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
12067 }
12068 }
12069#endif
12070 rcExit = 512;
12071 }
12072 pSandbox->fRunning = K_FALSE;
12073
12074 /* Now, restore the NT TIB. */
12075 *pTib = pSandbox->TibMainThread;
12076 }
12077 else
12078 rcExit = 42 + 4;
12079
12080 /*
12081 * Flush and clean up the essential bits only, postpone whatever we
12082 * can till after we've replied to kmk.
12083 */
12084#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12085 kwSandboxConsoleFlushAll(&g_Sandbox);
12086#endif
12087 kwSandboxCleanup(&g_Sandbox);
12088 /** @todo Flush sandboxed native CRTs too. */
12089 }
12090 else
12091 rcExit = 42 + 3;
12092
12093 return rcExit;
12094}
12095
12096
12097/**
12098 * Does the post command part of a job (optional).
12099 *
12100 * @returns The exit code of the job.
12101 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12102 * @param papszPostCmdArgs The post command and its argument.
12103 */
12104static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12105{
12106 const char *pszCmd = papszPostCmdArgs[0];
12107
12108 /* Allow the kmk builtin prefix. */
12109 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
12110 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
12111 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
12112
12113 /* Command switch. */
12114 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
12115 {
12116 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
12117 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
12118 }
12119
12120 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
12121}
12122
12123
12124/**
12125 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
12126 */
12127static unsigned kwGetCurrentProcessorGroup(void)
12128{
12129 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
12130 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
12131 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
12132 if (pfnGetter)
12133 {
12134 GROUP_AFFINITY GroupAffinity;
12135 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
12136 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
12137 return GroupAffinity.Group;
12138 }
12139 return 0;
12140}
12141
12142
12143/**
12144 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
12145 */
12146static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
12147{
12148 KSIZE cchRet = 0;
12149 HANDLE hToken;
12150 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
12151 {
12152 DWORD cbRet;
12153 TOKEN_STATISTICS TokenStats;
12154 memset(&TokenStats, 0, sizeof(TokenStats));
12155 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
12156 cchRet = sprintf(pszValue, "%" KX64_PRI,
12157 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
12158 else
12159 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
12160 CloseHandle(hToken);
12161 }
12162 else
12163 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
12164 return cchRet;
12165}
12166
12167
12168/**
12169 * Look for and expand the special environment variable.
12170 *
12171 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
12172 * couldn't accuratly determine. Currently the following variables are
12173 * implemented:
12174 * - "@@PROCESSOR_GROUP@@" - The processor group number.
12175 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
12176 * - "@@PID@@" - The kWorker process ID.
12177 * - "@@@@" - Escaped "@@".
12178 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
12179 */
12180static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
12181{
12182 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
12183 KU32 i = cEnvVars;
12184 while (i-- > 0)
12185 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
12186 && papszEnvVars[i][cchSpecialEnv] == '=')
12187 {
12188 /* We will expand stuff like @@NAME@@ */
12189 const char *pszValue = papszEnvVars[i];
12190 KSIZE offDst = 0;
12191 char szTmp[1024];
12192 for (;;)
12193 {
12194 const char *pszAt = kHlpStrChr(pszValue, '@');
12195 while (pszAt && pszAt[1] != '@')
12196 pszAt = kHlpStrChr(pszAt + 1, '@');
12197 if (pszAt)
12198 {
12199 KSIZE cchSrc = pszAt - pszValue;
12200 if (offDst + cchSrc < sizeof(szTmp))
12201 {
12202 char szSrc[64];
12203
12204 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12205 offDst += cchSrc;
12206 pszValue = pszAt + 2;
12207
12208 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
12209 {
12210 pszValue += 15;
12211 if (g_iProcessGroup == -1)
12212 g_iProcessGroup = kwGetCurrentProcessorGroup();
12213 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
12214 }
12215 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
12216 {
12217 pszValue += 19;
12218 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
12219 }
12220 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
12221 {
12222 pszValue += 5;
12223 cchSrc = sprintf(szSrc, "%d", getpid());
12224 }
12225 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
12226 {
12227 pszValue += 2;
12228 szSrc[0] = '@';
12229 szSrc[1] = '@';
12230 szSrc[2] = '\0';
12231 cchSrc = 2;
12232 }
12233 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
12234 {
12235 static unsigned int s_iCounter = 0;
12236 pszValue += 15;
12237 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
12238 }
12239 else
12240 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
12241 pszValue - 2);
12242 if (offDst + cchSrc < sizeof(szTmp))
12243 {
12244 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
12245 offDst += cchSrc;
12246 continue;
12247 }
12248 }
12249 }
12250 else
12251 {
12252 KSIZE cchSrc = kHlpStrLen(pszValue);
12253 if (offDst + cchSrc < sizeof(szTmp))
12254 {
12255 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
12256 offDst += cchSrc;
12257 break;
12258 }
12259 }
12260 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
12261 }
12262 szTmp[offDst] = '\0';
12263
12264 /* Return a copy of it: */
12265 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
12266 if (papszEnvVars[i])
12267 {
12268 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
12269 return 0;
12270 }
12271 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
12272 }
12273
12274 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
12275}
12276
12277
12278/**
12279 * Part 2 of the "JOB" command handler.
12280 *
12281 * @returns The exit code of the job.
12282 * @param pszExecutable The executable to execute.
12283 * @param pszCwd The current working directory of the job.
12284 * @param cArgs The number of arguments.
12285 * @param papszArgs The argument vector.
12286 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
12287 * @param cEnvVars The number of environment variables.
12288 * @param papszEnvVars The environment vector.
12289 * @param pszSpecialEnv Name of special environment variable that
12290 * requires selective expansion here.
12291 * @param fNoPchCaching Whether to disable precompiled header file
12292 * caching. Avoid trouble when creating them.
12293 * @param cPostCmdArgs Number of post command arguments (includes cmd).
12294 * @param papszPostCmdArgs The post command and its argument.
12295 */
12296static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
12297 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
12298 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
12299 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
12300{
12301 int rcExit;
12302 PKWTOOL pTool;
12303 char *pszSpecialEnvFree = NULL;
12304
12305 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
12306 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
12307#ifdef KW_LOG_ENABLED
12308 {
12309 KU32 i;
12310 for (i = 0; i < cArgs; i++)
12311 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
12312 for (i = 0; i < cPostCmdArgs; i++)
12313 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
12314 }
12315#endif
12316 g_cJobs++;
12317
12318 /*
12319 * Expand pszSpecialEnv if present.
12320 */
12321 if (pszSpecialEnv && *pszSpecialEnv)
12322 {
12323 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
12324 if (!rcExit)
12325 { /* likely */ }
12326 else
12327 return rcExit;
12328 }
12329
12330 /*
12331 * Lookup the tool.
12332 */
12333 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
12334 if (pTool)
12335 {
12336 /*
12337 * Change the directory if we're going to execute the job inside
12338 * this process. Then invoke the tool type specific handler.
12339 */
12340 switch (pTool->enmType)
12341 {
12342 case KWTOOLTYPE_SANDBOXED:
12343 case KWTOOLTYPE_WATCOM:
12344 {
12345 /* Change dir. */
12346 KFSLOOKUPERROR enmError;
12347 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
12348 if ( pNewCurDir == g_pCurDirObj
12349 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
12350 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12351 else if (SetCurrentDirectoryA(pszCwd))
12352 {
12353 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
12354 g_pCurDirObj = pNewCurDir;
12355 }
12356 else
12357 {
12358 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
12359 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
12360 rcExit = 42 + 1;
12361 break;
12362 }
12363
12364 /* Call specific handler. */
12365 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
12366 {
12367 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
12368 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
12369 cEnvVars, papszEnvVars, fNoPchCaching);
12370 }
12371 else
12372 {
12373 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
12374 rcExit = 42 + 2;
12375 }
12376 break;
12377 }
12378
12379 case KWTOOLTYPE_EXEC:
12380 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
12381 rcExit = 42 + 2;
12382 break;
12383
12384 default:
12385 kHlpAssertFailed();
12386 kwErrPrintf("Internal tool type corruption!!\n");
12387 rcExit = 42 + 2;
12388 g_fRestart = K_TRUE;
12389 break;
12390 }
12391
12392 /*
12393 * Do the post command, if present.
12394 */
12395 if (cPostCmdArgs && rcExit == 0)
12396 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
12397 }
12398 else
12399 rcExit = 42 + 1;
12400 if (pszSpecialEnvFree)
12401 {
12402 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
12403 kHlpFree(pszSpecialEnvFree);
12404 }
12405 return rcExit;
12406}
12407
12408
12409/**
12410 * Handles a "JOB" command.
12411 *
12412 * @returns The exit code of the job.
12413 * @param pszMsg Points to the "JOB" command part of the message.
12414 * @param cbMsg Number of message bytes at @a pszMsg. There are
12415 * 4 more zero bytes after the message body to
12416 * simplify parsing.
12417 */
12418static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
12419{
12420 int rcExit = 42;
12421
12422 /*
12423 * Unpack the message.
12424 */
12425 const char *pszExecutable;
12426 KSIZE cbTmp;
12427
12428 pszMsg += sizeof("JOB");
12429 cbMsg -= sizeof("JOB");
12430
12431 /* Executable name. */
12432 pszExecutable = pszMsg;
12433 cbTmp = kHlpStrLen(pszMsg) + 1;
12434 pszMsg += cbTmp;
12435 if ( cbTmp < cbMsg
12436 && cbTmp > 2)
12437 {
12438 const char *pszCwd;
12439 cbMsg -= cbTmp;
12440
12441 /* Current working directory. */
12442 pszCwd = pszMsg;
12443 cbTmp = kHlpStrLen(pszMsg) + 1;
12444 pszMsg += cbTmp;
12445 if ( cbTmp + sizeof(KU32) < cbMsg
12446 && cbTmp >= 2)
12447 {
12448 KU32 cArgs;
12449 cbMsg -= cbTmp;
12450
12451 /* Argument count. */
12452 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
12453 pszMsg += sizeof(cArgs);
12454 cbMsg -= sizeof(cArgs);
12455
12456 if (cArgs > 0 && cArgs < 4096)
12457 {
12458 /* The argument vector. */
12459 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
12460 if (papszArgs)
12461 {
12462 KU32 i;
12463 for (i = 0; i < cArgs; i++)
12464 {
12465 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
12466 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
12467 pszMsg += cbTmp;
12468 if (cbTmp < cbMsg)
12469 cbMsg -= cbTmp;
12470 else
12471 {
12472 cbMsg = 0;
12473 break;
12474 }
12475
12476 }
12477 papszArgs[cArgs] = 0;
12478
12479 /* Environment variable count. */
12480 if (cbMsg > sizeof(KU32))
12481 {
12482 KU32 cEnvVars;
12483 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
12484 pszMsg += sizeof(cEnvVars);
12485 cbMsg -= sizeof(cEnvVars);
12486
12487 if (cEnvVars >= 0 && cEnvVars < 4096)
12488 {
12489 /* The argument vector. */
12490 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
12491 if (papszEnvVars)
12492 {
12493 for (i = 0; i < cEnvVars; i++)
12494 {
12495 papszEnvVars[i] = pszMsg;
12496 cbTmp = kHlpStrLen(pszMsg) + 1;
12497 pszMsg += cbTmp;
12498 if (cbTmp < cbMsg)
12499 cbMsg -= cbTmp;
12500 else
12501 {
12502 cbMsg = 0;
12503 break;
12504 }
12505 }
12506 papszEnvVars[cEnvVars] = 0;
12507
12508 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
12509 if (cbMsg >= sizeof(KU8) * 2)
12510 {
12511 KBOOL fWatcomBrainDamange = *pszMsg++;
12512 KBOOL fNoPchCaching = *pszMsg++;
12513 cbMsg -= 2;
12514
12515 /* Name of special enviornment variable requiring selective expansion. */
12516 if (cbMsg >= 1)
12517 {
12518 const char *pszSpecialEnv = pszMsg;
12519 cbTmp = kHlpStrLen(pszMsg);
12520 pszMsg += cbTmp + 1;
12521 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
12522
12523 /* Post command argument count (can be zero). */
12524 if (cbMsg >= sizeof(KU32))
12525 {
12526 KU32 cPostCmdArgs;
12527 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
12528 pszMsg += sizeof(cPostCmdArgs);
12529 cbMsg -= sizeof(cPostCmdArgs);
12530
12531 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
12532 {
12533 char const *apszPostCmdArgs[32+1];
12534 for (i = 0; i < cPostCmdArgs; i++)
12535 {
12536 apszPostCmdArgs[i] = pszMsg;
12537 cbTmp = kHlpStrLen(pszMsg) + 1;
12538 pszMsg += cbTmp;
12539 if ( cbTmp < cbMsg
12540 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
12541 cbMsg -= cbTmp;
12542 else
12543 {
12544 cbMsg = KSIZE_MAX;
12545 break;
12546 }
12547 }
12548 if (cbMsg == 0)
12549 {
12550 apszPostCmdArgs[cPostCmdArgs] = NULL;
12551
12552 /*
12553 * The next step.
12554 */
12555 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
12556 cArgs, papszArgs, fWatcomBrainDamange,
12557 cEnvVars, papszEnvVars, pszSpecialEnv,
12558 fNoPchCaching,
12559 cPostCmdArgs, apszPostCmdArgs);
12560 }
12561 else if (cbMsg == KSIZE_MAX)
12562 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
12563 else
12564 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
12565 }
12566 else
12567 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
12568 }
12569 else
12570 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
12571 }
12572 else
12573 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
12574 }
12575 else
12576 kwErrPrintf("Detected bogus message unpacking flags!\n");
12577 kHlpFree((void *)papszEnvVars);
12578 }
12579 else
12580 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
12581 }
12582 else
12583 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
12584 }
12585 else
12586 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
12587 kHlpFree((void *)papszArgs);
12588 }
12589 else
12590 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
12591 }
12592 else
12593 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
12594 }
12595 else
12596 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
12597 }
12598 else
12599 kwErrPrintf("Detected bogus message unpacking executable path!\n");
12600 return rcExit;
12601}
12602
12603
12604/**
12605 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
12606 *
12607 * @retval 0 on success.
12608 * @retval -1 on error (fully bitched).
12609 *
12610 * @param hPipe The pipe handle.
12611 * @param pvBuf The buffer to write out out.
12612 * @param cbToWrite The number of bytes to write.
12613 */
12614static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
12615{
12616 KU8 const *pbBuf = (KU8 const *)pvBuf;
12617 KU32 cbLeft = cbToWrite;
12618 while (g_rcCtrlC == 0)
12619 {
12620 DWORD cbActuallyWritten = 0;
12621 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
12622 {
12623 cbLeft -= cbActuallyWritten;
12624 if (!cbLeft)
12625 return 0;
12626 pbBuf += cbActuallyWritten;
12627 }
12628 else
12629 {
12630 DWORD dwErr = GetLastError();
12631 if (cbLeft == cbToWrite)
12632 kwErrPrintf("WriteFile failed: %u\n", dwErr);
12633 else
12634 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
12635 return -1;
12636 }
12637 }
12638 return -1;
12639}
12640
12641
12642/**
12643 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
12644 *
12645 * @retval 0 on success.
12646 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
12647 * @retval -1 on error (fully bitched).
12648 * @param hPipe The pipe handle.
12649 * @param pvBuf The buffer to read into.
12650 * @param cbToRead The number of bytes to read.
12651 * @param fShutdownOkay Whether connection shutdown while reading the
12652 * first byte is okay or not.
12653 */
12654static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
12655{
12656 KU8 *pbBuf = (KU8 *)pvBuf;
12657 KU32 cbLeft = cbToRead;
12658 while (g_rcCtrlC == 0)
12659 {
12660 DWORD cbActuallyRead = 0;
12661 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
12662 {
12663 cbLeft -= cbActuallyRead;
12664 if (!cbLeft)
12665 return 0;
12666 pbBuf += cbActuallyRead;
12667 }
12668 else
12669 {
12670 DWORD dwErr = GetLastError();
12671 if (cbLeft == cbToRead)
12672 {
12673 if ( fMayShutdown
12674 && dwErr == ERROR_BROKEN_PIPE)
12675 return 1;
12676 kwErrPrintf("ReadFile failed: %u\n", dwErr);
12677 }
12678 else
12679 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
12680 return -1;
12681 }
12682 }
12683 return -1;
12684}
12685
12686
12687/**
12688 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
12689 *
12690 * @returns pszBuf
12691 * @param pszBuf The buffer (sufficiently large).
12692 * @param uValue The value.
12693 */
12694static const char *kwFmtU64(char *pszBuf, KU64 uValue)
12695{
12696 char szTmp[64];
12697 char *psz = &szTmp[63];
12698 int cch = 4;
12699
12700 *psz-- = '\0';
12701 do
12702 {
12703 if (--cch == 0)
12704 {
12705 *psz-- = ' ';
12706 cch = 3;
12707 }
12708 *psz-- = (uValue % 10) + '0';
12709 uValue /= 10;
12710 } while (uValue != 0);
12711
12712 return strcpy(pszBuf, psz + 1);
12713}
12714
12715
12716/**
12717 * Prints statistics.
12718 */
12719static void kwPrintStats(void)
12720{
12721 PROCESS_MEMORY_COUNTERS_EX MemInfo;
12722 MEMORYSTATUSEX MemStatus;
12723 IO_COUNTERS IoCounters;
12724 DWORD cHandles;
12725 KSIZE cMisses;
12726 char szBuf[16*1024];
12727 int off = 0;
12728 char szPrf[24];
12729 char sz1[64];
12730 char sz2[64];
12731 char sz3[64];
12732 char sz4[64];
12733
12734 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
12735
12736 szBuf[off++] = '\n';
12737
12738 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
12739 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
12740 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
12741 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
12742 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
12743
12744 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
12745 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
12746
12747 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
12748 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
12749 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
12750
12751 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
12752 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
12753 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
12754
12755 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
12756 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
12757 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
12758 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
12759 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
12760 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
12761 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
12762
12763 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
12764 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
12765 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
12766 kwFmtU64(sz1, g_pFsCache->cObjects),
12767 kwFmtU64(sz2, g_pFsCache->cbObjects),
12768 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
12769 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
12770 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
12771 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
12772 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
12773 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
12774#ifdef KFSCACHE_CFG_UTF16
12775 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
12776 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
12777 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
12778 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
12779 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
12780#endif
12781 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
12782 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
12783 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
12784 kwFmtU64(sz3, g_pFsCache->cChildHashed),
12785 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
12786
12787 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
12788 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
12789 kwFmtU64(sz1, g_pFsCache->cLookups),
12790 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
12791 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12792 kwFmtU64(sz3, g_pFsCache->cWalkHits),
12793 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
12794 kwFmtU64(sz4, cMisses),
12795 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
12796
12797 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
12798 kwFmtU64(sz1, g_pFsCache->cChildSearches),
12799 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
12800 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
12801 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
12802 kwFmtU64(sz1, g_pFsCache->cNameChanges),
12803 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
12804 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
12805
12806#ifdef WITH_HASH_CACHE
12807 off += sprintf(&szBuf[off], "%s %14s hashes calculated, %s cache hits (%u%%), %s fallbacks, %s partial\n", szPrf,
12808 kwFmtU64(sz1, g_cHashes),
12809 kwFmtU64(sz2, g_cHashesCached),
12810 (unsigned)(g_cHashesCached * 100 / K_MAX(g_cHashes, 1)),
12811 kwFmtU64(sz3, g_cHashesFallbacks),
12812 kwFmtU64(sz4, g_cHashesPartial));
12813 off += sprintf(&szBuf[off], "%s %14s MD5: %s SHA-1: %s SHA-256: %s SHA-512: %s\n", szPrf, "", kwFmtU64(sz1, g_cHashesMd5),
12814 kwFmtU64(sz2, g_cHashesSha1), kwFmtU64(sz3, g_cHashesSha256), kwFmtU64(sz4, g_cHashesSha512));
12815#endif
12816
12817 /*
12818 * Process & Memory details.
12819 */
12820 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
12821 cHandles = 0;
12822 MemInfo.cb = sizeof(MemInfo);
12823 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
12824 memset(&MemInfo, 0, sizeof(MemInfo));
12825 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
12826 kwFmtU64(sz1, cHandles),
12827 kwFmtU64(sz2, MemInfo.PageFaultCount),
12828 kwFmtU64(sz3, MemInfo.PagefileUsage),
12829 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
12830 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
12831 kwFmtU64(sz1, MemInfo.WorkingSetSize),
12832 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
12833 kwFmtU64(sz3, MemInfo.PrivateUsage));
12834 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
12835 szPrf,
12836 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
12837 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
12838 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
12839 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
12840
12841 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
12842 memset(&IoCounters, 0, sizeof(IoCounters));
12843 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
12844 kwFmtU64(sz1, IoCounters.ReadTransferCount),
12845 kwFmtU64(sz2, IoCounters.ReadOperationCount));
12846 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
12847 kwFmtU64(sz1, IoCounters.WriteTransferCount),
12848 kwFmtU64(sz2, IoCounters.WriteOperationCount));
12849 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
12850 kwFmtU64(sz1, IoCounters.OtherTransferCount),
12851 kwFmtU64(sz2, IoCounters.OtherOperationCount));
12852
12853 MemStatus.dwLength = sizeof(MemStatus);
12854 if (!GlobalMemoryStatusEx(&MemStatus))
12855 memset(&MemStatus, 0, sizeof(MemStatus));
12856 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
12857 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
12858 MemStatus.ullAvailVirtual);
12859 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
12860
12861 maybe_con_fwrite(szBuf, off, 1, stdout);
12862 fflush(stdout);
12863}
12864
12865
12866/**
12867 * Handles what comes after --test.
12868 *
12869 * @returns Exit code.
12870 * @param argc Number of arguments after --test.
12871 * @param argv Arguments after --test.
12872 */
12873static int kwTestRun(int argc, char **argv)
12874{
12875 int i;
12876 int j;
12877 int rcExit;
12878 int cRepeats;
12879 char szCwd[MAX_PATH];
12880 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
12881 KU32 cEnvVars;
12882 char **papszEnvVars;
12883 const char *pszSpecialEnv = "";
12884 const char *pszSpecialEnvFull = NULL;
12885 KBOOL fWatcomBrainDamange = K_FALSE;
12886 KBOOL fNoPchCaching = K_FALSE;
12887
12888 /*
12889 * Parse arguments.
12890 */
12891 /* Repeat count. */
12892 i = 0;
12893 if (i >= argc)
12894 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
12895 if (strcmp(argv[i], "--") != 0)
12896 {
12897 cRepeats = atoi(argv[i]);
12898 if (cRepeats <= 0)
12899 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
12900 i++;
12901
12902 /* Optional directory change. */
12903 if ( i < argc
12904 && ( strcmp(argv[i], "--chdir") == 0
12905 || strcmp(argv[i], "-C") == 0 ) )
12906 {
12907 i++;
12908 if (i >= argc)
12909 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
12910 pszCwd = argv[i++];
12911 }
12912
12913 /* Optional watcom flag directory change. */
12914 if ( i < argc
12915 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
12916 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
12917 {
12918 fWatcomBrainDamange = K_TRUE;
12919 i++;
12920 }
12921
12922 /* Optional watcom flag directory change. */
12923 if ( i < argc
12924 && strcmp(argv[i], "--no-pch-caching") == 0)
12925 {
12926 fNoPchCaching = K_TRUE;
12927 i++;
12928 }
12929
12930 /* Optional directory change. */
12931 if ( i < argc
12932 && ( strcmp(argv[i], "--set-special") == 0
12933 || strcmp(argv[i], "-s") == 0 ) )
12934 {
12935 i++;
12936 if (i >= argc)
12937 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
12938 pszSpecialEnvFull = argv[i++];
12939 putenv(pszSpecialEnvFull);
12940 pszSpecialEnv = strdup(pszSpecialEnvFull);
12941 *strchr(pszSpecialEnv, '=') = '\0';
12942 }
12943
12944 /* Trigger breakpoint */
12945 if ( i < argc
12946 && strcmp(argv[i], "--breakpoint") == 0)
12947 {
12948 __debugbreak();
12949 i++;
12950 }
12951
12952 /* Check for '--'. */
12953 if (i >= argc)
12954 return kwErrPrintfRc(2, "Missing '--'\n");
12955 if (strcmp(argv[i], "--") != 0)
12956 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
12957 i++;
12958 }
12959 else
12960 {
12961 cRepeats = 1;
12962 i++;
12963 }
12964 if (i >= argc)
12965 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
12966
12967 /*
12968 * Duplicate the environment.
12969 */
12970 cEnvVars = 0;
12971 while (environ[cEnvVars] != NULL)
12972 cEnvVars++;
12973 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
12974
12975 /*
12976 * Do the job.
12977 */
12978 rcExit = 0;
12979 for (j = 0; j < cRepeats; j++)
12980 {
12981 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
12982 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
12983 argc - i, &argv[i], fWatcomBrainDamange,
12984 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
12985 0, NULL);
12986 KW_LOG(("rcExit=%d\n", rcExit));
12987 kwSandboxCleanupLate(&g_Sandbox);
12988 }
12989
12990 if (getenv("KWORKER_STATS") != NULL)
12991 kwPrintStats();
12992
12993# ifdef WITH_LOG_FILE
12994 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12995 CloseHandle(g_hLogFile);
12996# endif
12997 return rcExit;
12998}
12999
13000
13001/**
13002 * Reads @a pszFile into memory and chops it up into an argument vector.
13003 *
13004 * @returns Pointer to the argument vector on success, NULL on failure.
13005 * @param pszFile The file to load.
13006 * @param pcArgs Where to return the number of arguments.
13007 * @param ppszFileContent Where to return the allocation.
13008 */
13009static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
13010{
13011 char **papszArgs = NULL;
13012 FILE *pFile = fopen(pszFile, "r");
13013 if (pFile)
13014 {
13015 long cbFile;
13016 if ( fseek(pFile, 0, SEEK_END) == 0
13017 && (cbFile = ftell(pFile)) >= 0
13018 && fseek(pFile, 0, SEEK_SET) == 0)
13019 {
13020 char *pszFile = kHlpAllocZ(cbFile + 3);
13021 if (pszFile)
13022 {
13023 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
13024 if ( feof(pFile)
13025 && !ferror(pFile))
13026 {
13027 size_t off = 0;
13028 int cArgs = 0;
13029 int cAllocated = 0;
13030 char ch;
13031
13032 pszFile[cbRead] = '\0';
13033 pszFile[cbRead + 1] = '\0';
13034 pszFile[cbRead + 2] = '\0';
13035
13036 while ((ch = pszFile[off]) != '\0')
13037 {
13038 char *pszArg;
13039 switch (ch)
13040 {
13041 case ' ':
13042 case '\t':
13043 case '\n':
13044 case '\r':
13045 off++;
13046 continue;
13047
13048 case '\\':
13049 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
13050 {
13051 off += 2;
13052 continue;
13053 }
13054 /* fall thru */
13055 default:
13056 pszArg = &pszFile[off];
13057 do
13058 ch = pszFile[++off];
13059 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
13060 pszFile[off++] = '\0';
13061 break;
13062
13063 case '\'':
13064 pszArg = &pszFile[++off];
13065 while ((ch = pszFile[off]) != '\0' && ch != '\'')
13066 off++;
13067 pszFile[off++] = '\0';
13068 break;
13069
13070 case '\"': /** @todo escape sequences */
13071 pszArg = &pszFile[++off];
13072 while ((ch = pszFile[off]) != '\0' && ch != '"')
13073 off++;
13074 pszFile[off++] = '\0';
13075 break;
13076 }
13077 if (cArgs + 1 >= cAllocated)
13078 {
13079 void *pvNew;
13080 cAllocated = cAllocated ? cAllocated * 2 : 16;
13081 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
13082 if (pvNew)
13083 papszArgs = (char **)pvNew;
13084 else
13085 {
13086 kHlpFree(papszArgs);
13087 papszArgs = NULL;
13088 break;
13089 }
13090 }
13091 papszArgs[cArgs] = pszArg;
13092 papszArgs[++cArgs] = NULL;
13093 }
13094 *pcArgs = cArgs;
13095 }
13096 else
13097 kwErrPrintf("Error reading '%s'!\n", pszFile);
13098 }
13099 else
13100 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
13101 }
13102 else
13103 kwErrPrintf("Error seeking '%s'!\n", pszFile);
13104 fclose(pFile);
13105 }
13106 else
13107 kwErrPrintf("Error opening '%s'!\n", pszFile);
13108 return papszArgs;
13109}
13110
13111/**
13112 * Appends a string to an string vector (arguments or enviornment).
13113 *
13114 * @returns 0 on success, non-zero on failure (exit code).
13115 * @param ppapszVector Pointer to the string pointer array.
13116 * @param pcEntries Pointer to the array size.
13117 * @param pszAppend The string to append.
13118 */
13119static int kwFullTestVectorAppend(const char ***ppapszVector, KU32 *pcEntries, char const *pszAppend)
13120{
13121 KU32 cEntries = *pcEntries;
13122 if (!(cEntries & 15))
13123 {
13124 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
13125 if (pvNew)
13126 *ppapszVector = (const char **)pvNew;
13127 else
13128 return kwErrPrintfRc(2, "Out of memory!\n");
13129 }
13130 (*ppapszVector)[cEntries] = pszAppend;
13131 (*ppapszVector)[++cEntries] = NULL;
13132 *pcEntries = cEntries;
13133 return 0;
13134}
13135
13136
13137/**
13138 * Parses arguments for --full-test.
13139 *
13140 * @returns 0 on success, non-zero on failure (exit code).
13141 */
13142static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
13143 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
13144{
13145 PKWONETEST pCur = *ppHead;
13146 int i;
13147 for (i = 0; i < argc; i++)
13148 {
13149 int rc = 0;
13150 const char *pszArg = argv[i];
13151 if (*pszArg == 'k')
13152 {
13153 if (kHlpStrComp(pszArg, "kSubmit") == 0)
13154 {
13155 if (*piState != 0)
13156 {
13157 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
13158 if (!pCur)
13159 return kwErrPrintfRc(2, "Out of memory!\n");
13160 pCur->fVirgin = K_TRUE;
13161 pCur->pszCwd = pszDefaultCwd;
13162 pCur->cRuns = 1;
13163 pCur->pNext = *ppHead;
13164 *ppHead = pCur;
13165 *piState = 0;
13166 }
13167 else if (!pCur->fVirgin)
13168 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
13169 pCur->pszJobSrc = pszJobSrc;
13170 pCur->iJobSrc = i;
13171 continue; /* (to stay virgin) */
13172 }
13173
13174 /* Ignore "kWorker 378172/62:" sequences that kmk/kSubmit spews out on failure. */
13175 if ( kHlpStrComp(pszArg, "kWorker") == 0
13176 && i + 1 < argc
13177 && (unsigned)(argv[i + 1][0] - '0') <= 9)
13178 {
13179 i++;
13180 continue;
13181 }
13182 }
13183
13184 if ( *pszArg == '-'
13185 && ( *piState == 0
13186 || pszArg[1] == '@'))
13187 {
13188 const char *pszValue = NULL;
13189 char ch = *++pszArg;
13190 pszArg++;
13191 if (ch == '-')
13192 {
13193 ch = '\0';
13194 if (*pszArg == '\0') /* -- */
13195 *piState = 2;
13196 /* Translate or handle long options: */
13197 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
13198 ch = 'E';
13199 else if (kHlpStrComp(pszArg, "special-env") == 0)
13200 ch = 's';
13201 else if (kHlpStrComp(pszArg, "default-env") == 0)
13202 {
13203 unsigned i;
13204 pCur->cEnvVars = 0;
13205 for (i = 0; environ[i] && rc == 0; i++)
13206 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
13207 }
13208 else if (kHlpStrComp(pszArg, "chdir") == 0)
13209 ch = 'C';
13210 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
13211 ch = 'P';
13212 else if (kHlpStrComp(pszArg, "response-file") == 0)
13213 ch = '@';
13214 else if (kHlpStrComp(pszArg, "runs") == 0)
13215 ch = 'R';
13216 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
13217 pCur->fWatcomBrainDamange = K_TRUE;
13218 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
13219 pCur->fNoPchCaching = K_TRUE;
13220 else if (kHlpStrComp(pszArg, "executable") == 0)
13221 ch = 'e';
13222 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
13223 {
13224 __debugbreak();
13225 continue; /* (to stay virgin) */
13226 }
13227 else
13228 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
13229 pszArg = "";
13230 }
13231
13232 while (ch != '\0' && rc == 0)
13233 {
13234 /* Fetch value if needed: */
13235 switch (ch)
13236 {
13237 case '@':
13238 case 'e':
13239 case 'E':
13240 case 's':
13241 case 'C':
13242 case 'R':
13243 if (*pszArg == ':' || *pszArg == '=')
13244 pszValue = &pszArg[1];
13245 else if (*pszArg)
13246 pszValue = pszArg;
13247 else if (i + 1 < argc)
13248 pszValue = argv[++i];
13249 else
13250 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
13251 pszArg = "";
13252 break;
13253 }
13254
13255 /* Handle the option: */
13256 switch (ch)
13257 {
13258 case 'E':
13259 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
13260 break;
13261 case 'C':
13262 pCur->pszCwd = pszValue;
13263 break;
13264 case 's':
13265 pCur->pszSpecialEnv = pszValue;
13266 break;
13267 case 'e':
13268 pCur->pszExecutable = pszValue;
13269 break;
13270 case 'P':
13271 *piState = 1;
13272 if (*pszArg)
13273 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
13274 break;
13275 case 'R':
13276 pCur->cRuns = atoi(pszValue);
13277 if ((int)pCur->cRuns < 0)
13278 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
13279 break;
13280 case '@':
13281 if (cRecursions < 5)
13282 {
13283 char *pszLeaked = NULL;
13284 int cArgs = 0;
13285 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
13286 if (papszArgsLeaked)
13287 {
13288 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
13289 cRecursions + 1, pszValue);
13290 pCur = *ppHead;
13291 }
13292 else
13293 return 2;
13294 }
13295 else
13296 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
13297 break;
13298 }
13299
13300 /* next */
13301 ch = *pszArg++;
13302 }
13303 }
13304 else if (*piState == 2)
13305 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
13306 else if (*piState == 1)
13307 {
13308 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
13309 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
13310 else
13311 *piState = 2;
13312 }
13313 else
13314 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
13315 if (rc)
13316 return rc;
13317 pCur->fVirgin = K_FALSE;
13318 }
13319 return 0;
13320}
13321
13322
13323/**
13324 * Handles what comes after --full-test.
13325 *
13326 * @returns Exit code.
13327 * @param argc Number of arguments after --full-test.
13328 * @param argv Arguments after --full-test.
13329 */
13330static int kwFullTestRun(int argc, char **argv)
13331{
13332 char szDefaultCwd[MAX_PATH];
13333 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
13334 KWONETEST FirstTest;
13335 PKWONETEST pHead = &FirstTest;
13336 PKWONETEST pCur;
13337 int iState = 0;
13338 int rcExit;
13339
13340 /*
13341 * Parse arguments.
13342 */
13343 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
13344 FirstTest.pszJobSrc = "command-line";
13345 FirstTest.iJobSrc = 1;
13346 FirstTest.fVirgin = K_TRUE;
13347 FirstTest.pszCwd = pszDefaultCwd;
13348 FirstTest.cRuns = 1;
13349
13350 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
13351 if (rcExit)
13352 return rcExit;
13353
13354 /*
13355 * Do the job. LIFO ordering (see kSubmit).
13356 */
13357 for (pCur = pHead; pCur; pCur = pCur->pNext)
13358 {
13359 if (!pCur->pszExecutable && pCur->papszArgs)
13360 pCur->pszExecutable = pCur->papszArgs[0];
13361 if ( pCur->pszExecutable
13362 && pCur->cArgs > 0
13363 && pCur->cEnvVars > 0)
13364 {
13365 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
13366 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
13367 unsigned iRun;
13368
13369 for (iRun = 0; iRun < pCur->cRuns; iRun++)
13370 {
13371 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
13372 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
13373 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
13374 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
13375
13376 KW_LOG(("rcExit=%d\n", rcExit));
13377 kwSandboxCleanupLate(&g_Sandbox);
13378
13379 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
13380 }
13381 kHlpFree(papszEnvVarsCopy);
13382 }
13383 else
13384 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
13385 pCur->pszExecutable ? "" : " No executable!",
13386 pCur->cArgs < 1 ? " No arguments!" : "",
13387 pCur->cEnvVars < 1 ? " No environment!" : "",
13388 pCur->iJobSrc, pCur->pszJobSrc);
13389 }
13390
13391 if (getenv("KWORKER_STATS") != NULL)
13392 kwPrintStats();
13393
13394# ifdef WITH_LOG_FILE
13395 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13396 CloseHandle(g_hLogFile);
13397# endif
13398 return rcExit;
13399}
13400
13401
13402/**
13403 * Helper for main() argument handling that sets the processor group if
13404 * possible.
13405 */
13406static void kwSetProcessorGroup(unsigned long uGroup)
13407{
13408 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
13409 HMODULE const hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
13410 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
13411
13412 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "SetThreadGroupAffinity");
13413 if (pfnSetThreadGroupAffinity)
13414 {
13415 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
13416 GROUP_AFFINITY NewAff = { 0, (WORD)uGroup, 0, 0, 0 };
13417 NewAff.Mask = win_get_processor_group_active_mask((WORD)uGroup);
13418 if (NewAff.Mask && (WORD)uGroup == uGroup)
13419 {
13420 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
13421 kwErrPrintf("Failed to set processor group to %lu (%p): %u\n", uGroup, NewAff.Mask, GetLastError());
13422 }
13423 else if (GetLastError() == NO_ERROR)
13424 kwErrPrintf("Cannot set processor group to %lu: No active processors in group!\n", uGroup);
13425 else
13426 kwErrPrintf("Cannot set processor group to %lu: GetLogicalProcessorInformationEx failed: %u\n",
13427 uGroup, GetLastError());
13428 }
13429 else
13430 {
13431 OSVERSIONINFOA VerInfo = {0};
13432 if (VerInfo.dwMajorVersion > 6 || (VerInfo.dwMajorVersion == 6 && VerInfo.dwMinorVersion >= 1))
13433 kwErrPrintf("Cannot set processor group to %lu: SetThreadGroupAffinity no found! (Windows version %lu.%lu)\n",
13434 uGroup, VerInfo.dwMajorVersion, VerInfo.dwMinorVersion);
13435 }
13436}
13437
13438
13439int main(int argc, char **argv)
13440{
13441 KSIZE cbMsgBuf = 0;
13442 KU8 *pbMsgBuf = NULL;
13443 int i;
13444 HANDLE hPipe = INVALID_HANDLE_VALUE;
13445 const char *pszTmp;
13446 KFSLOOKUPERROR enmIgnored;
13447 DWORD dwType;
13448#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
13449 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
13450 kwSandboxVecXcptEmulateChained);
13451#endif
13452#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13453 HANDLE hCurProc = GetCurrentProcess();
13454 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
13455 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
13456#endif
13457
13458
13459#ifdef WITH_FIXED_VIRTUAL_ALLOCS
13460 /*
13461 * Reserve memory for cl.exe
13462 */
13463 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
13464 {
13465 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
13466 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
13467 g_aFixedVirtualAllocs[i].cbFixed,
13468 MEM_RESERVE, PAGE_READWRITE);
13469 if ( !g_aFixedVirtualAllocs[i].pvReserved
13470 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
13471 {
13472 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
13473 GetLastError());
13474 if (g_aFixedVirtualAllocs[i].pvReserved)
13475 {
13476 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
13477 g_aFixedVirtualAllocs[i].pvReserved = NULL;
13478 }
13479 }
13480 }
13481#endif
13482
13483 /*
13484 * Register our Control-C and Control-Break handlers.
13485 */
13486 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
13487 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
13488
13489 /*
13490 * Create the cache and mark the temporary directory as using the custom revision.
13491 */
13492 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
13493 if (!g_pFsCache)
13494 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
13495
13496 pszTmp = getenv("TEMP");
13497 if (pszTmp && *pszTmp != '\0')
13498 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13499 pszTmp = getenv("TMP");
13500 if (pszTmp && *pszTmp != '\0')
13501 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13502 pszTmp = getenv("TMPDIR");
13503 if (pszTmp && *pszTmp != '\0')
13504 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
13505
13506 /*
13507 * Make g_abDefLdBuf executable.
13508 */
13509 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
13510 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
13511 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
13512 InitializeCriticalSection(&g_Sandbox.HandlesLock);
13513 InitializeCriticalSection(&g_Sandbox.VirtualAllocLock);
13514
13515#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
13516 /*
13517 * Get and duplicate the console handles.
13518 */
13519 /* Standard output. */
13520 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
13521 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
13522 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13523 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
13524 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
13525 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
13526 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13527 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13528 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
13529 g_Sandbox.HandleStdOut.cRefs = 0x10001;
13530 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
13531 g_Sandbox.HandleStdOut.tidOwner = KU32_MAX;
13532 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
13533 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
13534 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
13535 {
13536 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
13537 g_Sandbox.cFixedHandles++;
13538 else
13539 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
13540 }
13541 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13542 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
13543
13544 /* Standard error. */
13545 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
13546 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
13547 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
13548 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
13549 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
13550 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
13551 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
13552 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
13553 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
13554 g_Sandbox.HandleStdErr.cRefs = 0x10001;
13555 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
13556 g_Sandbox.HandleStdErr.tidOwner = KU32_MAX;
13557 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
13558 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
13559 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
13560 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
13561 {
13562 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
13563 g_Sandbox.cFixedHandles++;
13564 else
13565 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
13566 }
13567 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
13568 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
13569
13570 /* Combined console buffer. */
13571 if (g_Sandbox.StdErr.fIsConsole)
13572 {
13573 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
13574 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13575 }
13576 else if (g_Sandbox.StdOut.fIsConsole)
13577 {
13578 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
13579 g_Sandbox.Combined.uCodepage = GetConsoleCP();
13580 }
13581 else
13582 {
13583 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
13584 g_Sandbox.Combined.uCodepage = CP_ACP;
13585 }
13586 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
13587#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
13588
13589
13590 /*
13591 * Parse arguments.
13592 */
13593 for (i = 1; i < argc; i++)
13594 {
13595 if (strcmp(argv[i], "--pipe") == 0)
13596 {
13597 i++;
13598 if (i < argc)
13599 {
13600 char *pszEnd = NULL;
13601 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
13602 if ( *argv[i]
13603 && pszEnd != NULL
13604 && *pszEnd == '\0'
13605 && u64Value != 0
13606 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
13607 && (uintptr_t)u64Value == u64Value)
13608 hPipe = (HANDLE)(uintptr_t)u64Value;
13609 else
13610 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
13611 }
13612 else
13613 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
13614 }
13615 else if (strcmp(argv[i], "--volatile") == 0)
13616 {
13617 i++;
13618 if (i < argc)
13619 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
13620 else
13621 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
13622 }
13623 else if (strcmp(argv[i], "--test") == 0)
13624 return kwTestRun(argc - i - 1, &argv[i + 1]);
13625 else if (strcmp(argv[i], "--full-test") == 0)
13626 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
13627 else if (strcmp(argv[i], "--priority") == 0)
13628 {
13629 i++;
13630 if (i < argc)
13631 {
13632 char *pszEnd = NULL;
13633 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13634 if ( *argv[i]
13635 && pszEnd != NULL
13636 && *pszEnd == '\0'
13637 && uValue >= 1
13638 && uValue <= 5)
13639 {
13640 DWORD dwClass;
13641 int dwPriority;
13642 switch (uValue)
13643 {
13644 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
13645 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
13646 default:
13647 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
13648 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13649 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = INT_MAX; break;
13650 }
13651 SetPriorityClass(GetCurrentProcess(), dwClass);
13652 if (dwPriority != INT_MAX)
13653 SetThreadPriority(GetCurrentThread(), dwPriority);
13654 }
13655 else
13656 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13657 }
13658 else
13659 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13660 }
13661 else if (strcmp(argv[i], "--group") == 0)
13662 {
13663 i++;
13664 if (i < argc)
13665 {
13666 char *pszEnd = NULL;
13667 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
13668 if ( *argv[i]
13669 && pszEnd != NULL
13670 && *pszEnd == '\0'
13671 && uValue == (WORD)uValue)
13672 kwSetProcessorGroup(uValue);
13673 else
13674 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
13675 }
13676 else
13677 return kwErrPrintfRc(2, "--priority takes an argument!\n");
13678 }
13679 else if ( strcmp(argv[i], "--verbose") == 0
13680 || strcmp(argv[i], "-v") == 0)
13681 g_cVerbose++;
13682 else if ( strcmp(argv[i], "--help") == 0
13683 || strcmp(argv[i], "-h") == 0
13684 || strcmp(argv[i], "-?") == 0)
13685 {
13686 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
13687 "usage: kWorker <--help|-h>\n"
13688 "usage: kWorker <--version|-V>\n"
13689 "usage: kWorker [--volatile dir] --full-test kSubmit ...\n"
13690 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
13691 "\n"
13692 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
13693 return 0;
13694 }
13695 else if ( strcmp(argv[i], "--version") == 0
13696 || strcmp(argv[i], "-V") == 0)
13697 return kbuild_version(argv[0]);
13698 else
13699 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
13700 }
13701
13702 /*
13703 * If no --pipe argument, then assume its standard input.
13704 * We need to carefully replace the CRT stdin with a handle to "nul".
13705 */
13706 if (hPipe == INVALID_HANDLE_VALUE)
13707 {
13708 hPipe = GetStdHandle(STD_INPUT_HANDLE);
13709 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
13710 {
13711 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
13712 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
13713 {
13714 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
13715 if (fdNul >= 0)
13716 {
13717 if (_dup2(fdNul, 0) >= 0)
13718 {
13719 close(fdNul);
13720 hPipe = hDuplicate;
13721 }
13722 else
13723 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13724 }
13725 else
13726 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13727 }
13728 else
13729 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
13730 }
13731 else
13732 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
13733 GetFileType(hPipe), GetLastError());
13734 }
13735 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
13736 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
13737 GetFileType(hPipe), GetLastError());
13738 g_hPipe = hPipe;
13739
13740 /*
13741 * Serve the pipe.
13742 */
13743 for (;;)
13744 {
13745 KU32 cbMsg = 0;
13746 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
13747 if (rc == 0)
13748 {
13749 /* Make sure the message length is within sane bounds. */
13750 if ( cbMsg > 4
13751 && cbMsg <= 256*1024*1024)
13752 {
13753 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
13754 if (cbMsg + 4 <= cbMsgBuf)
13755 { /* likely */ }
13756 else
13757 {
13758 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
13759 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
13760 if (!pbMsgBuf)
13761 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
13762 }
13763
13764 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
13765 *(KU32 *)pbMsgBuf = cbMsg;
13766 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
13767 if (rc == 0)
13768 {
13769 const char *psz;
13770
13771 pbMsgBuf[cbMsg] = '\0';
13772 pbMsgBuf[cbMsg + 1] = '\0';
13773 pbMsgBuf[cbMsg + 2] = '\0';
13774 pbMsgBuf[cbMsg + 3] = '\0';
13775
13776 /* The first string after the header is the command. */
13777 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
13778 if ( strcmp(psz, "JOB") == 0
13779 && g_rcCtrlC == 0)
13780 {
13781 struct
13782 {
13783 KI32 rcExitCode;
13784 KU8 bExiting;
13785 KU8 abZero[3];
13786 } Reply;
13787 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
13788 Reply.bExiting = g_fRestart;
13789 Reply.abZero[0] = 0;
13790 Reply.abZero[1] = 0;
13791 Reply.abZero[2] = 0;
13792 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
13793 if ( rc == 0
13794 && !g_fRestart)
13795 {
13796 kwSandboxCleanupLate(&g_Sandbox);
13797 if (g_rcCtrlC == 0)
13798 continue;
13799 }
13800 }
13801 else
13802 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
13803 }
13804 }
13805 else
13806 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
13807 }
13808
13809 /*
13810 * If we're exitting because we're restarting, we need to delay till
13811 * kmk/kSubmit has read the result. Windows documentation says it
13812 * immediately discards pipe buffers once the pipe is broken by the
13813 * server (us). So, We flush the buffer and queues a 1 byte read
13814 * waiting for kSubmit to close the pipe when it receives the
13815 * bExiting = K_TRUE result.
13816 */
13817 if (g_fRestart)
13818 {
13819 DWORD cbIgnored = 1;
13820 KU8 b;
13821 FlushFileBuffers(hPipe);
13822 ReadFile(hPipe, &b, 1, &cbIgnored, NULL);
13823 }
13824
13825 CloseHandle(hPipe);
13826#ifdef WITH_LOG_FILE
13827 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
13828 CloseHandle(g_hLogFile);
13829#endif
13830 if (getenv("KWORKER_STATS") != NULL)
13831 kwPrintStats();
13832 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
13833 }
13834}
13835
13836
13837/** @page pg_kWorker kSubmit / kWorker
13838 *
13839 * @section sec_kWorker_Motivation Motivation / Inspiration
13840 *
13841 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
13842 * builds on machines "infested" by Anti Virus protection and disk encryption
13843 * software. Build times jumping from 35-40 min to 77-82 min after the machine
13844 * got "infected".
13845 *
13846 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
13847 * mainly a bunch of tiny assembly and C files being compiler a million times.
13848 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
13849 * own toolchain from within the same process, saving a lot of process creation
13850 * and teardown overhead.
13851 *
13852 *
13853 * @section sec_kWorker_kSubmit About kSubmit
13854 *
13855 * When wanting to execute a job in a kWorker instance, it must be submitted
13856 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
13857 * built into kmk and does not exist as an external program. The reason for
13858 * this is that it keep track of the kWorker instances.
13859 *
13860 * The kSubmit command has the --32-bit and --64-bit options for selecting
13861 * between 32-bit and 64-bit worker instance. We generally assume the user of
13862 * the command knows which bit count the executable has, so kSubmit is spared
13863 * the extra work of finding out.
13864 *
13865 * The kSubmit command shares a environment and current directory manipulation
13866 * with the kRedirect command, but not the file redirection. So long no file
13867 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
13868 * hand for tools like OpenWatcom, NASM and YASM which all require environment
13869 * and/or current directory changes to work.
13870 *
13871 * Unlike the kRedirect command, the kSubmit command can also specify an
13872 * internall post command to be executed after the main command succeeds.
13873 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
13874 * information from Microsoft COFF object files and Watcom OMF object files and
13875 * is scheduled to replace kDepIDB.
13876 *
13877 *
13878 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
13879 *
13880 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
13881 * A job request is written by kSubmit and kWorker read, unpacks it and executes
13882 * it. When the job is completed, kWorker writes a short reply with the exit
13883 * code and an internal status indicating whether it is going to restart.
13884 *
13885 * The kWorker intance will reply to kSubmit before completing all the internal
13886 * cleanup work, so as not to delay the next job execution unnecessarily. This
13887 * includes checking its own memory consumption and checking whether it needs
13888 * restarting. So, a decision to restart unfortunately have to wait till after
13889 * the next job has completed. This is a little bit unfortunate if the next job
13890 * requires a lot of memory and kWorker has already leaked/used a lot.
13891 *
13892 *
13893 * @section sec_kWorker_How_Works How kWorker Works
13894 *
13895 * kWorker will load the executable specified by kSubmit into memory and call
13896 * it's entrypoint in a lightly sandbox'ed environment.
13897 *
13898 *
13899 * @subsection ssec_kWorker_Loaing Image loading
13900 *
13901 * kWorker will manually load all the executable images into memory, fix them
13902 * up, and make a copy of the virgin image so it can be restored using memcpy
13903 * the next time it is used.
13904 *
13905 * Imported functions are monitored and replacements used for a few of them.
13906 * These replacements are serve the following purposes:
13907 * - Provide a different command line.
13908 * - Provide a different environment.
13909 * - Intercept process termination.
13910 * - Intercept thread creation (only linker is allowed to create threads).
13911 * - Intercept file reading for caching (header files, ++) as file system
13912 * access is made even slower by anti-virus software.
13913 * - Intercept crypto hash APIs to cache MD5 digests of header files
13914 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
13915 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
13916 * in memory as writing files grows expensive with encryption and
13917 * anti-virus software active.
13918 * - Intercept some file system queries to use the kFsCache instead of
13919 * going to the kernel and slowly worm thru the AV filter driver.
13920 * - Intercept standard output/error and console writes to aggressivly
13921 * buffer the output. The MS CRT does not buffer either when it goes to
13922 * the console, resulting in terrible performance and mixing up output
13923 * with other compile jobs.
13924 * This also allows us to filter out the annoying source file announcements
13925 * by cl.exe.
13926 * - Intercept VirtualAlloc and VirtualFree to prevent
13927 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
13928 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
13929 * the callbacks run after each job.
13930 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
13931 * executables and tools using custom heaps (like the microsoft linker).
13932 * [exectuable images only]
13933 * - Intercept atexit and _onexit registration to be able run them after
13934 * each job instead of crashing as kWorker exits. This also helps avoid
13935 * some leaks. [executable image only]
13936 *
13937 * DLLs falls into two categories, system DLLs which we always load using the
13938 * native loader, and tool DLLs which can be handled like the executable or
13939 * optionally using the native loader. We maintain a hardcoded white listing of
13940 * tool DLLs we trust to load using the native loader.
13941 *
13942 * Imports of natively loaded DLLs are processed too, but we only replace a
13943 * subset of the functions compared to natively loaded excutable and DLL images.
13944 *
13945 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
13946 * This is to speed up job execution.
13947 *
13948 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
13949 * for each job run, but so far this hasn't been necessary.
13950 *
13951 *
13952 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
13953 *
13954 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
13955 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
13956 * intermediate representation between the first (c1/c1xx.dll) and second pass
13957 * (c2.dll).
13958 *
13959 * kWorker helps the compiler as best as it can. Given a little knowledge about
13960 * stable and volatile file system areas, it can do a lot of caching that a
13961 * normal compiler driver cannot easily do when given a single file.
13962 *
13963 *
13964 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
13965 *
13966 * The preprocessor part will open and process header files exactly as they are
13967 * encountered in the source files. If string.h is included by the main source
13968 * and five other header files, it will be searched for (include path), opened,
13969 * read, MD5-summed, and pre-processed six times. The last five times is just a
13970 * waste of time because of the guards or \#pragma once. A smart compiler would
13971 * make a little extra effort and realize this.
13972 *
13973 * kWorker will cache help the preprocessor by remembering places where the
13974 * header was not found with help of kFsCache, and cache the file in memory when
13975 * found. The first part is taken care of by intercepting GetFileAttributesW,
13976 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
13977 * cached, the file is kept open and the CreateFileW call returns a duplicate of
13978 * that handle. An internal handle table is used by ReadFile and CloseFile to
13979 * keep track of intercepted handles (also used for temporary file, temporary
13980 * file mappings, console buffering, and standard out/err buffering).
13981 *
13982 * PS. The header search optimization also comes in handy when cl.exe goes on
13983 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
13984 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
13985 * optionally compile the three pass DLLs as executables during development
13986 * and problem analysis.
13987 *
13988 *
13989 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
13990 *
13991 * The issues of the temporary files is pretty severe on the Dell machine used
13992 * for benchmarking with full AV and encryption. The synthetic benchmark
13993 * improved by 30% when kWorker implemented measures to keep them entirely in
13994 * memory.
13995 *
13996 * kWorker implement these by recognizing the filename pattern in CreateFileW
13997 * and creating/opening the given file as needed. The handle returned is a
13998 * duplicate of the current process, thus giving us a good chance of catching
13999 * API calls we're not intercepting.
14000 *
14001 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
14002 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
14003 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
14004 * UnmapViewOfFile.
14005 *
14006 *
14007 * @section sec_kWorker_Numbers Some measurements.
14008 *
14009 * - r2881 building src/VBox/Runtime:
14010 * - without: 2m01.016388s = 120.016388 s
14011 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
14012 * - r2884 building vbox/debug (r110512):
14013 * - without: 11m14.446609s = 674.446609 s
14014 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
14015 * - r2896 building vbox/debug (r110577):
14016 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
14017 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
14018 * MS Defender as AV):
14019 * - without: 10m24.990389s = 624.990389s
14020 * - with: 08m04.738184s = 484.738184s
14021 * - delta: 624.99s - 484.74s = 140.25s
14022 * - saved: 140.25/624.99 = 22% faster
14023 *
14024 *
14025 * @subsection subsec_kWorker_Early_Numbers Early Experiments
14026 *
14027 * These are some early experiments doing 1024 compilations of
14028 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
14029 * main function:
14030 *
14031 * Skylake (W10/amd64, only stdandard MS defender):
14032 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
14033 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
14034 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
14035 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
14036 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
14037 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
14038 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
14039 *
14040 * Dell (W7/amd64, infected by mcafee):
14041 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
14042 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
14043 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
14044 *
14045 * The command line:
14046 * @code{.cpp}
14047 "\"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"
14048 * @endcode
14049 */
14050
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