VirtualBox

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

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

kWorker: Some GetModuleHandleW/A improvments to handle moc.exe/vcc141/5.6.3.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 478.9 KB
Line 
1/* $Id: kWorker.c 3335 2020-04-21 20:20:56Z 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 <psapi.h>
53
54#include "nt/kFsCache.h"
55#include "nt_fullpath.h"
56#include "quote_argv.h"
57#include "md5.h"
58#include "console.h"
59
60#include "../kmk/kmkbuiltin.h"
61
62
63/*********************************************************************************************************************************
64* Defined Constants And Macros *
65*********************************************************************************************************************************/
66/** @def WITH_TEMP_MEMORY_FILES
67 * Enables temporary memory files for cl.exe. */
68#define WITH_TEMP_MEMORY_FILES
69
70/** @def WITH_HASH_MD5_CACHE
71 * Enables caching of MD5 sums for cl.exe.
72 * This prevents wasting time on rehashing common headers each time
73 * they are included. */
74#define WITH_HASH_MD5_CACHE
75
76/** @def WITH_CRYPT_CTX_REUSE
77 * Enables reusing crypt contexts. The Visual C++ compiler always creates a
78 * context which is only used for MD5 and maybe some random bytes (VS 2010).
79 * So, only create it once and add a reference to it instead of creating new
80 * ones. Saves registry access among other things. */
81#define WITH_CRYPT_CTX_REUSE
82
83/** @def WITH_CONSOLE_OUTPUT_BUFFERING
84 * Enables buffering of all console output as well as removal of annoying
85 * source file echo by cl.exe. */
86#define WITH_CONSOLE_OUTPUT_BUFFERING
87
88/** @def WITH_STD_OUT_ERR_BUFFERING
89 * Enables buffering of standard output and standard error buffer as well as
90 * removal of annoying source file echo by cl.exe. */
91#define WITH_STD_OUT_ERR_BUFFERING
92
93/** @def WITH_LOG_FILE
94 * Log to file instead of stderr. */
95#define WITH_LOG_FILE
96
97/** @def WITH_HISTORY
98 * Keep history of the last jobs. For debugging. */
99#define WITH_HISTORY
100
101/** @def WITH_FIXED_VIRTUAL_ALLOCS
102 * Whether to pre allocate memory for known fixed VirtualAlloc calls (currently
103 * there is only one, but an important one, from cl.exe).
104 */
105#if K_ARCH == K_ARCH_X86_32
106# define WITH_FIXED_VIRTUAL_ALLOCS
107#endif
108
109/** @def WITH_PCH_CACHING
110 * Enables read caching of precompiled header files. */
111#if K_ARCH_BITS >= 64
112# define WITH_PCH_CACHING
113#endif
114
115
116#ifndef NDEBUG
117# define KW_LOG_ENABLED
118#endif
119
120/** @def KW_LOG
121 * Generic logging.
122 * @param a Argument list for kwDbgPrintf */
123#ifdef KW_LOG_ENABLED
124# define KW_LOG(a) kwDbgPrintf a
125#else
126# define KW_LOG(a) do { } while (0)
127#endif
128
129/** @def KWLDR_LOG
130 * Loader related logging.
131 * @param a Argument list for kwDbgPrintf */
132#ifdef KW_LOG_ENABLED
133# define KWLDR_LOG(a) kwDbgPrintf a
134#else
135# define KWLDR_LOG(a) do { } while (0)
136#endif
137
138
139/** @def KWFS_LOG
140 * FS cache logging.
141 * @param a Argument list for kwDbgPrintf */
142#ifdef KW_LOG_ENABLED
143# define KWFS_LOG(a) kwDbgPrintf a
144#else
145# define KWFS_LOG(a) do { } while (0)
146#endif
147
148/** @def KWOUT_LOG
149 * Output related logging.
150 * @param a Argument list for kwDbgPrintf */
151#ifdef KW_LOG_ENABLED
152# define KWOUT_LOG(a) kwDbgPrintf a
153#else
154# define KWOUT_LOG(a) do { } while (0)
155#endif
156
157/** @def KWCRYPT_LOG
158 * FS cache logging.
159 * @param a Argument list for kwDbgPrintf */
160#ifdef KW_LOG_ENABLED
161# define KWCRYPT_LOG(a) kwDbgPrintf a
162#else
163# define KWCRYPT_LOG(a) do { } while (0)
164#endif
165
166/** Converts a windows handle to a handle table index.
167 * @note We currently just mask off the 31th bit, and do no shifting or anything
168 * else to create an index of the handle.
169 * @todo consider shifting by 2 or 3. */
170#define KW_HANDLE_TO_INDEX(a_hHandle) ((KUPTR)(a_hHandle) & ~(KUPTR)KU32_C(0x8000000))
171/** Maximum handle value we can deal with. */
172#define KW_HANDLE_MAX 0x20000
173
174/** Max temporary file size (memory backed). */
175#if K_ARCH_BITS >= 64
176# define KWFS_TEMP_FILE_MAX (256*1024*1024)
177#else
178# define KWFS_TEMP_FILE_MAX (64*1024*1024)
179#endif
180
181/** Marks unfinished code. */
182#if 1
183# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); __debugbreak(); } while (0)
184#else
185# define KWFS_TODO() do { kwErrPrintf("\nHit TODO on line %u in %s!\n", __LINE__, __FUNCTION__); fflush(stderr); } while (0)
186#endif
187
188/** User data key for tools. */
189#define KW_DATA_KEY_TOOL (~(KUPTR)16381)
190/** User data key for a cached file. */
191#define KW_DATA_KEY_CACHED_FILE (~(KUPTR)65521)
192
193/** String constant comma length. */
194#define TUPLE(a_sz) a_sz, sizeof(a_sz) - 1
195
196
197/**
198 * Generate CRT slot wrapper functions.
199 */
200#define CRT_SLOT_FUNCTION_WRAPPER(a_RetTypeAndCallConv, a_FnName, a_aArgsDecl, a_aArgCall) \
201 static a_RetTypeAndCallConv a_FnName##00 a_aArgsDecl { const unsigned iCrtSlot = 0; return a_FnName##_wrapped a_aArgCall; } \
202 static a_RetTypeAndCallConv a_FnName##01 a_aArgsDecl { const unsigned iCrtSlot = 1; return a_FnName##_wrapped a_aArgCall; } \
203 static a_RetTypeAndCallConv a_FnName##02 a_aArgsDecl { const unsigned iCrtSlot = 2; return a_FnName##_wrapped a_aArgCall; } \
204 static a_RetTypeAndCallConv a_FnName##03 a_aArgsDecl { const unsigned iCrtSlot = 3; return a_FnName##_wrapped a_aArgCall; } \
205 static a_RetTypeAndCallConv a_FnName##04 a_aArgsDecl { const unsigned iCrtSlot = 4; return a_FnName##_wrapped a_aArgCall; } \
206 static a_RetTypeAndCallConv a_FnName##05 a_aArgsDecl { const unsigned iCrtSlot = 5; return a_FnName##_wrapped a_aArgCall; } \
207 static a_RetTypeAndCallConv a_FnName##06 a_aArgsDecl { const unsigned iCrtSlot = 6; return a_FnName##_wrapped a_aArgCall; } \
208 static a_RetTypeAndCallConv a_FnName##07 a_aArgsDecl { const unsigned iCrtSlot = 7; return a_FnName##_wrapped a_aArgCall; } \
209 static a_RetTypeAndCallConv a_FnName##08 a_aArgsDecl { const unsigned iCrtSlot = 8; return a_FnName##_wrapped a_aArgCall; } \
210 static a_RetTypeAndCallConv a_FnName##09 a_aArgsDecl { const unsigned iCrtSlot = 9; return a_FnName##_wrapped a_aArgCall; } \
211 static a_RetTypeAndCallConv a_FnName##10 a_aArgsDecl { const unsigned iCrtSlot = 10; return a_FnName##_wrapped a_aArgCall; } \
212 static a_RetTypeAndCallConv a_FnName##11 a_aArgsDecl { const unsigned iCrtSlot = 11; return a_FnName##_wrapped a_aArgCall; } \
213 static a_RetTypeAndCallConv a_FnName##12 a_aArgsDecl { const unsigned iCrtSlot = 12; return a_FnName##_wrapped a_aArgCall; } \
214 static a_RetTypeAndCallConv a_FnName##13 a_aArgsDecl { const unsigned iCrtSlot = 13; return a_FnName##_wrapped a_aArgCall; } \
215 static a_RetTypeAndCallConv a_FnName##14 a_aArgsDecl { const unsigned iCrtSlot = 14; return a_FnName##_wrapped a_aArgCall; } \
216 static a_RetTypeAndCallConv a_FnName##15 a_aArgsDecl { const unsigned iCrtSlot = 15; return a_FnName##_wrapped a_aArgCall; } \
217 static a_RetTypeAndCallConv a_FnName##16 a_aArgsDecl { const unsigned iCrtSlot = 16; return a_FnName##_wrapped a_aArgCall; } \
218 static a_RetTypeAndCallConv a_FnName##17 a_aArgsDecl { const unsigned iCrtSlot = 17; return a_FnName##_wrapped a_aArgCall; } \
219 static a_RetTypeAndCallConv a_FnName##18 a_aArgsDecl { const unsigned iCrtSlot = 18; return a_FnName##_wrapped a_aArgCall; } \
220 static a_RetTypeAndCallConv a_FnName##19 a_aArgsDecl { const unsigned iCrtSlot = 19; return a_FnName##_wrapped a_aArgCall; } \
221 static a_RetTypeAndCallConv a_FnName##20 a_aArgsDecl { const unsigned iCrtSlot = 20; return a_FnName##_wrapped a_aArgCall; } \
222 static a_RetTypeAndCallConv a_FnName##21 a_aArgsDecl { const unsigned iCrtSlot = 21; return a_FnName##_wrapped a_aArgCall; } \
223 static a_RetTypeAndCallConv a_FnName##22 a_aArgsDecl { const unsigned iCrtSlot = 22; return a_FnName##_wrapped a_aArgCall; } \
224 static a_RetTypeAndCallConv a_FnName##23 a_aArgsDecl { const unsigned iCrtSlot = 23; return a_FnName##_wrapped a_aArgCall; } \
225 static a_RetTypeAndCallConv a_FnName##24 a_aArgsDecl { const unsigned iCrtSlot = 24; return a_FnName##_wrapped a_aArgCall; } \
226 static a_RetTypeAndCallConv a_FnName##25 a_aArgsDecl { const unsigned iCrtSlot = 25; return a_FnName##_wrapped a_aArgCall; } \
227 static a_RetTypeAndCallConv a_FnName##26 a_aArgsDecl { const unsigned iCrtSlot = 26; return a_FnName##_wrapped a_aArgCall; } \
228 static a_RetTypeAndCallConv a_FnName##27 a_aArgsDecl { const unsigned iCrtSlot = 27; return a_FnName##_wrapped a_aArgCall; } \
229 static a_RetTypeAndCallConv a_FnName##28 a_aArgsDecl { const unsigned iCrtSlot = 28; return a_FnName##_wrapped a_aArgCall; } \
230 static a_RetTypeAndCallConv a_FnName##29 a_aArgsDecl { const unsigned iCrtSlot = 29; return a_FnName##_wrapped a_aArgCall; } \
231 static a_RetTypeAndCallConv a_FnName##30 a_aArgsDecl { const unsigned iCrtSlot = 30; return a_FnName##_wrapped a_aArgCall; } \
232 static a_RetTypeAndCallConv a_FnName##31 a_aArgsDecl { const unsigned iCrtSlot = 31; return a_FnName##_wrapped a_aArgCall; } \
233 static const KUPTR a_FnName[] = \
234 { \
235 (KUPTR)a_FnName##00, \
236 (KUPTR)a_FnName##01, \
237 (KUPTR)a_FnName##02, \
238 (KUPTR)a_FnName##03, \
239 (KUPTR)a_FnName##04, \
240 (KUPTR)a_FnName##05, \
241 (KUPTR)a_FnName##06, \
242 (KUPTR)a_FnName##07, \
243 (KUPTR)a_FnName##08, \
244 (KUPTR)a_FnName##09, \
245 (KUPTR)a_FnName##10, \
246 (KUPTR)a_FnName##11, \
247 (KUPTR)a_FnName##12, \
248 (KUPTR)a_FnName##13, \
249 (KUPTR)a_FnName##14, \
250 (KUPTR)a_FnName##15, \
251 (KUPTR)a_FnName##16, \
252 (KUPTR)a_FnName##17, \
253 (KUPTR)a_FnName##18, \
254 (KUPTR)a_FnName##19, \
255 (KUPTR)a_FnName##20, \
256 (KUPTR)a_FnName##21, \
257 (KUPTR)a_FnName##22, \
258 (KUPTR)a_FnName##23, \
259 (KUPTR)a_FnName##24, \
260 (KUPTR)a_FnName##25, \
261 (KUPTR)a_FnName##26, \
262 (KUPTR)a_FnName##27, \
263 (KUPTR)a_FnName##28, \
264 (KUPTR)a_FnName##29, \
265 (KUPTR)a_FnName##30, \
266 (KUPTR)a_FnName##31, \
267 }
268
269
270/*********************************************************************************************************************************
271* Structures and Typedefs *
272*********************************************************************************************************************************/
273typedef enum KWLOCATION
274{
275 KWLOCATION_INVALID = 0,
276 KWLOCATION_EXE_DIR,
277 KWLOCATION_IMPORTER_DIR,
278 KWLOCATION_SYSTEM32,
279 KWLOCATION_UNKNOWN_NATIVE,
280 KWLOCATION_UNKNOWN,
281} KWLOCATION;
282
283typedef enum KWMODSTATE
284{
285 KWMODSTATE_INVALID = 0,
286 KWMODSTATE_NEEDS_BITS,
287 KWMODSTATE_NEEDS_INIT,
288 KWMODSTATE_BEING_INITED,
289 KWMODSTATE_INIT_FAILED,
290 KWMODSTATE_READY,
291} KWMODSTATE;
292
293typedef struct KWMODULE *PKWMODULE;
294typedef struct KWMODULE
295{
296 /** Pointer to the next image withe the same hash. */
297 PKWMODULE pNextHash;
298 /** Pointer to the next image in the global list. */
299 PKWMODULE pNextList;
300 /** The normalized path to the image. */
301 const char *pszPath;
302 /** The hash of the program path. */
303 KU32 uHashPath;
304 /** Number of references. */
305 KU32 cRefs;
306 /** UTF-16 version of pszPath. */
307 const wchar_t *pwszPath;
308 /** The offset of the filename in pszPath. */
309 KU16 offFilename;
310 /** The offset of the filename in pwszPath. */
311 KU16 offFilenameW;
312 /** Set if executable. */
313 KBOOL fExe;
314 /** Set if native module entry. */
315 KBOOL fNative;
316 /** Loader module handle. */
317 PKLDRMOD pLdrMod;
318 /** The windows module handle. */
319 HMODULE hOurMod;
320 /** The of the loaded image bits. */
321 KSIZE cbImage;
322 /** The CRT slot for this module, if applicable (KU8_MAX when not). */
323 KU8 iCrtSlot;
324 /** Loop prevention when working the tree. */
325 KBOOL fVisited;
326 /** HACK: Set if re-init is needed (fReInitOnMsPdbSrvEndpointChange). */
327 KBOOL fNeedReInit;
328 /** HACK: Reinit when _MSPDBSRV_ENDPOINT_ changes, K_FALSE if not applicable.
329 * 1 if applicable but not yet used, 2 if used and have pszMsPdbSrvEndpoint. */
330 KU8 fReInitOnMsPdbSrvEndpointChange;
331 /** HACK: The old _MSPDBSRV_ENDPOINT_ value. */
332 char *pszMsPdbSrvEndpoint;
333
334 union
335 {
336 /** Data for a manually loaded image. */
337 struct
338 {
339 /** Where we load the image. */
340 KU8 *pbLoad;
341 /** Virgin copy of the image. */
342 KU8 *pbCopy;
343 /** Ldr pvBits argument. This is NULL till we've successfully resolved
344 * the imports. */
345 void *pvBits;
346 /** The state. */
347 KWMODSTATE enmState;
348#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
349 /** The number of entries in the table. */
350 KU32 cFunctions;
351 /** The function table address (in the copy). */
352 PRUNTIME_FUNCTION paFunctions;
353 /** Set if we've already registered a function table already. */
354 KBOOL fRegisteredFunctionTable;
355#endif
356 /** Set if we share memory with other executables. */
357 KBOOL fUseLdBuf;
358 /** Set after the first whole image copy is done. */
359 KBOOL fCanDoQuick;
360 /** Number of quick copy chunks. */
361 KU8 cQuickCopyChunks;
362 /** Number of quick zero chunks. */
363 KU8 cQuickZeroChunks;
364 /** Quicker image copy instructions that skips non-writable parts when
365 * possible. Need to check fCanDoQuick, fUseLdBuf and previous executable
366 * image. */
367 struct
368 {
369 /** The copy destination. */
370 KU8 *pbDst;
371 /** The copy source. */
372 KU8 const *pbSrc;
373 /** How much to copy. */
374 KSIZE cbToCopy;
375 } aQuickCopyChunks[3];
376 /** For handling BSS and zero alignment padding when using aQuickCopyChunks. */
377 struct
378 {
379 /** Where to start zeroing. */
380 KU8 *pbDst;
381 /** How much to zero. */
382 KSIZE cbToZero;
383 } aQuickZeroChunks[3];
384
385 /** TLS index if one was allocated, otherwise KU32_MAX. */
386 KU32 idxTls;
387 /** Offset (RVA) of the TLS initialization data. */
388 KU32 offTlsInitData;
389 /** Number of bytes of TLS initialization data. */
390 KU32 cbTlsInitData;
391 /** Number of allocated bytes for TLS. */
392 KU32 cbTlsAlloc;
393 /** Number of TLS callbacks. */
394 KU32 cTlsCallbacks;
395 /** Offset (RVA) of the TLS callback table. */
396 KU32 offTlsCallbacks;
397
398 /** Number of imported modules. */
399 KSIZE cImpMods;
400 /** Import array (variable size). */
401 PKWMODULE apImpMods[1];
402 } Manual;
403 } u;
404} KWMODULE;
405
406
407typedef struct KWDYNLOAD *PKWDYNLOAD;
408typedef struct KWDYNLOAD
409{
410 /** Pointer to the next in the list. */
411 PKWDYNLOAD pNext;
412
413 /** The module handle we present to the application.
414 * This is the LoadLibraryEx return value for special modules and the
415 * KWMODULE.hOurMod value for the others. */
416 HMODULE hmod;
417
418 /** The module for non-special resource stuff, NULL if special. */
419 PKWMODULE pMod;
420
421 /** The length of the LoadLibary filename. */
422 KSIZE cchRequest;
423 /** The LoadLibrary filename. */
424 char szRequest[1];
425} KWDYNLOAD;
426
427
428/**
429 * GetModuleHandle cache for system modules frequently queried.
430 */
431typedef struct KWGETMODULEHANDLECACHE
432{
433 const char *pszName;
434 const wchar_t *pwszName;
435 KU8 cchName;
436 KU8 cwcName;
437 KBOOL fAlwaysPresent;
438 HANDLE hmod;
439} KWGETMODULEHANDLECACHE;
440typedef KWGETMODULEHANDLECACHE *PKWGETMODULEHANDLECACHE;
441
442
443/**
444 * A cached file.
445 */
446typedef struct KFSWCACHEDFILE
447{
448 /** The user data core. */
449 KFSUSERDATA Core;
450
451 /** Cached file handle. */
452 HANDLE hCached;
453 /** Cached file section handle. */
454 HANDLE hSection;
455 /** Cached file content. */
456 KU8 *pbCached;
457 /** The file size. */
458 KU32 cbCached;
459#ifdef WITH_HASH_MD5_CACHE
460 /** Set if we've got a valid MD5 hash in abMd5Digest. */
461 KBOOL fValidMd5;
462 /** The MD5 digest if fValidMd5 is set. */
463 KU8 abMd5Digest[16];
464#endif
465
466 /** Circular self reference. Prevents the object from ever going away and
467 * keeps it handy for debugging. */
468 PKFSOBJ pFsObj;
469 /** The file path (for debugging). */
470 char szPath[1];
471} KFSWCACHEDFILE;
472/** Pointer to a cached filed. */
473typedef KFSWCACHEDFILE *PKFSWCACHEDFILE;
474
475#ifdef WITH_HASH_MD5_CACHE
476
477/** Pointer to a MD5 hash instance. */
478typedef struct KWHASHMD5 *PKWHASHMD5;
479/**
480 * A MD5 hash instance.
481 */
482typedef struct KWHASHMD5
483{
484 /** The magic value. */
485 KUPTR uMagic;
486 /** Pointer to the next hash handle. */
487 PKWHASHMD5 pNext;
488 /** The cached file we've associated this handle with. */
489 PKFSWCACHEDFILE pCachedFile;
490 /** The number of bytes we've hashed. */
491 KU32 cbHashed;
492 /** Set if this has gone wrong. */
493 KBOOL fGoneBad;
494 /** Set if we're in fallback mode (file not cached). */
495 KBOOL fFallbackMode;
496 /** Set if we've already finalized the digest. */
497 KBOOL fFinal;
498 /** The MD5 fallback context. */
499 struct MD5Context Md5Ctx;
500 /** The finalized digest. */
501 KU8 abDigest[16];
502
503} KWHASHMD5;
504/** Magic value for KWHASHMD5::uMagic (Les McCann). */
505# define KWHASHMD5_MAGIC KUPTR_C(0x19350923)
506
507#endif /* WITH_HASH_MD5_CACHE */
508#ifdef WITH_TEMP_MEMORY_FILES
509
510typedef struct KWFSTEMPFILESEG *PKWFSTEMPFILESEG;
511typedef struct KWFSTEMPFILESEG
512{
513 /** File offset of data. */
514 KU32 offData;
515 /** The size of the buffer pbData points to. */
516 KU32 cbDataAlloc;
517 /** The segment data. */
518 KU8 *pbData;
519} KWFSTEMPFILESEG;
520
521typedef struct KWFSTEMPFILE *PKWFSTEMPFILE;
522typedef struct KWFSTEMPFILE
523{
524 /** Pointer to the next temporary file for this run. */
525 PKWFSTEMPFILE pNext;
526 /** The UTF-16 path. (Allocated after this structure.) */
527 const wchar_t *pwszPath;
528 /** The path length. */
529 KU16 cwcPath;
530 /** Number of active handles using this file/mapping (<= 2). */
531 KU8 cActiveHandles;
532 /** Number of active mappings (mapped views) (0 or 1). */
533 KU8 cMappings;
534 /** The amount of space allocated in the segments. */
535 KU32 cbFileAllocated;
536 /** The current file size. */
537 KU32 cbFile;
538 /** The number of segments. */
539 KU32 cSegs;
540 /** Segments making up the file. */
541 PKWFSTEMPFILESEG paSegs;
542} KWFSTEMPFILE;
543
544#endif /* WITH_TEMP_MEMORY_FILES */
545#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
546
547/**
548 * Console line buffer or output full buffer.
549 */
550typedef struct KWOUTPUTSTREAMBUF
551{
552 /** The main output handle. */
553 HANDLE hOutput;
554 /** Our backup handle. */
555 HANDLE hBackup;
556 /** Set if this is a console handle and we're in line buffered mode.
557 * When clear, we may buffer multiple lines, though try flush on line
558 * boundraries when ever possible. */
559 KBOOL fIsConsole;
560 /** Compressed GetFileType result. */
561 KU8 fFileType;
562 KU8 abPadding[2];
563 union
564 {
565 /** Line buffer mode (fIsConsole == K_TRUE). */
566 struct
567 {
568 /** Amount of pending console output in wchar_t's. */
569 KU32 cwcBuf;
570 /** The allocated buffer size. */
571 KU32 cwcBufAlloc;
572 /** Pending console output. */
573 wchar_t *pwcBuf;
574 } Con;
575 /** Fully buffered mode (fIsConsole == K_FALSE). */
576 struct
577 {
578 /** Amount of pending output (in chars). */
579 KU32 cchBuf;
580#ifdef WITH_STD_OUT_ERR_BUFFERING
581 /** The allocated buffer size (in chars). */
582 KU32 cchBufAlloc;
583 /** Pending output. */
584 char *pchBuf;
585#endif
586 } Fully;
587 } u;
588} KWOUTPUTSTREAMBUF;
589/** Pointer to a console line buffer. */
590typedef KWOUTPUTSTREAMBUF *PKWOUTPUTSTREAMBUF;
591
592/**
593 * Combined console buffer of complete lines.
594 */
595typedef struct KWCONSOLEOUTPUT
596{
597 /** The console output handle.
598 * INVALID_HANDLE_VALUE if we haven't got a console and shouldn't be doing any
599 * combined output buffering. */
600 HANDLE hOutput;
601 /** The current code page for the console. */
602 KU32 uCodepage;
603 /** Amount of pending console output in wchar_t's. */
604 KU32 cwcBuf;
605 /** Number of times we've flushed it in any way (for cl.exe hack). */
606 KU32 cFlushes;
607 /** Pending console output. */
608 wchar_t wszBuf[8192];
609} KWCONSOLEOUTPUT;
610/** Pointer to a combined console buffer. */
611typedef KWCONSOLEOUTPUT *PKWCONSOLEOUTPUT;
612
613#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
614
615/** Handle type. */
616typedef enum KWHANDLETYPE
617{
618 KWHANDLETYPE_INVALID = 0,
619 KWHANDLETYPE_FSOBJ_READ_CACHE,
620 KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING,
621 KWHANDLETYPE_TEMP_FILE,
622 KWHANDLETYPE_TEMP_FILE_MAPPING,
623 KWHANDLETYPE_OUTPUT_BUF
624} KWHANDLETYPE;
625
626/** Handle data. */
627typedef struct KWHANDLE
628{
629 KWHANDLETYPE enmType;
630 /** Number of references */
631 KU32 cRefs;
632 /** The current file offset. */
633 KU32 offFile;
634 /** Handle access. */
635 KU32 dwDesiredAccess;
636 /** The handle. */
637 HANDLE hHandle;
638
639 /** Type specific data. */
640 union
641 {
642 /** The file system object. */
643 PKFSWCACHEDFILE pCachedFile;
644 /** Temporary file handle or mapping handle. */
645 PKWFSTEMPFILE pTempFile;
646#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
647 /** Buffered output stream. */
648 PKWOUTPUTSTREAMBUF pOutBuf;
649#endif
650 } u;
651} KWHANDLE;
652typedef KWHANDLE *PKWHANDLE;
653
654/**
655 * Tracking one of our memory mappings.
656 */
657typedef struct KWMEMMAPPING
658{
659 /** Number of references. */
660 KU32 cRefs;
661 /** The mapping type (KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING or
662 * KWHANDLETYPE_TEMP_FILE_MAPPING). */
663 KWHANDLETYPE enmType;
664 /** The mapping address. */
665 PVOID pvMapping;
666 /** Type specific data. */
667 union
668 {
669 /** The file system object. */
670 PKFSWCACHEDFILE pCachedFile;
671 /** Temporary file handle or mapping handle. */
672 PKWFSTEMPFILE pTempFile;
673 } u;
674} KWMEMMAPPING;
675/** Pointer to a memory mapping tracker. */
676typedef KWMEMMAPPING *PKWMEMMAPPING;
677
678
679/** Pointer to a VirtualAlloc tracker entry. */
680typedef struct KWVIRTALLOC *PKWVIRTALLOC;
681/**
682 * Tracking an VirtualAlloc allocation.
683 */
684typedef struct KWVIRTALLOC
685{
686 PKWVIRTALLOC pNext;
687 void *pvAlloc;
688 KSIZE cbAlloc;
689 /** This is KU32_MAX if not a preallocated chunk. */
690 KU32 idxPreAllocated;
691} KWVIRTALLOC;
692
693
694/** Pointer to a heap (HeapCreate) tracker entry. */
695typedef struct KWHEAP *PKWHEAP;
696/**
697 * Tracking an heap (HeapCreate)
698 */
699typedef struct KWHEAP
700{
701 PKWHEAP pNext;
702 HANDLE hHeap;
703} KWHEAP;
704
705
706/** Pointer to a FlsAlloc/TlsAlloc tracker entry. */
707typedef struct KWLOCALSTORAGE *PKWLOCALSTORAGE;
708/**
709 * Tracking an FlsAlloc/TlsAlloc index.
710 */
711typedef struct KWLOCALSTORAGE
712{
713 PKWLOCALSTORAGE pNext;
714 KU32 idx;
715} KWLOCALSTORAGE;
716
717
718/** Pointer to an at exit callback record */
719typedef struct KWEXITCALLACK *PKWEXITCALLACK;
720/**
721 * At exit callback record.
722 */
723typedef struct KWEXITCALLACK
724{
725 PKWEXITCALLACK pNext;
726 _onexit_t pfnCallback;
727 /** At exit doesn't have an exit code. */
728 KBOOL fAtExit;
729} KWEXITCALLACK;
730
731
732typedef enum KWTOOLTYPE
733{
734 KWTOOLTYPE_INVALID = 0,
735 KWTOOLTYPE_SANDBOXED,
736 KWTOOLTYPE_WATCOM,
737 KWTOOLTYPE_EXEC,
738 KWTOOLTYPE_END
739} KWTOOLTYPE;
740
741typedef enum KWTOOLHINT
742{
743 KWTOOLHINT_INVALID = 0,
744 KWTOOLHINT_NONE,
745 KWTOOLHINT_VISUAL_CPP_CL,
746 KWTOOLHINT_VISUAL_CPP_LINK,
747 KWTOOLHINT_END
748} KWTOOLHINT;
749
750
751/**
752 * A kWorker tool.
753 */
754typedef struct KWTOOL
755{
756 /** The user data core structure. */
757 KFSUSERDATA Core;
758
759 /** The normalized path to the program. */
760 const char *pszPath;
761 /** UTF-16 version of pszPath. */
762 wchar_t const *pwszPath;
763 /** The kind of tool. */
764 KWTOOLTYPE enmType;
765
766 union
767 {
768 struct
769 {
770 /** The main entry point. */
771 KUPTR uMainAddr;
772 /** The executable. */
773 PKWMODULE pExe;
774 /** List of dynamically loaded modules.
775 * These will be kept loaded till the tool is destroyed (if we ever do that). */
776 PKWDYNLOAD pDynLoadHead;
777 /** Module array sorted by hOurMod. */
778 PKWMODULE *papModules;
779 /** Number of entries in papModules. */
780 KU32 cModules;
781
782 /** Tool hint (for hacks and such). */
783 KWTOOLHINT enmHint;
784 } Sandboxed;
785 } u;
786} KWTOOL;
787/** Pointer to a tool. */
788typedef struct KWTOOL *PKWTOOL;
789
790
791typedef struct KWSANDBOX *PKWSANDBOX;
792typedef struct KWSANDBOX
793{
794 /** The tool currently running in the sandbox. */
795 PKWTOOL pTool;
796 /** Jump buffer. */
797 jmp_buf JmpBuf;
798 /** The thread ID of the main thread (owner of JmpBuf). */
799 DWORD idMainThread;
800 /** Copy of the NT TIB of the main thread. */
801 NT_TIB TibMainThread;
802 /** The NT_TIB::ExceptionList value inside the try case.
803 * We restore this prior to the longjmp. */
804 void *pOutXcptListHead;
805 /** The exit code in case of longjmp. */
806 int rcExitCode;
807 /** Set if we're running. */
808 KBOOL fRunning;
809 /** Whether to disable caching of ".pch" files. */
810 KBOOL fNoPchCaching;
811
812 /** The command line. */
813 char *pszCmdLine;
814 /** The UTF-16 command line. */
815 wchar_t *pwszCmdLine;
816 /** Number of arguments in papszArgs. */
817 int cArgs;
818 /** The argument vector. */
819 char **papszArgs;
820 /** The argument vector. */
821 wchar_t **papwszArgs;
822
823 /** The _pgmptr msvcrt variable. */
824 char *pgmptr;
825 /** The _wpgmptr msvcrt variable. */
826 wchar_t *wpgmptr;
827
828 /** The _initenv msvcrt variable. */
829 char **initenv;
830 /** The _winitenv msvcrt variable. */
831 wchar_t **winitenv;
832
833 /** Size of the array we've allocated (ASSUMES nobody messes with it!). */
834 KSIZE cEnvVarsAllocated;
835 /** The _environ msvcrt variable. */
836 char **environ;
837 /** The _wenviron msvcrt variable. */
838 wchar_t **wenviron;
839 /** The shadow _environ msvcrt variable. */
840 char **papszEnvVars;
841 /** The shadow _wenviron msvcrt variable. */
842 wchar_t **papwszEnvVars;
843
844
845 /** Handle table. */
846 PKWHANDLE *papHandles;
847 /** Size of the handle table. */
848 KU32 cHandles;
849 /** Number of active handles in the table. */
850 KU32 cActiveHandles;
851 /** Number of handles in the handle table that will not be freed. */
852 KU32 cFixedHandles;
853 /** Total number of leaked handles. */
854 KU32 cLeakedHandles;
855
856 /** Number of active memory mappings in paMemMappings. */
857 KU32 cMemMappings;
858 /** The allocated size of paMemMappings. */
859 KU32 cMemMappingsAlloc;
860 /** Memory mappings (MapViewOfFile / UnmapViewOfFile). */
861 PKWMEMMAPPING paMemMappings;
862
863 /** Head of the list of temporary file. */
864 PKWFSTEMPFILE pTempFileHead;
865
866 /** Head of the virtual alloc allocations. */
867 PKWVIRTALLOC pVirtualAllocHead;
868 /** Head of the heap list (HeapCreate).
869 * This is only done from images we forcibly restore. */
870 PKWHEAP pHeapHead;
871 /** Head of the FlsAlloc indexes. */
872 PKWLOCALSTORAGE pFlsAllocHead;
873 /** Head of the TlsAlloc indexes. */
874 PKWLOCALSTORAGE pTlsAllocHead;
875
876 /** The at exit callback head.
877 * This is only done from images we forcibly restore. */
878 PKWEXITCALLACK pExitCallbackHead;
879
880 MY_UNICODE_STRING SavedCommandLine;
881
882#ifdef WITH_HASH_MD5_CACHE
883 /** The special MD5 hash instance. */
884 PKWHASHMD5 pHashHead;
885 /** ReadFile sets these while CryptHashData claims and clears them.
886 *
887 * This is part of the heuristics we use for MD5 caching for header files. The
888 * observed pattern is that c1.dll/c1xx.dll first reads a chunk of a source or
889 * header, then passes the same buffer and read byte count to CryptHashData.
890 */
891 struct
892 {
893 /** The cached file last read from. */
894 PKFSWCACHEDFILE pCachedFile;
895 /** The file offset of the last cached read. */
896 KU32 offRead;
897 /** The number of bytes read last. */
898 KU32 cbRead;
899 /** The buffer pointer of the last read. */
900 void *pvRead;
901 } LastHashRead;
902#endif
903
904#ifdef WITH_CRYPT_CTX_REUSE
905 /** Reusable crypt contexts. */
906 struct
907 {
908 /** The creation provider type. */
909 KU32 dwProvType;
910 /** The creation flags. */
911 KU32 dwFlags;
912 /** The length of the container name. */
913 KU32 cwcContainer;
914 /** The length of the provider name. */
915 KU32 cwcProvider;
916 /** The container name string. */
917 wchar_t *pwszContainer;
918 /** The provider name string. */
919 wchar_t *pwszProvider;
920 /** The context handle. */
921 HCRYPTPROV hProv;
922 } aCryptCtxs[4];
923 /** Number of reusable crypt conexts in aCryptCtxs. */
924 KU32 cCryptCtxs;
925#endif
926
927
928#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
929 /** The internal standard output handle. */
930 KWHANDLE HandleStdOut;
931 /** The internal standard error handle. */
932 KWHANDLE HandleStdErr;
933 /** Standard output (and whatever else) buffer. */
934 KWOUTPUTSTREAMBUF StdOut;
935 /** Standard error buffer. */
936 KWOUTPUTSTREAMBUF StdErr;
937 /** Combined buffer of completed lines. */
938 KWCONSOLEOUTPUT Combined;
939#endif
940} KWSANDBOX;
941
942
943/** A CRT slot. */
944typedef struct KWCRTSLOT
945{
946 KU32 iSlot;
947
948 /** The CRT module data. */
949 PKWMODULE pModule;
950 /** Pointer to the malloc function. */
951 void * (__cdecl *pfnMalloc)(size_t);
952
953} KWCRTSLOT;
954typedef KWCRTSLOT *PKWCRTSLOT;
955
956
957/** Replacement function entry. */
958typedef struct KWREPLACEMENTFUNCTION
959{
960 /** The function name. */
961 const char *pszFunction;
962 /** The length of the function name. */
963 KSIZE cchFunction;
964 /** The module name (optional). */
965 const char *pszModule;
966 /** The replacement function, data address or CRT slot function array. */
967 KUPTR pfnReplacement;
968 /** Only replace in the executable.
969 * @todo fix the reinitialization of non-native DLLs! */
970 KBOOL fOnlyExe;
971 /** Set if pfnReplacement points to a CRT slot function array. */
972 KBOOL fCrtSlotArray;
973} KWREPLACEMENTFUNCTION;
974typedef KWREPLACEMENTFUNCTION const *PCKWREPLACEMENTFUNCTION;
975
976#if 0
977/** Replacement function entry. */
978typedef struct KWREPLACEMENTDATA
979{
980 /** The function name. */
981 const char *pszFunction;
982 /** The length of the function name. */
983 KSIZE cchFunction;
984 /** The module name (optional). */
985 const char *pszModule;
986 /** Function providing the replacement. */
987 KUPTR (*pfnMakeReplacement)(PKWMODULE pMod, const char *pchSymbol, KSIZE cchSymbol);
988} KWREPLACEMENTDATA;
989typedef KWREPLACEMENTDATA const *PCKWREPLACEMENTDATA;
990#endif
991
992/**
993 * One test job (--full-test).
994 */
995typedef struct KWONETEST
996{
997 /** Where this job originated. */
998 const char *pszJobSrc;
999 /** The argument number it started with. */
1000 unsigned iJobSrc;
1001 /** Set if virgin, clear if modified. */
1002 KBOOL fVirgin;
1003
1004 /** Number of runs to give it. */
1005 unsigned cRuns;
1006
1007 /** @name kSubmitHandleJobUnpacked arguments
1008 * @{ */
1009 const char *pszExecutable;
1010 const char *pszCwd;
1011 KU32 cArgs;
1012 const char **papszArgs;
1013 KU32 cEnvVars;
1014 const char **papszEnvVars;
1015 const char *pszSpecialEnv;
1016 KBOOL fWatcomBrainDamange;
1017 KBOOL fNoPchCaching;
1018 KU32 cPostCmdArgs;
1019 const char **papszPostCmdArgs;
1020 /** @} */
1021
1022 /** Pointer to the next one. */
1023 struct KWONETEST *pNext;
1024} KWONETEST;
1025/** Pointer to one test job. */
1026typedef KWONETEST *PKWONETEST;
1027
1028
1029/*********************************************************************************************************************************
1030* Global Variables *
1031*********************************************************************************************************************************/
1032/** The sandbox data. */
1033static KWSANDBOX g_Sandbox;
1034
1035/** The module currently occupying g_abDefLdBuf. */
1036static PKWMODULE g_pModInLdBuf = NULL;
1037
1038/** The module that previuosly occupied g_abDefLdBuf. */
1039static PKWMODULE g_pModPrevInLdBuf = NULL;
1040
1041/** Module list head. */
1042static PKWMODULE g_pModuleHead = NULL;
1043/** Where to insert the next module. */
1044static PKWMODULE *g_ppModuleNext = &g_pModuleHead;
1045
1046/** Module hash table. */
1047static PKWMODULE g_apModules[127];
1048
1049/** GetModuleHandle cache. */
1050static KWGETMODULEHANDLECACHE g_aGetModuleHandleCache[] =
1051{
1052#define MOD_CACHE_STRINGS(str) str, L##str, sizeof(str) - 1, (sizeof(L##str) / sizeof(wchar_t)) - 1
1053 { MOD_CACHE_STRINGS("KERNEL32.DLL"), K_TRUE, NULL },
1054#if 1
1055 { MOD_CACHE_STRINGS("KERNELBASE.DLL"), K_TRUE, NULL },
1056 { MOD_CACHE_STRINGS("NTDLL.DLL"), K_TRUE, NULL },
1057#endif
1058 { MOD_CACHE_STRINGS("mscoree.dll"), K_FALSE, NULL },
1059};
1060
1061/** Module pending TLS allocation. See kwLdrModuleCreateNonNativeSetupTls. */
1062static PKWMODULE g_pModPendingTlsAlloc = NULL;
1063
1064/** CRT slots.
1065 * @note The number of entires here must match CRT_SLOT_FUNCTION_WRAPPER. */
1066static KWCRTSLOT g_aCrtSlots[32];
1067
1068/** The file system cache. */
1069static PKFSCACHE g_pFsCache;
1070/** The current directory (referenced). */
1071static PKFSOBJ g_pCurDirObj = NULL;
1072#ifdef KBUILD_OS_WINDOWS
1073/** The windows system32 directory (referenced). */
1074static PKFSDIR g_pWinSys32 = NULL;
1075#endif
1076
1077/** Verbosity level. */
1078static int g_cVerbose = 2;
1079
1080/** Whether we should restart the worker. */
1081static KBOOL g_fRestart = K_FALSE;
1082
1083/** The process group this worker is tied to (--group option), -1 if none. */
1084static KI32 g_iProcessGroup = -1;
1085
1086/** Whether control-C/SIGINT or Control-Break/SIGBREAK have been seen. */
1087static int volatile g_rcCtrlC = 0;
1088
1089/** The communication pipe handle. We break this when we see Ctrl-C such. */
1090#ifdef KBUILD_OS_WINDOWS
1091static HANDLE g_hPipe = INVALID_HANDLE_VALUE;
1092#else
1093static int g_hPipe = -1;
1094#endif
1095
1096
1097/* Further down. */
1098extern KWREPLACEMENTFUNCTION const g_aSandboxReplacements[];
1099extern KU32 const g_cSandboxReplacements;
1100
1101extern KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[];
1102extern KU32 const g_cSandboxNativeReplacements;
1103
1104extern KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[];
1105extern KU32 const g_cSandboxGetProcReplacements;
1106
1107
1108/** Create a larget BSS blob that with help of /IMAGEBASE:0x10000 should
1109 * cover the default executable link address of 0x400000.
1110 * @remarks Early main() makes it read+write+executable. Attempts as having
1111 * it as a separate section failed because the linker insists on
1112 * writing out every zero in the uninitialized section, resulting in
1113 * really big binaries. */
1114__declspec(align(0x1000))
1115static KU8 g_abDefLdBuf[16*1024*1024];
1116
1117#ifdef WITH_LOG_FILE
1118/** Log file handle. */
1119static HANDLE g_hLogFile = INVALID_HANDLE_VALUE;
1120#endif
1121
1122
1123#ifdef WITH_FIXED_VIRTUAL_ALLOCS
1124/** Virtual address space reserved for CL.EXE heap manager.
1125 *
1126 * Visual C++ 2010 reserves a 78MB chunk of memory from cl.exe at a fixed
1127 * address. It's among other things used for precompiled headers, which
1128 * seemingly have addresses hardcoded into them and won't work if mapped
1129 * elsewhere. Thus, we have to make sure the area is available when cl.exe asks
1130 * for it. (The /Zm option may affect this allocation.)
1131 */
1132static struct
1133{
1134 /** The memory address we need. */
1135 KUPTR const uFixed;
1136 /** How much we need to fix. */
1137 KSIZE const cbFixed;
1138 /** What we actually got, NULL if given back. */
1139 void *pvReserved;
1140 /** Whether it is in use or not. */
1141 KBOOL fInUse;
1142} g_aFixedVirtualAllocs[] =
1143{
1144# if K_ARCH == K_ARCH_X86_32
1145 /* Visual C++ 2010 reserves 0x04b00000 by default, and Visual C++ 2015 reserves
1146 0x05300000. We get 0x0f000000 to handle large precompiled header files. */
1147 { KUPTR_C( 0x11000000), KSIZE_C( 0x0f000000), NULL },
1148# else
1149 { KUPTR_C(0x000006BB00000000), KSIZE_C(0x000000002EE00000), NULL },
1150# endif
1151};
1152#endif
1153
1154
1155#ifdef WITH_HISTORY
1156/** The job history. */
1157static char *g_apszHistory[32];
1158/** Index of the next history entry. */
1159static unsigned g_iHistoryNext = 0;
1160#endif
1161
1162
1163/** Number of jobs executed. */
1164static KU32 g_cJobs;
1165/** Number of tools. */
1166static KU32 g_cTools;
1167/** Number of modules. */
1168static KU32 g_cModules;
1169/** Number of non-native modules. */
1170static KU32 g_cNonNativeModules;
1171/** Number of read-cached files. */
1172static KU32 g_cReadCachedFiles;
1173/** Total size of read-cached files. */
1174static KSIZE g_cbReadCachedFiles;
1175
1176/** Total number of ReadFile calls. */
1177static KSIZE g_cReadFileCalls;
1178/** Total bytes read via ReadFile. */
1179static KSIZE g_cbReadFileTotal;
1180/** Total number of read from read-cached files. */
1181static KSIZE g_cReadFileFromReadCached;
1182/** Total bytes read from read-cached files. */
1183static KSIZE g_cbReadFileFromReadCached;
1184/** Total number of read from in-memory temporary files. */
1185static KSIZE g_cReadFileFromInMemTemp;
1186/** Total bytes read from in-memory temporary files. */
1187static KSIZE g_cbReadFileFromInMemTemp;
1188
1189/** Total number of WriteFile calls. */
1190static KSIZE g_cWriteFileCalls;
1191/** Total bytes written via WriteFile. */
1192static KSIZE g_cbWriteFileTotal;
1193/** Total number of written to from in-memory temporary files. */
1194static KSIZE g_cWriteFileToInMemTemp;
1195/** Total bytes written to in-memory temporary files. */
1196static KSIZE g_cbWriteFileToInMemTemp;
1197
1198
1199/*********************************************************************************************************************************
1200* Internal Functions *
1201*********************************************************************************************************************************/
1202static FNKLDRMODGETIMPORT kwLdrModuleGetImportCallback;
1203static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
1204 const char *pszSearchPath, PKWMODULE *ppMod);
1205static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot);
1206static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar);
1207static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle);
1208#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
1209static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite);
1210#endif
1211static PPEB kwSandboxGetProcessEnvironmentBlock(void);
1212
1213
1214
1215
1216/**
1217 * Debug printing.
1218 * @param pszFormat Debug format string.
1219 * @param ... Format argument.
1220 */
1221static void kwDbgPrintfV(const char *pszFormat, va_list va)
1222{
1223 if (g_cVerbose >= 2)
1224 {
1225 DWORD const dwSavedErr = GetLastError();
1226#ifdef WITH_LOG_FILE
1227 DWORD dwIgnored;
1228 char szTmp[2048];
1229 int cchPrefix = _snprintf(szTmp, sizeof(szTmp), "%x:%x: ", GetCurrentProcessId(), GetCurrentThreadId());
1230 int cch = vsnprintf(&szTmp[cchPrefix], sizeof(szTmp) - cchPrefix, pszFormat, va);
1231 if (cch < (int)sizeof(szTmp) - 1 - cchPrefix)
1232 cch += cchPrefix;
1233 else
1234 {
1235 cch = sizeof(szTmp) - 1;
1236 szTmp[cch] = '\0';
1237 }
1238
1239 if (g_hLogFile == INVALID_HANDLE_VALUE)
1240 {
1241 wchar_t wszFilename[128];
1242 _snwprintf(wszFilename, K_ELEMENTS(wszFilename), L"kWorker-%x-%x.log", GetTickCount(), GetCurrentProcessId());
1243 g_hLogFile = CreateFileW(wszFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL /*pSecAttrs*/, CREATE_ALWAYS,
1244 FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
1245 }
1246
1247 WriteFile(g_hLogFile, szTmp, cch, &dwIgnored, NULL /*pOverlapped*/);
1248#else
1249 fprintf(stderr, "debug: ");
1250 vfprintf(stderr, pszFormat, va);
1251#endif
1252
1253 SetLastError(dwSavedErr);
1254 }
1255}
1256
1257
1258/**
1259 * Debug printing.
1260 * @param pszFormat Debug format string.
1261 * @param ... Format argument.
1262 */
1263static void kwDbgPrintf(const char *pszFormat, ...)
1264{
1265 if (g_cVerbose >= 2)
1266 {
1267 va_list va;
1268 va_start(va, pszFormat);
1269 kwDbgPrintfV(pszFormat, va);
1270 va_end(va);
1271 }
1272}
1273
1274
1275/**
1276 * Debugger printing.
1277 * @param pszFormat Debug format string.
1278 * @param ... Format argument.
1279 */
1280static void kwDebuggerPrintfV(const char *pszFormat, va_list va)
1281{
1282 if (IsDebuggerPresent())
1283 {
1284 DWORD const dwSavedErr = GetLastError();
1285 char szTmp[2048];
1286
1287 _vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
1288 OutputDebugStringA(szTmp);
1289
1290 SetLastError(dwSavedErr);
1291 }
1292}
1293
1294
1295/**
1296 * Debugger printing.
1297 * @param pszFormat Debug format string.
1298 * @param ... Format argument.
1299 */
1300static void kwDebuggerPrintf(const char *pszFormat, ...)
1301{
1302 va_list va;
1303 va_start(va, pszFormat);
1304 kwDebuggerPrintfV(pszFormat, va);
1305 va_end(va);
1306}
1307
1308
1309
1310/**
1311 * Error printing.
1312 * @param pszFormat Message format string.
1313 * @param ... Format argument.
1314 */
1315static void kwErrPrintfV(const char *pszFormat, va_list va)
1316{
1317 DWORD const dwSavedErr = GetLastError();
1318
1319 fprintf(stderr, "kWorker: error: ");
1320 vfprintf(stderr, pszFormat, va);
1321 fflush(stderr); /* In case it's a pipe. */
1322
1323 SetLastError(dwSavedErr);
1324}
1325
1326
1327/**
1328 * Error printing.
1329 * @param pszFormat Message format string.
1330 * @param ... Format argument.
1331 */
1332static void kwErrPrintf(const char *pszFormat, ...)
1333{
1334 va_list va;
1335 va_start(va, pszFormat);
1336 kwErrPrintfV(pszFormat, va);
1337 va_end(va);
1338}
1339
1340
1341/**
1342 * Error printing.
1343 * @return rc;
1344 * @param rc Return value
1345 * @param pszFormat Message format string.
1346 * @param ... Format argument.
1347 */
1348static int kwErrPrintfRc(int rc, const char *pszFormat, ...)
1349{
1350 va_list va;
1351 va_start(va, pszFormat);
1352 kwErrPrintfV(pszFormat, va);
1353 va_end(va);
1354 return rc;
1355}
1356
1357
1358#ifdef K_STRICT
1359
1360KHLP_DECL(void) kHlpAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction)
1361{
1362 DWORD const dwSavedErr = GetLastError();
1363
1364 fprintf(stderr,
1365 "\n"
1366 "!!Assertion failed!!\n"
1367 "Expression: %s\n"
1368 "Function : %s\n"
1369 "File: %s\n"
1370 "Line: %d\n"
1371 , pszExpr, pszFunction, pszFile, iLine);
1372
1373 SetLastError(dwSavedErr);
1374}
1375
1376
1377KHLP_DECL(void) kHlpAssertMsg2(const char *pszFormat, ...)
1378{
1379 DWORD const dwSavedErr = GetLastError();
1380 va_list va;
1381
1382 va_start(va, pszFormat);
1383 fprintf(stderr, pszFormat, va);
1384 va_end(va);
1385
1386 SetLastError(dwSavedErr);
1387}
1388
1389#endif /* K_STRICT */
1390
1391
1392/**
1393 * Hashes a string.
1394 *
1395 * @returns 32-bit string hash.
1396 * @param pszString String to hash.
1397 */
1398static KU32 kwStrHash(const char *pszString)
1399{
1400 /* This algorithm was created for sdbm (a public-domain reimplementation of
1401 ndbm) database library. it was found to do well in scrambling bits,
1402 causing better distribution of the keys and fewer splits. it also happens
1403 to be a good general hashing function with good distribution. the actual
1404 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
1405 is the faster version used in gawk. [there is even a faster, duff-device
1406 version] the magic constant 65599 was picked out of thin air while
1407 experimenting with different constants, and turns out to be a prime.
1408 this is one of the algorithms used in berkeley db (see sleepycat) and
1409 elsewhere. */
1410 KU32 uHash = 0;
1411 KU32 uChar;
1412 while ((uChar = (unsigned char)*pszString++) != 0)
1413 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1414 return uHash;
1415}
1416
1417
1418/**
1419 * Hashes a string.
1420 *
1421 * @returns The string length.
1422 * @param pszString String to hash.
1423 * @param puHash Where to return the 32-bit string hash.
1424 */
1425static KSIZE kwStrHashEx(const char *pszString, KU32 *puHash)
1426{
1427 const char * const pszStart = pszString;
1428 KU32 uHash = 0;
1429 KU32 uChar;
1430 while ((uChar = (unsigned char)*pszString) != 0)
1431 {
1432 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1433 pszString++;
1434 }
1435 *puHash = uHash;
1436 return pszString - pszStart;
1437}
1438
1439
1440/**
1441 * Hashes a string.
1442 *
1443 * @returns The string length in wchar_t units.
1444 * @param pwszString String to hash.
1445 * @param puHash Where to return the 32-bit string hash.
1446 */
1447static KSIZE kwUtf16HashEx(const wchar_t *pwszString, KU32 *puHash)
1448{
1449 const wchar_t * const pwszStart = pwszString;
1450 KU32 uHash = 0;
1451 KU32 uChar;
1452 while ((uChar = *pwszString) != 0)
1453 {
1454 uHash = uChar + (uHash << 6) + (uHash << 16) - uHash;
1455 pwszString++;
1456 }
1457 *puHash = uHash;
1458 return pwszString - pwszStart;
1459}
1460
1461
1462/**
1463 * Converts the given string to unicode.
1464 *
1465 * @returns Length of the resulting string in wchar_t's.
1466 * @param pszSrc The source string.
1467 * @param pwszDst The destination buffer.
1468 * @param cwcDst The size of the destination buffer in wchar_t's.
1469 */
1470static KSIZE kwStrToUtf16(const char *pszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1471{
1472 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1473 KSIZE offDst = 0;
1474 while (offDst < cwcDst)
1475 {
1476 char ch = *pszSrc++;
1477 pwszDst[offDst++] = ch;
1478 if (!ch)
1479 return offDst - 1;
1480 kHlpAssert((unsigned)ch < 127);
1481 }
1482
1483 pwszDst[offDst - 1] = '\0';
1484 return offDst;
1485}
1486
1487
1488/**
1489 * Converts the given string to UTF-16, allocating the buffer.
1490 *
1491 * @returns Pointer to the new heap allocation containing the UTF-16 version of
1492 * the source string.
1493 * @param pchSrc The source string.
1494 * @param cchSrc The length of the source string.
1495 */
1496static wchar_t *kwStrToUtf16AllocN(const char *pchSrc, KSIZE cchSrc)
1497{
1498 DWORD const dwErrSaved = GetLastError();
1499 KSIZE cwcBuf = cchSrc + 1;
1500 wchar_t *pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1501 if (pwszBuf)
1502 {
1503 if (cchSrc > 0)
1504 {
1505 int cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1506 if (cwcRet > 0)
1507 {
1508 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1509 pwszBuf[cwcRet] = '\0';
1510 }
1511 else
1512 {
1513 kHlpFree(pwszBuf);
1514
1515 /* Figure the length and allocate the right buffer size. */
1516 SetLastError(NO_ERROR);
1517 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, 0);
1518 if (cwcRet)
1519 {
1520 cwcBuf = cwcRet + 2;
1521 pwszBuf = (wchar_t *)kHlpAlloc(cwcBuf * sizeof(pwszBuf));
1522 if (pwszBuf)
1523 {
1524 SetLastError(NO_ERROR);
1525 cwcRet = MultiByteToWideChar(CP_ACP, 0, pchSrc, (int)cchSrc, pwszBuf, (int)cwcBuf - 1);
1526 if (cwcRet)
1527 {
1528 kHlpAssert(cwcRet < (KSSIZE)cwcBuf);
1529 pwszBuf[cwcRet] = '\0';
1530 }
1531 else
1532 {
1533 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1534 kHlpFree(pwszBuf);
1535 pwszBuf = NULL;
1536 }
1537 }
1538 }
1539 else
1540 {
1541 kwErrPrintf("MultiByteToWideChar(,,%*.*s,,NULL,0) -> dwErr=%d\n", cchSrc, cchSrc, pchSrc, GetLastError());
1542 pwszBuf = NULL;
1543 }
1544 }
1545 }
1546 else
1547 pwszBuf[0] = '\0';
1548 }
1549 SetLastError(dwErrSaved);
1550 return pwszBuf;
1551}
1552
1553
1554/**
1555 * Converts the given UTF-16 to a normal string.
1556 *
1557 * @returns Length of the resulting string.
1558 * @param pwszSrc The source UTF-16 string.
1559 * @param pszDst The destination buffer.
1560 * @param cbDst The size of the destination buffer in bytes.
1561 */
1562static KSIZE kwUtf16ToStr(const wchar_t *pwszSrc, char *pszDst, KSIZE cbDst)
1563{
1564 /* Just to the quick ASCII stuff for now. correct ansi code page stuff later some time. */
1565 KSIZE offDst = 0;
1566 while (offDst < cbDst)
1567 {
1568 wchar_t wc = *pwszSrc++;
1569 pszDst[offDst++] = (char)wc;
1570 if (!wc)
1571 return offDst - 1;
1572 kHlpAssert((unsigned)wc < 127);
1573 }
1574
1575 pszDst[offDst - 1] = '\0';
1576 return offDst;
1577}
1578
1579
1580/**
1581 * Converts the given UTF-16 to ASSI, allocating the buffer.
1582 *
1583 * @returns Pointer to the new heap allocation containing the ANSI version of
1584 * the source string.
1585 * @param pwcSrc The source string.
1586 * @param cwcSrc The length of the source string.
1587 */
1588static char *kwUtf16ToStrAllocN(const wchar_t *pwcSrc, KSIZE cwcSrc)
1589{
1590 DWORD const dwErrSaved = GetLastError();
1591 KSIZE cbBuf = cwcSrc + (cwcSrc >> 1) + 1;
1592 char *pszBuf = (char *)kHlpAlloc(cbBuf);
1593 if (pszBuf)
1594 {
1595 if (cwcSrc > 0)
1596 {
1597 int cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1598 if (cchRet > 0)
1599 {
1600 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1601 pszBuf[cchRet] = '\0';
1602 }
1603 else
1604 {
1605 kHlpFree(pszBuf);
1606
1607 /* Figure the length and allocate the right buffer size. */
1608 SetLastError(NO_ERROR);
1609 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, 0, NULL, NULL);
1610 if (cchRet)
1611 {
1612 cbBuf = cchRet + 2;
1613 pszBuf = (char *)kHlpAlloc(cbBuf);
1614 if (pszBuf)
1615 {
1616 SetLastError(NO_ERROR);
1617 cchRet = WideCharToMultiByte(CP_ACP, 0, pwcSrc, (int)cwcSrc, pszBuf, (int)cbBuf - 1, NULL, NULL);
1618 if (cchRet)
1619 {
1620 kHlpAssert(cchRet < (KSSIZE)cbBuf);
1621 pszBuf[cchRet] = '\0';
1622 }
1623 else
1624 {
1625 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1626 kHlpFree(pszBuf);
1627 pszBuf = NULL;
1628 }
1629 }
1630 }
1631 else
1632 {
1633 kwErrPrintf("WideCharToMultiByte(,,%*.*ls,,NULL,0) -> dwErr=%d\n", cwcSrc, cwcSrc, pwcSrc, GetLastError());
1634 pszBuf = NULL;
1635 }
1636 }
1637 }
1638 else
1639 pszBuf[0] = '\0';
1640 }
1641 SetLastError(dwErrSaved);
1642 return pszBuf;
1643}
1644
1645
1646
1647/** UTF-16 string length. */
1648static KSIZE kwUtf16Len(wchar_t const *pwsz)
1649{
1650 KSIZE cwc = 0;
1651 while (*pwsz != '\0')
1652 cwc++, pwsz++;
1653 return cwc;
1654}
1655
1656/**
1657 * Copy out the UTF-16 string following the convension of GetModuleFileName
1658 */
1659static DWORD kwUtf16CopyStyle1(wchar_t const *pwszSrc, wchar_t *pwszDst, KSIZE cwcDst)
1660{
1661 KSIZE cwcSrc = kwUtf16Len(pwszSrc);
1662 if (cwcSrc + 1 <= cwcDst)
1663 {
1664 kHlpMemCopy(pwszDst, pwszSrc, (cwcSrc + 1) * sizeof(wchar_t));
1665 return (DWORD)cwcSrc;
1666 }
1667 if (cwcDst > 0)
1668 {
1669 KSIZE cwcDstTmp = cwcDst - 1;
1670 pwszDst[cwcDstTmp] = '\0';
1671 if (cwcDstTmp > 0)
1672 kHlpMemCopy(pwszDst, pwszSrc, cwcDstTmp);
1673 }
1674 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1675 return (DWORD)cwcDst;
1676}
1677
1678
1679/**
1680 * Copy out the ANSI string following the convension of GetModuleFileName
1681 */
1682static DWORD kwStrCopyStyle1(char const *pszSrc, char *pszDst, KSIZE cbDst)
1683{
1684 KSIZE cchSrc = kHlpStrLen(pszSrc);
1685 if (cchSrc + 1 <= cbDst)
1686 {
1687 kHlpMemCopy(pszDst, pszSrc, cchSrc + 1);
1688 return (DWORD)cchSrc;
1689 }
1690 if (cbDst > 0)
1691 {
1692 KSIZE cbDstTmp = cbDst - 1;
1693 pszDst[cbDstTmp] = '\0';
1694 if (cbDstTmp > 0)
1695 kHlpMemCopy(pszDst, pszSrc, cbDstTmp);
1696 }
1697 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1698 return (DWORD)cbDst;
1699}
1700
1701
1702/**
1703 * Normalizes the path so we get a consistent hash.
1704 *
1705 * @returns status code.
1706 * @param pszPath The path.
1707 * @param pszNormPath The output buffer.
1708 * @param cbNormPath The size of the output buffer.
1709 */
1710static int kwPathNormalize(const char *pszPath, char *pszNormPath, KSIZE cbNormPath)
1711{
1712 KFSLOOKUPERROR enmError;
1713 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
1714 if (pFsObj)
1715 {
1716 KBOOL fRc;
1717 fRc = kFsCacheObjGetFullPathA(pFsObj, pszNormPath, cbNormPath, '\\');
1718 kFsCacheObjRelease(g_pFsCache, pFsObj);
1719 if (fRc)
1720 return 0;
1721 return KERR_BUFFER_OVERFLOW;
1722 }
1723 return KERR_FILE_NOT_FOUND;
1724}
1725
1726
1727/**
1728 * Get the pointer to the filename part of the path.
1729 *
1730 * @returns Pointer to where the filename starts within the string pointed to by pszFilename.
1731 * @returns Pointer to the terminator char if no filename.
1732 * @param pszPath The path to parse.
1733 */
1734static wchar_t *kwPathGetFilenameW(const wchar_t *pwszPath)
1735{
1736 const wchar_t *pwszLast = NULL;
1737 for (;;)
1738 {
1739 wchar_t wc = *pwszPath;
1740#if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS
1741 if (wc == '/' || wc == '\\' || wc == ':')
1742 {
1743 while ((wc = *++pwszPath) == '/' || wc == '\\' || wc == ':')
1744 /* nothing */;
1745 pwszLast = pwszPath;
1746 }
1747#else
1748 if (wc == '/')
1749 {
1750 while ((wc = *++pszFilename) == '/')
1751 /* betsuni */;
1752 pwszLast = pwszPath;
1753 }
1754#endif
1755 if (!wc)
1756 return (wchar_t *)(pwszLast ? pwszLast : pwszPath);
1757 pwszPath++;
1758 }
1759}
1760
1761
1762
1763/**
1764 * Retains a new reference to the given module
1765 * @returns pMod
1766 * @param pMod The module to retain.
1767 */
1768static PKWMODULE kwLdrModuleRetain(PKWMODULE pMod)
1769{
1770 kHlpAssert(pMod->cRefs > 0);
1771 kHlpAssert(pMod->cRefs < 64);
1772 pMod->cRefs++;
1773 return pMod;
1774}
1775
1776
1777/**
1778 * Releases a module reference.
1779 *
1780 * @param pMod The module to release.
1781 */
1782static void kwLdrModuleRelease(PKWMODULE pMod)
1783{
1784 if (--pMod->cRefs == 0)
1785 {
1786 /* Unlink it from the hash table. */
1787 if (!pMod->fExe)
1788 {
1789 PKWMODULE pPrev = NULL;
1790 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1791 if (g_apModules[idx] == pMod)
1792 g_apModules[idx] = pMod->pNextHash;
1793 else
1794 {
1795 PKWMODULE pPrev = g_apModules[idx];
1796 kHlpAssert(pPrev != NULL);
1797 while (pPrev->pNextHash != pMod)
1798 {
1799 pPrev = pPrev->pNextHash;
1800 kHlpAssert(pPrev != NULL);
1801 }
1802 pPrev->pNextHash = pMod->pNextHash;
1803 }
1804 }
1805
1806 /* Unlink it from the list. */
1807 if (pMod != g_pModuleHead)
1808 {
1809 PKWMODULE pPrev = g_pModuleHead;
1810 while (pPrev)
1811 {
1812 if (pPrev->pNextList == pMod)
1813 {
1814 pPrev->pNextList = pMod->pNextList;
1815 if (!pMod->pNextList)
1816 g_ppModuleNext = &pPrev->pNextList;
1817 break;
1818 }
1819 pPrev = pPrev->pNextList;
1820 }
1821 kHlpAssert(pPrev != NULL);
1822 }
1823 else
1824 {
1825 g_pModuleHead = pMod->pNextList;
1826 if (!pMod->pNextList)
1827 g_ppModuleNext = &g_pModuleHead;
1828 }
1829
1830 /* Release import modules. */
1831 if (!pMod->fNative)
1832 {
1833 KSIZE idx = pMod->u.Manual.cImpMods;
1834 while (idx-- > 0)
1835 if (pMod->u.Manual.apImpMods[idx])
1836 {
1837 kwLdrModuleRelease(pMod->u.Manual.apImpMods[idx]);
1838 pMod->u.Manual.apImpMods[idx] = NULL;
1839 }
1840 }
1841
1842 /* Free our resources. */
1843 kLdrModClose(pMod->pLdrMod);
1844 pMod->pLdrMod = NULL;
1845
1846 if (!pMod->fNative)
1847 {
1848 kHlpPageFree(pMod->u.Manual.pbCopy, pMod->cbImage);
1849 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
1850 }
1851
1852 if (pMod->iCrtSlot != KU8_MAX)
1853 g_aCrtSlots[pMod->iCrtSlot].pModule = NULL;
1854
1855 if (pMod->pszMsPdbSrvEndpoint)
1856 {
1857 kHlpFree(pMod->pszMsPdbSrvEndpoint);
1858 pMod->pszMsPdbSrvEndpoint = NULL;
1859 }
1860
1861 kHlpFree(pMod);
1862 }
1863 else
1864 kHlpAssert(pMod->cRefs < 64);
1865}
1866
1867
1868/**
1869 * Links the module into the module hash table.
1870 *
1871 * @returns pMod
1872 * @param pMod The module to link.
1873 */
1874static PKWMODULE kwLdrModuleLink(PKWMODULE pMod)
1875{
1876 if (!pMod->fExe)
1877 {
1878 unsigned idx = pMod->uHashPath % K_ELEMENTS(g_apModules);
1879 pMod->pNextHash = g_apModules[idx];
1880 g_apModules[idx] = pMod;
1881 }
1882
1883 pMod->pNextList = NULL;
1884 *g_ppModuleNext = pMod;
1885 g_ppModuleNext = &pMod->pNextList;
1886
1887 return pMod;
1888}
1889
1890
1891/**
1892 * Replaces imports for this module according to g_aSandboxNativeReplacements.
1893 *
1894 * @param pMod The natively loaded module to process.
1895 */
1896static void kwLdrModuleDoNativeImportReplacements(PKWMODULE pMod)
1897{
1898 KSIZE const cbImage = (KSIZE)kLdrModSize(pMod->pLdrMod);
1899 KU8 const * const pbImage = (KU8 const *)pMod->hOurMod;
1900 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbImage;
1901 IMAGE_NT_HEADERS const *pNtHdrs;
1902 IMAGE_DATA_DIRECTORY const *pDirEnt;
1903
1904 kHlpAssert(pMod->fNative);
1905
1906 /*
1907 * Locate the export descriptors.
1908 */
1909 /* MZ header. */
1910 if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
1911 {
1912 kHlpAssertReturnVoid((KU32)pMzHdr->e_lfanew <= cbImage - sizeof(*pNtHdrs));
1913 pNtHdrs = (IMAGE_NT_HEADERS const *)&pbImage[pMzHdr->e_lfanew];
1914 }
1915 else
1916 pNtHdrs = (IMAGE_NT_HEADERS const *)pbImage;
1917
1918 /* Check PE header. */
1919 kHlpAssertReturnVoid(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
1920 kHlpAssertReturnVoid(pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pNtHdrs->OptionalHeader));
1921
1922 /* Locate the import descriptor array. */
1923 pDirEnt = (IMAGE_DATA_DIRECTORY const *)&pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
1924 if ( pDirEnt->Size > 0
1925 && pDirEnt->VirtualAddress != 0)
1926 {
1927 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc = (const IMAGE_IMPORT_DESCRIPTOR *)&pbImage[pDirEnt->VirtualAddress];
1928 KU32 cLeft = pDirEnt->Size / sizeof(*pImpDesc);
1929 MEMORY_BASIC_INFORMATION ProtInfo = { NULL, NULL, 0, 0, 0, 0, 0 };
1930 KU8 *pbProtRange = NULL;
1931 SIZE_T cbProtRange = 0;
1932 DWORD fOldProt = 0;
1933 KU32 const cbPage = 0x1000;
1934 BOOL fRc;
1935
1936
1937 kHlpAssertReturnVoid(pDirEnt->VirtualAddress < cbImage);
1938 kHlpAssertReturnVoid(pDirEnt->Size < cbImage);
1939 kHlpAssertReturnVoid(pDirEnt->VirtualAddress + pDirEnt->Size <= cbImage);
1940
1941 /*
1942 * Walk the import descriptor array.
1943 * Note! This only works if there's a backup thunk array, otherwise we cannot get at the name.
1944 */
1945 while ( cLeft-- > 0
1946 && pImpDesc->Name > 0
1947 && pImpDesc->FirstThunk > 0)
1948 {
1949 KU32 iThunk;
1950 const char * const pszImport = (const char *)&pbImage[pImpDesc->Name];
1951 PKWMODULE pImportMod = NULL;
1952 PIMAGE_THUNK_DATA paThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->FirstThunk];
1953 PIMAGE_THUNK_DATA paOrgThunks = (PIMAGE_THUNK_DATA)&pbImage[pImpDesc->OriginalFirstThunk];
1954 kHlpAssertReturnVoid(pImpDesc->Name < cbImage);
1955 kHlpAssertReturnVoid(pImpDesc->FirstThunk < cbImage);
1956 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk < cbImage);
1957 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk != pImpDesc->FirstThunk);
1958 kHlpAssertReturnVoid(pImpDesc->OriginalFirstThunk);
1959
1960 /* Iterate the thunks. */
1961 for (iThunk = 0; paOrgThunks[iThunk].u1.Ordinal != 0; iThunk++)
1962 {
1963 KUPTR const off = paOrgThunks[iThunk].u1.Function;
1964 kHlpAssertReturnVoid(off < cbImage);
1965 if (!IMAGE_SNAP_BY_ORDINAL(off))
1966 {
1967 IMAGE_IMPORT_BY_NAME const *pName = (IMAGE_IMPORT_BY_NAME const *)&pbImage[off];
1968 KSIZE const cchSymbol = kHlpStrLen(pName->Name);
1969 KU32 i = g_cSandboxNativeReplacements;
1970 while (i-- > 0)
1971 if ( g_aSandboxNativeReplacements[i].cchFunction == cchSymbol
1972 && kHlpMemComp(g_aSandboxNativeReplacements[i].pszFunction, pName->Name, cchSymbol) == 0)
1973 {
1974 if ( !g_aSandboxNativeReplacements[i].pszModule
1975 || kHlpStrICompAscii(g_aSandboxNativeReplacements[i].pszModule, pszImport) == 0)
1976 {
1977 KW_LOG(("%s: replacing %s!%s\n", pMod->pLdrMod->pszName, pszImport, pName->Name));
1978
1979 /* The .rdata section is normally read-only, so we need to make it writable first. */
1980 if ((KUPTR)&paThunks[iThunk] - (KUPTR)pbProtRange >= cbPage)
1981 {
1982 /* Restore previous .rdata page. */
1983 if (fOldProt)
1984 {
1985 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, NULL /*pfOldProt*/);
1986 kHlpAssert(fRc);
1987 fOldProt = 0;
1988 }
1989
1990 /* Query attributes for the current .rdata page. */
1991 pbProtRange = (KU8 *)((KUPTR)&paThunks[iThunk] & ~(KUPTR)(cbPage - 1));
1992 cbProtRange = VirtualQuery(pbProtRange, &ProtInfo, sizeof(ProtInfo));
1993 kHlpAssert(cbProtRange);
1994 if (cbProtRange)
1995 {
1996 switch (ProtInfo.Protect)
1997 {
1998 case PAGE_READWRITE:
1999 case PAGE_WRITECOPY:
2000 case PAGE_EXECUTE_READWRITE:
2001 case PAGE_EXECUTE_WRITECOPY:
2002 /* Already writable, nothing to do. */
2003 break;
2004
2005 default:
2006 kHlpAssertMsgFailed(("%#x\n", ProtInfo.Protect));
2007 case PAGE_READONLY:
2008 cbProtRange = cbPage;
2009 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_READWRITE, &fOldProt);
2010 break;
2011
2012 case PAGE_EXECUTE:
2013 case PAGE_EXECUTE_READ:
2014 cbProtRange = cbPage;
2015 fRc = VirtualProtect(pbProtRange, cbProtRange, PAGE_EXECUTE_READWRITE, &fOldProt);
2016 break;
2017 }
2018 kHlpAssertStmt(fRc, fOldProt = 0);
2019 }
2020 }
2021
2022 /*
2023 * Unslotted replacements are simple.
2024 */
2025 if (!g_aSandboxNativeReplacements[i].fCrtSlotArray)
2026 paThunks[iThunk].u1.AddressOfData = g_aSandboxNativeReplacements[i].pfnReplacement;
2027 else
2028 {
2029 /*
2030 * Must find our module entry for this module, possibly creating one.
2031 */
2032 if (!pImportMod)
2033 {
2034 pImportMod = kwLdrModuleForLoadedNative(pszImport, K_TRUE /*fEnsureCrtSlot*/);
2035 if (!pImportMod)
2036 {
2037 kwErrPrintf("Failed to get module '%s' when performing replacements on module '%s'!\n",
2038 pszImport, pMod->pszPath);
2039 break;
2040 }
2041 }
2042 paThunks[iThunk].u1.AddressOfData
2043 = ((KUPTR *)g_aSandboxNativeReplacements[i].pfnReplacement)[pImportMod->iCrtSlot];
2044 }
2045 break;
2046 }
2047 }
2048 }
2049 }
2050
2051
2052 /* Next import descriptor. */
2053 pImpDesc++;
2054 }
2055
2056
2057 if (fOldProt)
2058 {
2059 DWORD fIgnore = 0;
2060 fRc = VirtualProtect(pbProtRange, cbProtRange, fOldProt, &fIgnore);
2061 kHlpAssertMsg(fRc, ("%u\n", GetLastError())); K_NOREF(fRc);
2062 }
2063 }
2064
2065}
2066
2067
2068/**
2069 * Creates a module from a native kLdr module handle.
2070 *
2071 * @returns Module w/ 1 reference on success, NULL on failure.
2072 * @param pLdrMod The native kLdr module.
2073 * @param pszPath The normalized path to the module.
2074 * @param cbPath The module path length with terminator.
2075 * @param uHashPath The module path hash.
2076 * @param fDoReplacements Whether to do import replacements on this
2077 * module.
2078 */
2079static PKWMODULE kwLdrModuleCreateForNativekLdrModule(PKLDRMOD pLdrMod, const char *pszPath, KSIZE cbPath, KU32 uHashPath,
2080 KBOOL fDoReplacements)
2081{
2082 /*
2083 * Create the entry.
2084 */
2085 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod) + cbPath + cbPath * 2 * sizeof(wchar_t));
2086 if (pMod)
2087 {
2088 pMod->pwszPath = (wchar_t *)(pMod + 1);
2089 kwStrToUtf16(pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2090 pMod->pszPath = (char *)kHlpMemCopy((char *)&pMod->pwszPath[cbPath * 2], pszPath, cbPath);
2091 pMod->uHashPath = uHashPath;
2092 pMod->cRefs = 1;
2093 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2094 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2095 pMod->fExe = K_FALSE;
2096 pMod->fNative = K_TRUE;
2097 pMod->pLdrMod = pLdrMod;
2098 pMod->hOurMod = (HMODULE)(KUPTR)pLdrMod->aSegments[0].MapAddress;
2099 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2100 pMod->iCrtSlot = KU8_MAX;
2101 pMod->fNeedReInit = K_FALSE;
2102 pMod->pszMsPdbSrvEndpoint = NULL;
2103 pMod->fReInitOnMsPdbSrvEndpointChange = kHlpStrNICompAscii(&pMod->pszPath[pMod->offFilename], TUPLE("mspdb")) == 0;
2104
2105 if (fDoReplacements)
2106 {
2107 DWORD const dwSavedErr = GetLastError();
2108 kwLdrModuleDoNativeImportReplacements(pMod);
2109 SetLastError(dwSavedErr);
2110 }
2111
2112 KW_LOG(("New module: %p LB %#010x %s (native)\n",
2113 (KUPTR)pMod->pLdrMod->aSegments[0].MapAddress, kLdrModSize(pMod->pLdrMod), pMod->pszPath));
2114 g_cModules++;
2115 return kwLdrModuleLink(pMod);
2116 }
2117 return NULL;
2118}
2119
2120
2121
2122/**
2123 * Creates a module using the native loader.
2124 *
2125 * @returns Module w/ 1 reference on success, NULL on failure.
2126 * @param pszPath The normalized path to the module.
2127 * @param uHashPath The module path hash.
2128 * @param fDoReplacements Whether to do import replacements on this
2129 * module.
2130 */
2131static PKWMODULE kwLdrModuleCreateNative(const char *pszPath, KU32 uHashPath, KBOOL fDoReplacements)
2132{
2133 /*
2134 * Open the module and check the type.
2135 */
2136 PKLDRMOD pLdrMod;
2137 int rc = kLdrModOpenNative(pszPath, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
2138 if (rc == 0)
2139 {
2140 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, pszPath, kHlpStrLen(pszPath) + 1,
2141 uHashPath, fDoReplacements);
2142 if (pMod)
2143 return pMod;
2144 kLdrModClose(pLdrMod);
2145 }
2146 return NULL;
2147}
2148
2149
2150/**
2151 * Sets up the quick zero & copy tables for the non-native module.
2152 *
2153 * This is a worker for kwLdrModuleCreateNonNative.
2154 *
2155 * @param pMod The module.
2156 */
2157static void kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(PKWMODULE pMod)
2158{
2159 PCKLDRSEG paSegs = pMod->pLdrMod->aSegments;
2160 KU32 cSegs = pMod->pLdrMod->cSegments;
2161 KU32 iSeg;
2162
2163 KWLDR_LOG(("Setting up quick zero & copy for %s:\n", pMod->pszPath));
2164 pMod->u.Manual.cQuickCopyChunks = 0;
2165 pMod->u.Manual.cQuickZeroChunks = 0;
2166
2167 for (iSeg = 0; iSeg < cSegs; iSeg++)
2168 switch (paSegs[iSeg].enmProt)
2169 {
2170 case KPROT_READWRITE:
2171 case KPROT_WRITECOPY:
2172 case KPROT_EXECUTE_READWRITE:
2173 case KPROT_EXECUTE_WRITECOPY:
2174 if (paSegs[iSeg].cbMapped)
2175 {
2176 KU32 iChunk = pMod->u.Manual.cQuickCopyChunks;
2177 if (iChunk < K_ELEMENTS(pMod->u.Manual.aQuickCopyChunks))
2178 {
2179 /*
2180 * Check for trailing zero words.
2181 */
2182 KSIZE cbTrailingZeros;
2183 if ( paSegs[iSeg].cbMapped >= 64 * 2 * sizeof(KSIZE)
2184 && (paSegs[iSeg].cbMapped & 7) == 0
2185 && pMod->u.Manual.cQuickZeroChunks < K_ELEMENTS(pMod->u.Manual.aQuickZeroChunks) )
2186 {
2187 KSIZE const *pauNatural = (KSIZE const *)&pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2188 KSIZE cNatural = paSegs[iSeg].cbMapped / sizeof(KSIZE);
2189 KSIZE idxFirstZero = cNatural;
2190 while (idxFirstZero > 0)
2191 if (pauNatural[--idxFirstZero] == 0)
2192 { /* likely */ }
2193 else
2194 {
2195 idxFirstZero++;
2196 break;
2197 }
2198 cbTrailingZeros = (cNatural - idxFirstZero) * sizeof(KSIZE);
2199 if (cbTrailingZeros < 128)
2200 cbTrailingZeros = 0;
2201 }
2202 else
2203 cbTrailingZeros = 0;
2204
2205 /*
2206 * Add quick copy entry.
2207 */
2208 if (cbTrailingZeros < paSegs[iSeg].cbMapped)
2209 {
2210 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst = &pMod->u.Manual.pbLoad[(KSIZE)paSegs[iSeg].RVA];
2211 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc = &pMod->u.Manual.pbCopy[(KSIZE)paSegs[iSeg].RVA];
2212 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy = paSegs[iSeg].cbMapped - cbTrailingZeros;
2213 pMod->u.Manual.cQuickCopyChunks = iChunk + 1;
2214 KWLDR_LOG(("aQuickCopyChunks[%u]: %#p LB %#" KSIZE_PRI " <- %p (%*.*s)\n", iChunk,
2215 pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst,
2216 pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy,
2217 pMod->u.Manual.aQuickCopyChunks[iChunk].pbSrc,
2218 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2219 }
2220
2221 /*
2222 * Add quick zero entry.
2223 */
2224 if (cbTrailingZeros)
2225 {
2226 KU32 iZero = pMod->u.Manual.cQuickZeroChunks;
2227 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst = pMod->u.Manual.aQuickCopyChunks[iChunk].pbDst
2228 + pMod->u.Manual.aQuickCopyChunks[iChunk].cbToCopy;
2229 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero = cbTrailingZeros;
2230 pMod->u.Manual.cQuickZeroChunks = iZero + 1;
2231 KWLDR_LOG(("aQuickZeroChunks[%u]: %#p LB %#" KSIZE_PRI " <- zero (%*.*s)\n", iZero,
2232 pMod->u.Manual.aQuickZeroChunks[iZero].pbDst,
2233 pMod->u.Manual.aQuickZeroChunks[iZero].cbToZero,
2234 paSegs[iSeg].cchName, paSegs[iSeg].cchName, paSegs[iSeg].pchName));
2235 }
2236 }
2237 else
2238 {
2239 /*
2240 * We're out of quick copy table entries, so just copy the whole darn thing.
2241 * We cannot 104% guarantee that the segments are in mapping order, so this is simpler.
2242 */
2243 kHlpAssertFailed();
2244 pMod->u.Manual.aQuickCopyChunks[0].pbDst = pMod->u.Manual.pbLoad;
2245 pMod->u.Manual.aQuickCopyChunks[0].pbSrc = pMod->u.Manual.pbCopy;
2246 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy = pMod->cbImage;
2247 pMod->u.Manual.cQuickCopyChunks = 1;
2248 KWLDR_LOG(("Quick copy not possible!\n"));
2249 return;
2250 }
2251 }
2252 break;
2253
2254 default:
2255 break;
2256 }
2257}
2258
2259
2260/**
2261 * Called from TLS allocation DLL during DLL_PROCESS_ATTACH.
2262 *
2263 * @param hDll The DLL handle.
2264 * @param idxTls The allocated TLS index.
2265 * @param ppfnTlsCallback Pointer to the TLS callback table entry.
2266 */
2267__declspec(dllexport) void kwLdrTlsAllocationHook(void *hDll, ULONG idxTls, PIMAGE_TLS_CALLBACK *ppfnTlsCallback)
2268{
2269 /*
2270 * Do the module initialization thing first.
2271 */
2272 PKWMODULE pMod = g_pModPendingTlsAlloc;
2273 if (pMod)
2274 {
2275 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
2276 LIST_ENTRY *pHead;
2277 LIST_ENTRY *pCur;
2278
2279 pMod->u.Manual.idxTls = idxTls;
2280 KWLDR_LOG(("kwLdrTlsAllocationHook: idxTls=%d (%#x) for %s\n", idxTls, idxTls, pMod->pszPath));
2281
2282 /*
2283 * Try sabotage the DLL name so we can load this module again.
2284 */
2285 pHead = &pPeb->Ldr->InMemoryOrderModuleList;
2286 for (pCur = pHead->Blink; pCur != pHead; pCur = pCur->Blink)
2287 {
2288 LDR_DATA_TABLE_ENTRY *pMte;
2289 pMte = (LDR_DATA_TABLE_ENTRY *)((KUPTR)pCur - K_OFFSETOF(LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
2290 if (((KUPTR)pMte->DllBase & ~(KUPTR)31) == ((KUPTR)hDll & ~(KUPTR)31))
2291 {
2292 PUNICODE_STRING pStr = &pMte->FullDllName;
2293 KSIZE off = pStr->Length / sizeof(pStr->Buffer[0]);
2294 pStr->Buffer[--off]++;
2295 pStr->Buffer[--off]++;
2296 pStr->Buffer[--off]++;
2297 KWLDR_LOG(("kwLdrTlsAllocationHook: patched the MTE (%p) for %p\n", pMte, hDll));
2298 break;
2299 }
2300 }
2301 }
2302}
2303
2304
2305/**
2306 * Allocates and initializes TLS variables.
2307 *
2308 * @returns 0 on success, non-zero failure.
2309 * @param pMod The module.
2310 */
2311static int kwLdrModuleCreateNonNativeSetupTls(PKWMODULE pMod)
2312{
2313 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2314 IMAGE_NT_HEADERS const *pNtHdrs;
2315 IMAGE_DATA_DIRECTORY const *pTlsDir;
2316
2317 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2318 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2319 else
2320 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2321 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2322
2323 pTlsDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
2324 if (pTlsDir->Size >= sizeof(IMAGE_TLS_DIRECTORY))
2325 {
2326 PIMAGE_TLS_DIRECTORY const paEntries = (PIMAGE_TLS_DIRECTORY)&pbImg[pTlsDir->VirtualAddress];
2327 KU32 const cEntries = pTlsDir->Size / sizeof(IMAGE_TLS_DIRECTORY);
2328 KU32 iEntry;
2329 KUPTR offIndex;
2330 KUPTR offCallbacks;
2331 KUPTR const *puCallbacks;
2332 KSIZE cbData;
2333 const wchar_t *pwszTlsDll;
2334 HMODULE hmodTlsDll;
2335
2336 /*
2337 * Check and log.
2338 */
2339 for (iEntry = 0; iEntry < cEntries; iEntry++)
2340 {
2341 KUPTR offIndex = (KUPTR)paEntries[iEntry].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2342 KUPTR offCallbacks = (KUPTR)paEntries[iEntry].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2343 KUPTR const *puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2344 KWLDR_LOG(("TLS DIR #%u: %#x-%#x idx=@%#x (%#x) callbacks=@%#x (%#x) cbZero=%#x flags=%#x\n",
2345 iEntry, paEntries[iEntry].StartAddressOfRawData, paEntries[iEntry].EndAddressOfRawData,
2346 paEntries[iEntry].AddressOfIndex, offIndex, paEntries[iEntry].AddressOfCallBacks, offCallbacks,
2347 paEntries[iEntry].SizeOfZeroFill, paEntries[iEntry].Characteristics));
2348
2349 if (offIndex >= pMod->cbImage)
2350 {
2351 kwErrPrintf("TLS entry #%u in %s has an invalid index address: %p, RVA %p, image size %#x\n",
2352 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfIndex, offIndex, pMod->cbImage);
2353 return -1;
2354 }
2355 if (offCallbacks >= pMod->cbImage)
2356 {
2357 kwErrPrintf("TLS entry #%u in %s has an invalid callbacks address: %p, RVA %p, image size %#x\n",
2358 iEntry, pMod->pszPath, paEntries[iEntry].AddressOfCallBacks, offCallbacks, pMod->cbImage);
2359 return -1;
2360 }
2361 while (*puCallbacks != 0)
2362 {
2363 KWLDR_LOG(("TLS DIR #%u: callback %p, RVA %#x\n",
2364 iEntry, *puCallbacks, *puCallbacks - (KUPTR)pMod->u.Manual.pbLoad));
2365 puCallbacks++;
2366 }
2367 if (paEntries[iEntry].Characteristics > IMAGE_SCN_ALIGN_16BYTES)
2368 {
2369 kwErrPrintf("TLS entry #%u in %s has an unsupported alignment restriction: %#x\n",
2370 iEntry, pMod->pszPath, paEntries[iEntry].Characteristics);
2371 return -1;
2372 }
2373 }
2374
2375 if (cEntries > 1)
2376 {
2377 kwErrPrintf("More than one TLS directory entry in %s: %u\n", pMod->pszPath, cEntries);
2378 return -1;
2379 }
2380
2381 /*
2382 * Make the allocation by loading a new instance of one of the TLS dlls.
2383 * The DLL will make a call to
2384 */
2385 offIndex = (KUPTR)paEntries[0].AddressOfIndex - (KUPTR)pMod->u.Manual.pbLoad;
2386 offCallbacks = (KUPTR)paEntries[0].AddressOfCallBacks - (KUPTR)pMod->u.Manual.pbLoad;
2387 puCallbacks = (KUPTR const *)&pbImg[offCallbacks];
2388 cbData = paEntries[0].SizeOfZeroFill + (paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2389 if (cbData <= 1024)
2390 pwszTlsDll = L"kWorkerTls1K.dll";
2391 else if (cbData <= 65536)
2392 pwszTlsDll = L"kWorkerTls64K.dll";
2393 else if (cbData <= 524288)
2394 pwszTlsDll = L"kWorkerTls512K.dll";
2395 else
2396 {
2397 kwErrPrintf("TLS data size in %s is too big: %u (%#p), max 512KB\n", pMod->pszPath, (unsigned)cbData, cbData);
2398 return -1;
2399 }
2400
2401 pMod->u.Manual.idxTls = KU32_MAX;
2402 pMod->u.Manual.offTlsInitData = (KU32)((KUPTR)paEntries[0].StartAddressOfRawData - (KUPTR)pMod->u.Manual.pbLoad);
2403 pMod->u.Manual.cbTlsInitData = (KU32)(paEntries[0].EndAddressOfRawData - paEntries[0].StartAddressOfRawData);
2404 pMod->u.Manual.cbTlsAlloc = (KU32)cbData;
2405 pMod->u.Manual.cTlsCallbacks = 0;
2406 while (puCallbacks[pMod->u.Manual.cTlsCallbacks] != 0)
2407 pMod->u.Manual.cTlsCallbacks++;
2408 pMod->u.Manual.offTlsCallbacks = pMod->u.Manual.cTlsCallbacks ? (KU32)offCallbacks : KU32_MAX;
2409
2410 g_pModPendingTlsAlloc = pMod;
2411 hmodTlsDll = LoadLibraryExW(pwszTlsDll, NULL /*hFile*/, 0);
2412 g_pModPendingTlsAlloc = NULL;
2413 if (hmodTlsDll == NULL)
2414 {
2415 kwErrPrintf("TLS allocation failed for '%s': LoadLibraryExW(%ls) -> %u\n", pMod->pszPath, pwszTlsDll, GetLastError());
2416 return -1;
2417 }
2418 if (pMod->u.Manual.idxTls == KU32_MAX)
2419 {
2420 kwErrPrintf("TLS allocation failed for '%s': idxTls = KU32_MAX\n", pMod->pszPath, GetLastError());
2421 return -1;
2422 }
2423
2424 *(KU32 *)&pMod->u.Manual.pbCopy[offIndex] = pMod->u.Manual.idxTls;
2425 KWLDR_LOG(("kwLdrModuleCreateNonNativeSetupTls: idxTls=%d hmodTlsDll=%p (%ls) cbData=%#x\n",
2426 pMod->u.Manual.idxTls, hmodTlsDll, pwszTlsDll, cbData));
2427 }
2428 return 0;
2429}
2430
2431
2432/**
2433 * Creates a module using the our own loader.
2434 *
2435 * @returns Module w/ 1 reference on success, NULL on failure.
2436 * @param pszPath The normalized path to the module.
2437 * @param uHashPath The module path hash.
2438 * @param fExe K_TRUE if this is an executable image, K_FALSE
2439 * if not. Executable images does not get entered
2440 * into the global module table.
2441 * @param pExeMod The executable module of the process (for
2442 * resolving imports). NULL if fExe is set.
2443 * @param pszSearchPath The PATH to search for imports. Can be NULL.
2444 */
2445static PKWMODULE kwLdrModuleCreateNonNative(const char *pszPath, KU32 uHashPath, KBOOL fExe,
2446 PKWMODULE pExeMod, const char *pszSearchPath)
2447{
2448 /*
2449 * Open the module and check the type.
2450 */
2451 PKLDRMOD pLdrMod;
2452 int rc = kLdrModOpen(pszPath, 0 /*fFlags*/, (KCPUARCH)K_ARCH, &pLdrMod);
2453 if (rc == 0)
2454 {
2455 switch (pLdrMod->enmType)
2456 {
2457 case KLDRTYPE_EXECUTABLE_FIXED:
2458 case KLDRTYPE_EXECUTABLE_RELOCATABLE:
2459 case KLDRTYPE_EXECUTABLE_PIC:
2460 if (!fExe)
2461 rc = KERR_GENERAL_FAILURE;
2462 break;
2463
2464 case KLDRTYPE_SHARED_LIBRARY_RELOCATABLE:
2465 case KLDRTYPE_SHARED_LIBRARY_PIC:
2466 case KLDRTYPE_SHARED_LIBRARY_FIXED:
2467 if (fExe)
2468 rc = KERR_GENERAL_FAILURE;
2469 break;
2470
2471 default:
2472 rc = KERR_GENERAL_FAILURE;
2473 break;
2474 }
2475 if (rc == 0)
2476 {
2477 KI32 cImports = kLdrModNumberOfImports(pLdrMod, NULL /*pvBits*/);
2478 if (cImports >= 0)
2479 {
2480 /*
2481 * Create the entry.
2482 */
2483 KSIZE cbPath = kHlpStrLen(pszPath) + 1;
2484 PKWMODULE pMod = (PKWMODULE)kHlpAllocZ(sizeof(*pMod)
2485 + sizeof(pMod) * cImports
2486 + cbPath
2487 + cbPath * 2 * sizeof(wchar_t));
2488 if (pMod)
2489 {
2490 KBOOL fFixed;
2491
2492 pMod->cRefs = 1;
2493 pMod->offFilename = (KU16)(kHlpGetFilename(pszPath) - pszPath);
2494 pMod->uHashPath = uHashPath;
2495 pMod->fExe = fExe;
2496 pMod->fNative = K_FALSE;
2497 pMod->pLdrMod = pLdrMod;
2498 pMod->iCrtSlot = KU8_MAX;
2499 pMod->fNeedReInit = K_FALSE;
2500 pMod->fReInitOnMsPdbSrvEndpointChange = K_FALSE;
2501 pMod->pszMsPdbSrvEndpoint = NULL;
2502 pMod->u.Manual.cImpMods = (KU32)cImports;
2503#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2504 pMod->u.Manual.fRegisteredFunctionTable = K_FALSE;
2505#endif
2506 pMod->u.Manual.fUseLdBuf = K_FALSE;
2507 pMod->u.Manual.fCanDoQuick = K_FALSE;
2508 pMod->u.Manual.cQuickZeroChunks = 0;
2509 pMod->u.Manual.cQuickCopyChunks = 0;
2510 pMod->u.Manual.idxTls = KU32_MAX;
2511 pMod->u.Manual.offTlsInitData = KU32_MAX;
2512 pMod->u.Manual.cbTlsInitData = 0;
2513 pMod->u.Manual.cbTlsAlloc = 0;
2514 pMod->u.Manual.cTlsCallbacks = 0;
2515 pMod->u.Manual.offTlsCallbacks = 0;
2516 pMod->pszPath = (char *)kHlpMemCopy(&pMod->u.Manual.apImpMods[cImports + 1], pszPath, cbPath);
2517 pMod->pwszPath = (wchar_t *)(pMod->pszPath + cbPath + (cbPath & 1));
2518 kwStrToUtf16(pMod->pszPath, (wchar_t *)pMod->pwszPath, cbPath * 2);
2519 pMod->offFilenameW = (KU16)(kwPathGetFilenameW(pMod->pwszPath) - pMod->pwszPath);
2520
2521 /*
2522 * Figure out where to load it and get memory there.
2523 */
2524 fFixed = pLdrMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
2525 || pLdrMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
2526 pMod->u.Manual.pbLoad = fFixed ? (KU8 *)(KUPTR)pLdrMod->aSegments[0].LinkAddress : NULL;
2527 pMod->cbImage = (KSIZE)kLdrModSize(pLdrMod);
2528 if ( !fFixed
2529 || pLdrMod->enmType != KLDRTYPE_EXECUTABLE_FIXED /* only allow fixed executables */
2530 || (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf >= sizeof(g_abDefLdBuf)
2531 || sizeof(g_abDefLdBuf) - (KUPTR)pMod->u.Manual.pbLoad - (KUPTR)g_abDefLdBuf < pMod->cbImage)
2532 rc = kHlpPageAlloc((void **)&pMod->u.Manual.pbLoad, pMod->cbImage, KPROT_EXECUTE_READWRITE, fFixed);
2533 else
2534 pMod->u.Manual.fUseLdBuf = K_TRUE;
2535 if (rc == 0)
2536 {
2537 rc = kHlpPageAlloc(&pMod->u.Manual.pbCopy, pMod->cbImage, KPROT_READWRITE, K_FALSE);
2538 if (rc == 0)
2539 {
2540 KI32 iImp;
2541
2542 /*
2543 * Link the module (unless it's an executable image) and process the imports.
2544 */
2545 pMod->hOurMod = (HMODULE)pMod->u.Manual.pbLoad;
2546 kwLdrModuleLink(pMod);
2547 KW_LOG(("New module: %p LB %#010x %s (kLdr)\n",
2548 pMod->u.Manual.pbLoad, pMod->cbImage, pMod->pszPath));
2549 KW_LOG(("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad));
2550 kwDebuggerPrintf("TODO: .reload /f %s=%p\n", pMod->pszPath, pMod->u.Manual.pbLoad);
2551
2552 for (iImp = 0; iImp < cImports; iImp++)
2553 {
2554 char szName[1024];
2555 rc = kLdrModGetImport(pMod->pLdrMod, NULL /*pvBits*/, iImp, szName, sizeof(szName));
2556 if (rc == 0)
2557 {
2558 rc = kwLdrModuleResolveAndLookup(szName, pExeMod, pMod, pszSearchPath,
2559 &pMod->u.Manual.apImpMods[iImp]);
2560 if (rc == 0)
2561 continue;
2562 }
2563 break;
2564 }
2565
2566 if (rc == 0)
2567 {
2568 rc = kLdrModGetBits(pLdrMod, pMod->u.Manual.pbCopy, (KUPTR)pMod->u.Manual.pbLoad,
2569 kwLdrModuleGetImportCallback, pMod);
2570 if (rc == 0)
2571 {
2572#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
2573 /*
2574 * Find the function table. No validation here because the
2575 * loader did that already, right...
2576 */
2577 KU8 *pbImg = (KU8 *)pMod->u.Manual.pbCopy;
2578 IMAGE_NT_HEADERS const *pNtHdrs;
2579 IMAGE_DATA_DIRECTORY const *pXcptDir;
2580 if (((PIMAGE_DOS_HEADER)pbImg)->e_magic == IMAGE_DOS_SIGNATURE)
2581 pNtHdrs = (PIMAGE_NT_HEADERS)&pbImg[((PIMAGE_DOS_HEADER)pbImg)->e_lfanew];
2582 else
2583 pNtHdrs = (PIMAGE_NT_HEADERS)pbImg;
2584 pXcptDir = &pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
2585 kHlpAssert(pNtHdrs->Signature == IMAGE_NT_SIGNATURE);
2586 if (pXcptDir->Size > 0)
2587 {
2588 pMod->u.Manual.cFunctions = pXcptDir->Size / sizeof(pMod->u.Manual.paFunctions[0]);
2589 kHlpAssert( pMod->u.Manual.cFunctions * sizeof(pMod->u.Manual.paFunctions[0])
2590 == pXcptDir->Size);
2591 pMod->u.Manual.paFunctions = (PRUNTIME_FUNCTION)&pbImg[pXcptDir->VirtualAddress];
2592 }
2593 else
2594 {
2595 pMod->u.Manual.cFunctions = 0;
2596 pMod->u.Manual.paFunctions = NULL;
2597 }
2598#endif
2599
2600 kwLdrModuleCreateNonNativeSetupQuickZeroAndCopy(pMod);
2601
2602 rc = kwLdrModuleCreateNonNativeSetupTls(pMod);
2603 if (rc == 0)
2604 {
2605 /*
2606 * Final finish.
2607 */
2608 pMod->u.Manual.pvBits = pMod->u.Manual.pbCopy;
2609 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
2610 g_cModules++;
2611 g_cNonNativeModules++;
2612 return pMod;
2613 }
2614 }
2615 else
2616 kwErrPrintf("kLdrModGetBits failed for %s: %#x (%d)\n", pszPath, rc, rc);
2617 }
2618
2619 kwLdrModuleRelease(pMod);
2620 return NULL;
2621 }
2622
2623 kHlpPageFree(pMod->u.Manual.pbLoad, pMod->cbImage);
2624 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2625 }
2626 else if (fFixed)
2627 kwErrPrintf("Failed to allocate %#x bytes at %p\n",
2628 pMod->cbImage, (void *)(KUPTR)pLdrMod->aSegments[0].LinkAddress);
2629 else
2630 kwErrPrintf("Failed to allocate %#x bytes\n", pMod->cbImage);
2631 }
2632 }
2633 }
2634 kLdrModClose(pLdrMod);
2635 }
2636 else
2637 kwErrPrintf("kLdrOpen failed with %#x (%d) for %s\n", rc, rc, pszPath);
2638 return NULL;
2639}
2640
2641
2642/** Implements FNKLDRMODGETIMPORT, used by kwLdrModuleCreate. */
2643static int kwLdrModuleGetImportCallback(PKLDRMOD pMod, KU32 iImport, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol,
2644 const char *pszVersion, PKLDRADDR puValue, KU32 *pfKind, void *pvUser)
2645{
2646 PKWMODULE pCurMod = (PKWMODULE)pvUser;
2647 PKWMODULE pImpMod = pCurMod->u.Manual.apImpMods[iImport];
2648 int rc;
2649 K_NOREF(pMod);
2650
2651 if (pImpMod->fNative)
2652 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP,
2653 iSymbol, pchSymbol, cchSymbol, pszVersion,
2654 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2655 puValue, pfKind);
2656 else
2657 rc = kLdrModQuerySymbol(pImpMod->pLdrMod, pImpMod->u.Manual.pvBits, (KUPTR)pImpMod->u.Manual.pbLoad,
2658 iSymbol, pchSymbol, cchSymbol, pszVersion,
2659 NULL /*pfnGetForwarder*/, NULL /*pvUSer*/,
2660 puValue, pfKind);
2661 if (rc == 0)
2662 {
2663 KU32 i = g_cSandboxReplacements;
2664 while (i-- > 0)
2665 if ( g_aSandboxReplacements[i].cchFunction == cchSymbol
2666 && kHlpMemComp(g_aSandboxReplacements[i].pszFunction, pchSymbol, cchSymbol) == 0)
2667 {
2668 if ( !g_aSandboxReplacements[i].pszModule
2669 || kHlpStrICompAscii(g_aSandboxReplacements[i].pszModule, &pImpMod->pszPath[pImpMod->offFilename]) == 0)
2670 {
2671 if ( pCurMod->fExe
2672 || !g_aSandboxReplacements[i].fOnlyExe)
2673 {
2674 KW_LOG(("replacing %s!%s\n",&pImpMod->pszPath[pImpMod->offFilename], g_aSandboxReplacements[i].pszFunction));
2675 *puValue = g_aSandboxReplacements[i].pfnReplacement;
2676 }
2677 break;
2678 }
2679 }
2680 }
2681
2682 //printf("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc);
2683 KW_LOG(("iImport=%u (%s) %*.*s rc=%d\n", iImport, &pImpMod->pszPath[pImpMod->offFilename], cchSymbol, cchSymbol, pchSymbol, rc));
2684 return rc;
2685
2686}
2687
2688
2689/**
2690 * Gets the main entrypoint for a module.
2691 *
2692 * @returns 0 on success, KERR on failure
2693 * @param pMod The module.
2694 * @param puAddrMain Where to return the address.
2695 */
2696static int kwLdrModuleQueryMainEntrypoint(PKWMODULE pMod, KUPTR *puAddrMain)
2697{
2698 KLDRADDR uLdrAddrMain;
2699 int rc = kLdrModQueryMainEntrypoint(pMod->pLdrMod, pMod->u.Manual.pvBits, (KUPTR)pMod->u.Manual.pbLoad, &uLdrAddrMain);
2700 if (rc == 0)
2701 {
2702 *puAddrMain = (KUPTR)uLdrAddrMain;
2703 return 0;
2704 }
2705 return rc;
2706}
2707
2708
2709/**
2710 * Whether to apply g_aSandboxNativeReplacements to the imports of this module.
2711 *
2712 * @returns K_TRUE/K_FALSE.
2713 * @param pszFilename The filename (no path).
2714 * @param enmLocation The location.
2715 */
2716static KBOOL kwLdrModuleShouldDoNativeReplacements(const char *pszFilename, KWLOCATION enmLocation)
2717{
2718 if (enmLocation != KWLOCATION_SYSTEM32)
2719 return K_TRUE;
2720 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2721 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2722 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2723}
2724
2725
2726/**
2727 * Lazily initializes the g_pWinSys32 variable.
2728 */
2729static PKFSDIR kwLdrResolveWinSys32(void)
2730{
2731 KFSLOOKUPERROR enmError;
2732 PKFSDIR pWinSys32;
2733
2734 /* Get the path first. */
2735 char szSystem32[MAX_PATH];
2736 if (GetSystemDirectoryA(szSystem32, sizeof(szSystem32)) >= sizeof(szSystem32))
2737 {
2738 kwErrPrintf("GetSystemDirectory failed: %u\n", GetLastError());
2739 strcpy(szSystem32, "C:\\Windows\\System32");
2740 }
2741
2742 /* Look it up and verify it. */
2743 pWinSys32 = (PKFSDIR)kFsCacheLookupA(g_pFsCache, szSystem32, &enmError);
2744 if (pWinSys32)
2745 {
2746 if (pWinSys32->Obj.bObjType == KFSOBJ_TYPE_DIR)
2747 {
2748 g_pWinSys32 = pWinSys32;
2749 return pWinSys32;
2750 }
2751
2752 kwErrPrintf("System directory '%s' isn't of 'DIR' type: %u\n", szSystem32, g_pWinSys32->Obj.bObjType);
2753 }
2754 else
2755 kwErrPrintf("Failed to lookup system directory '%s': %u\n", szSystem32, enmError);
2756 return NULL;
2757}
2758
2759
2760/**
2761 * Whether we can load this DLL natively or not.
2762 *
2763 * @returns K_TRUE/K_FALSE.
2764 * @param pszFilename The filename (no path).
2765 * @param enmLocation The location.
2766 * @param pszFullPath The full filename and path.
2767 */
2768static KBOOL kwLdrModuleCanLoadNatively(const char *pszFilename, KWLOCATION enmLocation, const char *pszFullPath)
2769{
2770 if (enmLocation == KWLOCATION_SYSTEM32)
2771 return K_TRUE;
2772 if (enmLocation == KWLOCATION_UNKNOWN_NATIVE)
2773 return K_TRUE;
2774
2775 /* If the location is unknown, we must check if it's some dynamic loading
2776 of a SYSTEM32 DLL with a full path. We do not want to load these ourselves! */
2777 if (enmLocation == KWLOCATION_UNKNOWN)
2778 {
2779 PKFSDIR pWinSys32 = g_pWinSys32;
2780 if (!pWinSys32)
2781 pWinSys32 = kwLdrResolveWinSys32();
2782 if (pWinSys32)
2783 {
2784 KFSLOOKUPERROR enmError;
2785 PKFSOBJ pFsObj = kFsCacheLookupA(g_pFsCache, pszFullPath, &enmError);
2786 if (pFsObj)
2787 {
2788 KBOOL fInWinSys32 = pFsObj->pParent == pWinSys32;
2789 kFsCacheObjRelease(g_pFsCache, pFsObj);
2790 if (fInWinSys32)
2791 return K_TRUE;
2792 }
2793 }
2794 }
2795
2796 return kHlpStrNICompAscii(pszFilename, TUPLE("msvc")) == 0
2797 || kHlpStrNICompAscii(pszFilename, TUPLE("msdis")) == 0
2798 || kHlpStrNICompAscii(pszFilename, TUPLE("mspdb")) == 0;
2799}
2800
2801
2802/**
2803 * Check if the path leads to a regular file (that exists).
2804 *
2805 * @returns K_TRUE / K_FALSE
2806 * @param pszPath Path to the file to check out.
2807 */
2808static KBOOL kwLdrModuleIsRegularFile(const char *pszPath)
2809{
2810 /* For stuff with .DLL extensions, we can use the GetFileAttribute cache to speed this up! */
2811 KSIZE cchPath = kHlpStrLen(pszPath);
2812 if ( cchPath > 3
2813 && pszPath[cchPath - 4] == '.'
2814 && (pszPath[cchPath - 3] == 'd' || pszPath[cchPath - 3] == 'D')
2815 && (pszPath[cchPath - 2] == 'l' || pszPath[cchPath - 2] == 'L')
2816 && (pszPath[cchPath - 1] == 'l' || pszPath[cchPath - 1] == 'L') )
2817 {
2818 KFSLOOKUPERROR enmError;
2819 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
2820 if (pFsObj)
2821 {
2822 KBOOL fRc = pFsObj->bObjType == KFSOBJ_TYPE_FILE;
2823 kFsCacheObjRelease(g_pFsCache, pFsObj);
2824 return fRc;
2825 }
2826 }
2827 else
2828 {
2829 BirdStat_T Stat;
2830 int rc = birdStatFollowLink(pszPath, &Stat);
2831 if (rc == 0)
2832 {
2833 if (S_ISREG(Stat.st_mode))
2834 return K_TRUE;
2835 }
2836 }
2837 return K_FALSE;
2838}
2839
2840
2841/**
2842 * Worker for kwLdrModuleResolveAndLookup that checks out one possibility.
2843 *
2844 * If the file exists, we consult the module hash table before trying to load it
2845 * off the disk.
2846 *
2847 * @returns Pointer to module on success, NULL if not found, ~(KUPTR)0 on
2848 * failure.
2849 * @param pszPath The name of the import module.
2850 * @param enmLocation The location we're searching. This is used in
2851 * the heuristics for determining if we can use the
2852 * native loader or need to sandbox the DLL.
2853 * @param pExe The executable (optional).
2854 * @param pszSearchPath The PATH to search (optional).
2855 */
2856static PKWMODULE kwLdrModuleTryLoadDll(const char *pszPath, KWLOCATION enmLocation, PKWMODULE pExeMod, const char *pszSearchPath)
2857{
2858 /*
2859 * Does the file exists and is it a regular file?
2860 */
2861 if (kwLdrModuleIsRegularFile(pszPath))
2862 {
2863 /*
2864 * Yes! Normalize it and look it up in the hash table.
2865 */
2866 char szNormPath[1024];
2867 int rc = kwPathNormalize(pszPath, szNormPath, sizeof(szNormPath));
2868 if (rc == 0)
2869 {
2870 const char *pszName;
2871 KU32 const uHashPath = kwStrHash(szNormPath);
2872 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
2873 PKWMODULE pMod = g_apModules[idxHash];
2874 if (pMod)
2875 {
2876 do
2877 {
2878 if ( pMod->uHashPath == uHashPath
2879 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
2880 return kwLdrModuleRetain(pMod);
2881 pMod = pMod->pNextHash;
2882 } while (pMod);
2883 }
2884
2885 /*
2886 * Not in the hash table, so we have to load it from scratch.
2887 */
2888 pszName = kHlpGetFilename(szNormPath);
2889 if (kwLdrModuleCanLoadNatively(pszName, enmLocation, szNormPath))
2890 pMod = kwLdrModuleCreateNative(szNormPath, uHashPath,
2891 kwLdrModuleShouldDoNativeReplacements(pszName, enmLocation));
2892 else
2893 pMod = kwLdrModuleCreateNonNative(szNormPath, uHashPath, K_FALSE /*fExe*/, pExeMod, pszSearchPath);
2894 if (pMod)
2895 return pMod;
2896 return (PKWMODULE)~(KUPTR)0;
2897 }
2898 }
2899 return NULL;
2900}
2901
2902
2903/**
2904 * Gets a reference to the module by the given name.
2905 *
2906 * We must do the search path thing, as our hash table may multiple DLLs with
2907 * the same base name due to different tools version and similar. We'll use a
2908 * modified search sequence, though. No point in searching the current
2909 * directory for instance.
2910 *
2911 * @returns 0 on success, KERR on failure.
2912 * @param pszName The name of the import module.
2913 * @param pExe The executable (optional).
2914 * @param pImporter The module doing the importing (optional).
2915 * @param pszSearchPath The PATH to search (optional).
2916 * @param ppMod Where to return the module pointer w/ reference.
2917 */
2918static int kwLdrModuleResolveAndLookup(const char *pszName, PKWMODULE pExe, PKWMODULE pImporter,
2919 const char *pszSearchPath, PKWMODULE *ppMod)
2920{
2921 KSIZE const cchName = kHlpStrLen(pszName);
2922 char szPath[1024];
2923 char *psz;
2924 PKWMODULE pMod = NULL;
2925 KBOOL fNeedSuffix = *kHlpGetExt(pszName) == '\0' && kHlpGetFilename(pszName) == pszName;
2926 KSIZE cchSuffix = fNeedSuffix ? 4 : 0;
2927
2928
2929 /* The import path. */
2930 if (pMod == NULL && pImporter != NULL)
2931 {
2932 if (pImporter->offFilename + cchName + cchSuffix >= sizeof(szPath))
2933 return KERR_BUFFER_OVERFLOW;
2934
2935 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pImporter->pszPath, pImporter->offFilename), pszName, cchName + 1);
2936 if (fNeedSuffix)
2937 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2938 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_IMPORTER_DIR, pExe, pszSearchPath);
2939 }
2940
2941 /* Application directory first. */
2942 if (pMod == NULL && pExe != NULL && pExe != pImporter)
2943 {
2944 if (pExe->offFilename + cchName + cchSuffix >= sizeof(szPath))
2945 return KERR_BUFFER_OVERFLOW;
2946 psz = (char *)kHlpMemPCopy(kHlpMemPCopy(szPath, pExe->pszPath, pExe->offFilename), pszName, cchName + 1);
2947 if (fNeedSuffix)
2948 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2949 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_EXE_DIR, pExe, pszSearchPath);
2950 }
2951
2952 /* The windows directory. */
2953 if (pMod == NULL)
2954 {
2955 UINT cchDir = GetSystemDirectoryA(szPath, sizeof(szPath));
2956 if ( cchDir <= 2
2957 || cchDir + 1 + cchName + cchSuffix >= sizeof(szPath))
2958 return KERR_BUFFER_OVERFLOW;
2959 szPath[cchDir++] = '\\';
2960 psz = (char *)kHlpMemPCopy(&szPath[cchDir], pszName, cchName + 1);
2961 if (fNeedSuffix)
2962 kHlpMemCopy(psz - 1, ".dll", sizeof(".dll"));
2963 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2964 }
2965
2966 /* The path. */
2967 if ( pMod == NULL
2968 && pszSearchPath)
2969 {
2970 const char *pszCur = pszSearchPath;
2971 while (*pszCur != '\0')
2972 {
2973 /* Find the end of the component */
2974 KSIZE cch = 0;
2975 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
2976 cch++;
2977
2978 if ( cch > 0 /* wrong, but whatever */
2979 && cch + 1 + cchName + cchSuffix < sizeof(szPath))
2980 {
2981 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
2982 if ( szPath[cch - 1] != ':'
2983 && szPath[cch - 1] != '/'
2984 && szPath[cch - 1] != '\\')
2985 *pszDst++ = '\\';
2986 pszDst = kHlpMemPCopy(pszDst, pszName, cchName);
2987 if (fNeedSuffix)
2988 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
2989 *pszDst = '\0';
2990
2991 pMod = kwLdrModuleTryLoadDll(szPath, KWLOCATION_SYSTEM32, pExe, pszSearchPath);
2992 if (pMod)
2993 break;
2994 }
2995
2996 /* Advance */
2997 pszCur += cch;
2998 while (*pszCur == ';')
2999 pszCur++;
3000 }
3001 }
3002
3003 /* Return. */
3004 if (pMod != NULL && pMod != (PKWMODULE)~(KUPTR)0)
3005 {
3006 *ppMod = pMod;
3007 return 0;
3008 }
3009 *ppMod = NULL;
3010 return KERR_GENERAL_FAILURE;
3011}
3012
3013
3014/**
3015 * Creates a CRT slot for the given module.
3016 *
3017 * @returns 0 on success, non-zero on failure.
3018 * @param pModule The module.
3019 */
3020static int kwLdrModuleCreateCrtSlot(PKWMODULE pModule)
3021{
3022 KSIZE iSlot;
3023 kHlpAssert(pModule->iCrtSlot == KU8_MAX);
3024 for (iSlot = 0; iSlot < K_ELEMENTS(g_aCrtSlots); iSlot++)
3025 if (g_aCrtSlots[iSlot].pModule == NULL)
3026 {
3027 KLDRADDR uAddr;
3028 int rc;
3029
3030 /* Do the linking: */
3031 g_aCrtSlots[iSlot].pModule = pModule;
3032 g_aCrtSlots[iSlot].iSlot = (KU32)iSlot;
3033 pModule->iCrtSlot = (KU8)iSlot;
3034
3035 /* resolve symbols: */
3036 rc = kLdrModQuerySymbol(pModule->pLdrMod, NULL /*pvBits*/, KLDRMOD_BASEADDRESS_MAP, KU32_MAX, "malloc", 6,
3037 NULL /*pvszVersion*/, NULL /*pfnGetForwarder*/, NULL /*pvUser*/, &uAddr, NULL);
3038 *(KUPTR *)&g_aCrtSlots[iSlot].pfnMalloc = rc == 0 ? (KUPTR)uAddr : 0;
3039 if (rc != 0)
3040 kwErrPrintf("Failed to resolved 'malloc' in '%s': %d\n", pModule->pszPath, rc);
3041
3042 return 0;
3043 }
3044 kwErrPrintf("Out of CRT slots!\n");
3045 return KERR_NO_MEMORY;
3046}
3047
3048
3049/**
3050 * Locates the module structure for an already loaded native module.
3051 *
3052 * This will create a module structure if needed.
3053 *
3054 * @returns Pointer to the module structure on success, NULL on failure.
3055 * @param pszName The name of the module.
3056 * @param fEnsureCrtSlot Whether to ensure that it has a valid CRT slot.
3057 */
3058static PKWMODULE kwLdrModuleForLoadedNative(const char *pszName, KBOOL fEnsureCrtSlot)
3059{
3060 /*
3061 * Locate the module and get a normalized path for it.
3062 */
3063 HANDLE hModule = GetModuleHandleA(pszName);
3064 if (hModule)
3065 {
3066 char szModPath[1024];
3067 if (GetModuleFileNameA(hModule, szModPath, sizeof(szModPath)) > 0)
3068 {
3069 char szNormPath[1024];
3070 int rc = kwPathNormalize(szModPath, szNormPath, sizeof(szNormPath));
3071 if (rc == 0)
3072 {
3073 /*
3074 * Hash the path and look it up.
3075 */
3076 KU32 uHashPath;
3077 KSIZE const cchPath = kwStrHashEx(szNormPath, &uHashPath);
3078 unsigned idxHash = uHashPath % K_ELEMENTS(g_apModules);
3079 PKWMODULE pMod = g_apModules[idxHash];
3080 if (pMod)
3081 {
3082 do
3083 {
3084 if ( pMod->uHashPath == uHashPath
3085 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
3086 {
3087 kwLdrModuleRetain(pMod);
3088 break;
3089 }
3090 pMod = pMod->pNextHash;
3091 } while (pMod);
3092 }
3093
3094 /*
3095 * If not in the hash table, so create a module entry.
3096 */
3097 if (!pMod)
3098 {
3099 PKLDRMOD pLdrMod;
3100 rc = kLdrModOpenNativeByHandle((KUPTR)hModule, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
3101 if (rc == 0)
3102 {
3103 pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cchPath + 1, uHashPath,
3104 K_FALSE /*fDoReplacements*/);
3105 if (!pMod)
3106 {
3107 kLdrModClose(pLdrMod);
3108 kwErrPrintf("out of memory\n");
3109 }
3110 }
3111 else
3112 kwErrPrintf("kLdrModOpenNativeByHandle failed for %p / '%s': %d\n", hModule, pszName, rc);
3113 }
3114 if (pMod)
3115 {
3116 /*
3117 * Create a CRT slot for the module if necessary.
3118 */
3119 if (!fEnsureCrtSlot || pMod->iCrtSlot != KU8_MAX)
3120 return pMod;
3121 rc = kwLdrModuleCreateCrtSlot(pMod);
3122 if (rc == 0)
3123 return pMod;
3124 kwLdrModuleRelease(pMod);
3125 }
3126 }
3127 else
3128 kwErrPrintf("kwPathNormalize failed for '%s' (%s): %u!\n", szModPath, pszName, GetLastError());
3129 }
3130 else
3131 kwErrPrintf("GetModuleFileNameA failed for '%s': %u!\n", pszName, GetLastError());
3132 }
3133 else
3134 kwErrPrintf("Module '%s' was not found by GetModuleHandleA!\n", pszName);
3135 return NULL;
3136}
3137
3138
3139/**
3140 * Does the TLS memory initialization for a module on the current thread.
3141 *
3142 * @returns 0 on success, error on failure.
3143 * @param pMod The module.
3144 */
3145static int kwLdrCallTlsAllocateAndInit(PKWMODULE pMod)
3146{
3147 if (pMod->u.Manual.idxTls != KU32_MAX)
3148 {
3149 PTEB pTeb = NtCurrentTeb();
3150 void **ppvTls = *(void ***)( (KUPTR)pTeb + (sizeof(void *) == 4 ? 0x2c : 0x58) );
3151 KU8 *pbData = (KU8 *)ppvTls[pMod->u.Manual.idxTls];
3152 KWLDR_LOG(("%s: TLS: Initializing %#x (%#x), idxTls=%d\n",
3153 pMod->pszPath, pbData, pMod->u.Manual.cbTlsAlloc, pMod->u.Manual.cbTlsInitData, pMod->u.Manual.idxTls));
3154 if (pMod->u.Manual.cbTlsInitData < pMod->u.Manual.cbTlsAlloc)
3155 kHlpMemSet(&pbData[pMod->u.Manual.cbTlsInitData], 0, pMod->u.Manual.cbTlsAlloc);
3156 if (pMod->u.Manual.cbTlsInitData)
3157 kHlpMemCopy(pbData, &pMod->u.Manual.pbCopy[pMod->u.Manual.offTlsInitData], pMod->u.Manual.cbTlsInitData);
3158 }
3159 return 0;
3160}
3161
3162
3163/**
3164 * Does the TLS callbacks for a module.
3165 *
3166 * @param pMod The module.
3167 * @param dwReason The callback reason.
3168 */
3169static void kwLdrCallTlsCallbacks(PKWMODULE pMod, DWORD dwReason)
3170{
3171 if (pMod->u.Manual.cTlsCallbacks)
3172 {
3173 PIMAGE_TLS_CALLBACK *pCallback = (PIMAGE_TLS_CALLBACK *)&pMod->u.Manual.pbLoad[pMod->u.Manual.offTlsCallbacks];
3174 do
3175 {
3176 KWLDR_LOG(("%s: Calling TLS callback %p(%p,%#x,0)\n", pMod->pszPath, *pCallback, pMod->hOurMod, dwReason));
3177 (*pCallback)(pMod->hOurMod, dwReason, 0);
3178 } while (*++pCallback);
3179 }
3180}
3181
3182
3183/**
3184 * Does module initialization starting at @a pMod.
3185 *
3186 * This is initially used on the executable. Later it is used by the
3187 * LoadLibrary interceptor.
3188 *
3189 * @returns 0 on success, error on failure.
3190 * @param pMod The module to initialize.
3191 */
3192static int kwLdrModuleInitTree(PKWMODULE pMod)
3193{
3194 int rc = 0;
3195 if (!pMod->fNative)
3196 {
3197 KWLDR_LOG(("kwLdrModuleInitTree: enmState=%#x idxTls=%u %s\n",
3198 pMod->u.Manual.enmState, pMod->u.Manual.idxTls, pMod->pszPath));
3199
3200 /*
3201 * Need to copy bits?
3202 */
3203 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_BITS)
3204 {
3205 if (pMod->u.Manual.fUseLdBuf)
3206 {
3207#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3208 if (g_pModInLdBuf != NULL && g_pModInLdBuf != pMod && pMod->u.Manual.fRegisteredFunctionTable)
3209 {
3210 BOOLEAN fRc = RtlDeleteFunctionTable(pMod->u.Manual.paFunctions);
3211 kHlpAssert(fRc); K_NOREF(fRc);
3212 }
3213#endif
3214 g_pModPrevInLdBuf = g_pModInLdBuf;
3215 g_pModInLdBuf = pMod;
3216 }
3217
3218 /* Do quick zeroing and copying when we can. */
3219 pMod->u.Manual.fCanDoQuick = K_FALSE;
3220 if ( pMod->u.Manual.fCanDoQuick
3221 && ( !pMod->u.Manual.fUseLdBuf
3222 || g_pModPrevInLdBuf == pMod))
3223 {
3224 /* Zero first. */
3225 kHlpAssert(pMod->u.Manual.cQuickZeroChunks <= 3);
3226 switch (pMod->u.Manual.cQuickZeroChunks)
3227 {
3228 case 3: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[2].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[2].cbToZero);
3229 case 2: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[1].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[1].cbToZero);
3230 case 1: kHlpMemSet(pMod->u.Manual.aQuickZeroChunks[0].pbDst, 0, pMod->u.Manual.aQuickZeroChunks[0].cbToZero);
3231 case 0: break;
3232 }
3233
3234 /* Then copy. */
3235 kHlpAssert(pMod->u.Manual.cQuickCopyChunks > 0);
3236 kHlpAssert(pMod->u.Manual.cQuickCopyChunks <= 3);
3237 switch (pMod->u.Manual.cQuickCopyChunks)
3238 {
3239 case 3: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[2].pbDst, pMod->u.Manual.aQuickCopyChunks[2].pbSrc,
3240 pMod->u.Manual.aQuickCopyChunks[2].cbToCopy);
3241 case 2: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[1].pbDst, pMod->u.Manual.aQuickCopyChunks[1].pbSrc,
3242 pMod->u.Manual.aQuickCopyChunks[1].cbToCopy);
3243 case 1: kHlpMemCopy(pMod->u.Manual.aQuickCopyChunks[0].pbDst, pMod->u.Manual.aQuickCopyChunks[0].pbSrc,
3244 pMod->u.Manual.aQuickCopyChunks[0].cbToCopy);
3245 case 0: break;
3246 }
3247 }
3248 /* Must copy the whole image. */
3249 else
3250 {
3251 kHlpMemCopy(pMod->u.Manual.pbLoad, pMod->u.Manual.pbCopy, pMod->cbImage);
3252 pMod->u.Manual.fCanDoQuick = K_TRUE;
3253 }
3254 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_INIT;
3255 }
3256
3257#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_AMD64)
3258 /*
3259 * Need to register function table?
3260 */
3261 if ( !pMod->u.Manual.fRegisteredFunctionTable
3262 && pMod->u.Manual.cFunctions > 0)
3263 {
3264 pMod->u.Manual.fRegisteredFunctionTable = RtlAddFunctionTable(pMod->u.Manual.paFunctions,
3265 pMod->u.Manual.cFunctions,
3266 (KUPTR)pMod->u.Manual.pbLoad) != FALSE;
3267 kHlpAssert(pMod->u.Manual.fRegisteredFunctionTable);
3268 }
3269#endif
3270
3271
3272 if (pMod->u.Manual.enmState == KWMODSTATE_NEEDS_INIT)
3273 {
3274 /*
3275 * Must do imports first, but mark our module as being initialized to avoid
3276 * endless recursion should there be a dependency loop.
3277 */
3278 KSIZE iImp;
3279 pMod->u.Manual.enmState = KWMODSTATE_BEING_INITED;
3280
3281 for (iImp = 0; iImp < pMod->u.Manual.cImpMods; iImp++)
3282 {
3283 rc = kwLdrModuleInitTree(pMod->u.Manual.apImpMods[iImp]);
3284 if (rc != 0)
3285 return rc;
3286 }
3287
3288 /* Do TLS allocations for module init? */
3289 rc = kwLdrCallTlsAllocateAndInit(pMod);
3290 if (rc != 0)
3291 return rc;
3292 if (pMod->u.Manual.cTlsCallbacks > 0)
3293 kwLdrCallTlsCallbacks(pMod, DLL_PROCESS_ATTACH);
3294
3295 /* Finally call the entry point. */
3296 rc = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3297 if (rc == 0)
3298 pMod->u.Manual.enmState = KWMODSTATE_READY;
3299 else
3300 pMod->u.Manual.enmState = KWMODSTATE_INIT_FAILED;
3301 }
3302 }
3303 /*
3304 * Special hack to disconnect mspdbXXX.dll from mspdbsrv.exe when
3305 * _MSPDBSRV_ENDPOINT_ changes value.
3306 */
3307 else if (pMod->fNeedReInit)
3308 {
3309 int rc2;
3310 KWLDR_LOG(("kwLdrModuleInitTree: mspdb re-init hack: %s\n", pMod->pszPath));
3311 //fprintf(stderr, "%d: kwLdrModuleInitTree: mspdb re-init hack: %s\n", getpid(), kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"))); fflush(stderr);
3312 rc = kLdrModCallTerm(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3313 rc2 = kLdrModCallInit(pMod->pLdrMod, pMod->u.Manual.pbLoad, (KUPTR)pMod->hOurMod);
3314 if (!rc && !rc2)
3315 { /* likely */ }
3316 else
3317 {
3318 kwErrPrintf("Re-init of '%s' failed: rc=%d rc2=%d\n", pMod->pszPath, rc, rc2);
3319 if (rc2 && !rc)
3320 rc = rc2;
3321 }
3322 pMod->fNeedReInit = K_FALSE;
3323 }
3324 return rc;
3325}
3326
3327
3328/**
3329 * Looks up a module handle for a tool.
3330 *
3331 * @returns Referenced loader module on success, NULL on if not found.
3332 * @param pTool The tool.
3333 * @param hmod The module handle.
3334 */
3335static PKWMODULE kwToolLocateModuleByHandle(PKWTOOL pTool, HMODULE hmod)
3336{
3337 KUPTR const uHMod = (KUPTR)hmod;
3338 PKWMODULE *papMods;
3339 KU32 iEnd;
3340 KU32 i;
3341 PKWDYNLOAD pDynLoad;
3342
3343 /* The executable. */
3344 if ( hmod == NULL
3345 || pTool->u.Sandboxed.pExe->hOurMod == hmod)
3346 return kwLdrModuleRetain(pTool->u.Sandboxed.pExe);
3347
3348 /*
3349 * Binary lookup using the module table.
3350 */
3351 papMods = pTool->u.Sandboxed.papModules;
3352 iEnd = pTool->u.Sandboxed.cModules;
3353 if (iEnd)
3354 {
3355 KU32 iStart = 0;
3356 i = iEnd / 2;
3357 for (;;)
3358 {
3359 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3360 if (uHMod < uHModThis)
3361 {
3362 iEnd = i--;
3363 if (iStart <= i)
3364 { }
3365 else
3366 break;
3367 }
3368 else if (uHMod != uHModThis)
3369 {
3370 iStart = ++i;
3371 if (i < iEnd)
3372 { }
3373 else
3374 break;
3375 }
3376 else
3377 return kwLdrModuleRetain(papMods[i]);
3378
3379 i = iStart + (iEnd - iStart) / 2;
3380 }
3381
3382#ifndef NDEBUG
3383 iStart = pTool->u.Sandboxed.cModules;
3384 while (--iStart > 0)
3385 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3386 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3387#endif
3388 }
3389
3390 /*
3391 * Dynamically loaded images.
3392 */
3393 for (pDynLoad = pTool->u.Sandboxed.pDynLoadHead; pDynLoad != NULL; pDynLoad = pDynLoad->pNext)
3394 if (pDynLoad->hmod == hmod)
3395 {
3396 if (pDynLoad->pMod)
3397 return kwLdrModuleRetain(pDynLoad->pMod);
3398 KWFS_TODO();
3399 return NULL;
3400 }
3401
3402 return NULL;
3403}
3404
3405/**
3406 * Adds the given module to the tool import table.
3407 *
3408 * @returns 0 on success, non-zero on failure.
3409 * @param pTool The tool.
3410 * @param pMod The module.
3411 */
3412static int kwToolAddModule(PKWTOOL pTool, PKWMODULE pMod)
3413{
3414 /*
3415 * Binary lookup. Locating the right slot for it, return if already there.
3416 */
3417 KUPTR const uHMod = (KUPTR)pMod->hOurMod;
3418 PKWMODULE *papMods = pTool->u.Sandboxed.papModules;
3419 KU32 iEnd = pTool->u.Sandboxed.cModules;
3420 KU32 i;
3421 if (iEnd)
3422 {
3423 KU32 iStart = 0;
3424 i = iEnd / 2;
3425 for (;;)
3426 {
3427 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
3428 if (uHMod < uHModThis)
3429 {
3430 iEnd = i;
3431 if (iStart < i)
3432 { }
3433 else
3434 break;
3435 }
3436 else if (uHMod != uHModThis)
3437 {
3438 iStart = ++i;
3439 if (i < iEnd)
3440 { }
3441 else
3442 break;
3443 }
3444 else
3445 {
3446 /* Already there in the table. */
3447 return 0;
3448 }
3449
3450 i = iStart + (iEnd - iStart) / 2;
3451 }
3452#ifndef NDEBUG
3453 iStart = pTool->u.Sandboxed.cModules;
3454 while (--iStart > 0)
3455 {
3456 kHlpAssert(papMods[iStart] != pMod);
3457 kHlpAssert((KUPTR)papMods[iStart]->hOurMod != uHMod);
3458 }
3459 kHlpAssert(i == 0 || (KUPTR)papMods[i - 1]->hOurMod < uHMod);
3460 kHlpAssert(i == pTool->u.Sandboxed.cModules || (KUPTR)papMods[i]->hOurMod > uHMod);
3461#endif
3462 }
3463 else
3464 i = 0;
3465
3466 /*
3467 * Grow the table?
3468 */
3469 if ((pTool->u.Sandboxed.cModules % 16) == 0)
3470 {
3471 void *pvNew = kHlpRealloc(papMods, sizeof(papMods[0]) * (pTool->u.Sandboxed.cModules + 16));
3472 if (!pvNew)
3473 return KERR_NO_MEMORY;
3474 pTool->u.Sandboxed.papModules = papMods = (PKWMODULE *)pvNew;
3475 }
3476
3477 /* Insert it. */
3478 if (i != pTool->u.Sandboxed.cModules)
3479 kHlpMemMove(&papMods[i + 1], &papMods[i], (pTool->u.Sandboxed.cModules - i) * sizeof(papMods[0]));
3480 papMods[i] = kwLdrModuleRetain(pMod);
3481 pTool->u.Sandboxed.cModules++;
3482 KW_LOG(("kwToolAddModule: %u modules after adding %p=%s\n", pTool->u.Sandboxed.cModules, uHMod, pMod->pszPath));
3483 return 0;
3484}
3485
3486
3487/**
3488 * Adds the given module and all its imports to the
3489 *
3490 * @returns 0 on success, non-zero on failure.
3491 * @param pTool The tool.
3492 * @param pMod The module.
3493 */
3494static int kwToolAddModuleAndImports(PKWTOOL pTool, PKWMODULE pMod)
3495{
3496 int rc = kwToolAddModule(pTool, pMod);
3497 if (!pMod->fNative && rc == 0)
3498 {
3499 KSIZE iImp = pMod->u.Manual.cImpMods;
3500 while (iImp-- > 0)
3501 {
3502 rc = kwToolAddModuleAndImports(pTool, pMod->u.Manual.apImpMods[iImp]);
3503 if (rc == 0)
3504 { }
3505 else
3506 break;
3507 }
3508 }
3509 return 0;
3510}
3511
3512
3513/**
3514 * Creates a tool entry and inserts it.
3515 *
3516 * @returns Pointer to the tool entry. NULL on failure.
3517 * @param pToolFsObj The file object of the tool. The created tool
3518 * will be associated with it.
3519 *
3520 * A reference is donated by the caller and must be
3521 * released.
3522 * @param pszSearchPath The PATH environment variable value, or NULL.
3523 */
3524static PKWTOOL kwToolEntryCreate(PKFSOBJ pToolFsObj, const char *pszSearchPath)
3525{
3526 KSIZE cwcPath = pToolFsObj->cwcParent + pToolFsObj->cwcName + 1;
3527 KSIZE cbPath = pToolFsObj->cchParent + pToolFsObj->cchName + 1;
3528 PKWTOOL pTool = (PKWTOOL)kFsCacheObjAddUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL,
3529 sizeof(*pTool) + cwcPath * sizeof(wchar_t) + cbPath);
3530 if (pTool)
3531 {
3532 KBOOL fRc;
3533 pTool->pwszPath = (wchar_t const *)(pTool + 1);
3534 fRc = kFsCacheObjGetFullPathW(pToolFsObj, (wchar_t *)pTool->pwszPath, cwcPath, '\\');
3535 kHlpAssert(fRc); K_NOREF(fRc);
3536
3537 pTool->pszPath = (char const *)&pTool->pwszPath[cwcPath];
3538 fRc = kFsCacheObjGetFullPathA(pToolFsObj, (char *)pTool->pszPath, cbPath, '\\');
3539 kHlpAssert(fRc);
3540
3541 pTool->enmType = KWTOOLTYPE_SANDBOXED;
3542 pTool->u.Sandboxed.pExe = kwLdrModuleCreateNonNative(pTool->pszPath, kwStrHash(pTool->pszPath), K_TRUE /*fExe*/,
3543 NULL /*pEexeMod*/, pszSearchPath);
3544 if (pTool->u.Sandboxed.pExe)
3545 {
3546 int rc = kwLdrModuleQueryMainEntrypoint(pTool->u.Sandboxed.pExe, &pTool->u.Sandboxed.uMainAddr);
3547 if (rc == 0)
3548 {
3549 if (kHlpStrICompAscii(pToolFsObj->pszName, "cl.exe") == 0)
3550 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_CL;
3551 else if (kHlpStrICompAscii(pToolFsObj->pszName, "link.exe") == 0)
3552 pTool->u.Sandboxed.enmHint = KWTOOLHINT_VISUAL_CPP_LINK;
3553 else
3554 pTool->u.Sandboxed.enmHint = KWTOOLHINT_NONE;
3555 kwToolAddModuleAndImports(pTool, pTool->u.Sandboxed.pExe);
3556 }
3557 else
3558 {
3559 kwErrPrintf("Failed to get entrypoint for '%s': %u\n", pTool->pszPath, rc);
3560 kwLdrModuleRelease(pTool->u.Sandboxed.pExe);
3561 pTool->u.Sandboxed.pExe = NULL;
3562 pTool->enmType = KWTOOLTYPE_EXEC;
3563 }
3564 }
3565 else
3566 pTool->enmType = KWTOOLTYPE_EXEC;
3567
3568 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3569 g_cTools++;
3570 return pTool;
3571 }
3572 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3573 return NULL;
3574}
3575
3576
3577/**
3578 * Looks up the given tool, creating a new tool table entry if necessary.
3579 *
3580 * @returns Pointer to the tool entry. NULL on failure (fully bitched).
3581 * @param pszExe The executable for the tool (not normalized).
3582 * @param cEnvVars Number of environment varibles.
3583 * @param papszEnvVars Environment variables. For getting the PATH.
3584 */
3585static PKWTOOL kwToolLookup(const char *pszExe, KU32 cEnvVars, const char **papszEnvVars)
3586{
3587 /*
3588 * We associate the tools instances with the file system objects.
3589 *
3590 * We'd like to do the lookup without invaliding the volatile parts of the
3591 * cache, thus the double lookup here. The cache gets invalidate later on.
3592 */
3593 KFSLOOKUPERROR enmError;
3594 PKFSOBJ pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3595 if ( !pToolFsObj
3596 || pToolFsObj->bObjType != KFSOBJ_TYPE_FILE)
3597 {
3598 kFsCacheInvalidateCustomBoth(g_pFsCache);
3599 pToolFsObj = kFsCacheLookupA(g_pFsCache, pszExe, &enmError);
3600 }
3601 if (pToolFsObj)
3602 {
3603 if (pToolFsObj->bObjType == KFSOBJ_TYPE_FILE)
3604 {
3605 const char *pszSearchPath;
3606 PKWTOOL pTool = (PKWTOOL)kFsCacheObjGetUserData(g_pFsCache, pToolFsObj, KW_DATA_KEY_TOOL);
3607 if (pTool)
3608 {
3609 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3610 return pTool;
3611 }
3612
3613 /*
3614 * Need to create a new tool.
3615 */
3616 pszSearchPath = NULL;
3617 while (cEnvVars-- > 0)
3618 if (_strnicmp(papszEnvVars[cEnvVars], "PATH=", 5) == 0)
3619 {
3620 pszSearchPath = &papszEnvVars[cEnvVars][5];
3621 break;
3622 }
3623
3624 pTool = kwToolEntryCreate(pToolFsObj, pszSearchPath);
3625 if (pTool)
3626 return pTool;
3627
3628 kwErrPrintf("kwToolLookup(%s) -> NULL: kwToolEntryCreate failed\n", pszExe);
3629 }
3630 else
3631 {
3632 kFsCacheObjRelease(g_pFsCache, pToolFsObj);
3633 kwErrPrintf("kwToolLookup(%s) -> NULL: not file (bObjType=%d fFlags=%#x uCacheGen=%u auGenerationsMissing=[%u,%u])\n",
3634 pszExe, pToolFsObj->bObjType, pToolFsObj->fFlags, pToolFsObj->uCacheGen,
3635 g_pFsCache->auGenerationsMissing[0], g_pFsCache->auGenerationsMissing[1]);
3636 }
3637 }
3638 else
3639 kwErrPrintf("kwToolLookup(%s) -> NULL: enmError=%d\n", pszExe, enmError);
3640 return NULL;
3641}
3642
3643
3644
3645/*
3646 *
3647 * File system cache.
3648 * File system cache.
3649 * File system cache.
3650 *
3651 */
3652
3653
3654/**
3655 * This is for kDep.
3656 */
3657int kwFsPathExists(const char *pszPath)
3658{
3659 BirdTimeSpec_T TsIgnored;
3660 KFSLOOKUPERROR enmError;
3661 PKFSOBJ pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszPath, &enmError);
3662 if (pFsObj)
3663 {
3664 kFsCacheObjRelease(g_pFsCache, pFsObj);
3665 return 1;
3666 }
3667 return birdStatModTimeOnly(pszPath, &TsIgnored, 1) == 0;
3668}
3669
3670
3671/* duplicated in dir-nt-bird.c */
3672void nt_fullpath_cached(const char *pszPath, char *pszFull, size_t cbFull)
3673{
3674 KFSLOOKUPERROR enmError;
3675 PKFSOBJ pPathObj = kFsCacheLookupA(g_pFsCache, pszPath, &enmError);
3676 if (pPathObj)
3677 {
3678 KSIZE off = pPathObj->cchParent;
3679 if (off > 0)
3680 {
3681 KSIZE offEnd = off + pPathObj->cchName;
3682 if (offEnd < cbFull)
3683 {
3684 PKFSDIR pAncestor;
3685
3686 pszFull[off + pPathObj->cchName] = '\0';
3687 memcpy(&pszFull[off], pPathObj->pszName, pPathObj->cchName);
3688
3689 for (pAncestor = pPathObj->pParent; off > 0; pAncestor = pAncestor->Obj.pParent)
3690 {
3691 kHlpAssert(off > 1);
3692 kHlpAssert(pAncestor != NULL);
3693 kHlpAssert(pAncestor->Obj.cchName > 0);
3694 pszFull[--off] = '/';
3695 off -= pAncestor->Obj.cchName;
3696 kHlpAssert(pAncestor->Obj.cchParent == off);
3697 memcpy(&pszFull[off], pAncestor->Obj.pszName, pAncestor->Obj.cchName);
3698 }
3699 kFsCacheObjRelease(g_pFsCache, pPathObj);
3700 return;
3701 }
3702 }
3703 else
3704 {
3705 if ((size_t)pPathObj->cchName + 1 < cbFull)
3706 {
3707 memcpy(pszFull, pPathObj->pszName, pPathObj->cchName);
3708 pszFull[pPathObj->cchName] = '/';
3709 pszFull[pPathObj->cchName + 1] = '\0';
3710
3711 kFsCacheObjRelease(g_pFsCache, pPathObj);
3712 return;
3713 }
3714 }
3715
3716 /* do fallback. */
3717 kHlpAssertFailed();
3718 kFsCacheObjRelease(g_pFsCache, pPathObj);
3719 }
3720
3721 nt_fullpath(pszPath, pszFull, cbFull);
3722}
3723
3724
3725/**
3726 * Helper for getting the extension of a UTF-16 path.
3727 *
3728 * @returns Pointer to the extension or the terminator.
3729 * @param pwszPath The path.
3730 * @param pcwcExt Where to return the length of the extension.
3731 */
3732static wchar_t const *kwFsPathGetExtW(wchar_t const *pwszPath, KSIZE *pcwcExt)
3733{
3734 wchar_t const *pwszName = pwszPath;
3735 wchar_t const *pwszExt = NULL;
3736 for (;;)
3737 {
3738 wchar_t const wc = *pwszPath++;
3739 if (wc == '.')
3740 pwszExt = pwszPath;
3741 else if (wc == '/' || wc == '\\' || wc == ':')
3742 {
3743 pwszName = pwszPath;
3744 pwszExt = NULL;
3745 }
3746 else if (wc == '\0')
3747 {
3748 if (pwszExt)
3749 {
3750 *pcwcExt = pwszPath - pwszExt - 1;
3751 return pwszExt;
3752 }
3753 *pcwcExt = 0;
3754 return pwszPath - 1;
3755 }
3756 }
3757}
3758
3759
3760
3761/**
3762 * Parses the argument string passed in as pszSrc.
3763 *
3764 * @returns size of the processed arguments.
3765 * @param pszSrc Pointer to the commandline that's to be parsed.
3766 * @param pcArgs Where to return the number of arguments.
3767 * @param argv Pointer to argument vector to put argument pointers in. NULL allowed.
3768 * @param pchPool Pointer to memory pchPool to put the arguments into. NULL allowed.
3769 *
3770 * @remarks Lifted from startuphacks-win.c
3771 */
3772static int parse_args(const char *pszSrc, int *pcArgs, char **argv, char *pchPool)
3773{
3774 int bs;
3775 char chQuote;
3776 char *pfFlags;
3777 int cbArgs;
3778 int cArgs;
3779
3780#define PUTC(c) do { ++cbArgs; if (pchPool != NULL) *pchPool++ = (c); } while (0)
3781#define PUTV do { ++cArgs; if (argv != NULL) *argv++ = pchPool; } while (0)
3782#define WHITE(c) ((c) == ' ' || (c) == '\t')
3783
3784#define _ARG_DQUOTE 0x01 /* Argument quoted (") */
3785#define _ARG_RESPONSE 0x02 /* Argument read from response file */
3786#define _ARG_WILDCARD 0x04 /* Argument expanded from wildcard */
3787#define _ARG_ENV 0x08 /* Argument from environment */
3788#define _ARG_NONZERO 0x80 /* Always set, to avoid end of string */
3789
3790 cArgs = 0;
3791 cbArgs = 0;
3792
3793#if 0
3794 /* argv[0] */
3795 PUTC((char)_ARG_NONZERO);
3796 PUTV;
3797 for (;;)
3798 {
3799 PUTC(*pszSrc);
3800 if (*pszSrc == 0)
3801 break;
3802 ++pszSrc;
3803 }
3804 ++pszSrc;
3805#endif
3806
3807 for (;;)
3808 {
3809 while (WHITE(*pszSrc))
3810 ++pszSrc;
3811 if (*pszSrc == 0)
3812 break;
3813 pfFlags = pchPool;
3814 PUTC((char)_ARG_NONZERO);
3815 PUTV;
3816 bs = 0; chQuote = 0;
3817 for (;;)
3818 {
3819 if (!chQuote ? (*pszSrc == '"' /*|| *pszSrc == '\''*/) : *pszSrc == chQuote)
3820 {
3821 while (bs >= 2)
3822 {
3823 PUTC('\\');
3824 bs -= 2;
3825 }
3826 if (bs & 1)
3827 PUTC(*pszSrc);
3828 else
3829 {
3830 chQuote = chQuote ? 0 : *pszSrc;
3831 if (pfFlags != NULL)
3832 *pfFlags |= _ARG_DQUOTE;
3833 }
3834 bs = 0;
3835 }
3836 else if (*pszSrc == '\\')
3837 ++bs;
3838 else
3839 {
3840 while (bs != 0)
3841 {
3842 PUTC('\\');
3843 --bs;
3844 }
3845 if (*pszSrc == 0 || (WHITE(*pszSrc) && !chQuote))
3846 break;
3847 PUTC(*pszSrc);
3848 }
3849 ++pszSrc;
3850 }
3851 PUTC(0);
3852 }
3853
3854 *pcArgs = cArgs;
3855 return cbArgs;
3856}
3857
3858
3859
3860
3861/*
3862 *
3863 * Process and thread related APIs.
3864 * Process and thread related APIs.
3865 * Process and thread related APIs.
3866 *
3867 */
3868
3869/** Common worker for ExitProcess(), exit() and friends. */
3870static void WINAPI kwSandboxDoExit(int uExitCode)
3871{
3872 if (g_Sandbox.idMainThread == GetCurrentThreadId())
3873 {
3874 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
3875
3876 g_Sandbox.rcExitCode = (int)uExitCode;
3877
3878 /* Before we jump, restore the TIB as we're not interested in any
3879 exception chain stuff installed by the sandboxed executable. */
3880 *pTib = g_Sandbox.TibMainThread;
3881 pTib->ExceptionList = g_Sandbox.pOutXcptListHead;
3882
3883 longjmp(g_Sandbox.JmpBuf, 1);
3884 }
3885 KWFS_TODO();
3886}
3887
3888
3889/** ExitProcess replacement. */
3890static void WINAPI kwSandbox_Kernel32_ExitProcess(UINT uExitCode)
3891{
3892 KW_LOG(("kwSandbox_Kernel32_ExitProcess: %u\n", uExitCode));
3893 kwSandboxDoExit((int)uExitCode);
3894}
3895
3896
3897/** ExitProcess replacement. */
3898static BOOL WINAPI kwSandbox_Kernel32_TerminateProcess(HANDLE hProcess, UINT uExitCode)
3899{
3900 if (hProcess == GetCurrentProcess())
3901 kwSandboxDoExit(uExitCode);
3902 KWFS_TODO();
3903 return TerminateProcess(hProcess, uExitCode);
3904}
3905
3906
3907/** Normal CRT exit(). */
3908static void __cdecl kwSandbox_msvcrt_exit(int rcExitCode)
3909{
3910 KW_LOG(("kwSandbox_msvcrt_exit: %d\n", rcExitCode));
3911 kwSandboxDoExit(rcExitCode);
3912}
3913
3914
3915/** Quick CRT _exit(). */
3916static void __cdecl kwSandbox_msvcrt__exit(int rcExitCode)
3917{
3918 /* Quick. */
3919 KW_LOG(("kwSandbox_msvcrt__exit %d\n", rcExitCode));
3920 kwSandboxDoExit(rcExitCode);
3921}
3922
3923
3924/** Return to caller CRT _cexit(). */
3925static void __cdecl kwSandbox_msvcrt__cexit(int rcExitCode)
3926{
3927 KW_LOG(("kwSandbox_msvcrt__cexit: %d\n", rcExitCode));
3928 kwSandboxDoExit(rcExitCode);
3929}
3930
3931
3932/** Quick return to caller CRT _c_exit(). */
3933static void __cdecl kwSandbox_msvcrt__c_exit(int rcExitCode)
3934{
3935 KW_LOG(("kwSandbox_msvcrt__c_exit: %d\n", rcExitCode));
3936 kwSandboxDoExit(rcExitCode);
3937}
3938
3939
3940/** Runtime error and exit _amsg_exit(). */
3941static void __cdecl kwSandbox_msvcrt__amsg_exit(int iMsgNo)
3942{
3943 KW_LOG(("\nRuntime error #%u!\n", iMsgNo));
3944 kwSandboxDoExit(255);
3945}
3946
3947
3948/** CRT - terminate(). */
3949static void __cdecl kwSandbox_msvcrt_terminate(void)
3950{
3951 KW_LOG(("\nRuntime - terminate!\n"));
3952 kwSandboxDoExit(254);
3953}
3954
3955
3956/** CRT - _onexit */
3957static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
3958{
3959 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3960 {
3961 PKWEXITCALLACK pCallback;
3962 KW_LOG(("_onexit(%p)\n", pfnFunc));
3963 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3964
3965 pCallback = kHlpAlloc(sizeof(*pCallback));
3966 if (pCallback)
3967 {
3968 pCallback->pfnCallback = pfnFunc;
3969 pCallback->fAtExit = K_FALSE;
3970 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3971 g_Sandbox.pExitCallbackHead = pCallback;
3972 return pfnFunc;
3973 }
3974 return NULL;
3975 }
3976 KW_LOG(("_onexit(%p) - IGNORED\n", pfnFunc));
3977 return pfnFunc;
3978}
3979
3980
3981/** CRT - atexit */
3982static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
3983{
3984 //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
3985 {
3986 PKWEXITCALLACK pCallback;
3987 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
3988 KW_LOG(("atexit(%p)\n", pfnFunc));
3989
3990 pCallback = kHlpAlloc(sizeof(*pCallback));
3991 if (pCallback)
3992 {
3993 pCallback->pfnCallback = (_onexit_t)pfnFunc;
3994 pCallback->fAtExit = K_TRUE;
3995 pCallback->pNext = g_Sandbox.pExitCallbackHead;
3996 g_Sandbox.pExitCallbackHead = pCallback;
3997 return 0;
3998 }
3999 return -1;
4000 }
4001 KW_LOG(("atexit(%p) - IGNORED!\n", pfnFunc));
4002 return 0;
4003}
4004
4005
4006/** Kernel32 - SetConsoleCtrlHandler(). */
4007static BOOL WINAPI kwSandbox_Kernel32_SetConsoleCtrlHandler(PHANDLER_ROUTINE pfnHandler, BOOL fAdd)
4008{
4009 KW_LOG(("SetConsoleCtrlHandler(%p, %d) - ignoring\n"));
4010 return TRUE;
4011}
4012
4013
4014/** The CRT internal __getmainargs() API. */
4015static int __cdecl kwSandbox_msvcrt___getmainargs(int *pargc, char ***pargv, char ***penvp,
4016 int dowildcard, int const *piNewMode)
4017{
4018 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4019 *pargc = g_Sandbox.cArgs;
4020 *pargv = g_Sandbox.papszArgs;
4021 *penvp = g_Sandbox.environ;
4022
4023 /** @todo startinfo points at a newmode (setmode) value. */
4024 return 0;
4025}
4026
4027
4028/** The CRT internal __wgetmainargs() API. */
4029static int __cdecl kwSandbox_msvcrt___wgetmainargs(int *pargc, wchar_t ***pargv, wchar_t ***penvp,
4030 int dowildcard, int const *piNewMode)
4031{
4032 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4033 *pargc = g_Sandbox.cArgs;
4034 *pargv = g_Sandbox.papwszArgs;
4035 *penvp = g_Sandbox.wenviron;
4036
4037 /** @todo startinfo points at a newmode (setmode) value. */
4038 return 0;
4039}
4040
4041
4042
4043/** Kernel32 - GetCommandLineA() */
4044static LPCSTR /*LPSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineA(VOID)
4045{
4046 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4047 return g_Sandbox.pszCmdLine;
4048}
4049
4050
4051/** Kernel32 - GetCommandLineW() */
4052static LPCWSTR /*LPWSTR*/ WINAPI kwSandbox_Kernel32_GetCommandLineW(VOID)
4053{
4054 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4055 return g_Sandbox.pwszCmdLine;
4056}
4057
4058
4059/** Kernel32 - GetStartupInfoA() */
4060static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoA(LPSTARTUPINFOA pStartupInfo)
4061{
4062 KW_LOG(("GetStartupInfoA\n"));
4063 GetStartupInfoA(pStartupInfo);
4064 pStartupInfo->lpReserved = NULL;
4065 pStartupInfo->lpTitle = NULL;
4066 pStartupInfo->lpReserved2 = NULL;
4067 pStartupInfo->cbReserved2 = 0;
4068}
4069
4070
4071/** Kernel32 - GetStartupInfoW() */
4072static VOID WINAPI kwSandbox_Kernel32_GetStartupInfoW(LPSTARTUPINFOW pStartupInfo)
4073{
4074 KW_LOG(("GetStartupInfoW\n"));
4075 GetStartupInfoW(pStartupInfo);
4076 pStartupInfo->lpReserved = NULL;
4077 pStartupInfo->lpTitle = NULL;
4078 pStartupInfo->lpReserved2 = NULL;
4079 pStartupInfo->cbReserved2 = 0;
4080}
4081
4082
4083/** CRT - __p___argc(). */
4084static int * __cdecl kwSandbox_msvcrt___p___argc(void)
4085{
4086 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4087 return &g_Sandbox.cArgs;
4088}
4089
4090
4091/** CRT - __p___argv(). */
4092static char *** __cdecl kwSandbox_msvcrt___p___argv(void)
4093{
4094 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4095 return &g_Sandbox.papszArgs;
4096}
4097
4098
4099/** CRT - __p___sargv(). */
4100static wchar_t *** __cdecl kwSandbox_msvcrt___p___wargv(void)
4101{
4102 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4103 return &g_Sandbox.papwszArgs;
4104}
4105
4106
4107/** CRT - __p__acmdln(). */
4108static char ** __cdecl kwSandbox_msvcrt___p__acmdln(void)
4109{
4110 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4111 return (char **)&g_Sandbox.pszCmdLine;
4112}
4113
4114
4115/** CRT - __p__acmdln(). */
4116static wchar_t ** __cdecl kwSandbox_msvcrt___p__wcmdln(void)
4117{
4118 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4119 return &g_Sandbox.pwszCmdLine;
4120}
4121
4122
4123/** CRT - __p__pgmptr(). */
4124static char ** __cdecl kwSandbox_msvcrt___p__pgmptr(void)
4125{
4126 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4127 return &g_Sandbox.pgmptr;
4128}
4129
4130
4131/** CRT - __p__wpgmptr(). */
4132static wchar_t ** __cdecl kwSandbox_msvcrt___p__wpgmptr(void)
4133{
4134 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4135 return &g_Sandbox.wpgmptr;
4136}
4137
4138
4139/** CRT - _get_pgmptr(). */
4140static errno_t __cdecl kwSandbox_msvcrt__get_pgmptr(char **ppszValue)
4141{
4142 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4143 *ppszValue = g_Sandbox.pgmptr;
4144 return 0;
4145}
4146
4147
4148/** CRT - _get_wpgmptr(). */
4149static errno_t __cdecl kwSandbox_msvcrt__get_wpgmptr(wchar_t **ppwszValue)
4150{
4151 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4152 *ppwszValue = g_Sandbox.wpgmptr;
4153 return 0;
4154}
4155
4156/** Just in case. */
4157static void kwSandbox_msvcrt__wincmdln(void)
4158{
4159 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4160 KWFS_TODO();
4161}
4162
4163
4164/** Just in case. */
4165static void kwSandbox_msvcrt__wwincmdln(void)
4166{
4167 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4168 KWFS_TODO();
4169}
4170
4171/** CreateThread interceptor. */
4172static HANDLE WINAPI kwSandbox_Kernel32_CreateThread(LPSECURITY_ATTRIBUTES pSecAttr, SIZE_T cbStack,
4173 PTHREAD_START_ROUTINE pfnThreadProc, PVOID pvUser,
4174 DWORD fFlags, PDWORD pidThread)
4175{
4176 HANDLE hThread = NULL;
4177 KW_LOG(("CreateThread: pSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fFlags=%#x pidThread=%p\n",
4178 pSecAttr, pSecAttr ? pSecAttr->bInheritHandle : 0, cbStack, pfnThreadProc, pvUser, fFlags, pidThread));
4179 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4180 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
4181 {
4182 /* Allow link::DbgThread. */
4183 hThread = CreateThread(pSecAttr, cbStack, pfnThreadProc, pvUser, fFlags, pidThread);
4184 KW_LOG(("CreateThread -> %p, *pidThread=%#x\n", hThread, pidThread ? *pidThread : 0));
4185 }
4186 else
4187 KWFS_TODO();
4188 return hThread;
4189}
4190
4191
4192/** _beginthread - create a new thread. */
4193static uintptr_t __cdecl kwSandbox_msvcrt__beginthread(void (__cdecl *pfnThreadProc)(void *), unsigned cbStack, void *pvUser)
4194{
4195 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4196 KWFS_TODO();
4197 return 0;
4198}
4199
4200
4201/** _beginthreadex - create a new thread, msvcr120.dll hack for c2.dll. */
4202static uintptr_t __cdecl kwSandbox_msvcr120__beginthreadex(void *pvSecAttr, unsigned cbStack,
4203 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4204 unsigned fCreate, unsigned *pidThread)
4205{
4206 /*
4207 * The VC++ 12 (VS 2013) compiler pass two is now threaded. Let it do
4208 * whatever it needs to.
4209 */
4210 KW_LOG(("kwSandbox_msvcr120__beginthreadex: pvSecAttr=%p (inh=%d) cbStack=%#x pfnThreadProc=%p pvUser=%p fCreate=%#x pidThread=%p\n",
4211 pvSecAttr, pvSecAttr ? ((LPSECURITY_ATTRIBUTES)pvSecAttr)->bInheritHandle : 0, cbStack,
4212 pfnThreadProc, pvUser, fCreate, pidThread));
4213 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
4214 {
4215 uintptr_t rcRet;
4216 static uintptr_t (__cdecl *s_pfnReal)(void *, unsigned , unsigned (__stdcall *)(void *), void *, unsigned , unsigned *);
4217 if (!s_pfnReal)
4218 {
4219 *(FARPROC *)&s_pfnReal = GetProcAddress(GetModuleHandleA("msvcr120.dll"), "_beginthreadex");
4220 if (!s_pfnReal)
4221 {
4222 kwErrPrintf("kwSandbox_msvcr120__beginthreadex: Failed to resolve _beginthreadex in msvcr120.dll!\n");
4223 __debugbreak();
4224 }
4225 }
4226 rcRet = s_pfnReal(pvSecAttr, cbStack, pfnThreadProc, pvUser, fCreate, pidThread);
4227 KW_LOG(("kwSandbox_msvcr120__beginthreadex: returns %p *pidThread=%#x\n", rcRet, pidThread ? *pidThread : -1));
4228 return rcRet;
4229 }
4230
4231 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4232 KWFS_TODO();
4233 return 0;
4234}
4235
4236
4237/** _beginthreadex - create a new thread. */
4238static uintptr_t __cdecl kwSandbox_msvcrt__beginthreadex(void *pvSecAttr, unsigned cbStack,
4239 unsigned (__stdcall *pfnThreadProc)(void *), void *pvUser,
4240 unsigned fCreate, unsigned *pidThread)
4241{
4242 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4243 KWFS_TODO();
4244 return 0;
4245}
4246
4247
4248/*
4249 *
4250 * Environment related APIs.
4251 * Environment related APIs.
4252 * Environment related APIs.
4253 *
4254 */
4255
4256/** Kernel32 - GetEnvironmentStringsA (Watcom uses this one). */
4257static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsA(void)
4258{
4259 char *pszzEnv;
4260 char *pszCur;
4261 KSIZE cbNeeded = 1;
4262 KSIZE iVar = 0;
4263
4264 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4265
4266 /* Figure how space much we need first. */
4267 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4268 cbNeeded += kHlpStrLen(pszCur) + 1;
4269
4270 /* Allocate it. */
4271 pszzEnv = kHlpAlloc(cbNeeded);
4272 if (pszzEnv)
4273 {
4274 char *psz = pszzEnv;
4275 iVar = 0;
4276 while ((pszCur = g_Sandbox.papszEnvVars[iVar++]) != NULL)
4277 {
4278 KSIZE cbCur = kHlpStrLen(pszCur) + 1;
4279 kHlpAssert((KUPTR)(&psz[cbCur] - pszzEnv) < cbNeeded);
4280 psz = (char *)kHlpMemPCopy(psz, pszCur, cbCur);
4281 }
4282 *psz++ = '\0';
4283 kHlpAssert(psz - pszzEnv == cbNeeded);
4284 }
4285
4286 KW_LOG(("GetEnvironmentStringsA -> %p [%u]\n", pszzEnv, cbNeeded));
4287#if 0
4288 fprintf(stderr, "GetEnvironmentStringsA: %p LB %#x\n", pszzEnv, cbNeeded);
4289 pszCur = pszzEnv;
4290 iVar = 0;
4291 while (*pszCur)
4292 {
4293 fprintf(stderr, " %u:%p=%s<eos>\n\n", iVar, pszCur, pszCur);
4294 iVar++;
4295 pszCur += kHlpStrLen(pszCur) + 1;
4296 }
4297 fprintf(stderr, " %u:%p=<eos>\n\n", iVar, pszCur);
4298 pszCur++;
4299 fprintf(stderr, "ended at %p, after %u bytes (exepcted %u)\n", pszCur, pszCur - pszzEnv, cbNeeded);
4300#endif
4301 return pszzEnv;
4302}
4303
4304
4305/** Kernel32 - GetEnvironmentStrings */
4306static LPCH WINAPI kwSandbox_Kernel32_GetEnvironmentStrings(void)
4307{
4308 KW_LOG(("GetEnvironmentStrings!\n"));
4309 return kwSandbox_Kernel32_GetEnvironmentStringsA();
4310}
4311
4312
4313/** Kernel32 - GetEnvironmentStringsW */
4314static LPWCH WINAPI kwSandbox_Kernel32_GetEnvironmentStringsW(void)
4315{
4316 wchar_t *pwszzEnv;
4317 wchar_t *pwszCur;
4318 KSIZE cwcNeeded = 1;
4319 KSIZE iVar = 0;
4320
4321 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4322
4323 /* Figure how space much we need first. */
4324 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4325 cwcNeeded += kwUtf16Len(pwszCur) + 1;
4326
4327 /* Allocate it. */
4328 pwszzEnv = kHlpAlloc(cwcNeeded * sizeof(wchar_t));
4329 if (pwszzEnv)
4330 {
4331 wchar_t *pwsz = pwszzEnv;
4332 iVar = 0;
4333 while ((pwszCur = g_Sandbox.papwszEnvVars[iVar++]) != NULL)
4334 {
4335 KSIZE cwcCur = kwUtf16Len(pwszCur) + 1;
4336 kHlpAssert((KUPTR)(&pwsz[cwcCur] - pwszzEnv) < cwcNeeded);
4337 pwsz = (wchar_t *)kHlpMemPCopy(pwsz, pwszCur, cwcCur * sizeof(wchar_t));
4338 }
4339 *pwsz++ = '\0';
4340 kHlpAssert(pwsz - pwszzEnv == cwcNeeded);
4341 }
4342
4343 KW_LOG(("GetEnvironmentStringsW -> %p [%u]\n", pwszzEnv, cwcNeeded));
4344 return pwszzEnv;
4345}
4346
4347
4348/** Kernel32 - FreeEnvironmentStringsA */
4349static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsA(LPCH pszzEnv)
4350{
4351 KW_LOG(("FreeEnvironmentStringsA: %p -> TRUE\n", pszzEnv));
4352 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4353 kHlpFree(pszzEnv);
4354 return TRUE;
4355}
4356
4357
4358/** Kernel32 - FreeEnvironmentStringsW */
4359static BOOL WINAPI kwSandbox_Kernel32_FreeEnvironmentStringsW(LPWCH pwszzEnv)
4360{
4361 KW_LOG(("FreeEnvironmentStringsW: %p -> TRUE\n", pwszzEnv));
4362 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4363 kHlpFree(pwszzEnv);
4364 return TRUE;
4365}
4366
4367
4368/**
4369 * Grows the environment vectors (KWSANDBOX::environ, KWSANDBOX::papszEnvVars,
4370 * KWSANDBOX::wenviron, and KWSANDBOX::papwszEnvVars).
4371 *
4372 * @returns 0 on success, non-zero on failure.
4373 * @param pSandbox The sandbox.
4374 * @param cMin Minimum size, including terminator.
4375 */
4376static int kwSandboxGrowEnv(PKWSANDBOX pSandbox, KSIZE cMin)
4377{
4378 void *pvNew;
4379 KSIZE const cOld = pSandbox->cEnvVarsAllocated;
4380 KSIZE cNew = cOld + 256;
4381 while (cNew < cMin)
4382 cNew += 256;
4383
4384 pvNew = kHlpRealloc(pSandbox->environ, cNew * sizeof(pSandbox->environ[0]));
4385 if (pvNew)
4386 {
4387 pSandbox->environ = (char **)pvNew;
4388 pSandbox->environ[cOld] = NULL;
4389
4390 pvNew = kHlpRealloc(pSandbox->papszEnvVars, cNew * sizeof(pSandbox->papszEnvVars[0]));
4391 if (pvNew)
4392 {
4393 pSandbox->papszEnvVars = (char **)pvNew;
4394 pSandbox->papszEnvVars[cOld] = NULL;
4395
4396 pvNew = kHlpRealloc(pSandbox->wenviron, cNew * sizeof(pSandbox->wenviron[0]));
4397 if (pvNew)
4398 {
4399 pSandbox->wenviron = (wchar_t **)pvNew;
4400 pSandbox->wenviron[cOld] = NULL;
4401
4402 pvNew = kHlpRealloc(pSandbox->papwszEnvVars, cNew * sizeof(pSandbox->papwszEnvVars[0]));
4403 if (pvNew)
4404 {
4405 pSandbox->papwszEnvVars = (wchar_t **)pvNew;
4406 pSandbox->papwszEnvVars[cOld] = NULL;
4407
4408 pSandbox->cEnvVarsAllocated = cNew;
4409 KW_LOG(("kwSandboxGrowEnv: cNew=%d - crt: %p / %p; shadow: %p, %p\n",
4410 cNew, pSandbox->environ, pSandbox->wenviron, pSandbox->papszEnvVars, pSandbox->papwszEnvVars));
4411 return 0;
4412 }
4413 }
4414 }
4415 }
4416 kwErrPrintf("kwSandboxGrowEnv ran out of memory! cNew=%u\n", cNew);
4417 return KERR_NO_MEMORY;
4418}
4419
4420
4421/**
4422 * Sets an environment variable, ANSI style.
4423 *
4424 * @returns 0 on success, non-zero on failure.
4425 * @param pSandbox The sandbox.
4426 * @param pchVar The variable name.
4427 * @param cchVar The length of the name.
4428 * @param pszValue The value.
4429 */
4430static int kwSandboxDoSetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar, const char *pszValue)
4431{
4432 /* Allocate and construct the new strings. */
4433 KSIZE cchTmp = kHlpStrLen(pszValue);
4434 char *pszNew = (char *)kHlpAlloc(cchVar + 1 + cchTmp + 1);
4435 if (pszNew)
4436 {
4437 wchar_t *pwszNew;
4438 kHlpMemCopy(pszNew, pchVar, cchVar);
4439 pszNew[cchVar] = '=';
4440 kHlpMemCopy(&pszNew[cchVar + 1], pszValue, cchTmp);
4441 cchTmp += cchVar + 1;
4442 pszNew[cchTmp] = '\0';
4443
4444 pwszNew = kwStrToUtf16AllocN(pszNew, cchTmp);
4445 if (pwszNew)
4446 {
4447 /* Look it up. */
4448 KSIZE iVar = 0;
4449 char *pszEnv;
4450 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4451 {
4452 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4453 && pszEnv[cchVar] == '=')
4454 {
4455 KW_LOG(("kwSandboxDoSetEnvA: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4456 " iVar=%d: %p='%s' and %p='%ls'\n",
4457 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4458 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4459 iVar, pszNew, pszNew, pwszNew, pwszNew));
4460
4461 kHlpFree(pSandbox->papszEnvVars[iVar]);
4462 pSandbox->papszEnvVars[iVar] = pszNew;
4463 pSandbox->environ[iVar] = pszNew;
4464
4465 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4466 pSandbox->papwszEnvVars[iVar] = pwszNew;
4467 pSandbox->wenviron[iVar] = pwszNew;
4468 return 0;
4469 }
4470 iVar++;
4471 }
4472
4473 /* Not found, do we need to grow the table first? */
4474 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4475 kwSandboxGrowEnv(pSandbox, iVar + 2);
4476 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4477 {
4478 KW_LOG(("kwSandboxDoSetEnvA: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4479
4480 pSandbox->papszEnvVars[iVar + 1] = NULL;
4481 pSandbox->papszEnvVars[iVar] = pszNew;
4482 pSandbox->environ[iVar + 1] = NULL;
4483 pSandbox->environ[iVar] = pszNew;
4484
4485 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4486 pSandbox->papwszEnvVars[iVar] = pwszNew;
4487 pSandbox->wenviron[iVar + 1] = NULL;
4488 pSandbox->wenviron[iVar] = pwszNew;
4489 return 0;
4490 }
4491
4492 kHlpFree(pwszNew);
4493 }
4494 kHlpFree(pszNew);
4495 }
4496 KW_LOG(("Out of memory!\n"));
4497 return 0;
4498}
4499
4500
4501/**
4502 * Sets an environment variable, UTF-16 style.
4503 *
4504 * @returns 0 on success, non-zero on failure.
4505 * @param pSandbox The sandbox.
4506 * @param pwcVar The variable name.
4507 * @param cwcVar The length of the name.
4508 * @param pwszValue The value.
4509 */
4510static int kwSandboxDoSetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwchVar, KSIZE cwcVar, const wchar_t *pwszValue)
4511{
4512 /* Allocate and construct the new strings. */
4513 KSIZE cwcTmp = kwUtf16Len(pwszValue);
4514 wchar_t *pwszNew = (wchar_t *)kHlpAlloc((cwcVar + 1 + cwcTmp + 1) * sizeof(wchar_t));
4515 if (pwszNew)
4516 {
4517 char *pszNew;
4518 kHlpMemCopy(pwszNew, pwchVar, cwcVar * sizeof(wchar_t));
4519 pwszNew[cwcVar] = '=';
4520 kHlpMemCopy(&pwszNew[cwcVar + 1], pwszValue, cwcTmp * sizeof(wchar_t));
4521 cwcTmp += cwcVar + 1;
4522 pwszNew[cwcVar] = '\0';
4523
4524 pszNew = kwUtf16ToStrAllocN(pwszNew, cwcVar);
4525 if (pszNew)
4526 {
4527 /* Look it up. */
4528 KSIZE iVar = 0;
4529 wchar_t *pwszEnv;
4530 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4531 {
4532 if ( _wcsnicmp(pwszEnv, pwchVar, cwcVar) == 0
4533 && pwszEnv[cwcVar] == '=')
4534 {
4535 KW_LOG(("kwSandboxDoSetEnvW: Replacing iVar=%d: %p='%s' and %p='%ls'\n"
4536 " iVar=%d: %p='%s' and %p='%ls'\n",
4537 iVar, pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4538 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar],
4539 iVar, pszNew, pszNew, pwszNew, pwszNew));
4540
4541 kHlpFree(pSandbox->papszEnvVars[iVar]);
4542 pSandbox->papszEnvVars[iVar] = pszNew;
4543 pSandbox->environ[iVar] = pszNew;
4544
4545 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4546 pSandbox->papwszEnvVars[iVar] = pwszNew;
4547 pSandbox->wenviron[iVar] = pwszNew;
4548 return 0;
4549 }
4550 iVar++;
4551 }
4552
4553 /* Not found, do we need to grow the table first? */
4554 if (iVar + 1 >= pSandbox->cEnvVarsAllocated)
4555 kwSandboxGrowEnv(pSandbox, iVar + 2);
4556 if (iVar + 1 < pSandbox->cEnvVarsAllocated)
4557 {
4558 KW_LOG(("kwSandboxDoSetEnvW: Adding iVar=%d: %p='%s' and %p='%ls'\n", iVar, pszNew, pszNew, pwszNew, pwszNew));
4559
4560 pSandbox->papszEnvVars[iVar + 1] = NULL;
4561 pSandbox->papszEnvVars[iVar] = pszNew;
4562 pSandbox->environ[iVar + 1] = NULL;
4563 pSandbox->environ[iVar] = pszNew;
4564
4565 pSandbox->papwszEnvVars[iVar + 1] = NULL;
4566 pSandbox->papwszEnvVars[iVar] = pwszNew;
4567 pSandbox->wenviron[iVar + 1] = NULL;
4568 pSandbox->wenviron[iVar] = pwszNew;
4569 return 0;
4570 }
4571
4572 kHlpFree(pwszNew);
4573 }
4574 kHlpFree(pszNew);
4575 }
4576 KW_LOG(("Out of memory!\n"));
4577 return 0;
4578}
4579
4580
4581/** ANSI unsetenv worker. */
4582static int kwSandboxDoUnsetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4583{
4584 KSIZE iVar = 0;
4585 char *pszEnv;
4586 while ((pszEnv = pSandbox->papszEnvVars[iVar]) != NULL)
4587 {
4588 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4589 && pszEnv[cchVar] == '=')
4590 {
4591 KSIZE cVars = iVar;
4592 while (pSandbox->papszEnvVars[cVars])
4593 cVars++;
4594 kHlpAssert(pSandbox->papwszEnvVars[iVar] != NULL);
4595 kHlpAssert(pSandbox->papwszEnvVars[cVars] == NULL);
4596
4597 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4598 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4599 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4600
4601 kHlpFree(pSandbox->papszEnvVars[iVar]);
4602 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4603 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4604 pSandbox->papszEnvVars[cVars] = NULL;
4605 pSandbox->environ[cVars] = NULL;
4606
4607 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4608 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4609 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4610 pSandbox->papwszEnvVars[cVars] = NULL;
4611 pSandbox->wenviron[cVars] = NULL;
4612 return 0;
4613 }
4614 iVar++;
4615 }
4616 return KERR_ENVVAR_NOT_FOUND;
4617}
4618
4619
4620/** UTF-16 unsetenv worker. */
4621static int kwSandboxDoUnsetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4622{
4623 KSIZE iVar = 0;
4624 wchar_t *pwszEnv;
4625 while ((pwszEnv = pSandbox->papwszEnvVars[iVar]) != NULL)
4626 {
4627 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4628 && pwszEnv[cwcVar] == '=')
4629 {
4630 KSIZE cVars = iVar;
4631 while (pSandbox->papwszEnvVars[cVars])
4632 cVars++;
4633 kHlpAssert(pSandbox->papszEnvVars[iVar] != NULL);
4634 kHlpAssert(pSandbox->papszEnvVars[cVars] == NULL);
4635
4636 KW_LOG(("kwSandboxDoUnsetEnvA: Removing iVar=%d: %p='%s' and %p='%ls'; new cVars=%d\n", iVar,
4637 pSandbox->papszEnvVars[iVar], pSandbox->papszEnvVars[iVar],
4638 pSandbox->papwszEnvVars[iVar], pSandbox->papwszEnvVars[iVar], cVars - 1));
4639
4640 kHlpFree(pSandbox->papszEnvVars[iVar]);
4641 pSandbox->papszEnvVars[iVar] = pSandbox->papszEnvVars[cVars];
4642 pSandbox->environ[iVar] = pSandbox->papszEnvVars[cVars];
4643 pSandbox->papszEnvVars[cVars] = NULL;
4644 pSandbox->environ[cVars] = NULL;
4645
4646 kHlpFree(pSandbox->papwszEnvVars[iVar]);
4647 pSandbox->papwszEnvVars[iVar] = pSandbox->papwszEnvVars[cVars];
4648 pSandbox->wenviron[iVar] = pSandbox->papwszEnvVars[cVars];
4649 pSandbox->papwszEnvVars[cVars] = NULL;
4650 pSandbox->wenviron[cVars] = NULL;
4651 return 0;
4652 }
4653 iVar++;
4654 }
4655 return KERR_ENVVAR_NOT_FOUND;
4656}
4657
4658
4659
4660/** ANSI getenv worker. */
4661static char *kwSandboxDoGetEnvA(PKWSANDBOX pSandbox, const char *pchVar, KSIZE cchVar)
4662{
4663 KSIZE iVar = 0;
4664 char *pszEnv;
4665 while ((pszEnv = pSandbox->papszEnvVars[iVar++]) != NULL)
4666 if ( _strnicmp(pszEnv, pchVar, cchVar) == 0
4667 && pszEnv[cchVar] == '=')
4668 return &pszEnv[cchVar + 1];
4669 return NULL;
4670}
4671
4672
4673/** UTF-16 getenv worker. */
4674static wchar_t *kwSandboxDoGetEnvW(PKWSANDBOX pSandbox, const wchar_t *pwcVar, KSIZE cwcVar)
4675{
4676 KSIZE iVar = 0;
4677 wchar_t *pwszEnv;
4678 while ((pwszEnv = pSandbox->papwszEnvVars[iVar++]) != NULL)
4679 if ( _wcsnicmp(pwszEnv, pwcVar, cwcVar) == 0
4680 && pwszEnv[cwcVar] == '=')
4681 return &pwszEnv[cwcVar + 1];
4682 return NULL;
4683}
4684
4685
4686/** Kernel32 - GetEnvironmentVariableA() */
4687static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableA(LPCSTR pszVar, LPSTR pszValue, DWORD cbValue)
4688{
4689 char *pszFoundValue;
4690 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4691
4692 pszFoundValue = kwSandboxDoGetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4693 if (pszFoundValue)
4694 {
4695 DWORD cchRet = kwStrCopyStyle1(pszFoundValue, pszValue, cbValue);
4696 KW_LOG(("GetEnvironmentVariableA: '%s' -> %u (%s)\n", pszVar, cchRet, pszFoundValue));
4697 return cchRet;
4698 }
4699 KW_LOG(("GetEnvironmentVariableA: '%s' -> 0\n", pszVar));
4700 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4701 return 0;
4702}
4703
4704
4705/** Kernel32 - GetEnvironmentVariableW() */
4706static DWORD WINAPI kwSandbox_Kernel32_GetEnvironmentVariableW(LPCWSTR pwszVar, LPWSTR pwszValue, DWORD cwcValue)
4707{
4708 wchar_t *pwszFoundValue;
4709 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4710
4711 pwszFoundValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4712 if (pwszFoundValue)
4713 {
4714 DWORD cchRet = kwUtf16CopyStyle1(pwszFoundValue, pwszValue, cwcValue);
4715 KW_LOG(("GetEnvironmentVariableW: '%ls' -> %u (%ls)\n", pwszVar, cchRet, pwszFoundValue));
4716 return cchRet;
4717 }
4718 KW_LOG(("GetEnvironmentVariableW: '%ls' -> 0\n", pwszVar));
4719 SetLastError(ERROR_ENVVAR_NOT_FOUND);
4720 return 0;
4721}
4722
4723
4724/** Kernel32 - SetEnvironmentVariableA() */
4725static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableA(LPCSTR pszVar, LPCSTR pszValue)
4726{
4727 int rc;
4728 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4729
4730 if (pszValue)
4731 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4732 else
4733 {
4734 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4735 rc = 0; //??
4736 }
4737 if (rc == 0)
4738 {
4739 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> TRUE\n", pszVar, pszValue));
4740 return TRUE;
4741 }
4742 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4743 KW_LOG(("SetEnvironmentVariableA(%s,%s) -> FALSE!\n", pszVar, pszValue));
4744 return FALSE;
4745}
4746
4747
4748/** Kernel32 - SetEnvironmentVariableW() */
4749static BOOL WINAPI kwSandbox_Kernel32_SetEnvironmentVariableW(LPCWSTR pwszVar, LPCWSTR pwszValue)
4750{
4751 int rc;
4752 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4753
4754 if (pwszValue)
4755 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4756 else
4757 {
4758 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4759 rc = 0; //??
4760 }
4761 if (rc == 0)
4762 {
4763 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> TRUE\n", pwszVar, pwszValue));
4764 return TRUE;
4765 }
4766 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
4767 KW_LOG(("SetEnvironmentVariableA(%ls,%ls) -> FALSE!\n", pwszVar, pwszValue));
4768 return FALSE;
4769}
4770
4771
4772/** Kernel32 - ExpandEnvironmentStringsA() */
4773static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pwszDst, DWORD cbDst)
4774{
4775 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4776 KWFS_TODO();
4777 return 0;
4778}
4779
4780
4781/** Kernel32 - ExpandEnvironmentStringsW() */
4782static DWORD WINAPI kwSandbox_Kernel32_ExpandEnvironmentStringsW(LPCWSTR pwszSrc, LPWSTR pwszDst, DWORD cbDst)
4783{
4784 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4785 KWFS_TODO();
4786 return 0;
4787}
4788
4789
4790/** CRT - _putenv(). */
4791static int __cdecl kwSandbox_msvcrt__putenv(const char *pszVarEqualValue)
4792{
4793 int rc;
4794 char const *pszEqual;
4795 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4796
4797 pszEqual = kHlpStrChr(pszVarEqualValue, '=');
4798 if (pszEqual)
4799 {
4800 rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVarEqualValue, pszEqual - pszVarEqualValue, pszEqual + 1);
4801 if (rc == 0)
4802 { }
4803 else
4804 rc = -1;
4805 }
4806 else
4807 {
4808 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVarEqualValue, kHlpStrLen(pszVarEqualValue));
4809 rc = 0;
4810 }
4811 KW_LOG(("_putenv(%s) -> %d\n", pszVarEqualValue, rc));
4812 return rc;
4813}
4814
4815
4816/** CRT - _wputenv(). */
4817static int __cdecl kwSandbox_msvcrt__wputenv(const wchar_t *pwszVarEqualValue)
4818{
4819 int rc;
4820 wchar_t const *pwszEqual;
4821 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4822
4823 pwszEqual = wcschr(pwszVarEqualValue, '=');
4824 if (pwszEqual)
4825 {
4826 rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVarEqualValue, pwszEqual - pwszVarEqualValue, pwszEqual + 1);
4827 if (rc == 0)
4828 { }
4829 else
4830 rc = -1;
4831 }
4832 else
4833 {
4834 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVarEqualValue, kwUtf16Len(pwszVarEqualValue));
4835 rc = 0;
4836 }
4837 KW_LOG(("_wputenv(%ls) -> %d\n", pwszVarEqualValue, rc));
4838 return rc;
4839}
4840
4841
4842/** CRT - _putenv_s(). */
4843static errno_t __cdecl kwSandbox_msvcrt__putenv_s(const char *pszVar, const char *pszValue)
4844{
4845 char const *pszEqual;
4846 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4847
4848 pszEqual = kHlpStrChr(pszVar, '=');
4849 if (pszEqual == NULL)
4850 {
4851 if (pszValue)
4852 {
4853 int rc = kwSandboxDoSetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar), pszValue);
4854 if (rc == 0)
4855 {
4856 KW_LOG(("_putenv_s(%s,%s) -> 0\n", pszVar, pszValue));
4857 return 0;
4858 }
4859 }
4860 else
4861 {
4862 kwSandboxDoUnsetEnvA(&g_Sandbox, pszVar, kHlpStrLen(pszVar));
4863 KW_LOG(("_putenv_s(%ls,NULL) -> 0\n", pszVar));
4864 return 0;
4865 }
4866 KW_LOG(("_putenv_s(%s,%s) -> ENOMEM\n", pszVar, pszValue));
4867 return ENOMEM;
4868 }
4869 KW_LOG(("_putenv_s(%s,%s) -> EINVAL\n", pszVar, pszValue));
4870 return EINVAL;
4871}
4872
4873
4874/** CRT - _wputenv_s(). */
4875static errno_t __cdecl kwSandbox_msvcrt__wputenv_s(const wchar_t *pwszVar, const wchar_t *pwszValue)
4876{
4877 wchar_t const *pwszEqual;
4878 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4879
4880 pwszEqual = wcschr(pwszVar, '=');
4881 if (pwszEqual == NULL)
4882 {
4883 if (pwszValue)
4884 {
4885 int rc = kwSandboxDoSetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar), pwszValue);
4886 if (rc == 0)
4887 {
4888 KW_LOG(("_wputenv_s(%ls,%ls) -> 0\n", pwszVar, pwszValue));
4889 return 0;
4890 }
4891 }
4892 else
4893 {
4894 kwSandboxDoUnsetEnvW(&g_Sandbox, pwszVar, kwUtf16Len(pwszVar));
4895 KW_LOG(("_wputenv_s(%ls,NULL) -> 0\n", pwszVar));
4896 return 0;
4897 }
4898 KW_LOG(("_wputenv_s(%ls,%ls) -> ENOMEM\n", pwszVar, pwszValue));
4899 return ENOMEM;
4900 }
4901 KW_LOG(("_wputenv_s(%ls,%ls) -> EINVAL\n", pwszVar, pwszValue));
4902 return EINVAL;
4903}
4904
4905
4906/** CRT - get pointer to the __initenv variable (initial environment). */
4907static char *** __cdecl kwSandbox_msvcrt___p___initenv(void)
4908{
4909 KW_LOG(("__p___initenv\n"));
4910 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4911 KWFS_TODO();
4912 return &g_Sandbox.initenv;
4913}
4914
4915
4916/** CRT - get pointer to the __winitenv variable (initial environment). */
4917static wchar_t *** __cdecl kwSandbox_msvcrt___p___winitenv(void)
4918{
4919 KW_LOG(("__p___winitenv\n"));
4920 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4921 KWFS_TODO();
4922 return &g_Sandbox.winitenv;
4923}
4924
4925
4926/** CRT - get pointer to the _environ variable (current environment). */
4927static char *** __cdecl kwSandbox_msvcrt___p__environ(void)
4928{
4929 KW_LOG(("__p__environ\n"));
4930 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4931 return &g_Sandbox.environ;
4932}
4933
4934
4935/** CRT - get pointer to the _wenviron variable (current environment). */
4936static wchar_t *** __cdecl kwSandbox_msvcrt___p__wenviron(void)
4937{
4938 KW_LOG(("__p__wenviron\n"));
4939 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4940 return &g_Sandbox.wenviron;
4941}
4942
4943
4944/** CRT - get the _environ variable (current environment).
4945 * @remarks Not documented or prototyped? */
4946static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_environ(char ***ppapszEnviron)
4947{
4948 KWFS_TODO(); /** @todo check the callers expectations! */
4949 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4950 *ppapszEnviron = g_Sandbox.environ;
4951 return 0;
4952}
4953
4954
4955/** CRT - get the _wenviron variable (current environment).
4956 * @remarks Not documented or prototyped? */
4957static KUPTR /*void*/ __cdecl kwSandbox_msvcrt__get_wenviron(wchar_t ***ppapwszEnviron)
4958{
4959 KWFS_TODO(); /** @todo check the callers expectations! */
4960 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4961 *ppapwszEnviron = g_Sandbox.wenviron;
4962 return 0;
4963}
4964
4965
4966/** CRT - _wdupenv_s() (see _tdupenv_s(). */
4967static errno_t __cdecl kwSandbox_msvcrt__wdupenv_s_wrapped(wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName,
4968 PKWCRTSLOT pSlot)
4969{
4970 errno_t rc;
4971 wchar_t *pwszValue;
4972 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
4973
4974 if (ppwszValue)
4975 {
4976 pwszValue = kwSandboxDoGetEnvW(&g_Sandbox, pwszVarName, wcslen(pwszVarName));
4977 if (pwszValue)
4978 {
4979 size_t cwcValue = wcslen(pwszValue);
4980 wchar_t *pwszDst = pSlot->pfnMalloc ? (wchar_t *)pSlot->pfnMalloc((cwcValue + 1) * sizeof(wchar_t)) : NULL;
4981 if (pwszDst)
4982 {
4983 memcpy(pwszDst, pwszValue, cwcValue * sizeof(wchar_t));
4984 pwszDst[cwcValue] = '\0';
4985 *ppwszValue = pwszDst;
4986 if (pcwcValue)
4987 *pcwcValue = cwcValue;
4988 rc = 0;
4989 }
4990 else
4991 {
4992 *ppwszValue = NULL;
4993 if (pcwcValue)
4994 *pcwcValue = 0;
4995 rc = ENOMEM;
4996 }
4997 }
4998 else
4999 {
5000 *ppwszValue = NULL;
5001 if (pcwcValue)
5002 *pcwcValue = 0;
5003 rc = 0;
5004 }
5005 KW_LOG(("_wdupenv_s(,,%ls) -> %d '%ls'\n", pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"));
5006 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> %d '%ls'\n", getpid(), pwszVarName, rc, *ppwszValue ? *ppwszValue : L"<null>"); fflush(stderr); // HACKING
5007 }
5008 else
5009 {
5010 /*
5011 * Warning! If mspdb100.dll ends up here, it won't reinitialize the event name
5012 * and continue to use the one it constructed when _MSPDBSRV_ENDPOINT_
5013 * was set to a value.
5014 */
5015 if (pcwcValue)
5016 *pcwcValue = 0;
5017 rc = EINVAL;
5018 KW_LOG(("_wdupenv_s(,,%ls) -> EINVAL\n", pwszVarName));
5019 //fprintf(stderr, "%d: _wdupenv_s(,,%ls) -> EINVAL\n", getpid(), pwszVarName); fflush(stderr); // HACKING
5020 }
5021 return rc;
5022}
5023CRT_SLOT_FUNCTION_WRAPPER(errno_t __cdecl, kwSandbox_msvcrt__wdupenv_s,
5024 (wchar_t **ppwszValue, size_t *pcwcValue, const wchar_t *pwszVarName),
5025 (ppwszValue, pcwcValue, pwszVarName, &g_aCrtSlots[iCrtSlot]));
5026
5027
5028
5029/*
5030 *
5031 * Loader related APIs
5032 * Loader related APIs
5033 * Loader related APIs
5034 *
5035 */
5036
5037/**
5038 * Kernel32 - LoadLibraryExA() worker that loads resource files and such.
5039 */
5040static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_Resource(PKWDYNLOAD pDynLoad, DWORD fFlags)
5041{
5042 /* Load it first. */
5043 HMODULE hmod = LoadLibraryExA(pDynLoad->szRequest, NULL /*hFile*/, fFlags);
5044 if (hmod)
5045 {
5046 pDynLoad->hmod = hmod;
5047 pDynLoad->pMod = NULL; /* indicates special */
5048
5049 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5050 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5051 KW_LOG(("LoadLibraryExA(%s,,[resource]) -> %p\n", pDynLoad->szRequest, pDynLoad->hmod));
5052 }
5053 else
5054 kHlpFree(pDynLoad);
5055 return hmod;
5056}
5057
5058
5059/**
5060 * Kernel32 - LoadLibraryExA() worker that deals with the api-ms-xxx modules.
5061 */
5062static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(PKWDYNLOAD pDynLoad, DWORD fFlags)
5063{
5064 HMODULE hmod;
5065 PKWMODULE pMod;
5066 KU32 uHashPath;
5067 KSIZE idxHash;
5068 char szNormPath[256];
5069 KSIZE cbFilename = kHlpStrLen(pDynLoad->szRequest) + 1;
5070
5071 /*
5072 * Lower case it and make it ends with .dll.
5073 */
5074 if (cbFilename <= sizeof(szNormPath))
5075 {
5076 static const char s_szDll[] = ".dll";
5077 kHlpMemCopy(szNormPath, pDynLoad->szRequest, cbFilename);
5078 _strlwr(szNormPath);
5079 if ( ( cbFilename <= 4
5080 || strcmp(&szNormPath[cbFilename - 5], s_szDll) != 0)
5081 && cbFilename + sizeof(s_szDll) - 1 <= sizeof(szNormPath))
5082 {
5083 memcpy(&szNormPath[cbFilename - sizeof(s_szDll)], s_szDll, sizeof(s_szDll));
5084 cbFilename += sizeof(s_szDll) - 1;
5085 }
5086 }
5087 else
5088 {
5089 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5090 return NULL;
5091 }
5092
5093 /*
5094 * Check if it has already been loaded so we don't create an unnecessary
5095 * loader module for it.
5096 */
5097 uHashPath = kwStrHash(szNormPath);
5098 idxHash = uHashPath % K_ELEMENTS(g_apModules);
5099 pMod = g_apModules[idxHash];
5100 if (pMod)
5101 {
5102 do
5103 {
5104 if ( pMod->uHashPath == uHashPath
5105 && kHlpStrComp(pMod->pszPath, szNormPath) == 0)
5106 {
5107 pDynLoad->pMod = kwLdrModuleRetain(pMod);
5108 pDynLoad->hmod = pMod->hOurMod;
5109
5110 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5111 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5112 KW_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - already loaded]\n", pDynLoad->szRequest, pDynLoad->hmod));
5113 return pDynLoad->hmod;
5114 }
5115 pMod = pMod->pNextHash;
5116 } while (pMod);
5117 }
5118
5119
5120 /*
5121 * Try load it and make a kLdr module for it.
5122 */
5123 hmod = LoadLibraryExA(szNormPath, NULL /*hFile*/, fFlags);
5124 if (hmod)
5125 {
5126 PKLDRMOD pLdrMod;
5127 int rc = kLdrModOpenNativeByHandle((KUPTR)hmod, KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM, &pLdrMod);
5128 if (rc == 0)
5129 {
5130 PKWMODULE pMod = kwLdrModuleCreateForNativekLdrModule(pLdrMod, szNormPath, cbFilename, uHashPath,
5131 K_FALSE /*fDoReplacements*/);
5132 if (pMod)
5133 {
5134 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5135
5136 pDynLoad->pMod = pMod;
5137 pDynLoad->hmod = hmod;
5138
5139 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5140 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5141 KW_LOG(("LoadLibraryExA(%s,,) -> %p [virtual API module - new]\n", pDynLoad->szRequest, pDynLoad->hmod));
5142 return hmod;
5143 }
5144 KWFS_TODO();
5145 }
5146 else
5147 KWFS_TODO();
5148 }
5149 kHlpFree(pDynLoad);
5150 return hmod;
5151}
5152
5153
5154/** Kernel32 - LoadLibraryExA() */
5155static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5156{
5157 KSIZE cchFilename = kHlpStrLen(pszFilename);
5158 const char *pszSearchPath;
5159 PKWDYNLOAD pDynLoad;
5160 PKWMODULE pMod;
5161 int rc;
5162 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5163
5164 /*
5165 * Deal with a couple of extremely unlikely special cases right away.
5166 */
5167 if ( ( !(fFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE)
5168 || (fFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE))
5169 && (hFile == NULL || hFile == INVALID_HANDLE_VALUE) )
5170 { /* likely */ }
5171 else
5172 {
5173 KWFS_TODO();
5174 return LoadLibraryExA(pszFilename, hFile, fFlags);
5175 }
5176
5177 /*
5178 * Check if we've already got a dynload entry for this one.
5179 */
5180 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5181 if ( pDynLoad->cchRequest == cchFilename
5182 && kHlpMemComp(pDynLoad->szRequest, pszFilename, cchFilename) == 0)
5183 {
5184 if (pDynLoad->pMod)
5185 rc = kwLdrModuleInitTree(pDynLoad->pMod);
5186 else
5187 rc = 0;
5188 if (rc == 0)
5189 {
5190 KW_LOG(("LoadLibraryExA(%s,,) -> %p [cached]\n", pszFilename, pDynLoad->hmod));
5191 return pDynLoad->hmod;
5192 }
5193 SetLastError(ERROR_DLL_INIT_FAILED);
5194 return NULL;
5195 }
5196
5197 /*
5198 * Allocate a dynload entry for the request.
5199 */
5200 pDynLoad = (PKWDYNLOAD)kHlpAlloc(sizeof(*pDynLoad) + cchFilename + 1);
5201 if (pDynLoad)
5202 {
5203 pDynLoad->cchRequest = cchFilename;
5204 kHlpMemCopy(pDynLoad->szRequest, pszFilename, cchFilename + 1);
5205 }
5206 else
5207 {
5208 KW_LOG(("LoadLibraryExA: Out of memory!\n"));
5209 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
5210 return NULL;
5211 }
5212
5213 /*
5214 * Deal with resource / data DLLs.
5215 */
5216 if (fFlags & ( DONT_RESOLVE_DLL_REFERENCES
5217 | LOAD_LIBRARY_AS_DATAFILE
5218 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
5219 | LOAD_LIBRARY_AS_IMAGE_RESOURCE) )
5220 return kwSandbox_Kernel32_LoadLibraryExA_Resource(pDynLoad, fFlags);
5221
5222 /*
5223 * Special case: api-ms-win-core-synch-l1-2-0 and friends (32-bit yasm, built with VS2015).
5224 */
5225 if ( strnicmp(pszFilename, TUPLE("api-ms-")) == 0
5226 && kHlpIsFilenameOnly(pszFilename))
5227 return kwSandbox_Kernel32_LoadLibraryExA_VirtualApiModule(pDynLoad, fFlags);
5228
5229 /*
5230 * Normal library loading.
5231 * We start by being very lazy and reusing the code for resolving imports.
5232 */
5233 pszSearchPath = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5234 if (!kHlpIsFilenameOnly(pszFilename))
5235 pMod = kwLdrModuleTryLoadDll(pszFilename, KWLOCATION_UNKNOWN, g_Sandbox.pTool->u.Sandboxed.pExe, pszSearchPath);
5236 else
5237 {
5238 rc = kwLdrModuleResolveAndLookup(pszFilename, g_Sandbox.pTool->u.Sandboxed.pExe, NULL /*pImporter*/, pszSearchPath, &pMod);
5239 if (rc != 0)
5240 pMod = NULL;
5241 }
5242 if (pMod && pMod != (PKWMODULE)~(KUPTR)0)
5243 {
5244 /* Enter it into the tool module table and dynamic link request cache. */
5245 kwToolAddModuleAndImports(g_Sandbox.pTool, pMod);
5246
5247 pDynLoad->pMod = pMod;
5248 pDynLoad->hmod = pMod->hOurMod;
5249
5250 pDynLoad->pNext = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead;
5251 g_Sandbox.pTool->u.Sandboxed.pDynLoadHead = pDynLoad;
5252
5253 /*
5254 * Make sure it's initialized (need to link it first since DllMain may
5255 * use loader APIs).
5256 */
5257 rc = kwLdrModuleInitTree(pMod);
5258 if (rc == 0)
5259 {
5260 KW_LOG(("LoadLibraryExA(%s,,) -> %p\n", pszFilename, pDynLoad->hmod));
5261 return pDynLoad->hmod;
5262 }
5263
5264 SetLastError(ERROR_DLL_INIT_FAILED);
5265 }
5266 else
5267 {
5268 KWFS_TODO();
5269 kHlpFree(pDynLoad);
5270 SetLastError(pMod ? ERROR_BAD_EXE_FORMAT : ERROR_MOD_NOT_FOUND);
5271 }
5272 return NULL;
5273}
5274
5275
5276/** Kernel32 - LoadLibraryExA() for native overloads */
5277static HMODULE WINAPI kwSandbox_Kernel32_Native_LoadLibraryExA(LPCSTR pszFilename, HANDLE hFile, DWORD fFlags)
5278{
5279 char szPath[1024];
5280 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA(%s, %p, %#x)\n", pszFilename, hFile, fFlags));
5281
5282 /*
5283 * We may have to help resolved unqualified DLLs living in the executable directory.
5284 */
5285 if (kHlpIsFilenameOnly(pszFilename))
5286 {
5287 KSIZE cchFilename = kHlpStrLen(pszFilename);
5288 KSIZE cchExePath = g_Sandbox.pTool->u.Sandboxed.pExe->offFilename;
5289 if (cchExePath + cchFilename + 1 <= sizeof(szPath))
5290 {
5291 kHlpMemCopy(szPath, g_Sandbox.pTool->u.Sandboxed.pExe->pszPath, cchExePath);
5292 kHlpMemCopy(&szPath[cchExePath], pszFilename, cchFilename + 1);
5293 if (kwFsPathExists(szPath))
5294 {
5295 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5296 pszFilename = szPath;
5297 }
5298 }
5299
5300 if (pszFilename != szPath)
5301 {
5302 KSIZE cchSuffix = 0;
5303 KBOOL fNeedSuffix = K_FALSE;
5304 const char *pszCur = kwSandboxDoGetEnvA(&g_Sandbox, "PATH", 4);
5305 while (*pszCur != '\0')
5306 {
5307 /* Find the end of the component */
5308 KSIZE cch = 0;
5309 while (pszCur[cch] != ';' && pszCur[cch] != '\0')
5310 cch++;
5311
5312 if ( cch > 0 /* wrong, but whatever */
5313 && cch + 1 + cchFilename + cchSuffix < sizeof(szPath))
5314 {
5315 char *pszDst = kHlpMemPCopy(szPath, pszCur, cch);
5316 if ( szPath[cch - 1] != ':'
5317 && szPath[cch - 1] != '/'
5318 && szPath[cch - 1] != '\\')
5319 *pszDst++ = '\\';
5320 pszDst = kHlpMemPCopy(pszDst, pszFilename, cchFilename);
5321 if (fNeedSuffix)
5322 pszDst = kHlpMemPCopy(pszDst, ".dll", 4);
5323 *pszDst = '\0';
5324
5325 if (kwFsPathExists(szPath))
5326 {
5327 KWLDR_LOG(("kwSandbox_Kernel32_Native_LoadLibraryExA: %s -> %s\n", pszFilename, szPath));
5328 pszFilename = szPath;
5329 break;
5330 }
5331 }
5332
5333 /* Advance */
5334 pszCur += cch;
5335 while (*pszCur == ';')
5336 pszCur++;
5337 }
5338 }
5339 }
5340
5341 return LoadLibraryExA(pszFilename, hFile, fFlags);
5342}
5343
5344
5345/** Kernel32 - LoadLibraryExW() */
5346static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryExW(LPCWSTR pwszFilename, HANDLE hFile, DWORD fFlags)
5347{
5348 char szTmp[4096];
5349 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5350 if (cchTmp < sizeof(szTmp))
5351 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, hFile, fFlags);
5352
5353 KWFS_TODO();
5354 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5355 return NULL;
5356}
5357
5358/** Kernel32 - LoadLibraryA() */
5359static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryA(LPCSTR pszFilename)
5360{
5361 return kwSandbox_Kernel32_LoadLibraryExA(pszFilename, NULL /*hFile*/, 0 /*fFlags*/);
5362}
5363
5364
5365/** Kernel32 - LoadLibraryW() */
5366static HMODULE WINAPI kwSandbox_Kernel32_LoadLibraryW(LPCWSTR pwszFilename)
5367{
5368 char szTmp[4096];
5369 KSIZE cchTmp = kwUtf16ToStr(pwszFilename, szTmp, sizeof(szTmp));
5370 if (cchTmp < sizeof(szTmp))
5371 return kwSandbox_Kernel32_LoadLibraryExA(szTmp, NULL /*hFile*/, 0 /*fFlags*/);
5372 KWFS_TODO();
5373 SetLastError(ERROR_FILENAME_EXCED_RANGE);
5374 return NULL;
5375}
5376
5377
5378/** Kernel32 - FreeLibrary() */
5379static BOOL WINAPI kwSandbox_Kernel32_FreeLibrary(HMODULE hmod)
5380{
5381 /* Ignored, we like to keep everything loaded. */
5382 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5383 return TRUE;
5384}
5385
5386
5387/** Worker for GetModuleHandleA/W for handling cached modules. */
5388static HMODULE kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(KSIZE i)
5389{
5390 HMODULE hmod = g_aGetModuleHandleCache[i].hmod;
5391 if (hmod)
5392 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [cached]\n",
5393 hmod, g_aGetModuleHandleCache[i].pszName));
5394 else
5395 {
5396 /*
5397 * The first time around we have to make sure we have a module table
5398 * entry for it, if not we add one. We need to add it to the tools
5399 * module list to for it to work.
5400 */
5401 PKWMODULE pMod = kwLdrModuleForLoadedNative(g_aGetModuleHandleCache[i].pszName, K_FALSE);
5402 if (pMod)
5403 {
5404 hmod = pMod->hOurMod;
5405 if (!kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod))
5406 {
5407 kwToolAddModule(g_Sandbox.pTool, pMod);
5408 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [added to tool]\n",
5409 hmod, g_aGetModuleHandleCache[i].pszName));
5410 }
5411 else
5412 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(%u/%s -> %p [known to tool]\n",
5413 hmod, g_aGetModuleHandleCache[i].pszName));
5414
5415 }
5416 }
5417 return hmod;
5418}
5419
5420
5421/** Kernel32 - GetModuleHandleA() */
5422static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleA(LPCSTR pszModule)
5423{
5424 KSIZE i;
5425 KSIZE cchModule;
5426 PKWDYNLOAD pDynLoad;
5427 KSIZE cchSuffix;
5428 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5429 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5430
5431 /*
5432 * The executable.
5433 */
5434 if (pszModule == NULL)
5435 {
5436 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5437 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5438 }
5439
5440 /*
5441 * If no path of suffix, pretend it ends with .DLL.
5442 */
5443 cchSuffix = strpbrk(pszModule, ":/\\.") ? 0 : 4;
5444
5445 /*
5446 * Cache of system modules we've seen queried.
5447 */
5448 cchModule = kHlpStrLen(pszModule);
5449 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5450 if ( ( g_aGetModuleHandleCache[i].cchName == cchModule
5451 && stricmp(pszModule, g_aGetModuleHandleCache[i].pszName) == 0)
5452 || ( cchSuffix > 0
5453 && g_aGetModuleHandleCache[i].cchName == cchModule + cchSuffix
5454 && strnicmp(pszModule, g_aGetModuleHandleCache[i].pszName, cchModule)
5455 && stricmp(&g_aGetModuleHandleCache[i].pszName[cchModule], ".dll") == 0))
5456 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5457
5458 /*
5459 * Modules we've dynamically loaded.
5460 */
5461 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5462 if (pDynLoad->pMod)
5463 {
5464 const char *pszPath = pDynLoad->pMod->pszPath;
5465 const char *pszName = &pszPath[pDynLoad->pMod->offFilename];
5466 if ( stricmp(pszPath, pszModule) == 0
5467 || stricmp(pszName, pszModule) == 0
5468 || ( cchSuffix > 0
5469 && strnicmp(pszName, pszModule, cchModule) == 0
5470 && stricmp(&pszName[cchModule], ".dll") == 0))
5471 {
5472 if ( pDynLoad->pMod->fNative
5473 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5474 {
5475 KW_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s,,) -> %p [dynload]\n", pszModule, pDynLoad->hmod));
5476 return pDynLoad->hmod;
5477 }
5478 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s) -> NULL (not read)\n", pszModule));
5479 SetLastError(ERROR_MOD_NOT_FOUND);
5480 return NULL;
5481 }
5482 }
5483
5484 /*
5485 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5486 * to and go via the g_aGetModuleHandleCache cache.
5487 */
5488 if (strnicmp(pszModule, "api-ms-win-", 11) == 0)
5489 {
5490 HMODULE hmod = GetModuleHandleA(pszModule);
5491 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleA(%s); hmod=%p\n", pszModule, hmod));
5492 if (hmod)
5493 {
5494 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5495 return kwSandbox_Kernel32_GetModuleHandleA("KERNELBASE.DLL");
5496 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5497 return kwSandbox_Kernel32_GetModuleHandleA("KERNEL32.DLL");
5498 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5499 return kwSandbox_Kernel32_GetModuleHandleA("NTDLL.DLL");
5500 }
5501 else
5502 dwErr = GetLastError();
5503 }
5504
5505 kwErrPrintf("pszModule=%s\n", pszModule);
5506 KWFS_TODO();
5507 SetLastError(ERROR_MOD_NOT_FOUND);
5508 return NULL;
5509}
5510
5511
5512/** Kernel32 - GetModuleHandleW() */
5513static HMODULE WINAPI kwSandbox_Kernel32_GetModuleHandleW(LPCWSTR pwszModule)
5514{
5515 KSIZE i;
5516 KSIZE cwcModule;
5517 PKWDYNLOAD pDynLoad;
5518 KSIZE cwcSuffix;
5519 DWORD dwErr = ERROR_MOD_NOT_FOUND;
5520 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5521
5522 /*
5523 * The executable.
5524 */
5525 if (pwszModule == NULL)
5526 {
5527 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(NULL) -> %p (exe)\n", g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod));
5528 return (HMODULE)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod;
5529 }
5530
5531 /*
5532 * If no path of suffix, pretend it ends with .DLL.
5533 */
5534 cwcSuffix = wcspbrk(pwszModule, L":/\\.") ? 0 : 4;
5535
5536 /*
5537 * Cache of system modules we've seen queried.
5538 */
5539 cwcModule = kwUtf16Len(pwszModule);
5540 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5541 if ( ( g_aGetModuleHandleCache[i].cwcName == cwcModule
5542 && _wcsicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName) == 0)
5543 || ( cwcSuffix > 0
5544 && g_aGetModuleHandleCache[i].cwcName == cwcModule + cwcSuffix
5545 && _wcsnicmp(pwszModule, g_aGetModuleHandleCache[i].pwszName, cwcModule) == 0
5546 && _wcsicmp(&g_aGetModuleHandleCache[i].pwszName[cwcModule], L".dll") == 0))
5547 return kwSandbox_Kernel32_GetModuleHandle_ReturnedCachedEntry(i);
5548
5549 /*
5550 * Modules we've dynamically loaded.
5551 */
5552 for (pDynLoad = g_Sandbox.pTool->u.Sandboxed.pDynLoadHead; pDynLoad; pDynLoad = pDynLoad->pNext)
5553 if (pDynLoad->pMod)
5554 {
5555 const wchar_t *pwszPath = pDynLoad->pMod->pwszPath;
5556 const wchar_t *pwszName = &pwszPath[pDynLoad->pMod->offFilenameW];
5557 if ( _wcsicmp(pwszPath, pwszModule) == 0
5558 || _wcsicmp(pwszName, pwszModule) == 0
5559 || ( cwcSuffix
5560 && _wcsnicmp(pwszName, pwszModule, cwcModule) == 0
5561 && _wcsicmp(&pwszName[cwcModule], L".dll") == 0))
5562 {
5563 if ( pDynLoad->pMod->fNative
5564 || pDynLoad->pMod->u.Manual.enmState == KWMODSTATE_READY)
5565 {
5566 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls,,) -> %p [dynload]\n", pwszModule, pDynLoad->hmod));
5567 return pDynLoad->hmod;
5568 }
5569 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls) -> NULL (not read)\n", pwszModule));
5570 SetLastError(ERROR_MOD_NOT_FOUND);
5571 return NULL;
5572 }
5573 }
5574
5575 /*
5576 * Hack for the api-ms-win-xxxxx.dll modules. Find which module they map
5577 * to and go via the g_aGetModuleHandleCache cache.
5578 */
5579 if (_wcsnicmp(pwszModule, L"api-ms-win-", 11) == 0)
5580 {
5581 HMODULE hmod = GetModuleHandleW(pwszModule);
5582 KWLDR_LOG(("kwSandbox_Kernel32_GetModuleHandleW(%ls); hmod=%p\n", pwszModule, hmod));
5583 if (hmod)
5584 {
5585 if (hmod == GetModuleHandleW(L"KERNELBASE.DLL"))
5586 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNELBASE.DLL");
5587 if (hmod == GetModuleHandleW(L"KERNEL32.DLL"))
5588 return kwSandbox_Kernel32_GetModuleHandleW(L"KERNEL32.DLL");
5589 if (hmod == GetModuleHandleW(L"NTDLL.DLL"))
5590 return kwSandbox_Kernel32_GetModuleHandleW(L"NTDLL.DLL");
5591 }
5592 else
5593 dwErr = GetLastError();
5594 }
5595
5596 kwErrPrintf("pwszModule=%ls\n", pwszModule);
5597 KWFS_TODO();
5598 SetLastError(dwErr);
5599 return NULL;
5600}
5601
5602
5603/** Used to debug dynamically resolved procedures. */
5604static UINT WINAPI kwSandbox_BreakIntoDebugger(void *pv1, void *pv2, void *pv3, void *pv4)
5605{
5606#ifdef _MSC_VER
5607 __debugbreak();
5608#else
5609 KWFS_TODO();
5610#endif
5611 return -1;
5612}
5613
5614
5615#ifndef NDEBUG
5616/*
5617 * This wraps up to three InvokeCompilerPassW functions and dumps their arguments to the log.
5618 */
5619# if K_ARCH == K_ARCH_X86_32
5620static char g_szInvokeCompilePassW[] = "_InvokeCompilerPassW@16";
5621# else
5622static char g_szInvokeCompilePassW[] = "InvokeCompilerPassW";
5623# endif
5624typedef KIPTR __stdcall FNINVOKECOMPILERPASSW(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance);
5625typedef FNINVOKECOMPILERPASSW *PFNINVOKECOMPILERPASSW;
5626typedef struct KWCXINTERCEPTORENTRY
5627{
5628 PFNINVOKECOMPILERPASSW pfnOrg;
5629 PKWMODULE pModule;
5630 PFNINVOKECOMPILERPASSW pfnWrap;
5631} KWCXINTERCEPTORENTRY;
5632
5633static KIPTR kwSandbox_Cx_InvokeCompilerPassW_Common(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance,
5634 KWCXINTERCEPTORENTRY *pEntry)
5635{
5636 int i;
5637 KIPTR rcExit;
5638 KW_LOG(("%s!InvokeCompilerPassW(%d, %p, %#x, %p)\n",
5639 &pEntry->pModule->pszPath[pEntry->pModule->offFilename], cArgs, papwszArgs, fFlags, phCluiInstance));
5640 for (i = 0; i < cArgs; i++)
5641 KW_LOG((" papwszArgs[%u]='%ls'\n", i, papwszArgs[i]));
5642
5643 rcExit = pEntry->pfnOrg(cArgs, papwszArgs, fFlags, phCluiInstance);
5644
5645 KW_LOG(("%s!InvokeCompilerPassW returns %d\n", &pEntry->pModule->pszPath[pEntry->pModule->offFilename], rcExit));
5646 return rcExit;
5647}
5648
5649static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_0;
5650static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_1;
5651static FNINVOKECOMPILERPASSW kwSandbox_Cx_InvokeCompilerPassW_2;
5652
5653static KWCXINTERCEPTORENTRY g_aCxInterceptorEntries[] =
5654{
5655 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_0 },
5656 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_1 },
5657 { NULL, NULL, kwSandbox_Cx_InvokeCompilerPassW_2 },
5658};
5659
5660static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_0(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5661{
5662 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[0]);
5663}
5664
5665static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_1(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5666{
5667 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[1]);
5668}
5669
5670static KIPTR __stdcall kwSandbox_Cx_InvokeCompilerPassW_2(int cArgs, wchar_t **papwszArgs, KUPTR fFlags, void **phCluiInstance)
5671{
5672 return kwSandbox_Cx_InvokeCompilerPassW_Common(cArgs, papwszArgs, fFlags, phCluiInstance, &g_aCxInterceptorEntries[2]);
5673}
5674
5675#endif /* !NDEBUG */
5676
5677
5678/** Kernel32 - GetProcAddress() */
5679static FARPROC WINAPI kwSandbox_Kernel32_GetProcAddress(HMODULE hmod, LPCSTR pszProc)
5680{
5681 KSIZE i;
5682 PKWMODULE pMod;
5683 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5684
5685 /*
5686 * Try locate the module.
5687 */
5688 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5689 if (pMod)
5690 {
5691 KLDRADDR uValue;
5692 int rc = kLdrModQuerySymbol(pMod->pLdrMod,
5693 pMod->fNative ? NULL : pMod->u.Manual.pvBits,
5694 pMod->fNative ? KLDRMOD_BASEADDRESS_MAP : (KUPTR)pMod->u.Manual.pbLoad,
5695 KU32_MAX /*iSymbol*/,
5696 pszProc,
5697 kHlpStrLen(pszProc),
5698 NULL /*pszVersion*/,
5699 NULL /*pfnGetForwarder*/, NULL /*pvUser*/,
5700 &uValue,
5701 NULL /*pfKind*/);
5702 if (rc == 0)
5703 {
5704 //static int s_cDbgGets = 0;
5705 KU32 cchProc = (KU32)kHlpStrLen(pszProc);
5706 KU32 i = g_cSandboxGetProcReplacements;
5707 while (i-- > 0)
5708 if ( g_aSandboxGetProcReplacements[i].cchFunction == cchProc
5709 && kHlpMemComp(g_aSandboxGetProcReplacements[i].pszFunction, pszProc, cchProc) == 0)
5710 {
5711 if ( !g_aSandboxGetProcReplacements[i].pszModule
5712 || kHlpStrICompAscii(g_aSandboxGetProcReplacements[i].pszModule, &pMod->pszPath[pMod->offFilename]) == 0)
5713 {
5714 if ( !g_aSandboxGetProcReplacements[i].fOnlyExe
5715 || (KUPTR)_ReturnAddress() - (KUPTR)g_Sandbox.pTool->u.Sandboxed.pExe->hOurMod
5716 < g_Sandbox.pTool->u.Sandboxed.pExe->cbImage)
5717 {
5718 uValue = g_aSandboxGetProcReplacements[i].pfnReplacement;
5719 KW_LOG(("GetProcAddress(%s, %s) -> %p replaced\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5720 }
5721 kwLdrModuleRelease(pMod);
5722 return (FARPROC)(KUPTR)uValue;
5723 }
5724 }
5725
5726#ifndef NDEBUG
5727 /* Intercept the compiler pass method, dumping arguments. */
5728 if (kHlpStrComp(pszProc, g_szInvokeCompilePassW) == 0)
5729 {
5730 KU32 i;
5731 for (i = 0; i < K_ELEMENTS(g_aCxInterceptorEntries); i++)
5732 if ((KUPTR)g_aCxInterceptorEntries[i].pfnOrg == uValue)
5733 {
5734 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5735 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW\n"));
5736 break;
5737 }
5738 if (i >= K_ELEMENTS(g_aCxInterceptorEntries))
5739 while (i-- > 0)
5740 if (g_aCxInterceptorEntries[i].pfnOrg == NULL)
5741 {
5742 g_aCxInterceptorEntries[i].pfnOrg = (PFNINVOKECOMPILERPASSW)(KUPTR)uValue;
5743 g_aCxInterceptorEntries[i].pModule = pMod;
5744 uValue = (KUPTR)g_aCxInterceptorEntries[i].pfnWrap;
5745 KW_LOG(("GetProcAddress: intercepting InvokeCompilerPassW (new)\n"));
5746 break;
5747 }
5748 }
5749#endif
5750 KW_LOG(("GetProcAddress(%s, %s) -> %p\n", pMod->pszPath, pszProc, (KUPTR)uValue));
5751 kwLdrModuleRelease(pMod);
5752 //s_cDbgGets++;
5753 //if (s_cGets >= 3)
5754 // return (FARPROC)kwSandbox_BreakIntoDebugger;
5755 return (FARPROC)(KUPTR)uValue;
5756 }
5757
5758 KWFS_TODO();
5759 SetLastError(ERROR_PROC_NOT_FOUND);
5760 kwLdrModuleRelease(pMod);
5761 return NULL;
5762 }
5763
5764 /*
5765 * Hmm... could be a cached module-by-name.
5766 */
5767 for (i = 0; i < K_ELEMENTS(g_aGetModuleHandleCache); i++)
5768 if (g_aGetModuleHandleCache[i].hmod == hmod)
5769 return GetProcAddress(hmod, pszProc);
5770
5771 KWFS_TODO();
5772 return GetProcAddress(hmod, pszProc);
5773}
5774
5775
5776/** Kernel32 - GetModuleFileNameA() */
5777static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameA(HMODULE hmod, LPSTR pszFilename, DWORD cbFilename)
5778{
5779 PKWMODULE pMod;
5780 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5781
5782 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5783 if (pMod != NULL)
5784 {
5785 DWORD cbRet = kwStrCopyStyle1(pMod->pszPath, pszFilename, cbFilename);
5786 kwLdrModuleRelease(pMod);
5787 return cbRet;
5788 }
5789 KWFS_TODO();
5790 return 0;
5791}
5792
5793
5794/** Kernel32 - GetModuleFileNameW() */
5795static DWORD WINAPI kwSandbox_Kernel32_GetModuleFileNameW(HMODULE hmod, LPWSTR pwszFilename, DWORD cbFilename)
5796{
5797 PKWMODULE pMod;
5798 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
5799
5800 pMod = kwToolLocateModuleByHandle(g_Sandbox.pTool, hmod);
5801 if (pMod)
5802 {
5803 DWORD cwcRet = kwUtf16CopyStyle1(pMod->pwszPath, pwszFilename, cbFilename);
5804 kwLdrModuleRelease(pMod);
5805 return cwcRet;
5806 }
5807
5808 KWFS_TODO();
5809 return 0;
5810}
5811
5812
5813/** NtDll - RtlPcToFileHeader
5814 * This is necessary for msvcr100.dll!CxxThrowException. */
5815static PVOID WINAPI kwSandbox_ntdll_RtlPcToFileHeader(PVOID pvPC, PVOID *ppvImageBase)
5816{
5817 PVOID pvRet;
5818
5819 /*
5820 * Do a binary lookup of the module table for the current tool.
5821 * This will give us a
5822 */
5823 if (g_Sandbox.fRunning)
5824 {
5825 KUPTR const uPC = (KUPTR)pvPC;
5826 PKWMODULE *papMods = g_Sandbox.pTool->u.Sandboxed.papModules;
5827 KU32 iEnd = g_Sandbox.pTool->u.Sandboxed.cModules;
5828 KU32 i;
5829 if (iEnd)
5830 {
5831 KU32 iStart = 0;
5832 i = iEnd / 2;
5833 for (;;)
5834 {
5835 KUPTR const uHModThis = (KUPTR)papMods[i]->hOurMod;
5836 if (uPC < uHModThis)
5837 {
5838 iEnd = i;
5839 if (iStart < i)
5840 { }
5841 else
5842 break;
5843 }
5844 else if (uPC != uHModThis)
5845 {
5846 iStart = ++i;
5847 if (i < iEnd)
5848 { }
5849 else
5850 break;
5851 }
5852 else
5853 {
5854 /* This isn't supposed to happen. */
5855 break;
5856 }
5857
5858 i = iStart + (iEnd - iStart) / 2;
5859 }
5860
5861 /* For reasons of simplicity (= copy & paste), we end up with the
5862 module after the one we're interested in here. */
5863 i--;
5864 if (i < g_Sandbox.pTool->u.Sandboxed.cModules
5865 && papMods[i]->pLdrMod)
5866 {
5867 KSIZE uRvaPC = uPC - (KUPTR)papMods[i]->hOurMod;
5868 if (uRvaPC < papMods[i]->cbImage)
5869 {
5870 *ppvImageBase = papMods[i]->hOurMod;
5871 pvRet = papMods[i]->hOurMod;
5872 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p [our]\n", pvPC, pvRet, *ppvImageBase));
5873 return pvRet;
5874 }
5875 }
5876 }
5877 else
5878 i = 0;
5879 }
5880
5881 /*
5882 * Call the regular API.
5883 */
5884 pvRet = RtlPcToFileHeader(pvPC, ppvImageBase);
5885 KW_LOG(("RtlPcToFileHeader(PC=%p) -> %p, *ppvImageBase=%p \n", pvPC, pvRet, *ppvImageBase));
5886 return pvRet;
5887}
5888
5889
5890/*
5891 *
5892 * File access APIs (for speeding them up).
5893 * File access APIs (for speeding them up).
5894 * File access APIs (for speeding them up).
5895 *
5896 */
5897
5898
5899/**
5900 * Converts a lookup error to a windows error code.
5901 *
5902 * @returns The windows error code.
5903 * @param enmError The lookup error.
5904 */
5905static DWORD kwFsLookupErrorToWindowsError(KFSLOOKUPERROR enmError)
5906{
5907 switch (enmError)
5908 {
5909 case KFSLOOKUPERROR_NOT_FOUND:
5910 case KFSLOOKUPERROR_NOT_DIR:
5911 return ERROR_FILE_NOT_FOUND;
5912
5913 case KFSLOOKUPERROR_PATH_COMP_NOT_FOUND:
5914 case KFSLOOKUPERROR_PATH_COMP_NOT_DIR:
5915 return ERROR_PATH_NOT_FOUND;
5916
5917 case KFSLOOKUPERROR_PATH_TOO_LONG:
5918 return ERROR_FILENAME_EXCED_RANGE;
5919
5920 case KFSLOOKUPERROR_OUT_OF_MEMORY:
5921 return ERROR_NOT_ENOUGH_MEMORY;
5922
5923 default:
5924 return ERROR_PATH_NOT_FOUND;
5925 }
5926}
5927
5928#ifdef WITH_TEMP_MEMORY_FILES
5929
5930/**
5931 * Checks for a cl.exe temporary file.
5932 *
5933 * There are quite a bunch of these. They seems to be passing data between the
5934 * first and second compiler pass. Since they're on disk, they get subjected to
5935 * AV software screening and normal file consistency rules. So, not necessarily
5936 * a very efficient way of handling reasonably small amounts of data.
5937 *
5938 * We make the files live in virtual memory by intercepting their opening,
5939 * writing, reading, closing , mapping, unmapping, and maybe some more stuff.
5940 *
5941 * @returns K_TRUE / K_FALSE
5942 * @param pwszFilename The file name being accessed.
5943 */
5944static KBOOL kwFsIsClTempFileW(const wchar_t *pwszFilename)
5945{
5946 wchar_t const *pwszName = kwPathGetFilenameW(pwszFilename);
5947 if (pwszName)
5948 {
5949 /* The name starts with _CL_... */
5950 if ( pwszName[0] == '_'
5951 && pwszName[1] == 'C'
5952 && pwszName[2] == 'L'
5953 && pwszName[3] == '_' )
5954 {
5955 /* ... followed by 8 xdigits and ends with a two letter file type. Simplify
5956 this check by just checking that it's alpha numerical ascii from here on. */
5957 wchar_t wc;
5958 pwszName += 4;
5959 while ((wc = *pwszName++) != '\0')
5960 {
5961 if (wc < 127 && iswalnum(wc))
5962 { /* likely */ }
5963 else
5964 return K_FALSE;
5965 }
5966 return K_TRUE;
5967 }
5968 }
5969 return K_FALSE;
5970}
5971
5972
5973/**
5974 * Creates a handle to a temporary file.
5975 *
5976 * @returns The handle on success.
5977 * INVALID_HANDLE_VALUE and SetLastError on failure.
5978 * @param pTempFile The temporary file.
5979 * @param dwDesiredAccess The desired access to the handle.
5980 * @param fMapping Whether this is a mapping (K_TRUE) or file
5981 * (K_FALSE) handle type.
5982 */
5983static HANDLE kwFsTempFileCreateHandle(PKWFSTEMPFILE pTempFile, DWORD dwDesiredAccess, KBOOL fMapping)
5984{
5985 /*
5986 * Create a handle to the temporary file.
5987 */
5988 HANDLE hFile = INVALID_HANDLE_VALUE;
5989 HANDLE hProcSelf = GetCurrentProcess();
5990 if (DuplicateHandle(hProcSelf, hProcSelf,
5991 hProcSelf, &hFile,
5992 SYNCHRONIZE, FALSE,
5993 0 /*dwOptions*/))
5994 {
5995 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
5996 if (pHandle)
5997 {
5998 pHandle->enmType = !fMapping ? KWHANDLETYPE_TEMP_FILE : KWHANDLETYPE_TEMP_FILE_MAPPING;
5999 pHandle->cRefs = 1;
6000 pHandle->offFile = 0;
6001 pHandle->hHandle = hFile;
6002 pHandle->dwDesiredAccess = dwDesiredAccess;
6003 pHandle->u.pTempFile = pTempFile;
6004 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, hFile))
6005 {
6006 pTempFile->cActiveHandles++;
6007 kHlpAssert(pTempFile->cActiveHandles >= 1);
6008 kHlpAssert(pTempFile->cActiveHandles <= 2);
6009 KWFS_LOG(("kwFsTempFileCreateHandle: Temporary file '%ls' -> %p\n", pTempFile->pwszPath, hFile));
6010 return hFile;
6011 }
6012
6013 kHlpFree(pHandle);
6014 }
6015 else
6016 KWFS_LOG(("kwFsTempFileCreateHandle: Out of memory!\n"));
6017 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6018 }
6019 else
6020 KWFS_LOG(("kwFsTempFileCreateHandle: DuplicateHandle failed: err=%u\n", GetLastError()));
6021 return INVALID_HANDLE_VALUE;
6022}
6023
6024
6025static HANDLE kwFsTempFileCreateW(const wchar_t *pwszFilename, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
6026{
6027 HANDLE hFile;
6028 DWORD dwErr;
6029
6030 /*
6031 * Check if we've got an existing temp file.
6032 * ASSUME exact same path for now.
6033 */
6034 KSIZE const cwcFilename = kwUtf16Len(pwszFilename);
6035 PKWFSTEMPFILE pTempFile;
6036 for (pTempFile = g_Sandbox.pTempFileHead; pTempFile != NULL; pTempFile = pTempFile->pNext)
6037 {
6038 /* Since the last two chars are usually the only difference, we check them manually before calling memcmp. */
6039 if ( pTempFile->cwcPath == cwcFilename
6040 && pTempFile->pwszPath[cwcFilename - 1] == pwszFilename[cwcFilename - 1]
6041 && pTempFile->pwszPath[cwcFilename - 2] == pwszFilename[cwcFilename - 2]
6042 && kHlpMemComp(pTempFile->pwszPath, pwszFilename, cwcFilename) == 0)
6043 break;
6044 }
6045
6046 /*
6047 * Create a new temporary file instance if not found.
6048 */
6049 if (pTempFile == NULL)
6050 {
6051 KSIZE cbFilename;
6052
6053 switch (dwCreationDisposition)
6054 {
6055 case CREATE_ALWAYS:
6056 case OPEN_ALWAYS:
6057 dwErr = NO_ERROR;
6058 break;
6059
6060 case CREATE_NEW:
6061 kHlpAssertFailed();
6062 SetLastError(ERROR_ALREADY_EXISTS);
6063 return INVALID_HANDLE_VALUE;
6064
6065 case OPEN_EXISTING:
6066 case TRUNCATE_EXISTING:
6067 kHlpAssertFailed();
6068 SetLastError(ERROR_FILE_NOT_FOUND);
6069 return INVALID_HANDLE_VALUE;
6070
6071 default:
6072 kHlpAssertFailed();
6073 SetLastError(ERROR_INVALID_PARAMETER);
6074 return INVALID_HANDLE_VALUE;
6075 }
6076
6077 cbFilename = (cwcFilename + 1) * sizeof(wchar_t);
6078 pTempFile = (PKWFSTEMPFILE)kHlpAlloc(sizeof(*pTempFile) + cbFilename);
6079 if (pTempFile)
6080 {
6081 pTempFile->cwcPath = (KU16)cwcFilename;
6082 pTempFile->cbFile = 0;
6083 pTempFile->cbFileAllocated = 0;
6084 pTempFile->cActiveHandles = 0;
6085 pTempFile->cMappings = 0;
6086 pTempFile->cSegs = 0;
6087 pTempFile->paSegs = NULL;
6088 pTempFile->pwszPath = (wchar_t const *)kHlpMemCopy(pTempFile + 1, pwszFilename, cbFilename);
6089
6090 pTempFile->pNext = g_Sandbox.pTempFileHead;
6091 g_Sandbox.pTempFileHead = pTempFile;
6092 KWFS_LOG(("kwFsTempFileCreateW: Created new temporary file '%ls'\n", pwszFilename));
6093 }
6094 else
6095 {
6096 KWFS_LOG(("kwFsTempFileCreateW: Out of memory!\n"));
6097 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
6098 return INVALID_HANDLE_VALUE;
6099 }
6100 }
6101 else
6102 {
6103 switch (dwCreationDisposition)
6104 {
6105 case OPEN_EXISTING:
6106 dwErr = NO_ERROR;
6107 break;
6108 case OPEN_ALWAYS:
6109 dwErr = ERROR_ALREADY_EXISTS ;
6110 break;
6111
6112 case TRUNCATE_EXISTING:
6113 case CREATE_ALWAYS:
6114 kHlpAssertFailed();
6115 pTempFile->cbFile = 0;
6116 dwErr = ERROR_ALREADY_EXISTS;
6117 break;
6118
6119 case CREATE_NEW:
6120 kHlpAssertFailed();
6121 SetLastError(ERROR_FILE_EXISTS);
6122 return INVALID_HANDLE_VALUE;
6123
6124 default:
6125 kHlpAssertFailed();
6126 SetLastError(ERROR_INVALID_PARAMETER);
6127 return INVALID_HANDLE_VALUE;
6128 }
6129 }
6130
6131 /*
6132 * Create a handle to the temporary file.
6133 */
6134 hFile = kwFsTempFileCreateHandle(pTempFile, dwDesiredAccess, K_FALSE /*fMapping*/);
6135 if (hFile != INVALID_HANDLE_VALUE)
6136 SetLastError(dwErr);
6137 return hFile;
6138}
6139
6140#endif /* WITH_TEMP_MEMORY_FILES */
6141
6142/**
6143 * Worker for kwFsIsCacheableExtensionA and kwFsIsCacheableExtensionW
6144 *
6145 * @returns K_TRUE if cacheable, K_FALSE if not.
6146 * @param wcFirst The first extension character.
6147 * @param wcSecond The second extension character.
6148 * @param wcThird The third extension character.
6149 * @param fAttrQuery Set if it's for an attribute query, clear if for
6150 * file creation.
6151 */
6152static KBOOL kwFsIsCacheableExtensionCommon(wchar_t wcFirst, wchar_t wcSecond, wchar_t wcThird, KBOOL fAttrQuery)
6153{
6154 /* C++ header without an extension or a directory. */
6155 if (wcFirst == '\0')
6156 {
6157 /** @todo exclude temporary files... */
6158 return K_TRUE;
6159 }
6160
6161 /* C Header: .h */
6162 if (wcFirst == 'h' || wcFirst == 'H')
6163 {
6164 if (wcSecond == '\0')
6165 return K_TRUE;
6166
6167 /* C++ Header: .hpp, .hxx */
6168 if ( (wcSecond == 'p' || wcSecond == 'P')
6169 && (wcThird == 'p' || wcThird == 'P'))
6170 return K_TRUE;
6171 if ( (wcSecond == 'x' || wcSecond == 'X')
6172 && (wcThird == 'x' || wcThird == 'X'))
6173 return K_TRUE;
6174 }
6175 /* Misc starting with i. */
6176 else if (wcFirst == 'i' || wcFirst == 'I')
6177 {
6178 if (wcSecond != '\0')
6179 {
6180 if (wcSecond == 'n' || wcSecond == 'N')
6181 {
6182 /* C++ inline header: .inl */
6183 if (wcThird == 'l' || wcThird == 'L')
6184 return K_TRUE;
6185
6186 /* Assembly include file: .inc */
6187 if (wcThird == 'c' || wcThird == 'C')
6188 return K_TRUE;
6189 }
6190 }
6191 }
6192 /* Assembly header: .mac */
6193 else if (wcFirst == 'm' || wcFirst == 'M')
6194 {
6195 if (wcSecond == 'a' || wcSecond == 'A')
6196 {
6197 if (wcThird == 'c' || wcThird == 'C')
6198 return K_TRUE;
6199 }
6200 }
6201#ifdef WITH_PCH_CACHING
6202 /* Precompiled header: .pch */
6203 else if (wcFirst == 'p' || wcFirst == 'P')
6204 {
6205 if (wcSecond == 'c' || wcSecond == 'C')
6206 {
6207 if (wcThird == 'h' || wcThird == 'H')
6208 return !g_Sandbox.fNoPchCaching;
6209 }
6210 }
6211#endif
6212#if 0 /* Experimental - need to flush these afterwards as they're highly unlikely to be used after the link is done. */
6213 /* Linker - Object file: .obj */
6214 if ((wcFirst == 'o' || wcFirst == 'O') && g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6215 {
6216 if (wcSecond == 'b' || wcSecond == 'B')
6217 {
6218 if (wcThird == 'j' || wcThird == 'J')
6219 return K_TRUE;
6220 }
6221 }
6222#endif
6223 else if (fAttrQuery)
6224 {
6225 /* Dynamic link library: .dll */
6226 if (wcFirst == 'd' || wcFirst == 'D')
6227 {
6228 if (wcSecond == 'l' || wcSecond == 'L')
6229 {
6230 if (wcThird == 'l' || wcThird == 'L')
6231 return K_TRUE;
6232 }
6233 }
6234 /* Executable file: .exe */
6235 else if (wcFirst == 'e' || wcFirst == 'E')
6236 {
6237 if (wcSecond == 'x' || wcSecond == 'X')
6238 {
6239 if (wcThird == 'e' || wcThird == 'E')
6240 return K_TRUE;
6241 }
6242 }
6243 /* Response file: .rsp */
6244 else if (wcFirst == 'r' || wcFirst == 'R')
6245 {
6246 if (wcSecond == 's' || wcSecond == 'S')
6247 {
6248 if (wcThird == 'p' || wcThird == 'P')
6249 return !g_Sandbox.fNoPchCaching;
6250 }
6251 }
6252 /* Linker: */
6253 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
6254 {
6255 /* Object file: .obj */
6256 if (wcFirst == 'o' || wcFirst == 'O')
6257 {
6258 if (wcSecond == 'b' || wcSecond == 'B')
6259 {
6260 if (wcThird == 'j' || wcThird == 'J')
6261 return K_TRUE;
6262 }
6263 }
6264 /* Library file: .lib */
6265 else if (wcFirst == 'l' || wcFirst == 'L')
6266 {
6267 if (wcSecond == 'i' || wcSecond == 'I')
6268 {
6269 if (wcThird == 'b' || wcThird == 'B')
6270 return K_TRUE;
6271 }
6272 }
6273 /* Linker definition file: .def */
6274 else if (wcFirst == 'd' || wcFirst == 'D')
6275 {
6276 if (wcSecond == 'e' || wcSecond == 'E')
6277 {
6278 if (wcThird == 'f' || wcThird == 'F')
6279 return K_TRUE;
6280 }
6281 }
6282 }
6283 }
6284
6285 return K_FALSE;
6286}
6287
6288
6289/**
6290 * Checks if the file extension indicates that the file/dir is something we
6291 * ought to cache.
6292 *
6293 * @returns K_TRUE if cachable, K_FALSE if not.
6294 * @param pszExt The kHlpGetExt result.
6295 * @param fAttrQuery Set if it's for an attribute query, clear if for
6296 * file creation.
6297 */
6298static KBOOL kwFsIsCacheableExtensionA(const char *pszExt, KBOOL fAttrQuery)
6299{
6300 wchar_t const wcFirst = *pszExt;
6301 if (wcFirst)
6302 {
6303 wchar_t const wcSecond = pszExt[1];
6304 if (wcSecond)
6305 {
6306 wchar_t const wcThird = pszExt[2];
6307 if (pszExt[3] == '\0')
6308 return kwFsIsCacheableExtensionCommon(wcFirst, wcSecond, wcThird, fAttrQuery);
6309 return K_FALSE;
6310 }
6311 return kwFsIsCacheableExtensionCommon(wcFirst, 0, 0, fAttrQuery);
6312 }
6313 return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6314}
6315
6316
6317/**
6318 * Checks if the extension of the given UTF-16 path indicates that the file/dir
6319 * should be cached.
6320 *
6321 * @returns K_TRUE if cachable, K_FALSE if not.
6322 * @param pwszPath The UTF-16 path to examine.
6323 * @param fAttrQuery Set if it's for an attribute query, clear if for
6324 * file creation.
6325 */
6326static KBOOL kwFsIsCacheablePathExtensionW(const wchar_t *pwszPath, KBOOL fAttrQuery)
6327{
6328 KSIZE cwcExt;
6329 wchar_t const *pwszExt = kwFsPathGetExtW(pwszPath, &cwcExt);
6330 switch (cwcExt)
6331 {
6332 case 3: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], pwszExt[2], fAttrQuery);
6333 case 2: return kwFsIsCacheableExtensionCommon(pwszExt[0], pwszExt[1], 0, fAttrQuery);
6334 case 1: return kwFsIsCacheableExtensionCommon(pwszExt[0], 0, 0, fAttrQuery);
6335 case 0: return kwFsIsCacheableExtensionCommon(0, 0, 0, fAttrQuery);
6336 }
6337 return K_FALSE;
6338}
6339
6340
6341
6342/**
6343 * Creates a new
6344 *
6345 * @returns
6346 * @param pFsObj .
6347 * @param pwszFilename .
6348 */
6349static PKFSWCACHEDFILE kwFsObjCacheNewFile(PKFSOBJ pFsObj)
6350{
6351 HANDLE hFile;
6352 MY_IO_STATUS_BLOCK Ios;
6353 MY_OBJECT_ATTRIBUTES ObjAttr;
6354 MY_UNICODE_STRING UniStr;
6355 MY_NTSTATUS rcNt;
6356 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6357
6358 /*
6359 * Open the file relative to the parent directory.
6360 */
6361 kHlpAssert(pFsObj->bObjType == KFSOBJ_TYPE_FILE);
6362 kHlpAssert(pFsObj->pParent);
6363 kHlpAssertReturn(pFsObj->pParent->hDir != INVALID_HANDLE_VALUE, NULL);
6364
6365 Ios.Information = -1;
6366 Ios.u.Status = -1;
6367
6368 UniStr.Buffer = (wchar_t *)pFsObj->pwszName;
6369 UniStr.Length = (USHORT)(pFsObj->cwcName * sizeof(wchar_t));
6370 UniStr.MaximumLength = UniStr.Length + sizeof(wchar_t);
6371
6372 MyInitializeObjectAttributes(&ObjAttr, &UniStr, OBJ_CASE_INSENSITIVE, pFsObj->pParent->hDir, NULL /*pSecAttr*/);
6373
6374 rcNt = g_pfnNtCreateFile(&hFile,
6375 GENERIC_READ | SYNCHRONIZE,
6376 &ObjAttr,
6377 &Ios,
6378 NULL, /*cbFileInitialAlloc */
6379 FILE_ATTRIBUTE_NORMAL,
6380 FILE_SHARE_READ,
6381 FILE_OPEN,
6382 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
6383 NULL, /*pEaBuffer*/
6384 0); /*cbEaBuffer*/
6385 if (MY_NT_SUCCESS(rcNt))
6386 {
6387 /*
6388 * Read the whole file into memory.
6389 */
6390 LARGE_INTEGER cbFile;
6391 if (GetFileSizeEx(hFile, &cbFile))
6392 {
6393 if ( cbFile.QuadPart >= 0
6394#ifdef WITH_PCH_CACHING
6395 && ( cbFile.QuadPart < 16*1024*1024
6396 || ( cbFile.QuadPart < 96*1024*1024
6397 && pFsObj->cchName > 4
6398 && !g_Sandbox.fNoPchCaching
6399 && kHlpStrICompAscii(&pFsObj->pszName[pFsObj->cchName - 4], ".pch") == 0) )
6400#endif
6401 )
6402 {
6403 KU32 cbCache = (KU32)cbFile.QuadPart;
6404 HANDLE hMapping = CreateFileMappingW(hFile, NULL /*pSecAttrs*/, PAGE_READONLY,
6405 0 /*cbMaxLow*/, 0 /*cbMaxHigh*/, NULL /*pwszName*/);
6406 if (hMapping != NULL)
6407 {
6408 KU8 *pbCache = (KU8 *)MapViewOfFile(hMapping, FILE_MAP_READ, 0 /*offFileHigh*/, 0 /*offFileLow*/, cbCache);
6409 if (pbCache)
6410 {
6411 /*
6412 * Create the cached file object.
6413 */
6414 PKFSWCACHEDFILE pCachedFile;
6415 KU32 cbPath = pFsObj->cchParent + pFsObj->cchName + 2;
6416 pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjAddUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE,
6417 sizeof(*pCachedFile) + cbPath);
6418 if (pCachedFile)
6419 {
6420 pCachedFile->hCached = hFile;
6421 pCachedFile->hSection = hMapping;
6422 pCachedFile->cbCached = cbCache;
6423 pCachedFile->pbCached = pbCache;
6424 pCachedFile->pFsObj = pFsObj;
6425 kFsCacheObjGetFullPathA(pFsObj, pCachedFile->szPath, cbPath, '/');
6426 kFsCacheObjRetain(pFsObj);
6427
6428 g_cReadCachedFiles++;
6429 g_cbReadCachedFiles += cbCache;
6430
6431 KWFS_LOG(("Cached '%s': %p LB %#x, hCached=%p\n", pCachedFile->szPath, pbCache, cbCache, hFile));
6432 return pCachedFile;
6433 }
6434
6435 KWFS_LOG(("Failed to allocate KFSWCACHEDFILE structure!\n"));
6436 }
6437 else
6438 KWFS_LOG(("Failed to cache file: MapViewOfFile failed: %u\n", GetLastError()));
6439 CloseHandle(hMapping);
6440 }
6441 else
6442 KWFS_LOG(("Failed to cache file: CreateFileMappingW failed: %u\n", GetLastError()));
6443 }
6444 else
6445 KWFS_LOG(("File to big to cache! %#llx\n", cbFile.QuadPart));
6446 }
6447 else
6448 KWFS_LOG(("File to get file size! err=%u\n", GetLastError()));
6449 g_pfnNtClose(hFile);
6450 }
6451 else
6452 KWFS_LOG(("Error opening '%ls' for caching: %#x\n", pFsObj->pwszName, rcNt));
6453 return NULL;
6454}
6455
6456
6457/**
6458 * Kernel32 - Common code for kwFsObjCacheCreateFile and CreateFileMappingW/A.
6459 */
6460static KBOOL kwFsObjCacheCreateFileHandle(PKFSWCACHEDFILE pCachedFile, DWORD dwDesiredAccess, BOOL fInheritHandle,
6461 KBOOL fIsFileHandle, HANDLE *phFile)
6462{
6463 HANDLE hProcSelf = GetCurrentProcess();
6464 if (DuplicateHandle(hProcSelf, fIsFileHandle ? pCachedFile->hCached : pCachedFile->hSection,
6465 hProcSelf, phFile,
6466 dwDesiredAccess, fInheritHandle,
6467 0 /*dwOptions*/))
6468 {
6469 /*
6470 * Create handle table entry for the duplicate handle.
6471 */
6472 PKWHANDLE pHandle = (PKWHANDLE)kHlpAlloc(sizeof(*pHandle));
6473 if (pHandle)
6474 {
6475 pHandle->enmType = fIsFileHandle ? KWHANDLETYPE_FSOBJ_READ_CACHE : KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING;
6476 pHandle->cRefs = 1;
6477 pHandle->offFile = 0;
6478 pHandle->hHandle = *phFile;
6479 pHandle->dwDesiredAccess = dwDesiredAccess;
6480 pHandle->u.pCachedFile = pCachedFile;
6481 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, pHandle->hHandle))
6482 return K_TRUE;
6483
6484 kHlpFree(pHandle);
6485 }
6486 else
6487 KWFS_LOG(("Out of memory for handle!\n"));
6488
6489 CloseHandle(*phFile);
6490 *phFile = INVALID_HANDLE_VALUE;
6491 }
6492 else
6493 KWFS_LOG(("DuplicateHandle failed! err=%u\n", GetLastError()));
6494 return K_FALSE;
6495}
6496
6497
6498/**
6499 * Kernel32 - Common code for CreateFileW and CreateFileA.
6500 */
6501static KBOOL kwFsObjCacheCreateFile(PKFSOBJ pFsObj, DWORD dwDesiredAccess, BOOL fInheritHandle, HANDLE *phFile)
6502{
6503 *phFile = INVALID_HANDLE_VALUE;
6504
6505 /*
6506 * At the moment we only handle existing files.
6507 */
6508 if (pFsObj->bObjType == KFSOBJ_TYPE_FILE)
6509 {
6510 PKFSWCACHEDFILE pCachedFile = (PKFSWCACHEDFILE)kFsCacheObjGetUserData(g_pFsCache, pFsObj, KW_DATA_KEY_CACHED_FILE);
6511 kHlpAssert(pFsObj->fHaveStats);
6512 if ( pCachedFile != NULL
6513 || (pCachedFile = kwFsObjCacheNewFile(pFsObj)) != NULL)
6514 {
6515 if (kwFsObjCacheCreateFileHandle(pCachedFile, dwDesiredAccess, fInheritHandle, K_TRUE /*fIsFileHandle*/, phFile))
6516 return K_TRUE;
6517 }
6518 }
6519 /** @todo Deal with non-existing files if it becomes necessary (it's not for VS2010). */
6520
6521 /* Do fallback, please. */
6522 return K_FALSE;
6523}
6524
6525
6526/** Kernel32 - CreateFileA */
6527static HANDLE WINAPI kwSandbox_Kernel32_CreateFileA(LPCSTR pszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6528 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6529 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6530{
6531 HANDLE hFile;
6532
6533 /*
6534 * Check for include files and similar that we do read-only caching of.
6535 */
6536 if (dwCreationDisposition == FILE_OPEN_IF)
6537 {
6538 if ( dwDesiredAccess == GENERIC_READ
6539 || dwDesiredAccess == FILE_GENERIC_READ)
6540 {
6541 if (dwShareMode & FILE_SHARE_READ)
6542 {
6543 if ( !pSecAttrs
6544 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6545 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6546 {
6547 const char *pszExt = kHlpGetExt(pszFilename);
6548 if (kwFsIsCacheableExtensionA(pszExt, K_FALSE /*fAttrQuery*/))
6549 {
6550 KFSLOOKUPERROR enmError;
6551 PKFSOBJ pFsObj;
6552 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6553
6554 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
6555 if (pFsObj)
6556 {
6557 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6558 &hFile);
6559 kFsCacheObjRelease(g_pFsCache, pFsObj);
6560 if (fRc)
6561 {
6562 KWFS_LOG(("CreateFileA(%s) -> %p [cached]\n", pszFilename, hFile));
6563 return hFile;
6564 }
6565 }
6566 /* These are for nasm and yasm header searching. Cache will already
6567 have checked the directories for the file, no need to call
6568 CreateFile to do it again. */
6569 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6570 {
6571 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pszFilename));
6572 return INVALID_HANDLE_VALUE;
6573 }
6574 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6575 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6576 {
6577 KWFS_LOG(("CreateFileA(%s) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pszFilename));
6578 return INVALID_HANDLE_VALUE;
6579 }
6580
6581 /* fallback */
6582 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6583 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6584 KWFS_LOG(("CreateFileA(%s) -> %p (err=%u) [fallback]\n", pszFilename, hFile, GetLastError()));
6585 return hFile;
6586 }
6587 }
6588 }
6589 }
6590 }
6591
6592 /*
6593 * Okay, normal.
6594 */
6595 hFile = CreateFileA(pszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6596 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6597 if (hFile != INVALID_HANDLE_VALUE)
6598 {
6599 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6600 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6601 }
6602 KWFS_LOG(("CreateFileA(%s) -> %p\n", pszFilename, hFile));
6603 return hFile;
6604}
6605
6606
6607/** Kernel32 - CreateFileW */
6608static HANDLE WINAPI kwSandbox_Kernel32_CreateFileW(LPCWSTR pwszFilename, DWORD dwDesiredAccess, DWORD dwShareMode,
6609 LPSECURITY_ATTRIBUTES pSecAttrs, DWORD dwCreationDisposition,
6610 DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
6611{
6612 HANDLE hFile;
6613
6614#ifdef WITH_TEMP_MEMORY_FILES
6615 /*
6616 * Check for temporary files (cl.exe only).
6617 */
6618 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
6619 && !(dwFlagsAndAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_FLAG_BACKUP_SEMANTICS))
6620 && !(dwDesiredAccess & (GENERIC_EXECUTE | FILE_EXECUTE))
6621 && kwFsIsClTempFileW(pwszFilename))
6622 {
6623 hFile = kwFsTempFileCreateW(pwszFilename, dwDesiredAccess, dwCreationDisposition);
6624 KWFS_LOG(("CreateFileW(%ls) -> %p [temp]\n", pwszFilename, hFile));
6625 return hFile;
6626 }
6627#endif
6628
6629 /*
6630 * Check for include files and similar that we do read-only caching of.
6631 */
6632 if (dwCreationDisposition == FILE_OPEN_IF)
6633 {
6634 if ( dwDesiredAccess == GENERIC_READ
6635 || dwDesiredAccess == FILE_GENERIC_READ)
6636 {
6637 if (dwShareMode & FILE_SHARE_READ)
6638 {
6639 if ( !pSecAttrs
6640 || ( pSecAttrs->nLength == sizeof(*pSecAttrs)
6641 && pSecAttrs->lpSecurityDescriptor == NULL ) )
6642 {
6643 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_FALSE /*fAttrQuery*/))
6644 {
6645 KFSLOOKUPERROR enmError;
6646 PKFSOBJ pFsObj;
6647 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6648
6649 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
6650 if (pFsObj)
6651 {
6652 KBOOL fRc = kwFsObjCacheCreateFile(pFsObj, dwDesiredAccess, pSecAttrs && pSecAttrs->bInheritHandle,
6653 &hFile);
6654 kFsCacheObjRelease(g_pFsCache, pFsObj);
6655 if (fRc)
6656 {
6657 KWFS_LOG(("CreateFileW(%ls) -> %p [cached]\n", pwszFilename, hFile));
6658 return hFile;
6659 }
6660 }
6661 /* These are for nasm and yasm style header searching. Cache will
6662 already have checked the directories for the file, no need to call
6663 CreateFile to do it again. */
6664 else if (enmError == KFSLOOKUPERROR_NOT_FOUND)
6665 {
6666 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_FILE_NOT_FOUND\n", pwszFilename));
6667 return INVALID_HANDLE_VALUE;
6668 }
6669 else if ( enmError == KFSLOOKUPERROR_PATH_COMP_NOT_FOUND
6670 || enmError == KFSLOOKUPERROR_PATH_COMP_NOT_DIR)
6671 {
6672 KWFS_LOG(("CreateFileW(%ls) -> INVALID_HANDLE_VALUE, ERROR_PATH_NOT_FOUND\n", pwszFilename));
6673 return INVALID_HANDLE_VALUE;
6674 }
6675
6676 /* fallback */
6677 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6678 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6679 KWFS_LOG(("CreateFileW(%ls) -> %p (err=%u) [fallback]\n", pwszFilename, hFile, GetLastError()));
6680 return hFile;
6681 }
6682 }
6683 else
6684 KWFS_LOG(("CreateFileW: incompatible security attributes (nLength=%#x pDesc=%p)\n",
6685 pSecAttrs->nLength, pSecAttrs->lpSecurityDescriptor));
6686 }
6687 else
6688 KWFS_LOG(("CreateFileW: incompatible sharing mode %#x\n", dwShareMode));
6689 }
6690 else
6691 KWFS_LOG(("CreateFileW: incompatible desired access %#x\n", dwDesiredAccess));
6692 }
6693 else
6694 KWFS_LOG(("CreateFileW: incompatible disposition %u\n", dwCreationDisposition));
6695
6696 /*
6697 * Okay, normal.
6698 */
6699 hFile = CreateFileW(pwszFilename, dwDesiredAccess, dwShareMode, pSecAttrs,
6700 dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
6701 if (hFile != INVALID_HANDLE_VALUE)
6702 {
6703 kHlpAssert( KW_HANDLE_TO_INDEX(hFile) >= g_Sandbox.cHandles
6704 || g_Sandbox.papHandles[KW_HANDLE_TO_INDEX(hFile)] == NULL);
6705 }
6706 KWFS_LOG(("CreateFileW(%ls) -> %p\n", pwszFilename, hFile));
6707 return hFile;
6708}
6709
6710
6711/** Kernel32 - SetFilePointer */
6712static DWORD WINAPI kwSandbox_Kernel32_SetFilePointer(HANDLE hFile, LONG cbMove, PLONG pcbMoveHi, DWORD dwMoveMethod)
6713{
6714 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6715 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6716 if (idxHandle < g_Sandbox.cHandles)
6717 {
6718 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6719 if (pHandle != NULL)
6720 {
6721 KU32 cbFile;
6722 KI64 offMove = pcbMoveHi ? ((KI64)*pcbMoveHi << 32) | cbMove : cbMove;
6723 switch (pHandle->enmType)
6724 {
6725 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6726 cbFile = pHandle->u.pCachedFile->cbCached;
6727 break;
6728#ifdef WITH_TEMP_MEMORY_FILES
6729 case KWHANDLETYPE_TEMP_FILE:
6730 cbFile = pHandle->u.pTempFile->cbFile;
6731 break;
6732#endif
6733 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6734 case KWHANDLETYPE_OUTPUT_BUF:
6735 default:
6736 kHlpAssertFailed();
6737 SetLastError(ERROR_INVALID_FUNCTION);
6738 return INVALID_SET_FILE_POINTER;
6739 }
6740
6741 switch (dwMoveMethod)
6742 {
6743 case FILE_BEGIN:
6744 break;
6745 case FILE_CURRENT:
6746 offMove += pHandle->offFile;
6747 break;
6748 case FILE_END:
6749 offMove += cbFile;
6750 break;
6751 default:
6752 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6753 SetLastError(ERROR_INVALID_PARAMETER);
6754 return INVALID_SET_FILE_POINTER;
6755 }
6756 if (offMove >= 0)
6757 {
6758 if (offMove >= (KSSIZE)cbFile)
6759 {
6760 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6761 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6762 offMove = (KSSIZE)cbFile;
6763 /* For writable files, seeking beyond the end is fine, but check that we've got
6764 the type range for the request. */
6765 else if (((KU64)offMove & KU32_MAX) != (KU64)offMove)
6766 {
6767 kHlpAssertMsgFailed(("%#llx\n", offMove));
6768 SetLastError(ERROR_SEEK);
6769 return INVALID_SET_FILE_POINTER;
6770 }
6771 }
6772 pHandle->offFile = (KU32)offMove;
6773 }
6774 else
6775 {
6776 KWFS_LOG(("SetFilePointer(%p) - negative seek! [cached]\n", hFile));
6777 SetLastError(ERROR_NEGATIVE_SEEK);
6778 return INVALID_SET_FILE_POINTER;
6779 }
6780 if (pcbMoveHi)
6781 *pcbMoveHi = (KU64)offMove >> 32;
6782 KWFS_LOG(("SetFilePointer(%p,%#x,?,%u) -> %#llx [%s]\n", hFile, cbMove, dwMoveMethod, offMove,
6783 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6784 SetLastError(NO_ERROR);
6785 return (KU32)offMove;
6786 }
6787 }
6788 KWFS_LOG(("SetFilePointer(%p, %d, %p=%d, %d)\n", hFile, cbMove, pcbMoveHi ? *pcbMoveHi : 0, dwMoveMethod));
6789 return SetFilePointer(hFile, cbMove, pcbMoveHi, dwMoveMethod);
6790}
6791
6792
6793/** Kernel32 - SetFilePointerEx */
6794static BOOL WINAPI kwSandbox_Kernel32_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offMove, PLARGE_INTEGER poffNew,
6795 DWORD dwMoveMethod)
6796{
6797 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6798 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6799 if (idxHandle < g_Sandbox.cHandles)
6800 {
6801 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6802 if (pHandle != NULL)
6803 {
6804 KI64 offMyMove = offMove.QuadPart;
6805 KU32 cbFile;
6806 switch (pHandle->enmType)
6807 {
6808 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6809 cbFile = pHandle->u.pCachedFile->cbCached;
6810 break;
6811#ifdef WITH_TEMP_MEMORY_FILES
6812 case KWHANDLETYPE_TEMP_FILE:
6813 cbFile = pHandle->u.pTempFile->cbFile;
6814 break;
6815#endif
6816 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6817 case KWHANDLETYPE_OUTPUT_BUF:
6818 default:
6819 kHlpAssertFailed();
6820 SetLastError(ERROR_INVALID_FUNCTION);
6821 return INVALID_SET_FILE_POINTER;
6822 }
6823
6824 switch (dwMoveMethod)
6825 {
6826 case FILE_BEGIN:
6827 break;
6828 case FILE_CURRENT:
6829 offMyMove += pHandle->offFile;
6830 break;
6831 case FILE_END:
6832 offMyMove += cbFile;
6833 break;
6834 default:
6835 KWFS_LOG(("SetFilePointer(%p) - invalid seek method %u! [cached]\n", hFile));
6836 SetLastError(ERROR_INVALID_PARAMETER);
6837 return INVALID_SET_FILE_POINTER;
6838 }
6839 if (offMyMove >= 0)
6840 {
6841 if (offMyMove >= (KSSIZE)cbFile)
6842 {
6843 /* For read-only files, seeking beyond the end isn't useful to us, so clamp it. */
6844 if (pHandle->enmType != KWHANDLETYPE_TEMP_FILE)
6845 offMyMove = (KSSIZE)cbFile;
6846 /* For writable files, seeking beyond the end is fine, but check that we've got
6847 the type range for the request. */
6848 else if (((KU64)offMyMove & KU32_MAX) != (KU64)offMyMove)
6849 {
6850 kHlpAssertMsgFailed(("%#llx\n", offMyMove));
6851 SetLastError(ERROR_SEEK);
6852 return INVALID_SET_FILE_POINTER;
6853 }
6854 }
6855 pHandle->offFile = (KU32)offMyMove;
6856 }
6857 else
6858 {
6859 KWFS_LOG(("SetFilePointerEx(%p) - negative seek! [cached]\n", hFile));
6860 SetLastError(ERROR_NEGATIVE_SEEK);
6861 return INVALID_SET_FILE_POINTER;
6862 }
6863 if (poffNew)
6864 poffNew->QuadPart = offMyMove;
6865 KWFS_LOG(("SetFilePointerEx(%p,%#llx,,%u) -> TRUE, %#llx [%s]\n", hFile, offMove.QuadPart, dwMoveMethod, offMyMove,
6866 pHandle->enmType == KWHANDLETYPE_FSOBJ_READ_CACHE ? "cached" : "temp"));
6867 return TRUE;
6868 }
6869 }
6870 KWFS_LOG(("SetFilePointerEx(%p)\n", hFile));
6871 return SetFilePointerEx(hFile, offMove, poffNew, dwMoveMethod);
6872}
6873
6874
6875/** Kernel32 - ReadFile */
6876static BOOL WINAPI kwSandbox_Kernel32_ReadFile(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPDWORD pcbActuallyRead,
6877 LPOVERLAPPED pOverlapped)
6878{
6879 BOOL fRet;
6880 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
6881 g_cReadFileCalls++;
6882 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
6883 if (idxHandle < g_Sandbox.cHandles)
6884 {
6885 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
6886 if (pHandle != NULL)
6887 {
6888 switch (pHandle->enmType)
6889 {
6890 case KWHANDLETYPE_FSOBJ_READ_CACHE:
6891 {
6892 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
6893 KU32 cbActually = pCachedFile->cbCached - pHandle->offFile;
6894 if (cbActually > cbToRead)
6895 cbActually = cbToRead;
6896
6897#ifdef WITH_HASH_MD5_CACHE
6898 if (g_Sandbox.pHashHead)
6899 {
6900 g_Sandbox.LastHashRead.pCachedFile = pCachedFile;
6901 g_Sandbox.LastHashRead.offRead = pHandle->offFile;
6902 g_Sandbox.LastHashRead.cbRead = cbActually;
6903 g_Sandbox.LastHashRead.pvRead = pvBuffer;
6904 }
6905#endif
6906
6907 kHlpMemCopy(pvBuffer, &pCachedFile->pbCached[pHandle->offFile], cbActually);
6908 pHandle->offFile += cbActually;
6909
6910 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6911 *pcbActuallyRead = cbActually;
6912
6913 g_cbReadFileFromReadCached += cbActually;
6914 g_cbReadFileTotal += cbActually;
6915 g_cReadFileFromReadCached++;
6916
6917 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [cached]\n", hFile, cbToRead, cbActually));
6918 return TRUE;
6919 }
6920
6921#ifdef WITH_TEMP_MEMORY_FILES
6922 case KWHANDLETYPE_TEMP_FILE:
6923 {
6924 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
6925 KU32 cbActually;
6926 if (pHandle->offFile < pTempFile->cbFile)
6927 {
6928 cbActually = pTempFile->cbFile - pHandle->offFile;
6929 if (cbActually > cbToRead)
6930 cbActually = cbToRead;
6931
6932 /* Copy the data. */
6933 if (cbActually > 0)
6934 {
6935 KU32 cbLeft;
6936 KU32 offSeg;
6937 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
6938
6939 /* Locate the segment containing the byte at offFile. */
6940 KU32 iSeg = pTempFile->cSegs - 1;
6941 kHlpAssert(pTempFile->cSegs > 0);
6942 while (paSegs[iSeg].offData > pHandle->offFile)
6943 iSeg--;
6944
6945 /* Copy out the data. */
6946 cbLeft = cbActually;
6947 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
6948 for (;;)
6949 {
6950 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
6951 if (cbAvail >= cbLeft)
6952 {
6953 kHlpMemCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbLeft);
6954 break;
6955 }
6956
6957 pvBuffer = kHlpMemPCopy(pvBuffer, &paSegs[iSeg].pbData[offSeg], cbAvail);
6958 cbLeft -= cbAvail;
6959 offSeg = 0;
6960 iSeg++;
6961 kHlpAssert(iSeg < pTempFile->cSegs);
6962 }
6963
6964 /* Update the file offset. */
6965 pHandle->offFile += cbActually;
6966 }
6967 }
6968 /* Read does not commit file space, so return zero bytes. */
6969 else
6970 cbActually = 0;
6971
6972 kHlpAssert(!pOverlapped); kHlpAssert(pcbActuallyRead);
6973 *pcbActuallyRead = cbActually;
6974
6975 g_cbReadFileTotal += cbActually;
6976 g_cbReadFileFromInMemTemp += cbActually;
6977 g_cReadFileFromInMemTemp++;
6978
6979 KWFS_LOG(("ReadFile(%p,,%#x) -> TRUE, %#x bytes [temp]\n", hFile, cbToRead, (KU32)cbActually));
6980 return TRUE;
6981 }
6982#endif /* WITH_TEMP_MEMORY_FILES */
6983
6984 case KWHANDLETYPE_TEMP_FILE_MAPPING:
6985 case KWHANDLETYPE_OUTPUT_BUF:
6986 default:
6987 kHlpAssertFailed();
6988 SetLastError(ERROR_INVALID_FUNCTION);
6989 *pcbActuallyRead = 0;
6990 return FALSE;
6991 }
6992 }
6993 }
6994
6995 fRet = ReadFile(hFile, pvBuffer, cbToRead, pcbActuallyRead, pOverlapped);
6996 if (fRet && pcbActuallyRead)
6997 g_cbReadFileTotal += *pcbActuallyRead;
6998 KWFS_LOG(("ReadFile(%p,%p,%#x,,) -> %d, %#x\n", hFile, pvBuffer, cbToRead, fRet, pcbActuallyRead ? *pcbActuallyRead : 0));
6999 return fRet;
7000}
7001
7002
7003/** Kernel32 - ReadFileEx */
7004static BOOL WINAPI kwSandbox_Kernel32_ReadFileEx(HANDLE hFile, LPVOID pvBuffer, DWORD cbToRead, LPOVERLAPPED pOverlapped,
7005 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7006{
7007 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7008 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7009 if (idxHandle < g_Sandbox.cHandles)
7010 {
7011 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7012 if (pHandle != NULL)
7013 {
7014 kHlpAssertFailed();
7015 }
7016 }
7017
7018 KWFS_LOG(("ReadFile(%p)\n", hFile));
7019 return ReadFileEx(hFile, pvBuffer, cbToRead, pOverlapped, pfnCompletionRoutine);
7020}
7021
7022#ifdef WITH_STD_OUT_ERR_BUFFERING
7023
7024/**
7025 * Write something to a handle, making sure everything is actually written.
7026 *
7027 * @param hHandle Where to write it to.
7028 * @param pchBuf What to write
7029 * @param cchToWrite How much to write.
7030 */
7031static void kwSandboxOutBufWriteIt(HANDLE hFile, char const *pchBuf, KU32 cchToWrite)
7032{
7033 if (cchToWrite > 0)
7034 {
7035 DWORD cchWritten = 0;
7036 if (WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL))
7037 {
7038 if (cchWritten == cchToWrite)
7039 { /* likely */ }
7040 else
7041 {
7042 do
7043 {
7044 pchBuf += cchWritten;
7045 cchToWrite -= cchWritten;
7046 cchWritten = 0;
7047 } while ( cchToWrite > 0
7048 && WriteFile(hFile, pchBuf, cchToWrite, &cchWritten, NULL));
7049 }
7050 }
7051 else
7052 kHlpAssertFailed();
7053 }
7054}
7055
7056
7057/**
7058 * Worker for WriteFile when the output isn't going to the console.
7059 *
7060 * @param pSandbox The sandbox.
7061 * @param pOutBuf The output buffer.
7062 * @param pchBuffer What to write.
7063 * @param cchToWrite How much to write.
7064 */
7065static void kwSandboxOutBufWrite(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pOutBuf, const char *pchBuffer, KU32 cchToWrite)
7066{
7067 if (pOutBuf->u.Fully.cchBufAlloc > 0)
7068 { /* likely */ }
7069 else
7070 {
7071 /* No realloc, max size is 64KB. */
7072 pOutBuf->u.Fully.cchBufAlloc = 0x10000;
7073 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7074 if (!pOutBuf->u.Fully.pchBuf)
7075 {
7076 while ( !pOutBuf->u.Fully.pchBuf
7077 && pOutBuf->u.Fully.cchBufAlloc > 64)
7078 {
7079 pOutBuf->u.Fully.cchBufAlloc /= 2;
7080 pOutBuf->u.Fully.pchBuf = (char *)kHlpAlloc(pOutBuf->u.Fully.cchBufAlloc);
7081 }
7082 if (!pOutBuf->u.Fully.pchBuf)
7083 {
7084 pOutBuf->u.Fully.cchBufAlloc = sizeof(pOutBuf->abPadding);
7085 pOutBuf->u.Fully.pchBuf = pOutBuf->abPadding;
7086 }
7087 }
7088 }
7089
7090 /*
7091 * Special case: Output ends with newline and fits in the buffer.
7092 */
7093 if ( cchToWrite > 1
7094 && pchBuffer[cchToWrite - 1] == '\n'
7095 && cchToWrite <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7096 {
7097 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchToWrite);
7098 pOutBuf->u.Fully.cchBuf += cchToWrite;
7099 }
7100 else
7101 {
7102 /*
7103 * Work thru the text line by line, flushing the buffer when
7104 * appropriate. The buffer is not a line buffer here, it's a
7105 * full buffer.
7106 */
7107 while (cchToWrite > 0)
7108 {
7109 char const *pchNewLine = (const char *)memchr(pchBuffer, '\n', cchToWrite);
7110 KU32 cchLine = pchNewLine ? (KU32)(pchNewLine - pchBuffer) + 1 : cchToWrite;
7111 if (cchLine <= pOutBuf->u.Fully.cchBufAlloc - pOutBuf->u.Fully.cchBuf)
7112 {
7113 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf], pchBuffer, cchLine);
7114 pOutBuf->u.Fully.cchBuf += cchLine;
7115 }
7116 /*
7117 * Option one: Flush the buffer and the current line.
7118 *
7119 * We choose this one when the line won't ever fit, or when we have
7120 * an incomplete line in the buffer.
7121 */
7122 else if ( cchLine >= pOutBuf->u.Fully.cchBufAlloc
7123 || pOutBuf->u.Fully.cchBuf == 0
7124 || pOutBuf->u.Fully.pchBuf[pOutBuf->u.Fully.cchBuf - 1] != '\n')
7125 {
7126 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes, writing %u bytes\n", pOutBuf->u.Fully.cchBuf, cchLine));
7127 if (pOutBuf->u.Fully.cchBuf > 0)
7128 {
7129 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7130 pOutBuf->u.Fully.cchBuf = 0;
7131 }
7132 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pchBuffer, cchLine);
7133 }
7134 /*
7135 * Option two: Only flush the lines in the buffer.
7136 */
7137 else
7138 {
7139 KWOUT_LOG(("kwSandboxOutBufWrite: flushing %u bytes\n", pOutBuf->u.Fully.cchBuf));
7140 kwSandboxOutBufWriteIt(pOutBuf->hBackup, pOutBuf->u.Fully.pchBuf, pOutBuf->u.Fully.cchBuf);
7141 kHlpMemCopy(&pOutBuf->u.Fully.pchBuf[0], pchBuffer, cchLine);
7142 pOutBuf->u.Fully.cchBuf = cchLine;
7143 }
7144
7145 /* advance */
7146 pchBuffer += cchLine;
7147 cchToWrite -= cchLine;
7148 }
7149 }
7150}
7151
7152#endif /* WITH_STD_OUT_ERR_BUFFERING */
7153
7154#ifdef WITH_TEMP_MEMORY_FILES
7155static KBOOL kwFsTempFileEnsureSpace(PKWFSTEMPFILE pTempFile, KU32 offFile, KU32 cbNeeded)
7156{
7157 KU32 cbMinFile = offFile + cbNeeded;
7158 if (cbMinFile >= offFile)
7159 {
7160 /* Calc how much space we've already allocated and */
7161 if (cbMinFile <= pTempFile->cbFileAllocated)
7162 return K_TRUE;
7163
7164 /* Grow the file. */
7165 if (cbMinFile <= KWFS_TEMP_FILE_MAX)
7166 {
7167 int rc;
7168 KU32 cSegs = pTempFile->cSegs;
7169 KU32 cbNewSeg = cbMinFile > 4*1024*1024 ? 256*1024 : 4*1024*1024;
7170 do
7171 {
7172 /* grow the segment array? */
7173 if ((cSegs % 16) == 0)
7174 {
7175 void *pvNew = kHlpRealloc(pTempFile->paSegs, (cSegs + 16) * sizeof(pTempFile->paSegs[0]));
7176 if (!pvNew)
7177 return K_FALSE;
7178 pTempFile->paSegs = (PKWFSTEMPFILESEG)pvNew;
7179 }
7180
7181 /* Use page alloc here to simplify mapping later. */
7182 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7183 if (rc == 0)
7184 { /* likely */ }
7185 else
7186 {
7187 cbNewSeg = 64*1024;
7188 rc = kHlpPageAlloc((void **)&pTempFile->paSegs[cSegs].pbData, cbNewSeg, KPROT_READWRITE, K_FALSE);
7189 if (rc != 0)
7190 {
7191 kHlpAssertFailed();
7192 return K_FALSE;
7193 }
7194 }
7195 pTempFile->paSegs[cSegs].offData = pTempFile->cbFileAllocated;
7196 pTempFile->paSegs[cSegs].cbDataAlloc = cbNewSeg;
7197 pTempFile->cbFileAllocated += cbNewSeg;
7198 pTempFile->cSegs = ++cSegs;
7199
7200 } while (pTempFile->cbFileAllocated < cbMinFile);
7201
7202 return K_TRUE;
7203 }
7204 }
7205
7206 kHlpAssertMsgFailed(("Out of bounds offFile=%#x + cbNeeded=%#x = %#x\n", offFile, cbNeeded, offFile + cbNeeded));
7207 return K_FALSE;
7208}
7209#endif /* WITH_TEMP_MEMORY_FILES */
7210
7211
7212#if defined(WITH_TEMP_MEMORY_FILES) || defined(WITH_STD_OUT_ERR_BUFFERING)
7213/** Kernel32 - WriteFile */
7214static BOOL WINAPI kwSandbox_Kernel32_WriteFile(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPDWORD pcbActuallyWritten,
7215 LPOVERLAPPED pOverlapped)
7216{
7217 BOOL fRet;
7218 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7219 g_cWriteFileCalls++;
7220 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7221 if (idxHandle < g_Sandbox.cHandles)
7222 {
7223 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7224 if (pHandle != NULL)
7225 {
7226 switch (pHandle->enmType)
7227 {
7228# ifdef WITH_TEMP_MEMORY_FILES
7229 case KWHANDLETYPE_TEMP_FILE:
7230 {
7231 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7232
7233 kHlpAssert(!pOverlapped);
7234 kHlpAssert(pcbActuallyWritten);
7235
7236 if (kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, cbToWrite))
7237 {
7238 KU32 cbLeft;
7239 KU32 offSeg;
7240
7241 /* Locate the segment containing the byte at offFile. */
7242 KWFSTEMPFILESEG const *paSegs = pTempFile->paSegs;
7243 KU32 iSeg = pTempFile->cSegs - 1;
7244 kHlpAssert(pTempFile->cSegs > 0);
7245 while (paSegs[iSeg].offData > pHandle->offFile)
7246 iSeg--;
7247
7248 /* Copy in the data. */
7249 cbLeft = cbToWrite;
7250 offSeg = (pHandle->offFile - paSegs[iSeg].offData);
7251 for (;;)
7252 {
7253 KU32 cbAvail = paSegs[iSeg].cbDataAlloc - offSeg;
7254 if (cbAvail >= cbLeft)
7255 {
7256 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbLeft);
7257 break;
7258 }
7259
7260 kHlpMemCopy(&paSegs[iSeg].pbData[offSeg], pvBuffer, cbAvail);
7261 pvBuffer = (KU8 const *)pvBuffer + cbAvail;
7262 cbLeft -= cbAvail;
7263 offSeg = 0;
7264 iSeg++;
7265 kHlpAssert(iSeg < pTempFile->cSegs);
7266 }
7267
7268 /* Update the file offset. */
7269 pHandle->offFile += cbToWrite;
7270 if (pHandle->offFile > pTempFile->cbFile)
7271 pTempFile->cbFile = pHandle->offFile;
7272
7273 *pcbActuallyWritten = cbToWrite;
7274
7275 g_cbWriteFileTotal += cbToWrite;
7276 g_cbWriteFileToInMemTemp += cbToWrite;
7277 g_cWriteFileToInMemTemp++;
7278
7279 KWFS_LOG(("WriteFile(%p,,%#x) -> TRUE [temp]\n", hFile, cbToWrite));
7280 return TRUE;
7281 }
7282
7283 kHlpAssertFailed();
7284 *pcbActuallyWritten = 0;
7285 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7286 return FALSE;
7287 }
7288# endif
7289
7290 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7291 kHlpAssertFailed();
7292 SetLastError(ERROR_ACCESS_DENIED);
7293 *pcbActuallyWritten = 0;
7294 return FALSE;
7295
7296# if defined(WITH_STD_OUT_ERR_BUFFERING) || defined(WITH_CONSOLE_OUTPUT_BUFFERING)
7297 /*
7298 * Standard output & error.
7299 */
7300 case KWHANDLETYPE_OUTPUT_BUF:
7301 {
7302 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7303 if (pOutBuf->fIsConsole)
7304 {
7305 kwSandboxConsoleWriteA(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7306 KWOUT_LOG(("WriteFile(%p [console]) -> TRUE\n", hFile));
7307 }
7308 else
7309 {
7310# ifdef WITH_STD_OUT_ERR_BUFFERING
7311 kwSandboxOutBufWrite(&g_Sandbox, pOutBuf, (const char *)pvBuffer, cbToWrite);
7312 KWOUT_LOG(("WriteFile(%p [std%s], 's*.*', %#x) -> TRUE\n", hFile,
7313 pOutBuf == &g_Sandbox.StdErr ? "err" : "out", cbToWrite, cbToWrite, pvBuffer, cbToWrite));
7314# else
7315 kHlpAssertFailed();
7316# endif
7317 }
7318 if (pcbActuallyWritten)
7319 *pcbActuallyWritten = cbToWrite;
7320 g_cbWriteFileTotal += cbToWrite;
7321 return TRUE;
7322 }
7323# endif
7324
7325 default:
7326 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7327 kHlpAssertFailed();
7328 SetLastError(ERROR_INVALID_FUNCTION);
7329 *pcbActuallyWritten = 0;
7330 return FALSE;
7331 }
7332 }
7333 }
7334
7335 fRet = WriteFile(hFile, pvBuffer, cbToWrite, pcbActuallyWritten, pOverlapped);
7336 if (fRet && pcbActuallyWritten)
7337 g_cbWriteFileTotal += *pcbActuallyWritten;
7338 KWFS_LOG(("WriteFile(%p,,%#x) -> %d, %#x\n", hFile, cbToWrite, fRet, pcbActuallyWritten ? *pcbActuallyWritten : 0));
7339 return fRet;
7340}
7341
7342
7343/** Kernel32 - WriteFileEx */
7344static BOOL WINAPI kwSandbox_Kernel32_WriteFileEx(HANDLE hFile, LPCVOID pvBuffer, DWORD cbToWrite, LPOVERLAPPED pOverlapped,
7345 LPOVERLAPPED_COMPLETION_ROUTINE pfnCompletionRoutine)
7346{
7347 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7348 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7349 if (idxHandle < g_Sandbox.cHandles)
7350 {
7351 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7352 if (pHandle != NULL)
7353 {
7354 kHlpAssertFailed();
7355 }
7356 }
7357
7358 KWFS_LOG(("WriteFileEx(%p)\n", hFile));
7359 return WriteFileEx(hFile, pvBuffer, cbToWrite, pOverlapped, pfnCompletionRoutine);
7360}
7361
7362#endif /* WITH_TEMP_MEMORY_FILES || WITH_STD_OUT_ERR_BUFFERING */
7363
7364#ifdef WITH_TEMP_MEMORY_FILES
7365
7366/** Kernel32 - SetEndOfFile; */
7367static BOOL WINAPI kwSandbox_Kernel32_SetEndOfFile(HANDLE hFile)
7368{
7369 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7370 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7371 if (idxHandle < g_Sandbox.cHandles)
7372 {
7373 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7374 if (pHandle != NULL)
7375 {
7376 switch (pHandle->enmType)
7377 {
7378 case KWHANDLETYPE_TEMP_FILE:
7379 {
7380 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7381 if ( pHandle->offFile > pTempFile->cbFile
7382 && !kwFsTempFileEnsureSpace(pTempFile, pHandle->offFile, 0))
7383 {
7384 kHlpAssertFailed();
7385 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7386 return FALSE;
7387 }
7388
7389 pTempFile->cbFile = pHandle->offFile;
7390 KWFS_LOG(("SetEndOfFile(%p) -> TRUE (cbFile=%#x)\n", hFile, pTempFile->cbFile));
7391 return TRUE;
7392 }
7393
7394 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7395 kHlpAssertFailed();
7396 SetLastError(ERROR_ACCESS_DENIED);
7397 return FALSE;
7398
7399 case KWHANDLETYPE_OUTPUT_BUF:
7400 kHlpAssertFailed();
7401 SetLastError(pHandle->u.pOutBuf->fIsConsole ? ERROR_INVALID_OPERATION : ERROR_ACCESS_DENIED);
7402 return FALSE;
7403
7404 default:
7405 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7406 kHlpAssertFailed();
7407 SetLastError(ERROR_INVALID_FUNCTION);
7408 return FALSE;
7409 }
7410 }
7411 }
7412
7413 KWFS_LOG(("SetEndOfFile(%p)\n", hFile));
7414 return SetEndOfFile(hFile);
7415}
7416
7417
7418/** Kernel32 - GetFileType */
7419static BOOL WINAPI kwSandbox_Kernel32_GetFileType(HANDLE hFile)
7420{
7421 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7422 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7423 if (idxHandle < g_Sandbox.cHandles)
7424 {
7425 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7426 if (pHandle != NULL)
7427 {
7428 switch (pHandle->enmType)
7429 {
7430 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7431 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [cached]\n", hFile));
7432 return FILE_TYPE_DISK;
7433
7434 case KWHANDLETYPE_TEMP_FILE:
7435 KWFS_LOG(("GetFileType(%p) -> FILE_TYPE_DISK [temp]\n", hFile));
7436 return FILE_TYPE_DISK;
7437
7438 case KWHANDLETYPE_OUTPUT_BUF:
7439 {
7440 PKWOUTPUTSTREAMBUF pOutBuf = pHandle->u.pOutBuf;
7441 DWORD fRet;
7442 if (pOutBuf->fFileType != KU8_MAX)
7443 {
7444 fRet = (pOutBuf->fFileType & 0xf) | ((pOutBuf->fFileType & (FILE_TYPE_REMOTE >> 8)) << 8);
7445 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf]\n", hFile, fRet));
7446 }
7447 else
7448 {
7449 fRet = GetFileType(hFile);
7450 KWFS_LOG(("GetFileType(%p) -> %#x [outbuf, fallback]\n", hFile, fRet));
7451 }
7452 return fRet;
7453 }
7454
7455 }
7456 }
7457 }
7458
7459 KWFS_LOG(("GetFileType(%p)\n", hFile));
7460 return GetFileType(hFile);
7461}
7462
7463
7464/** Kernel32 - GetFileSize */
7465static DWORD WINAPI kwSandbox_Kernel32_GetFileSize(HANDLE hFile, LPDWORD pcbHighDword)
7466{
7467 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7468 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7469 if (idxHandle < g_Sandbox.cHandles)
7470 {
7471 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7472 if (pHandle != NULL)
7473 {
7474 if (pcbHighDword)
7475 *pcbHighDword = 0;
7476 SetLastError(NO_ERROR);
7477 switch (pHandle->enmType)
7478 {
7479 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7480 KWFS_LOG(("GetFileSize(%p) -> %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7481 return pHandle->u.pCachedFile->cbCached;
7482
7483 case KWHANDLETYPE_TEMP_FILE:
7484 KWFS_LOG(("GetFileSize(%p) -> %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7485 return pHandle->u.pTempFile->cbFile;
7486
7487 case KWHANDLETYPE_OUTPUT_BUF:
7488 /* do default */
7489 break;
7490
7491 default:
7492 kHlpAssertFailed();
7493 SetLastError(ERROR_INVALID_FUNCTION);
7494 return INVALID_FILE_SIZE;
7495 }
7496 }
7497 }
7498
7499 KWFS_LOG(("GetFileSize(%p,)\n", hFile));
7500 return GetFileSize(hFile, pcbHighDword);
7501}
7502
7503
7504/** Kernel32 - GetFileSizeEx */
7505static BOOL WINAPI kwSandbox_Kernel32_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
7506{
7507 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7508 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7509 if (idxHandle < g_Sandbox.cHandles)
7510 {
7511 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7512 if (pHandle != NULL)
7513 {
7514 switch (pHandle->enmType)
7515 {
7516 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7517 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [cached]\n", hFile, pHandle->u.pCachedFile->cbCached));
7518 pcbFile->QuadPart = pHandle->u.pCachedFile->cbCached;
7519 return TRUE;
7520
7521 case KWHANDLETYPE_TEMP_FILE:
7522 KWFS_LOG(("GetFileSizeEx(%p) -> TRUE, %#x [temp]\n", hFile, pHandle->u.pTempFile->cbFile));
7523 pcbFile->QuadPart = pHandle->u.pTempFile->cbFile;
7524 return TRUE;
7525
7526 case KWHANDLETYPE_OUTPUT_BUF:
7527 /* do default */
7528 break;
7529
7530 default:
7531 kHlpAssertFailed();
7532 SetLastError(ERROR_INVALID_FUNCTION);
7533 return INVALID_FILE_SIZE;
7534 }
7535 }
7536 }
7537
7538 KWFS_LOG(("GetFileSizeEx(%p,)\n", hFile));
7539 return GetFileSizeEx(hFile, pcbFile);
7540}
7541
7542
7543/** Kernel32 - CreateFileMappingW */
7544static HANDLE WINAPI kwSandbox_Kernel32_CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES pSecAttrs,
7545 DWORD fProtect, DWORD dwMaximumSizeHigh,
7546 DWORD dwMaximumSizeLow, LPCWSTR pwszName)
7547{
7548 HANDLE hMapping;
7549 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hFile);
7550 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7551 if (idxHandle < g_Sandbox.cHandles)
7552 {
7553 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7554 if (pHandle != NULL)
7555 {
7556 switch (pHandle->enmType)
7557 {
7558 case KWHANDLETYPE_TEMP_FILE:
7559 {
7560 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7561 if ( ( fProtect == PAGE_READONLY
7562 || fProtect == PAGE_EXECUTE_READ)
7563 && dwMaximumSizeHigh == 0
7564 && ( dwMaximumSizeLow == 0
7565 || dwMaximumSizeLow == pTempFile->cbFile)
7566 && pwszName == NULL)
7567 {
7568 hMapping = kwFsTempFileCreateHandle(pHandle->u.pTempFile, GENERIC_READ, K_TRUE /*fMapping*/);
7569 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [temp]\n", hFile, fProtect, hMapping));
7570 return hMapping;
7571 }
7572 kHlpAssertMsgFailed(("fProtect=%#x cb=%#x'%08x name=%p\n",
7573 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7574 SetLastError(ERROR_ACCESS_DENIED);
7575 return INVALID_HANDLE_VALUE;
7576 }
7577
7578 /* moc.exe benefits from this. */
7579 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7580 {
7581 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7582 if ( ( fProtect == PAGE_READONLY
7583 || fProtect == PAGE_EXECUTE_READ)
7584 && dwMaximumSizeHigh == 0
7585 && ( dwMaximumSizeLow == 0
7586 || dwMaximumSizeLow == pCachedFile->cbCached)
7587 && pwszName == NULL)
7588 {
7589 if (kwFsObjCacheCreateFileHandle(pCachedFile, GENERIC_READ, FALSE /*fInheritHandle*/,
7590 K_FALSE /*fIsFileHandle*/, &hMapping))
7591 { /* likely */ }
7592 else
7593 hMapping = NULL;
7594 KWFS_LOG(("CreateFileMappingW(%p, %u) -> %p [cached]\n", hFile, fProtect, hMapping));
7595 return hMapping;
7596 }
7597
7598 /* Do fallback (for .pch). */
7599 kHlpAssertMsg(fProtect == PAGE_WRITECOPY,
7600 ("fProtect=%#x cb=%#x'%08x name=%p\n",
7601 fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName));
7602
7603 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7604 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p [cached-fallback]\n",
7605 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7606 return hMapping;
7607 }
7608
7609 /** @todo read cached memory mapped files too for moc. */
7610 }
7611 }
7612 }
7613
7614 hMapping = CreateFileMappingW(hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName);
7615 KWFS_LOG(("CreateFileMappingW(%p, %p, %#x, %#x, %#x, %p) -> %p\n",
7616 hFile, pSecAttrs, fProtect, dwMaximumSizeHigh, dwMaximumSizeLow, pwszName, hMapping));
7617 return hMapping;
7618}
7619
7620
7621/** Kernel32 - MapViewOfFile */
7622static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFile(HANDLE hSection, DWORD dwDesiredAccess,
7623 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap)
7624{
7625 PVOID pvRet;
7626 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7627 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7628 if (idxHandle < g_Sandbox.cHandles)
7629 {
7630 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7631 if (pHandle != NULL)
7632 {
7633 KU32 idxMapping;
7634
7635 /*
7636 * Ensure one free entry in the mapping tracking table first,
7637 * since this is common to both temporary and cached files.
7638 */
7639 if (g_Sandbox.cMemMappings + 1 <= g_Sandbox.cMemMappingsAlloc)
7640 { /* likely */ }
7641 else
7642 {
7643 void *pvNew;
7644 KU32 cNew = g_Sandbox.cMemMappingsAlloc;
7645 if (cNew)
7646 cNew *= 2;
7647 else
7648 cNew = 32;
7649 pvNew = kHlpRealloc(g_Sandbox.paMemMappings, cNew * sizeof(g_Sandbox.paMemMappings));
7650 if (pvNew)
7651 g_Sandbox.paMemMappings = (PKWMEMMAPPING)pvNew;
7652 else
7653 {
7654 kwErrPrintf("Failed to grow paMemMappings from %#x to %#x!\n", g_Sandbox.cMemMappingsAlloc, cNew);
7655 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7656 return NULL;
7657 }
7658 g_Sandbox.cMemMappingsAlloc = cNew;
7659 }
7660
7661 /*
7662 * Type specific work.
7663 */
7664 switch (pHandle->enmType)
7665 {
7666 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7667 case KWHANDLETYPE_TEMP_FILE:
7668 case KWHANDLETYPE_OUTPUT_BUF:
7669 default:
7670 kHlpAssertFailed();
7671 SetLastError(ERROR_INVALID_OPERATION);
7672 return NULL;
7673
7674 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7675 {
7676 PKWFSTEMPFILE pTempFile = pHandle->u.pTempFile;
7677 if ( dwDesiredAccess == FILE_MAP_READ
7678 && offFileHigh == 0
7679 && offFileLow == 0
7680 && (cbToMap == 0 || cbToMap == pTempFile->cbFile) )
7681 {
7682 kHlpAssert(pTempFile->cMappings == 0 || pTempFile->cSegs == 1);
7683 if (pTempFile->cSegs != 1)
7684 {
7685 KU32 iSeg;
7686 KU32 cbLeft;
7687 KU32 cbAll = pTempFile->cbFile ? (KU32)K_ALIGN_Z(pTempFile->cbFile, 0x2000) : 0x1000;
7688 KU8 *pbAll = NULL;
7689 int rc = kHlpPageAlloc((void **)&pbAll, cbAll, KPROT_READWRITE, K_FALSE);
7690 if (rc != 0)
7691 {
7692 kHlpAssertFailed();
7693 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7694 return NULL;
7695 }
7696
7697 cbLeft = pTempFile->cbFile;
7698 for (iSeg = 0; iSeg < pTempFile->cSegs && cbLeft > 0; iSeg++)
7699 {
7700 KU32 cbToCopy = K_MIN(cbLeft, pTempFile->paSegs[iSeg].cbDataAlloc);
7701 kHlpMemCopy(&pbAll[pTempFile->paSegs[iSeg].offData], pTempFile->paSegs[iSeg].pbData, cbToCopy);
7702 cbLeft -= cbToCopy;
7703 }
7704
7705 for (iSeg = 0; iSeg < pTempFile->cSegs; iSeg++)
7706 {
7707 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
7708 pTempFile->paSegs[iSeg].pbData = NULL;
7709 pTempFile->paSegs[iSeg].cbDataAlloc = 0;
7710 }
7711
7712 pTempFile->cSegs = 1;
7713 pTempFile->cbFileAllocated = cbAll;
7714 pTempFile->paSegs[0].cbDataAlloc = cbAll;
7715 pTempFile->paSegs[0].pbData = pbAll;
7716 pTempFile->paSegs[0].offData = 0;
7717 }
7718
7719 pTempFile->cMappings++;
7720 kHlpAssert(pTempFile->cMappings == 1);
7721
7722 pvRet = pTempFile->paSegs[0].pbData;
7723 KWFS_LOG(("CreateFileMappingW(%p) -> %p [temp]\n", hSection, pvRet));
7724 break;
7725 }
7726
7727 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7728 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pTempFile->cbFile));
7729 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7730 return NULL;
7731 }
7732
7733 /*
7734 * This is simple in comparison to the above temporary file code.
7735 */
7736 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7737 {
7738 PKFSWCACHEDFILE pCachedFile = pHandle->u.pCachedFile;
7739 if ( dwDesiredAccess == FILE_MAP_READ
7740 && offFileHigh == 0
7741 && offFileLow == 0
7742 && (cbToMap == 0 || cbToMap == pCachedFile->cbCached) )
7743 {
7744 pvRet = pCachedFile->pbCached;
7745 KWFS_LOG(("CreateFileMappingW(%p) -> %p [cached]\n", hSection, pvRet));
7746 break;
7747 }
7748 kHlpAssertMsgFailed(("dwDesiredAccess=%#x offFile=%#x'%08x cbToMap=%#x (cbFile=%#x)\n",
7749 dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pCachedFile->cbCached));
7750 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7751 return NULL;
7752 }
7753 }
7754
7755 /*
7756 * Insert into the mapping tracking table. This is common
7757 * and we should only get here with a non-NULL pvRet.
7758 *
7759 * Note! We could look for duplicates and do ref counting, but it's
7760 * easier to just append for now.
7761 */
7762 kHlpAssert(pvRet != NULL);
7763 idxMapping = g_Sandbox.cMemMappings;
7764 kHlpAssert(idxMapping < g_Sandbox.cMemMappingsAlloc);
7765
7766 g_Sandbox.paMemMappings[idxMapping].cRefs = 1;
7767 g_Sandbox.paMemMappings[idxMapping].pvMapping = pvRet;
7768 g_Sandbox.paMemMappings[idxMapping].enmType = pHandle->enmType;
7769 g_Sandbox.paMemMappings[idxMapping].u.pCachedFile = pHandle->u.pCachedFile;
7770 g_Sandbox.cMemMappings++;
7771
7772 return pvRet;
7773 }
7774 }
7775
7776 pvRet = MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7777 KWFS_LOG(("MapViewOfFile(%p, %#x, %#x, %#x, %#x) -> %p\n",
7778 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvRet));
7779 return pvRet;
7780}
7781
7782
7783/** Kernel32 - MapViewOfFileEx */
7784static PVOID WINAPI kwSandbox_Kernel32_MapViewOfFileEx(HANDLE hSection, DWORD dwDesiredAccess,
7785 DWORD offFileHigh, DWORD offFileLow, SIZE_T cbToMap, PVOID pvMapAddr)
7786{
7787 PVOID pvRet;
7788 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSection);
7789 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7790 if (idxHandle < g_Sandbox.cHandles)
7791 {
7792 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7793 if (pHandle != NULL)
7794 {
7795 switch (pHandle->enmType)
7796 {
7797 case KWHANDLETYPE_TEMP_FILE_MAPPING:
7798 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - temporary file!\n",
7799 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7800 if (!pvMapAddr)
7801 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7802 kHlpAssertFailed();
7803 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7804 return NULL;
7805
7806 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
7807 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) - read cached file!\n",
7808 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr));
7809 if (!pvMapAddr)
7810 return kwSandbox_Kernel32_MapViewOfFile(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap);
7811 /* We can use fallback here as the handle is an actual section handle. */
7812 break;
7813
7814 case KWHANDLETYPE_FSOBJ_READ_CACHE:
7815 case KWHANDLETYPE_TEMP_FILE:
7816 case KWHANDLETYPE_OUTPUT_BUF:
7817 default:
7818 kHlpAssertFailed();
7819 SetLastError(ERROR_INVALID_OPERATION);
7820 return NULL;
7821 }
7822 }
7823 }
7824
7825 pvRet = MapViewOfFileEx(hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr);
7826 KWFS_LOG(("MapViewOfFileEx(%p, %#x, %#x, %#x, %#x, %p) -> %p\n",
7827 hSection, dwDesiredAccess, offFileHigh, offFileLow, cbToMap, pvMapAddr, pvRet));
7828 return pvRet;
7829
7830}
7831
7832/** Kernel32 - UnmapViewOfFile */
7833static BOOL WINAPI kwSandbox_Kernel32_UnmapViewOfFile(LPCVOID pvBase)
7834{
7835 /*
7836 * Consult the memory mapping tracker.
7837 */
7838 PKWMEMMAPPING paMemMappings = g_Sandbox.paMemMappings;
7839 KU32 idxMapping = g_Sandbox.cMemMappings;
7840 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7841 while (idxMapping-- > 0)
7842 if (paMemMappings[idxMapping].pvMapping == pvBase)
7843 {
7844 /* Type specific stuff. */
7845 if (paMemMappings[idxMapping].enmType == KWHANDLETYPE_TEMP_FILE_MAPPING)
7846 {
7847 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [temp]\n", pvBase));
7848 paMemMappings[idxMapping].u.pTempFile->cMappings--;
7849 }
7850 else
7851 KWFS_LOG(("UnmapViewOfFile(%p) -> TRUE [cached]\n", pvBase));
7852
7853 /* Deref and probably free it. */
7854 if (--paMemMappings[idxMapping].cRefs == 0)
7855 {
7856 g_Sandbox.cMemMappings--;
7857 if (idxMapping != g_Sandbox.cMemMappings)
7858 paMemMappings[idxMapping] = paMemMappings[g_Sandbox.cMemMappings];
7859 }
7860 return TRUE;
7861 }
7862
7863 KWFS_LOG(("UnmapViewOfFile(%p)\n", pvBase));
7864 return UnmapViewOfFile(pvBase);
7865}
7866
7867/** @todo UnmapViewOfFileEx */
7868
7869#endif /* WITH_TEMP_MEMORY_FILES */
7870
7871
7872/** Kernel32 - DuplicateHandle */
7873static BOOL WINAPI kwSandbox_Kernel32_DuplicateHandle(HANDLE hSrcProc, HANDLE hSrc, HANDLE hDstProc, PHANDLE phNew,
7874 DWORD dwDesiredAccess, BOOL fInheritHandle, DWORD dwOptions)
7875{
7876 BOOL fRet;
7877
7878 /*
7879 * We must catch our handles being duplicated.
7880 */
7881 if (hSrcProc == GetCurrentProcess())
7882 {
7883 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hSrc);
7884 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7885 if (idxHandle < g_Sandbox.cHandles)
7886 {
7887 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7888 if (pHandle)
7889 {
7890 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7891 if (fRet)
7892 {
7893 if (kwSandboxHandleTableEnter(&g_Sandbox, pHandle, *phNew))
7894 {
7895 pHandle->cRefs++;
7896 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> TRUE, %p [intercepted handle] enmType=%d cRef=%d\n",
7897 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew,
7898 pHandle->enmType, pHandle->cRefs));
7899 }
7900 else
7901 {
7902 fRet = FALSE;
7903 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
7904 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> !FALSE!, %p [intercepted handle] enmType=%d\n",
7905 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, *phNew, pHandle->enmType));
7906 }
7907 }
7908 else
7909 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> FALSE [intercepted handle] enmType=%d\n",
7910 hSrcProc, hSrc, hDstProc, dwDesiredAccess, fInheritHandle, dwOptions, pHandle->enmType));
7911 return fRet;
7912 }
7913 }
7914 }
7915
7916 /*
7917 * Not one of ours, just do what the caller asks and log it.
7918 */
7919 fRet = DuplicateHandle(hSrcProc, hSrc, hDstProc, phNew, dwDesiredAccess, fInheritHandle, dwOptions);
7920 KWFS_LOG(("DuplicateHandle(%p, %p, %p, , %#x, %d, %#x) -> %d, %p\n", hSrcProc, hSrc, hDstProc, dwDesiredAccess,
7921 fInheritHandle, dwOptions, fRet, *phNew));
7922 return fRet;
7923}
7924
7925
7926/** Kernel32 - CloseHandle */
7927static BOOL WINAPI kwSandbox_Kernel32_CloseHandle(HANDLE hObject)
7928{
7929 BOOL fRet;
7930 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hObject);
7931 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread || g_rcCtrlC != 0);
7932 if (idxHandle < g_Sandbox.cHandles)
7933 {
7934 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7935 if (pHandle)
7936 {
7937 /* Prevent the closing of the standard output and error handles. */
7938 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
7939 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle))
7940 {
7941 fRet = CloseHandle(hObject);
7942 if (fRet)
7943 {
7944 PKWHANDLE pHandle = g_Sandbox.papHandles[idxHandle];
7945 g_Sandbox.papHandles[idxHandle] = NULL;
7946 g_Sandbox.cActiveHandles--;
7947 kHlpAssert(g_Sandbox.cActiveHandles >= g_Sandbox.cFixedHandles);
7948 if (--pHandle->cRefs == 0)
7949 {
7950#ifdef WITH_TEMP_MEMORY_FILES
7951 if (pHandle->enmType == KWHANDLETYPE_TEMP_FILE)
7952 {
7953 kHlpAssert(pHandle->u.pTempFile->cActiveHandles > 0);
7954 pHandle->u.pTempFile->cActiveHandles--;
7955 }
7956#endif
7957 kHlpFree(pHandle);
7958 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, freed]\n", hObject));
7959 }
7960 else
7961 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle, not freed]\n", hObject));
7962 }
7963 else
7964 KWFS_LOG(("CloseHandle(%p) -> FALSE [intercepted handle] err=%u!\n", hObject, GetLastError()));
7965 }
7966 else
7967 {
7968 KWFS_LOG(("CloseHandle(%p) -> TRUE [intercepted handle] Ignored closing of std%s!\n",
7969 hObject, hObject == g_Sandbox.StdErr.hOutput ? "err" : "out"));
7970 fRet = TRUE;
7971 }
7972 return fRet;
7973 }
7974 }
7975
7976 fRet = CloseHandle(hObject);
7977 KWFS_LOG(("CloseHandle(%p) -> %d\n", hObject, fRet));
7978 return fRet;
7979}
7980
7981
7982/** Kernel32 - GetFileAttributesA. */
7983static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesA(LPCSTR pszFilename)
7984{
7985 DWORD fRet;
7986 const char *pszExt = kHlpGetExt(pszFilename);
7987 if (kwFsIsCacheableExtensionA(pszExt, K_TRUE /*fAttrQuery*/))
7988 {
7989 KFSLOOKUPERROR enmError;
7990 PKFSOBJ pFsObj;
7991 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
7992
7993 pFsObj = kFsCacheLookupNoMissingA(g_pFsCache, pszFilename, &enmError);
7994 if (pFsObj)
7995 {
7996 kHlpAssert(pFsObj->fHaveStats);
7997 fRet = pFsObj->Stats.st_attribs;
7998 kFsCacheObjRelease(g_pFsCache, pFsObj);
7999 }
8000 else
8001 {
8002 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8003 fRet = INVALID_FILE_ATTRIBUTES;
8004 }
8005
8006 KWFS_LOG(("GetFileAttributesA(%s) -> %#x [cached]\n", pszFilename, fRet));
8007 return fRet;
8008 }
8009
8010 fRet = GetFileAttributesA(pszFilename);
8011 KWFS_LOG(("GetFileAttributesA(%s) -> %#x\n", pszFilename, fRet));
8012 return fRet;
8013}
8014
8015
8016/** Kernel32 - GetFileAttributesW. */
8017static DWORD WINAPI kwSandbox_Kernel32_GetFileAttributesW(LPCWSTR pwszFilename)
8018{
8019 DWORD fRet;
8020 if (kwFsIsCacheablePathExtensionW(pwszFilename, K_TRUE /*fAttrQuery*/))
8021 {
8022 KFSLOOKUPERROR enmError;
8023 PKFSOBJ pFsObj;
8024 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8025
8026 pFsObj = kFsCacheLookupNoMissingW(g_pFsCache, pwszFilename, &enmError);
8027 if (pFsObj)
8028 {
8029 kHlpAssert(pFsObj->fHaveStats);
8030 fRet = pFsObj->Stats.st_attribs;
8031 kFsCacheObjRelease(g_pFsCache, pFsObj);
8032 }
8033 else
8034 {
8035 SetLastError(kwFsLookupErrorToWindowsError(enmError));
8036 fRet = INVALID_FILE_ATTRIBUTES;
8037 }
8038
8039 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x [cached]\n", pwszFilename, fRet));
8040 return fRet;
8041 }
8042
8043 fRet = GetFileAttributesW(pwszFilename);
8044 KWFS_LOG(("GetFileAttributesW(%ls) -> %#x\n", pwszFilename, fRet));
8045 return fRet;
8046}
8047
8048
8049/** Kernel32 - GetShortPathNameW - c1[xx].dll of VS2010 does this to the
8050 * directory containing each include file. We cache the result to speed
8051 * things up a little. */
8052static DWORD WINAPI kwSandbox_Kernel32_GetShortPathNameW(LPCWSTR pwszLongPath, LPWSTR pwszShortPath, DWORD cwcShortPath)
8053{
8054 DWORD cwcRet;
8055 if (kwFsIsCacheablePathExtensionW(pwszLongPath, K_TRUE /*fAttrQuery*/))
8056 {
8057 KFSLOOKUPERROR enmError;
8058 PKFSOBJ pObj;
8059 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8060
8061 pObj = kFsCacheLookupW(g_pFsCache, pwszLongPath, &enmError);
8062 if (pObj)
8063 {
8064 if (pObj->bObjType != KFSOBJ_TYPE_MISSING)
8065 {
8066 if (kFsCacheObjGetFullShortPathW(pObj, pwszShortPath, cwcShortPath, '\\'))
8067 {
8068 cwcRet = (DWORD)kwUtf16Len(pwszShortPath);
8069
8070 /* Should preserve trailing slash on directory paths. */
8071 if (pObj->bObjType == KFSOBJ_TYPE_DIR)
8072 {
8073 if ( cwcRet + 1 < cwcShortPath
8074 && pwszShortPath[cwcRet - 1] != '\\')
8075 {
8076 KSIZE cwcIn = kwUtf16Len(pwszLongPath);
8077 if ( cwcIn > 0
8078 && (pwszLongPath[cwcIn - 1] == '\\' || pwszLongPath[cwcIn - 1] == '/') )
8079 {
8080 pwszShortPath[cwcRet++] = '\\';
8081 pwszShortPath[cwcRet] = '\0';
8082 }
8083 }
8084 }
8085
8086 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x [cached]\n",
8087 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8088 kFsCacheObjRelease(g_pFsCache, pObj);
8089 return cwcRet;
8090 }
8091
8092 /* fall back for complicated cases. */
8093 }
8094 kFsCacheObjRelease(g_pFsCache, pObj);
8095 }
8096 }
8097 cwcRet = GetShortPathNameW(pwszLongPath, pwszShortPath, cwcShortPath);
8098 KWFS_LOG(("GetShortPathNameW(%ls) -> '%*.*ls' & %#x\n",
8099 pwszLongPath, K_MIN(cwcShortPath, cwcRet), K_MIN(cwcShortPath, cwcRet), pwszShortPath, cwcRet));
8100 return cwcRet;
8101}
8102
8103
8104#ifdef WITH_TEMP_MEMORY_FILES
8105/** Kernel32 - DeleteFileW
8106 * Skip deleting the in-memory files. */
8107static BOOL WINAPI kwSandbox_Kernel32_DeleteFileW(LPCWSTR pwszFilename)
8108{
8109 BOOL fRc;
8110 if ( g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8111 && kwFsIsClTempFileW(pwszFilename))
8112 {
8113 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8114 KWFS_LOG(("DeleteFileW(%s) -> TRUE [temp]\n", pwszFilename));
8115 fRc = TRUE;
8116 }
8117 else
8118 {
8119 fRc = DeleteFileW(pwszFilename);
8120 KWFS_LOG(("DeleteFileW(%s) -> %d (%d)\n", pwszFilename, fRc, GetLastError()));
8121 }
8122 return fRc;
8123}
8124#endif /* WITH_TEMP_MEMORY_FILES */
8125
8126
8127
8128#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
8129
8130/*
8131 *
8132 * Console output buffering.
8133 * Console output buffering.
8134 * Console output buffering.
8135 *
8136 */
8137
8138
8139/**
8140 * Write a wide char string to the console.
8141 *
8142 * @param pSandbox The sandbox which output buffer to flush.
8143 */
8144static void kwSandboxConsoleWriteIt(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcToWrite)
8145{
8146 if (cwcToWrite > 0)
8147 {
8148 DWORD cwcWritten = 0;
8149 if (WriteConsoleW(pSandbox->Combined.hOutput, pwcBuf, cwcToWrite, &cwcWritten, NULL))
8150 {
8151 if (cwcWritten == cwcToWrite)
8152 { /* likely */ }
8153 else
8154 {
8155 DWORD off = 0;
8156 do
8157 {
8158 off += cwcWritten;
8159 cwcWritten = 0;
8160 } while ( off < cwcToWrite
8161 && WriteConsoleW(pSandbox->Combined.hOutput, &pwcBuf[off], cwcToWrite - off, &cwcWritten, NULL));
8162 kHlpAssert(off == cwcWritten);
8163 }
8164 }
8165 else
8166 kHlpAssertFailed();
8167 pSandbox->Combined.cFlushes++;
8168 }
8169}
8170
8171
8172/**
8173 * Flushes the combined console output buffer.
8174 *
8175 * @param pSandbox The sandbox which output buffer to flush.
8176 */
8177static void kwSandboxConsoleFlushCombined(PKWSANDBOX pSandbox)
8178{
8179 if (pSandbox->Combined.cwcBuf > 0)
8180 {
8181 KWOUT_LOG(("kwSandboxConsoleFlushCombined: %u wchars\n", pSandbox->Combined.cwcBuf));
8182 kwSandboxConsoleWriteIt(pSandbox, pSandbox->Combined.wszBuf, pSandbox->Combined.cwcBuf);
8183 pSandbox->Combined.cwcBuf = 0;
8184 }
8185}
8186
8187
8188/**
8189 * For handling combined buffer overflow cases line by line.
8190 *
8191 * @param pSandbox The sandbox.
8192 * @param pwcBuf What to add to the combined buffer. Usually a
8193 * line, unless we're really low on buffer space.
8194 * @param cwcBuf The length of what to add.
8195 * @param fBrokenLine Whether this is a broken line.
8196 */
8197static void kwSandboxConsoleAddToCombined(PKWSANDBOX pSandbox, wchar_t const *pwcBuf, KU32 cwcBuf, KBOOL fBrokenLine)
8198{
8199 if (fBrokenLine)
8200 kwSandboxConsoleFlushCombined(pSandbox);
8201 if (pSandbox->Combined.cwcBuf + cwcBuf > K_ELEMENTS(pSandbox->Combined.wszBuf))
8202 {
8203 kwSandboxConsoleFlushCombined(pSandbox);
8204 kwSandboxConsoleWriteIt(pSandbox, pwcBuf, cwcBuf);
8205 }
8206 else
8207 {
8208 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuf, cwcBuf * sizeof(wchar_t));
8209 pSandbox->Combined.cwcBuf += cwcBuf;
8210 }
8211}
8212
8213
8214/**
8215 * Called to final flush a line buffer via the combined buffer (if applicable).
8216 *
8217 * @param pSandbox The sandbox.
8218 * @param pLineBuf The line buffer.
8219 * @param pszName The line buffer name (for logging)
8220 */
8221static void kwSandboxConsoleFinalFlushLineBuf(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pszName)
8222{
8223 if (pLineBuf->fIsConsole)
8224 {
8225 if (pLineBuf->u.Con.cwcBuf > 0)
8226 {
8227 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u wchars\n", pszName, pLineBuf->u.Con.cwcBuf));
8228
8229 if (pLineBuf->u.Con.cwcBuf < pLineBuf->u.Con.cwcBufAlloc)
8230 {
8231 pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf++] = '\n';
8232 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_FALSE /*fBrokenLine*/);
8233 }
8234 else
8235 {
8236 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBrokenLine*/);
8237 kwSandboxConsoleAddToCombined(pSandbox, L"\n", 1, K_TRUE /*fBrokenLine*/);
8238 }
8239 pLineBuf->u.Con.cwcBuf = 0;
8240 }
8241 }
8242#ifdef WITH_STD_OUT_ERR_BUFFERING
8243 else if (pLineBuf->u.Fully.cchBuf > 0)
8244 {
8245 KWOUT_LOG(("kwSandboxConsoleFinalFlushLineBuf: %s: %u bytes\n", pszName, pLineBuf->u.Fully.cchBuf));
8246
8247 kwSandboxOutBufWriteIt(pLineBuf->hBackup, pLineBuf->u.Fully.pchBuf, pLineBuf->u.Fully.cchBuf);
8248 pLineBuf->u.Fully.cchBuf = 0;
8249 }
8250#endif
8251}
8252
8253
8254/**
8255 * Called at the end of sandboxed execution to flush both stream buffers.
8256 *
8257 * @param pSandbox The sandbox.
8258 */
8259static void kwSandboxConsoleFlushAll(PKWSANDBOX pSandbox)
8260{
8261 /*
8262 * First do the cl.exe source file supression trick, if applicable.
8263 * The output ends up on CONOUT$ if either StdOut or StdErr is a console
8264 * handle.
8265 */
8266 if ( pSandbox->pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL
8267 && pSandbox->Combined.cFlushes == 0)
8268 {
8269 if ( pSandbox->StdOut.fIsConsole
8270 || pSandbox->StdErr.fIsConsole)
8271 {
8272 if ( pSandbox->Combined.cwcBuf >= 3
8273 && (pSandbox->StdOut.fIsConsole ? pSandbox->StdOut.u.Con.cwcBuf : pSandbox->StdOut.u.Fully.cchBuf) == 0
8274 && (pSandbox->StdErr.fIsConsole ? pSandbox->StdErr.u.Con.cwcBuf : pSandbox->StdErr.u.Fully.cchBuf) == 0 )
8275 {
8276 KI32 off = pSandbox->Combined.cwcBuf - 1;
8277 if (pSandbox->Combined.wszBuf[off] == '\n')
8278 {
8279 KBOOL fOk = K_TRUE;
8280 while (off-- > 0)
8281 {
8282 wchar_t const wc = pSandbox->Combined.wszBuf[off];
8283 if (iswalnum(wc) || wc == '.' || wc == ' ' || wc == '_' || wc == '-')
8284 { /* likely */ }
8285 else
8286 {
8287 fOk = K_FALSE;
8288 break;
8289 }
8290 }
8291 if (fOk)
8292 {
8293 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*ls in combined console buffer\n",
8294 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8295 pSandbox->Combined.cwcBuf = 0;
8296 return;
8297 }
8298 }
8299 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*ls in combined console buffer\n",
8300 pSandbox->Combined.cwcBuf, pSandbox->Combined.cwcBuf, pSandbox->Combined.wszBuf));
8301 }
8302 }
8303#ifdef WITH_STD_OUT_ERR_BUFFERING
8304 /*
8305 * Otherwise, it goes to standard output (redirected).
8306 */
8307 else if ( pSandbox->StdErr.u.Fully.cchBuf == 0
8308 && pSandbox->StdOut.u.Fully.cchBuf >= 3)
8309 {
8310 char const *pchBuf = pSandbox->StdOut.u.Fully.pchBuf;
8311 KI32 off = pSandbox->StdOut.u.Fully.cchBuf - 1;
8312 kHlpAssert(pSandbox->Combined.cFlushes == 0 && pSandbox->Combined.cwcBuf == 0); /* unused! */
8313
8314 if (pchBuf[off] == '\n')
8315 {
8316 KBOOL fOk = K_TRUE;
8317 if (pchBuf[off - 1] == '\r')
8318 off--;
8319 while (off-- > 0)
8320 {
8321 char const ch = pchBuf[off];
8322 if (isalnum(ch) || ch == '.' || ch == ' ' || ch == '_' || ch == '-')
8323 { /* likely */ }
8324 else
8325 {
8326 fOk = K_FALSE;
8327 break;
8328 }
8329 }
8330 if (fOk)
8331 {
8332 KWOUT_LOG(("kwSandboxConsoleFlushAll: Dropping '%*.*s in stdout buffer\n",
8333 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8334 pSandbox->StdOut.u.Fully.cchBuf = 0;
8335 return;
8336 }
8337 }
8338 KWOUT_LOG(("kwSandboxConsoleFlushAll: Unable to drop '%*.*s in stdout buffer\n",
8339 pSandbox->StdOut.u.Fully.cchBuf, pSandbox->StdOut.u.Fully.cchBuf, pchBuf));
8340 }
8341#endif
8342 }
8343
8344 /*
8345 * Flush the two line buffer, then the combined buffer.
8346 */
8347 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdErr, "StdErr");
8348 kwSandboxConsoleFinalFlushLineBuf(pSandbox, &pSandbox->StdOut, "StdOut");
8349 kwSandboxConsoleFlushCombined(pSandbox);
8350}
8351
8352
8353/**
8354 * Writes a string to the given output stream.
8355 *
8356 * @param pSandbox The sandbox.
8357 * @param pLineBuf The line buffer for the output stream.
8358 * @param pwcBuffer The buffer to write.
8359 * @param cwcToWrite The number of wchar_t's in the buffer.
8360 */
8361static void kwSandboxConsoleWriteW(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, wchar_t const *pwcBuffer, KU32 cwcToWrite)
8362{
8363 kHlpAssert(pLineBuf->fIsConsole);
8364 if (cwcToWrite > 0)
8365 {
8366 /*
8367 * First, find the start of the last incomplete line so we can figure
8368 * out how much line buffering we need to do.
8369 */
8370 KU32 cchLastIncompleteLine;
8371 KU32 offLastIncompleteLine = cwcToWrite;
8372 while ( offLastIncompleteLine > 0
8373 && pwcBuffer[offLastIncompleteLine - 1] != '\n')
8374 offLastIncompleteLine--;
8375 cchLastIncompleteLine = cwcToWrite - offLastIncompleteLine;
8376
8377 /* Was there anything to line buffer? */
8378 if (offLastIncompleteLine < cwcToWrite)
8379 {
8380 /* Need to grow the line buffer? */
8381 KU32 cwcNeeded = offLastIncompleteLine == 0
8382 ? pLineBuf->u.Con.cwcBuf + cchLastIncompleteLine /* incomplete line, append to line buffer */
8383 : cchLastIncompleteLine; /* Only the final incomplete line (if any) goes to the line buffer. */
8384 if (cwcNeeded > pLineBuf->u.Con.cwcBufAlloc)
8385 {
8386 void *pvNew;
8387 KU32 cwcNew = !pLineBuf->u.Con.cwcBufAlloc ? 1024 : pLineBuf->u.Con.cwcBufAlloc * 2;
8388 while (cwcNew < cwcNeeded)
8389 cwcNew *= 2;
8390 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNew * sizeof(wchar_t));
8391 if (pvNew)
8392 {
8393 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8394 pLineBuf->u.Con.cwcBufAlloc = cwcNew;
8395 }
8396 else
8397 {
8398 pvNew = kHlpRealloc(pLineBuf->u.Con.pwcBuf, cwcNeeded * sizeof(wchar_t));
8399 if (pvNew)
8400 {
8401 pLineBuf->u.Con.pwcBuf = (wchar_t *)pvNew;
8402 pLineBuf->u.Con.cwcBufAlloc = cwcNeeded;
8403 }
8404 else
8405 {
8406 /* This isn't perfect, but it will have to do for now. */
8407 if (pLineBuf->u.Con.cwcBuf > 0)
8408 {
8409 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8410 K_TRUE /*fBrokenLine*/);
8411 pLineBuf->u.Con.cwcBuf = 0;
8412 }
8413 kwSandboxConsoleAddToCombined(pSandbox, pwcBuffer, cwcToWrite, K_TRUE /*fBrokenLine*/);
8414 return;
8415 }
8416 }
8417 }
8418
8419 /*
8420 * Handle the case where we only add to the line buffer.
8421 */
8422 if (offLastIncompleteLine == 0)
8423 {
8424 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcToWrite * sizeof(wchar_t));
8425 pLineBuf->u.Con.cwcBuf += cwcToWrite;
8426 return;
8427 }
8428 }
8429
8430 /*
8431 * If there is sufficient combined buffer to handle this request, this is rather simple.
8432 */
8433 kHlpAssert(pSandbox->Combined.cwcBuf <= K_ELEMENTS(pSandbox->Combined.wszBuf));
8434 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offLastIncompleteLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8435 {
8436 if (pLineBuf->u.Con.cwcBuf > 0)
8437 {
8438 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8439 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8440 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8441 pLineBuf->u.Con.cwcBuf = 0;
8442 }
8443
8444 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8445 pwcBuffer, offLastIncompleteLine * sizeof(wchar_t));
8446 pSandbox->Combined.cwcBuf += offLastIncompleteLine;
8447 }
8448 else
8449 {
8450 /*
8451 * Do line-by-line processing of the input, flusing the combined buffer
8452 * when it becomes necessary. We may have to write lines
8453 */
8454 KU32 off = 0;
8455 KU32 offNextLine = 0;
8456
8457 /* If there are buffered chars, we handle the first line outside the
8458 main loop. We must try our best outputting it as a complete line. */
8459 if (pLineBuf->u.Con.cwcBuf > 0)
8460 {
8461 while (offNextLine < cwcToWrite && pwcBuffer[offNextLine] != '\n')
8462 offNextLine++;
8463 offNextLine++;
8464 kHlpAssert(offNextLine <= offLastIncompleteLine);
8465
8466 if (pSandbox->Combined.cwcBuf + pLineBuf->u.Con.cwcBuf + offNextLine <= K_ELEMENTS(pSandbox->Combined.wszBuf))
8467 {
8468 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf],
8469 pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf * sizeof(wchar_t));
8470 pSandbox->Combined.cwcBuf += pLineBuf->u.Con.cwcBuf;
8471 pLineBuf->u.Con.cwcBuf = 0;
8472
8473 kHlpMemCopy(&pSandbox->Combined.wszBuf[pSandbox->Combined.cwcBuf], pwcBuffer, offNextLine * sizeof(wchar_t));
8474 pSandbox->Combined.cwcBuf += offNextLine;
8475 }
8476 else
8477 {
8478 KU32 cwcLeft = pLineBuf->u.Con.cwcBufAlloc - pLineBuf->u.Con.cwcBuf;
8479 if (cwcLeft > 0)
8480 {
8481 KU32 cwcCopy = K_MIN(cwcLeft, offNextLine);
8482 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[pLineBuf->u.Con.cwcBuf], pwcBuffer, cwcCopy * sizeof(wchar_t));
8483 pLineBuf->u.Con.cwcBuf += cwcCopy;
8484 off += cwcCopy;
8485 }
8486 if (pLineBuf->u.Con.cwcBuf > 0)
8487 {
8488 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf,
8489 K_TRUE /*fBrokenLine*/);
8490 pLineBuf->u.Con.cwcBuf = 0;
8491 }
8492 if (off < offNextLine)
8493 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_TRUE /*fBrokenLine*/);
8494 }
8495 off = offNextLine;
8496 }
8497
8498 /* Deal with the remaining lines */
8499 while (off < offLastIncompleteLine)
8500 {
8501 while (offNextLine < offLastIncompleteLine && pwcBuffer[offNextLine] != '\n')
8502 offNextLine++;
8503 offNextLine++;
8504 kHlpAssert(offNextLine <= offLastIncompleteLine);
8505 kwSandboxConsoleAddToCombined(pSandbox, &pwcBuffer[off], offNextLine - off, K_FALSE /*fBrokenLine*/);
8506 off = offNextLine;
8507 }
8508 }
8509
8510 /*
8511 * Buffer any remaining incomplete line chars.
8512 */
8513 if (cchLastIncompleteLine)
8514 {
8515 kHlpMemCopy(&pLineBuf->u.Con.pwcBuf[0], &pwcBuffer[offLastIncompleteLine], cchLastIncompleteLine * sizeof(wchar_t));
8516 pLineBuf->u.Con.cwcBuf = cchLastIncompleteLine;
8517 }
8518 }
8519}
8520
8521
8522/**
8523 * Worker for WriteConsoleA and WriteFile.
8524 *
8525 * @param pSandbox The sandbox.
8526 * @param pLineBuf The line buffer.
8527 * @param pchBuffer What to write.
8528 * @param cchToWrite How much to write.
8529 */
8530static void kwSandboxConsoleWriteA(PKWSANDBOX pSandbox, PKWOUTPUTSTREAMBUF pLineBuf, const char *pchBuffer, KU32 cchToWrite)
8531{
8532 /*
8533 * Convert it to wide char and use the 'W' to do the work.
8534 */
8535 int cwcRet;
8536 KU32 cwcBuf = cchToWrite * 2 + 1;
8537 wchar_t *pwcBufFree = NULL;
8538 wchar_t *pwcBuf;
8539 kHlpAssert(pLineBuf->fIsConsole);
8540
8541 if (cwcBuf <= 4096)
8542 pwcBuf = alloca(cwcBuf * sizeof(wchar_t));
8543 else
8544 pwcBuf = pwcBufFree = kHlpAlloc(cwcBuf * sizeof(wchar_t));
8545
8546 cwcRet = MultiByteToWideChar(pSandbox->Combined.uCodepage, 0/*dwFlags*/, pchBuffer, cchToWrite, pwcBuf, cwcBuf);
8547 if (cwcRet > 0)
8548 kwSandboxConsoleWriteW(pSandbox, pLineBuf, pwcBuf, cwcRet);
8549 else
8550 {
8551 DWORD cchWritten;
8552 kHlpAssertFailed();
8553
8554 /* Flush the line buffer and combined buffer before calling WriteConsoleA. */
8555 if (pLineBuf->u.Con.cwcBuf > 0)
8556 {
8557 kwSandboxConsoleAddToCombined(pSandbox, pLineBuf->u.Con.pwcBuf, pLineBuf->u.Con.cwcBuf, K_TRUE /*fBroken*/);
8558 pLineBuf->u.Con.cwcBuf = 0;
8559 }
8560 kwSandboxConsoleFlushCombined(pSandbox);
8561
8562 if (WriteConsoleA(pLineBuf->hBackup, pchBuffer, cchToWrite, &cchWritten, NULL /*pvReserved*/))
8563 {
8564 if (cchWritten >= cchToWrite)
8565 { /* likely */ }
8566 else
8567 {
8568 KU32 off = 0;
8569 do
8570 {
8571 off += cchWritten;
8572 cchWritten = 0;
8573 } while ( off < cchToWrite
8574 && WriteConsoleA(pLineBuf->hBackup, &pchBuffer[off], cchToWrite - off, &cchWritten, NULL));
8575 }
8576 }
8577 }
8578
8579 if (pwcBufFree)
8580 kHlpFree(pwcBufFree);
8581}
8582
8583
8584/** Kernel32 - WriteConsoleA */
8585BOOL WINAPI kwSandbox_Kernel32_WriteConsoleA(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cbToWrite, PDWORD pcbWritten,
8586 PVOID pvReserved)
8587{
8588 BOOL fRc;
8589 PKWOUTPUTSTREAMBUF pLineBuf;
8590 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8591
8592 if (hConOutput == g_Sandbox.StdErr.hOutput)
8593 pLineBuf = &g_Sandbox.StdErr;
8594 else
8595 pLineBuf = &g_Sandbox.StdOut;
8596 if (pLineBuf->fIsConsole)
8597 {
8598 kwSandboxConsoleWriteA(&g_Sandbox, pLineBuf, (char const *)pvBuffer, cbToWrite);
8599
8600 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> TRUE [cached]\n",
8601 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved));
8602 if (pcbWritten)
8603 *pcbWritten = cbToWrite;
8604 fRc = TRUE;
8605 }
8606 else
8607 {
8608 fRc = WriteConsoleA(hConOutput, pvBuffer, cbToWrite, pcbWritten, pvReserved);
8609 KWOUT_LOG(("WriteConsoleA: %p, %p LB %#x (%*.*s), %p, %p -> %d !fallback!\n",
8610 hConOutput, pvBuffer, cbToWrite, cbToWrite, cbToWrite, pvBuffer, pcbWritten, pvReserved, fRc));
8611 }
8612 return fRc;
8613}
8614
8615
8616/** Kernel32 - WriteConsoleW */
8617BOOL WINAPI kwSandbox_Kernel32_WriteConsoleW(HANDLE hConOutput, CONST VOID *pvBuffer, DWORD cwcToWrite, PDWORD pcwcWritten,
8618 PVOID pvReserved)
8619{
8620 BOOL fRc;
8621 PKWOUTPUTSTREAMBUF pLineBuf;
8622 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8623
8624 if (hConOutput == g_Sandbox.StdErr.hOutput)
8625 pLineBuf = &g_Sandbox.StdErr;
8626 else if (hConOutput == g_Sandbox.StdOut.hOutput)
8627 pLineBuf = &g_Sandbox.StdOut;
8628 else
8629 pLineBuf = g_Sandbox.StdErr.fIsConsole ? &g_Sandbox.StdErr : &g_Sandbox.StdOut;
8630 if (pLineBuf->fIsConsole)
8631 {
8632 kwSandboxConsoleWriteW(&g_Sandbox, pLineBuf, (wchar_t const *)pvBuffer, cwcToWrite);
8633
8634 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> TRUE [cached]\n",
8635 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved));
8636 if (pcwcWritten)
8637 *pcwcWritten = cwcToWrite;
8638 fRc = TRUE;
8639 }
8640 else
8641 {
8642 fRc = WriteConsoleW(hConOutput, pvBuffer, cwcToWrite, pcwcWritten, pvReserved);
8643 KWOUT_LOG(("WriteConsoleW: %p, %p LB %#x (%*.*ls), %p, %p -> %d !fallback!\n",
8644 hConOutput, pvBuffer, cwcToWrite, cwcToWrite, cwcToWrite, pvBuffer, pcwcWritten, pvReserved, fRc));
8645 }
8646 return fRc;
8647}
8648
8649#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
8650
8651
8652
8653/*
8654 *
8655 * Virtual memory leak prevension.
8656 * Virtual memory leak prevension.
8657 * Virtual memory leak prevension.
8658 *
8659 */
8660
8661#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8662
8663/** For debug logging. */
8664# ifndef NDEBUG
8665static void kwSandboxLogFixedAllocation(KU32 idxFixed, const char *pszWhere)
8666{
8667 MEMORY_BASIC_INFORMATION MemInfo = { NULL, NULL, 0, 0, 0, 0, 0};
8668 SIZE_T cbMemInfo = VirtualQuery(g_aFixedVirtualAllocs[idxFixed].pvReserved, &MemInfo, sizeof(MemInfo));
8669 kHlpAssert(cbMemInfo == sizeof(MemInfo));
8670 if (cbMemInfo != 0)
8671 KW_LOG(("%s: #%u %p LB %#x: base=%p alloc=%p region=%#x state=%#x prot=%#x type=%#x\n",
8672 pszWhere, idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed,
8673 MemInfo.BaseAddress,
8674 MemInfo.AllocationBase,
8675 MemInfo.RegionSize,
8676 MemInfo.State,
8677 MemInfo.Protect,
8678 MemInfo.Type));
8679}
8680# else
8681# define kwSandboxLogFixedAllocation(idxFixed, pszWhere) do { } while (0)
8682# endif
8683
8684/**
8685 * Used by both kwSandbox_Kernel32_VirtualFree and kwSandboxCleanupLate
8686 *
8687 * @param idxFixed The fixed allocation index to "free".
8688 */
8689static void kwSandboxResetFixedAllocation(KU32 idxFixed)
8690{
8691 BOOL fRc;
8692 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pre]");
8693 fRc = VirtualFree(g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed, MEM_DECOMMIT);
8694 kHlpAssert(fRc); K_NOREF(fRc);
8695 kwSandboxLogFixedAllocation(idxFixed, "kwSandboxResetFixedAllocation[pst]");
8696 g_aFixedVirtualAllocs[idxFixed].fInUse = K_FALSE;
8697}
8698
8699#endif /* WITH_FIXED_VIRTUAL_ALLOCS */
8700
8701
8702/** Kernel32 - VirtualAlloc - for managing cl.exe / c1[xx].dll heap with fixed
8703 * location (~78MB in 32-bit 2010 compiler). */
8704static PVOID WINAPI kwSandbox_Kernel32_VirtualAlloc(PVOID pvAddr, SIZE_T cb, DWORD fAllocType, DWORD fProt)
8705{
8706 PVOID pvMem;
8707 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8708 {
8709 KU32 idxPreAllocated = KU32_MAX;
8710
8711#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8712 /*
8713 * Look for a pre-reserved CL.exe heap allocation.
8714 */
8715 pvMem = NULL;
8716 if ( pvAddr != 0
8717 && (fAllocType & MEM_RESERVE))
8718 {
8719 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8720 kHlpAssert(!(fAllocType & ~(MEM_RESERVE | MEM_TOP_DOWN)));
8721 while (idxFixed-- > 0)
8722 if ( g_aFixedVirtualAllocs[idxFixed].uFixed == (KUPTR)pvAddr
8723 && g_aFixedVirtualAllocs[idxFixed].pvReserved)
8724 {
8725 if (g_aFixedVirtualAllocs[idxFixed].cbFixed >= cb)
8726 {
8727 if (!g_aFixedVirtualAllocs[idxFixed].fInUse)
8728 {
8729 g_aFixedVirtualAllocs[idxFixed].fInUse = K_TRUE;
8730 pvMem = pvAddr;
8731 idxPreAllocated = idxFixed;
8732 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p [pre allocated]\n",
8733 pvAddr, cb, fAllocType, fProt, pvMem));
8734 kwSandboxLogFixedAllocation(idxFixed, "kwSandbox_Kernel32_VirtualAlloc");
8735 SetLastError(NO_ERROR);
8736 break;
8737 }
8738 kwErrPrintf("VirtualAlloc: Fixed allocation at %p is already in use!\n", pvAddr);
8739 }
8740 else
8741 kwErrPrintf("VirtualAlloc: Fixed allocation at %p LB %#x not large enough: %#x\n",
8742 pvAddr, g_aFixedVirtualAllocs[idxFixed].cbFixed, cb);
8743 }
8744 }
8745 if (!pvMem)
8746#endif
8747 {
8748 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8749 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8750 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8751 if (pvAddr && pvAddr != pvMem)
8752 kwErrPrintf("VirtualAlloc %p LB %#x (%#x,%#x) failed: %p / %u\n",
8753 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError());
8754 }
8755
8756 if (pvMem)
8757 {
8758 /*
8759 * Track it.
8760 */
8761 PKWVIRTALLOC pTracker;
8762 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8763
8764 pTracker = g_Sandbox.pVirtualAllocHead;
8765 while ( pTracker
8766 && (KUPTR)pvMem - (KUPTR)pTracker->pvAlloc >= pTracker->cbAlloc)
8767 pTracker = pTracker->pNext;
8768 if (!pTracker)
8769 {
8770 DWORD dwErr = GetLastError();
8771 PKWVIRTALLOC pTracker = (PKWVIRTALLOC)kHlpAlloc(sizeof(*pTracker));
8772 if (pTracker)
8773 {
8774 pTracker->pvAlloc = pvMem;
8775 pTracker->cbAlloc = cb;
8776 pTracker->idxPreAllocated = idxPreAllocated;
8777 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8778 g_Sandbox.pVirtualAllocHead = pTracker;
8779 }
8780 SetLastError(dwErr);
8781 }
8782 }
8783 }
8784 else
8785 pvMem = VirtualAlloc(pvAddr, cb, fAllocType, fProt);
8786 KW_LOG(("VirtualAlloc: pvAddr=%p cb=%p type=%#x prot=%#x -> %p (last=%d)\n",
8787 pvAddr, cb, fAllocType, fProt, pvMem, GetLastError()));
8788 return pvMem;
8789}
8790
8791
8792/** Kernel32 - VirtualFree. */
8793static BOOL WINAPI kwSandbox_Kernel32_VirtualFree(PVOID pvAddr, SIZE_T cb, DWORD dwFreeType)
8794{
8795 BOOL fRc;
8796 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
8797 {
8798 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8799 if (dwFreeType & MEM_RELEASE)
8800 {
8801 PKWVIRTALLOC pTracker = g_Sandbox.pVirtualAllocHead;
8802 if (pTracker)
8803 {
8804 if (pTracker->pvAlloc == pvAddr)
8805 g_Sandbox.pVirtualAllocHead = pTracker->pNext;
8806 else
8807 {
8808 PKWVIRTALLOC pPrev;
8809 do
8810 {
8811 pPrev = pTracker;
8812 pTracker = pTracker->pNext;
8813 } while (pTracker && pTracker->pvAlloc != pvAddr);
8814 if (pTracker)
8815 pPrev->pNext = pTracker->pNext;
8816 }
8817 if (pTracker)
8818 {
8819#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8820 if (pTracker->idxPreAllocated != KU32_MAX)
8821 {
8822 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
8823 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> TRUE [pre allocated #%u]\n",
8824 pvAddr, cb, dwFreeType, pTracker->idxPreAllocated));
8825 kHlpFree(pTracker);
8826 return TRUE;
8827 }
8828#endif
8829
8830 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8831 if (fRc)
8832 kHlpFree(pTracker);
8833 else
8834 {
8835 pTracker->pNext = g_Sandbox.pVirtualAllocHead;
8836 g_Sandbox.pVirtualAllocHead = pTracker;
8837 }
8838 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8839 return fRc;
8840 }
8841
8842 KW_LOG(("VirtualFree: pvAddr=%p not found!\n", pvAddr));
8843 }
8844 }
8845 }
8846
8847#ifdef WITH_FIXED_VIRTUAL_ALLOCS
8848 /*
8849 * Protect our fixed allocations (this isn't just paranoia, btw.).
8850 */
8851 if (dwFreeType & MEM_RELEASE)
8852 {
8853 KU32 idxFixed = K_ELEMENTS(g_aFixedVirtualAllocs);
8854 while (idxFixed-- > 0)
8855 if (g_aFixedVirtualAllocs[idxFixed].pvReserved == pvAddr)
8856 {
8857 KW_LOG(("VirtualFree: Damn it! Don't free g_aFixedVirtualAllocs[#%u]: %p LB %#x\n",
8858 idxFixed, g_aFixedVirtualAllocs[idxFixed].pvReserved, g_aFixedVirtualAllocs[idxFixed].cbFixed));
8859 return TRUE;
8860 }
8861 }
8862#endif
8863
8864 /*
8865 * Not tracker or not actually free the virtual range.
8866 */
8867 fRc = VirtualFree(pvAddr, cb, dwFreeType);
8868 KW_LOG(("VirtualFree: pvAddr=%p cb=%p type=%#x -> %d\n", pvAddr, cb, dwFreeType, fRc));
8869 return fRc;
8870}
8871
8872
8873/** Kernel32 - HeapCreate / NtDll - RTlCreateHeap */
8874HANDLE WINAPI kwSandbox_Kernel32_HeapCreate(DWORD fOptions, SIZE_T cbInitial, SIZE_T cbMax)
8875{
8876 HANDLE hHeap;
8877 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8878
8879 hHeap = HeapCreate(fOptions, cbInitial, cbMax);
8880 if (hHeap != NULL)
8881 {
8882 DWORD dwErr = GetLastError();
8883 PKWHEAP pTracker = (PKWHEAP)kHlpAlloc(sizeof(*pTracker));
8884 if (pTracker)
8885 {
8886 pTracker->hHeap = hHeap;
8887 pTracker->pNext = g_Sandbox.pHeapHead;
8888 g_Sandbox.pHeapHead = pTracker;
8889 }
8890
8891 SetLastError(dwErr);
8892 }
8893 return hHeap;
8894
8895}
8896
8897
8898/** Kernel32 - HeapDestroy / NtDll - RTlDestroyHeap */
8899BOOL WINAPI kwSandbox_Kernel32_HeapDestroy(HANDLE hHeap)
8900{
8901 BOOL fRc = HeapDestroy(hHeap);
8902 KW_LOG(("HeapDestroy: hHeap=%p -> %d\n", hHeap, fRc));
8903 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8904 if (fRc)
8905 {
8906 PKWHEAP pTracker = g_Sandbox.pHeapHead;
8907 if (pTracker)
8908 {
8909 if (pTracker->hHeap == hHeap)
8910 g_Sandbox.pHeapHead = pTracker->pNext;
8911 else
8912 {
8913 PKWHEAP pPrev;
8914 do
8915 {
8916 pPrev = pTracker;
8917 pTracker = pTracker->pNext;
8918 } while (pTracker && pTracker->hHeap == hHeap);
8919 if (pTracker)
8920 pPrev->pNext = pTracker->pNext;
8921 }
8922 if (pTracker)
8923 kHlpFree(pTracker);
8924 else
8925 KW_LOG(("HeapDestroy: pvAddr=%p not found!\n", hHeap));
8926 }
8927 }
8928
8929 return fRc;
8930}
8931
8932
8933
8934/*
8935 *
8936 * Thread/Fiber local storage leak prevention.
8937 * Thread/Fiber local storage leak prevention.
8938 * Thread/Fiber local storage leak prevention.
8939 *
8940 * Note! The FlsAlloc/Free & TlsAlloc/Free causes problems for statically
8941 * linked VS2010 code like VBoxBs3ObjConverter.exe. One thing is that
8942 * we're leaking these indexes, but more importantely we crash during
8943 * worker exit since the callback is triggered multiple times.
8944 */
8945
8946
8947/** Kernel32 - FlsAlloc */
8948DWORD WINAPI kwSandbox_Kernel32_FlsAlloc(PFLS_CALLBACK_FUNCTION pfnCallback)
8949{
8950 DWORD idxFls = FlsAlloc(pfnCallback);
8951 KW_LOG(("FlsAlloc(%p) -> %#x\n", pfnCallback, idxFls));
8952 if (idxFls != FLS_OUT_OF_INDEXES)
8953 {
8954 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
8955 if (pTracker)
8956 {
8957 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8958 pTracker->idx = idxFls;
8959 pTracker->pNext = g_Sandbox.pFlsAllocHead;
8960 g_Sandbox.pFlsAllocHead = pTracker;
8961 }
8962 }
8963
8964 return idxFls;
8965}
8966
8967/** Kernel32 - FlsFree */
8968BOOL WINAPI kwSandbox_Kernel32_FlsFree(DWORD idxFls)
8969{
8970 BOOL fRc = FlsFree(idxFls);
8971 KW_LOG(("FlsFree(%#x) -> %d\n", idxFls, fRc));
8972 if (fRc)
8973 {
8974 PKWLOCALSTORAGE pTracker;
8975 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
8976
8977 pTracker = g_Sandbox.pFlsAllocHead;
8978 if (pTracker)
8979 {
8980 if (pTracker->idx == idxFls)
8981 g_Sandbox.pFlsAllocHead = pTracker->pNext;
8982 else
8983 {
8984 PKWLOCALSTORAGE pPrev;
8985 do
8986 {
8987 pPrev = pTracker;
8988 pTracker = pTracker->pNext;
8989 } while (pTracker && pTracker->idx != idxFls);
8990 if (pTracker)
8991 pPrev->pNext = pTracker->pNext;
8992 }
8993 if (pTracker)
8994 {
8995 pTracker->idx = FLS_OUT_OF_INDEXES;
8996 pTracker->pNext = NULL;
8997 kHlpFree(pTracker);
8998 }
8999 }
9000 }
9001 return fRc;
9002}
9003
9004
9005/** Kernel32 - TlsAlloc */
9006DWORD WINAPI kwSandbox_Kernel32_TlsAlloc(VOID)
9007{
9008 DWORD idxTls = TlsAlloc();
9009 KW_LOG(("TlsAlloc() -> %#x\n", idxTls));
9010 if (idxTls != TLS_OUT_OF_INDEXES)
9011 {
9012 PKWLOCALSTORAGE pTracker = (PKWLOCALSTORAGE)kHlpAlloc(sizeof(*pTracker));
9013 if (pTracker)
9014 {
9015 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9016 pTracker->idx = idxTls;
9017 pTracker->pNext = g_Sandbox.pTlsAllocHead;
9018 g_Sandbox.pTlsAllocHead = pTracker;
9019 }
9020 }
9021
9022 return idxTls;
9023}
9024
9025/** Kernel32 - TlsFree */
9026BOOL WINAPI kwSandbox_Kernel32_TlsFree(DWORD idxTls)
9027{
9028 BOOL fRc = TlsFree(idxTls);
9029 KW_LOG(("TlsFree(%#x) -> %d\n", idxTls, fRc));
9030 if (fRc)
9031 {
9032 PKWLOCALSTORAGE pTracker;
9033 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9034
9035 pTracker = g_Sandbox.pTlsAllocHead;
9036 if (pTracker)
9037 {
9038 if (pTracker->idx == idxTls)
9039 g_Sandbox.pTlsAllocHead = pTracker->pNext;
9040 else
9041 {
9042 PKWLOCALSTORAGE pPrev;
9043 do
9044 {
9045 pPrev = pTracker;
9046 pTracker = pTracker->pNext;
9047 } while (pTracker && pTracker->idx != idxTls);
9048 if (pTracker)
9049 pPrev->pNext = pTracker->pNext;
9050 }
9051 if (pTracker)
9052 {
9053 pTracker->idx = TLS_OUT_OF_INDEXES;
9054 pTracker->pNext = NULL;
9055 kHlpFree(pTracker);
9056 }
9057 }
9058 }
9059 return fRc;
9060}
9061
9062
9063
9064/*
9065 *
9066 * Header file hashing.
9067 * Header file hashing.
9068 * Header file hashing.
9069 *
9070 * c1.dll / c1XX.dll hashes the input files. The Visual C++ 2010 profiler
9071 * indicated that ~12% of the time was spent doing MD5 caluclation when
9072 * rebuiling openssl. The hashing it done right after reading the source
9073 * via ReadFile, same buffers and sizes.
9074 */
9075
9076#ifdef WITH_HASH_MD5_CACHE
9077
9078/** AdvApi32 - CryptCreateHash */
9079static BOOL WINAPI kwSandbox_Advapi32_CryptCreateHash(HCRYPTPROV hProv, ALG_ID idAlg, HCRYPTKEY hKey, DWORD dwFlags,
9080 HCRYPTHASH *phHash)
9081{
9082 BOOL fRc;
9083
9084 /*
9085 * Only do this for cl.exe when it request normal MD5.
9086 */
9087 if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_CL)
9088 {
9089 if (idAlg == CALG_MD5)
9090 {
9091 if (hKey == 0)
9092 {
9093 if (dwFlags == 0)
9094 {
9095 PKWHASHMD5 pHash = (PKWHASHMD5)kHlpAllocZ(sizeof(*pHash));
9096 if (pHash)
9097 {
9098 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9099 pHash->uMagic = KWHASHMD5_MAGIC;
9100 pHash->cbHashed = 0;
9101 pHash->fGoneBad = K_FALSE;
9102 pHash->fFallbackMode = K_FALSE;
9103 pHash->fFinal = K_FALSE;
9104
9105 /* link it. */
9106 pHash->pNext = g_Sandbox.pHashHead;
9107 g_Sandbox.pHashHead = pHash;
9108
9109 *phHash = (KUPTR)pHash;
9110 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=CALG_MD5, 0, 0, *phHash=%p) -> %d [cached]\n",
9111 hProv, *phHash, TRUE));
9112 return TRUE;
9113 }
9114
9115 kwErrPrintf("CryptCreateHash: out of memory!\n");
9116 }
9117 else
9118 kwErrPrintf("CryptCreateHash: dwFlags=%p is not supported with CALG_MD5\n", hKey);
9119 }
9120 else
9121 kwErrPrintf("CryptCreateHash: hKey=%p is not supported with CALG_MD5\n", hKey);
9122 }
9123 else
9124 kwErrPrintf("CryptCreateHash: idAlg=%#x is not supported\n", idAlg);
9125 }
9126
9127 /*
9128 * Fallback.
9129 */
9130 fRc = CryptCreateHash(hProv, idAlg, hKey, dwFlags, phHash);
9131 KWCRYPT_LOG(("CryptCreateHash(hProv=%p, idAlg=%#x (%d), hKey=%p, dwFlags=%#x, *phHash=%p) -> %d\n",
9132 hProv, idAlg, idAlg, hKey, dwFlags, *phHash, fRc));
9133 return fRc;
9134}
9135
9136
9137/** AdvApi32 - CryptHashData */
9138static BOOL WINAPI kwSandbox_Advapi32_CryptHashData(HCRYPTHASH hHash, CONST BYTE *pbData, DWORD cbData, DWORD dwFlags)
9139{
9140 BOOL fRc;
9141 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9142 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9143 while (pHash && (KUPTR)pHash != hHash)
9144 pHash = pHash->pNext;
9145 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p, pbData=%p, cbData=%#x, dwFlags=%#x)\n",
9146 hHash, pHash, pbData, cbData, dwFlags));
9147 if (pHash)
9148 {
9149 /*
9150 * Validate the state.
9151 */
9152 if ( pHash->uMagic == KWHASHMD5_MAGIC
9153 && !pHash->fFinal)
9154 {
9155 if (!pHash->fFallbackMode)
9156 {
9157 /*
9158 * Does this match the previous ReadFile call to a cached file?
9159 * If it doesn't, try falling back.
9160 */
9161 if ( g_Sandbox.LastHashRead.cbRead == cbData
9162 && g_Sandbox.LastHashRead.pvRead == (void *)pbData)
9163 {
9164 PKFSWCACHEDFILE pCachedFile = g_Sandbox.LastHashRead.pCachedFile;
9165 if ( pCachedFile
9166 && kHlpMemComp(pbData, &pCachedFile->pbCached[g_Sandbox.LastHashRead.offRead], K_MIN(cbData, 64)) == 0)
9167 {
9168
9169 if (g_Sandbox.LastHashRead.offRead == pHash->cbHashed)
9170 {
9171 if ( pHash->pCachedFile == NULL
9172 && pHash->cbHashed == 0)
9173 pHash->pCachedFile = pCachedFile;
9174 if (pHash->pCachedFile == pCachedFile)
9175 {
9176 pHash->cbHashed += cbData;
9177 g_Sandbox.LastHashRead.pCachedFile = NULL;
9178 g_Sandbox.LastHashRead.pvRead = NULL;
9179 g_Sandbox.LastHashRead.cbRead = 0;
9180 g_Sandbox.LastHashRead.offRead = 0;
9181 KWCRYPT_LOG(("CryptHashData(hHash=%p/%p/%s, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [cached]\n",
9182 hHash, pCachedFile, pCachedFile->szPath, pbData, cbData, dwFlags));
9183 return TRUE;
9184 }
9185
9186 /* Note! it's possible to fall back here too, if necessary. */
9187 kwErrPrintf("CryptHashData: Expected pCachedFile=%p, last read was made to %p!!\n",
9188 pHash->pCachedFile, g_Sandbox.LastHashRead.pCachedFile);
9189 }
9190 else
9191 kwErrPrintf("CryptHashData: Expected last read at %#x, instead it was made at %#x\n",
9192 pHash->cbHashed, g_Sandbox.LastHashRead.offRead);
9193 }
9194 else if (!pCachedFile)
9195 kwErrPrintf("CryptHashData: Last pCachedFile is NULL when buffer address and size matches!\n");
9196 else
9197 kwErrPrintf("CryptHashData: First 64 bytes of the buffer doesn't match the cache.\n");
9198 }
9199 else if (g_Sandbox.LastHashRead.cbRead != 0 && pHash->cbHashed != 0)
9200 kwErrPrintf("CryptHashData: Expected cbRead=%#x and pbData=%p, got %#x and %p instead\n",
9201 g_Sandbox.LastHashRead.cbRead, g_Sandbox.LastHashRead.pvRead, cbData, pbData);
9202 if (pHash->cbHashed == 0)
9203 pHash->fFallbackMode = K_TRUE;
9204 if (pHash->fFallbackMode)
9205 {
9206 /* Initiate fallback mode (file that we don't normally cache, like .c/.cpp). */
9207 pHash->fFallbackMode = K_TRUE;
9208 MD5Init(&pHash->Md5Ctx);
9209 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9210 pHash->cbHashed = cbData;
9211 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback!]\n",
9212 hHash, pbData, cbData, dwFlags));
9213 return TRUE;
9214 }
9215 pHash->fGoneBad = K_TRUE;
9216 SetLastError(ERROR_INVALID_PARAMETER);
9217 fRc = FALSE;
9218 }
9219 else
9220 {
9221 /* fallback. */
9222 MD5Update(&pHash->Md5Ctx, pbData, cbData);
9223 pHash->cbHashed += cbData;
9224 fRc = TRUE;
9225 KWCRYPT_LOG(("CryptHashData(hHash=%p/fallback, pbData=%p, cbData=%#x, dwFlags=%#x) -> 1 [fallback]\n",
9226 hHash, pbData, cbData, dwFlags));
9227 }
9228 }
9229 /*
9230 * Bad handle state.
9231 */
9232 else
9233 {
9234 if (pHash->uMagic != KWHASHMD5_MAGIC)
9235 kwErrPrintf("CryptHashData: Invalid cached hash handle!!\n");
9236 else
9237 kwErrPrintf("CryptHashData: Hash is already finalized!!\n");
9238 SetLastError(NTE_BAD_HASH);
9239 fRc = FALSE;
9240 }
9241 }
9242 else
9243 {
9244
9245 fRc = CryptHashData(hHash, pbData, cbData, dwFlags);
9246 KWCRYPT_LOG(("CryptHashData(hHash=%p, pbData=%p, cbData=%#x, dwFlags=%#x) -> %d\n", hHash, pbData, cbData, dwFlags, fRc));
9247 }
9248 return fRc;
9249}
9250
9251
9252/** AdvApi32 - CryptGetHashParam */
9253static BOOL WINAPI kwSandbox_Advapi32_CryptGetHashParam(HCRYPTHASH hHash, DWORD dwParam,
9254 BYTE *pbData, DWORD *pcbData, DWORD dwFlags)
9255{
9256 BOOL fRc;
9257 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9258 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9259 while (pHash && (KUPTR)pHash != hHash)
9260 pHash = pHash->pNext;
9261 if (pHash)
9262 {
9263 if (pHash->uMagic == KWHASHMD5_MAGIC)
9264 {
9265 if (dwFlags == 0)
9266 {
9267 DWORD cbRet;
9268 void *pvRet;
9269 union
9270 {
9271 DWORD dw;
9272 } uBuf;
9273
9274 switch (dwParam)
9275 {
9276 case HP_HASHVAL:
9277 {
9278 /* Check the hash progress. */
9279 PKFSWCACHEDFILE pCachedFile = pHash->pCachedFile;
9280 if (pCachedFile)
9281 {
9282 if ( pCachedFile->cbCached == pHash->cbHashed
9283 && !pHash->fGoneBad)
9284 {
9285 if (pCachedFile->fValidMd5)
9286 KWCRYPT_LOG(("Already calculated hash for %p/%s! [hit]\n", pCachedFile, pCachedFile->szPath));
9287 else
9288 {
9289 MD5Init(&pHash->Md5Ctx);
9290 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pCachedFile->cbCached);
9291 MD5Final(pCachedFile->abMd5Digest, &pHash->Md5Ctx);
9292 pCachedFile->fValidMd5 = K_TRUE;
9293 KWCRYPT_LOG(("Calculated hash for %p/%s.\n", pCachedFile, pCachedFile->szPath));
9294 }
9295 pvRet = pCachedFile->abMd5Digest;
9296 }
9297 else
9298 {
9299 /* This actually happens (iprt/string.h + common/alloc/alloc.cpp), at least
9300 from what I can tell, so just deal with it. */
9301 KWCRYPT_LOG(("CryptGetHashParam/HP_HASHVAL: Not at end of cached file! cbCached=%#x cbHashed=%#x fGoneBad=%d (%p/%p/%s)\n",
9302 pHash->pCachedFile->cbCached, pHash->cbHashed, pHash->fGoneBad,
9303 pHash, pCachedFile, pCachedFile->szPath));
9304 pHash->fFallbackMode = K_TRUE;
9305 pHash->pCachedFile = NULL;
9306 MD5Init(&pHash->Md5Ctx);
9307 MD5Update(&pHash->Md5Ctx, pCachedFile->pbCached, pHash->cbHashed);
9308 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9309 pvRet = pHash->abDigest;
9310 }
9311 pHash->fFinal = K_TRUE;
9312 cbRet = 16;
9313 break;
9314 }
9315 else if (pHash->fFallbackMode)
9316 {
9317 if (!pHash->fFinal)
9318 {
9319 pHash->fFinal = K_TRUE;
9320 MD5Final(pHash->abDigest, &pHash->Md5Ctx);
9321 }
9322 pvRet = pHash->abDigest;
9323 cbRet = 16;
9324 break;
9325 }
9326 else
9327 {
9328 kwErrPrintf("CryptGetHashParam/HP_HASHVAL: pCachedFile is NULL!!\n");
9329 SetLastError(ERROR_INVALID_SERVER_STATE);
9330 }
9331 return FALSE;
9332 }
9333
9334 case HP_HASHSIZE:
9335 uBuf.dw = 16;
9336 pvRet = &uBuf;
9337 cbRet = sizeof(DWORD);
9338 break;
9339
9340 case HP_ALGID:
9341 uBuf.dw = CALG_MD5;
9342 pvRet = &uBuf;
9343 cbRet = sizeof(DWORD);
9344 break;
9345
9346 default:
9347 kwErrPrintf("CryptGetHashParam: Unknown dwParam=%#x\n", dwParam);
9348 SetLastError(NTE_BAD_TYPE);
9349 return FALSE;
9350 }
9351
9352 /*
9353 * Copy out cbRet from pvRet.
9354 */
9355 if (pbData)
9356 {
9357 if (*pcbData >= cbRet)
9358 {
9359 *pcbData = cbRet;
9360 kHlpMemCopy(pbData, pvRet, cbRet);
9361 if (cbRet == 4)
9362 KWCRYPT_LOG(("CryptGetHashParam/%#x/%p/%p: TRUE, cbRet=%#x data=%#x [cached]\n",
9363 dwParam, pHash, pHash->pCachedFile, cbRet, (DWORD *)pbData));
9364 else if (cbRet == 16)
9365 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",
9366 dwParam, pHash, pHash->pCachedFile, cbRet,
9367 pbData[0], pbData[1], pbData[2], pbData[3],
9368 pbData[4], pbData[5], pbData[6], pbData[7],
9369 pbData[8], pbData[9], pbData[10], pbData[11],
9370 pbData[12], pbData[13], pbData[14], pbData[15]));
9371 else
9372 KWCRYPT_LOG(("CryptGetHashParam/%#x%/p%/%p: TRUE, cbRet=%#x [cached]\n",
9373 dwParam, pHash, pHash->pCachedFile, cbRet));
9374 return TRUE;
9375 }
9376
9377 kHlpMemCopy(pbData, pvRet, *pcbData);
9378 }
9379 SetLastError(ERROR_MORE_DATA);
9380 *pcbData = cbRet;
9381 KWCRYPT_LOG(("CryptGetHashParam/%#x: ERROR_MORE_DATA\n"));
9382 }
9383 else
9384 {
9385 kwErrPrintf("CryptGetHashParam: dwFlags is not zero: %#x!\n", dwFlags);
9386 SetLastError(NTE_BAD_FLAGS);
9387 }
9388 }
9389 else
9390 {
9391 kwErrPrintf("CryptGetHashParam: Invalid cached hash handle!!\n");
9392 SetLastError(NTE_BAD_HASH);
9393 }
9394 fRc = FALSE;
9395 }
9396 /*
9397 * Regular handle.
9398 */
9399 else
9400 {
9401 fRc = CryptGetHashParam(hHash, dwParam, pbData, pcbData, dwFlags);
9402 KWCRYPT_LOG(("CryptGetHashParam(hHash=%p, dwParam=%#x (%d), pbData=%p, *pcbData=%#x, dwFlags=%#x) -> %d\n",
9403 hHash, dwParam, pbData, *pcbData, dwFlags, fRc));
9404 }
9405
9406 return fRc;
9407}
9408
9409
9410/** AdvApi32 - CryptDestroyHash */
9411static BOOL WINAPI kwSandbox_Advapi32_CryptDestroyHash(HCRYPTHASH hHash)
9412{
9413 BOOL fRc;
9414 PKWHASHMD5 pPrev = NULL;
9415 PKWHASHMD5 pHash = g_Sandbox.pHashHead;
9416 kHlpAssert(GetCurrentThreadId() == g_Sandbox.idMainThread);
9417 while (pHash && (KUPTR)pHash != hHash)
9418 {
9419 pPrev = pHash;
9420 pHash = pHash->pNext;
9421 }
9422 if (pHash)
9423 {
9424 if (pHash->uMagic == KWHASHMD5_MAGIC)
9425 {
9426 pHash->uMagic = 0;
9427 if (!pPrev)
9428 g_Sandbox.pHashHead = pHash->pNext;
9429 else
9430 pPrev->pNext = pHash->pNext;
9431 kHlpFree(pHash);
9432 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> 1 [cached]\n", hHash));
9433 fRc = TRUE;
9434 }
9435 else
9436 {
9437 kwErrPrintf("CryptDestroyHash: Invalid cached hash handle!!\n");
9438 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> FALSE! [cached]\n", hHash));
9439 SetLastError(ERROR_INVALID_HANDLE);
9440 fRc = FALSE;
9441 }
9442 }
9443 /*
9444 * Regular handle.
9445 */
9446 else
9447 {
9448 fRc = CryptDestroyHash(hHash);
9449 KWCRYPT_LOG(("CryptDestroyHash(hHash=%p) -> %d\n", hHash, fRc));
9450 }
9451 return fRc;
9452}
9453
9454#endif /* WITH_HASH_MD5_CACHE */
9455
9456
9457/*
9458 *
9459 * Reuse crypt context.
9460 * Reuse crypt context.
9461 * Reuse crypt context.
9462 *
9463 *
9464 * This saves a little bit of time and registry accesses each time CL, C1 or C1XX runs.
9465 *
9466 */
9467
9468#ifdef WITH_CRYPT_CTX_REUSE
9469
9470/** AdvApi32 - CryptAcquireContextW. */
9471static BOOL WINAPI kwSandbox_Advapi32_CryptAcquireContextW(HCRYPTPROV *phProv, LPCWSTR pwszContainer, LPCWSTR pwszProvider,
9472 DWORD dwProvType, DWORD dwFlags)
9473{
9474 BOOL fRet;
9475
9476 /*
9477 * Lookup reusable context based on the input.
9478 */
9479 KSIZE const cwcContainer = pwszContainer ? kwUtf16Len(pwszContainer) : 0;
9480 KSIZE const cwcProvider = pwszProvider ? kwUtf16Len(pwszProvider) : 0;
9481 KU32 iCtx = g_Sandbox.cCryptCtxs;
9482 while (iCtx-- > 0)
9483 {
9484 if ( g_Sandbox.aCryptCtxs[iCtx].cwcContainer == cwcContainer
9485 && g_Sandbox.aCryptCtxs[iCtx].cwcProvider == cwcProvider
9486 && g_Sandbox.aCryptCtxs[iCtx].dwProvType == dwProvType
9487 && g_Sandbox.aCryptCtxs[iCtx].dwFlags == dwFlags
9488 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszContainer, pwszContainer, cwcContainer * sizeof(wchar_t)) == 0
9489 && kHlpMemComp(g_Sandbox.aCryptCtxs[iCtx].pwszProvider, pwszProvider, cwcProvider * sizeof(wchar_t)) == 0)
9490 {
9491 if (CryptContextAddRef(g_Sandbox.aCryptCtxs[iCtx].hProv, NULL, 0))
9492 {
9493 *phProv = g_Sandbox.aCryptCtxs[iCtx].hProv;
9494 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [reused]\n",
9495 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9496 return TRUE;
9497 }
9498 }
9499 }
9500
9501 /*
9502 * Create it and enter it into the reused array if possible.
9503 */
9504 fRet = CryptAcquireContextW(phProv, pwszContainer, pwszProvider, dwProvType, dwFlags);
9505 if (fRet)
9506 {
9507 iCtx = g_Sandbox.cCryptCtxs;
9508 if (iCtx < K_ELEMENTS(g_Sandbox.aCryptCtxs))
9509 {
9510 /* Try duplicate the input strings. */
9511 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = kHlpDup(pwszContainer ? pwszContainer : L"",
9512 (cwcContainer + 1) * sizeof(wchar_t));
9513 if (g_Sandbox.aCryptCtxs[iCtx].pwszContainer)
9514 {
9515 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = kHlpDup(pwszProvider ? pwszProvider : L"",
9516 (cwcProvider + 1) * sizeof(wchar_t));
9517 if (g_Sandbox.aCryptCtxs[iCtx].pwszProvider)
9518 {
9519 /* Add a couple of references just to be on the safe side and all that. */
9520 HCRYPTPROV hProv = *phProv;
9521 if (CryptContextAddRef(hProv, NULL, 0))
9522 {
9523 if (CryptContextAddRef(hProv, NULL, 0))
9524 {
9525 /* Okay, finish the entry and return success */
9526 g_Sandbox.aCryptCtxs[iCtx].hProv = hProv;
9527 g_Sandbox.aCryptCtxs[iCtx].dwProvType = dwProvType;
9528 g_Sandbox.aCryptCtxs[iCtx].dwFlags = dwFlags;
9529 g_Sandbox.cCryptCtxs = iCtx + 1;
9530
9531 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> TRUE, %p [new]\n",
9532 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9533 return TRUE;
9534 }
9535 CryptReleaseContext(hProv, 0);
9536 }
9537 KWCRYPT_LOG(("CryptAcquireContextW: CryptContextAddRef failed!\n"));
9538
9539 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszProvider);
9540 g_Sandbox.aCryptCtxs[iCtx].pwszProvider = NULL;
9541 }
9542 kHlpFree(g_Sandbox.aCryptCtxs[iCtx].pwszContainer);
9543 g_Sandbox.aCryptCtxs[iCtx].pwszContainer = NULL;
9544 }
9545 }
9546 else
9547 KWCRYPT_LOG(("CryptAcquireContextW: Too many crypt contexts to keep and reuse!\n"));
9548 }
9549
9550 KWCRYPT_LOG(("CryptAcquireContextW(,%ls, %ls, %#x, %#x) -> %d, %p\n",
9551 pwszContainer, pwszProvider, dwProvType, dwFlags, *phProv));
9552 return fRet;
9553}
9554
9555
9556/** AdvApi32 - CryptReleaseContext */
9557static BOOL WINAPI kwSandbox_Advapi32_CryptReleaseContext(HCRYPTPROV hProv, DWORD dwFlags)
9558{
9559 BOOL fRet = CryptReleaseContext(hProv, dwFlags);
9560 KWCRYPT_LOG(("CryptReleaseContext(%p,%#x) -> %d\n", hProv, dwFlags, fRet));
9561 return fRet;
9562}
9563
9564
9565/** AdvApi32 - CryptContextAddRef */
9566static BOOL WINAPI kwSandbox_Advapi32_CryptContextAddRef(HCRYPTPROV hProv, DWORD *pdwReserved, DWORD dwFlags)
9567{
9568 BOOL fRet = CryptContextAddRef(hProv, pdwReserved, dwFlags);
9569 KWCRYPT_LOG(("CryptContextAddRef(%p,%p,%#x) -> %d\n", hProv, pdwReserved, dwFlags, fRet));
9570 return fRet;
9571}
9572
9573#endif /* WITH_CRYPT_CTX_REUSE */
9574
9575/*
9576 *
9577 * Structured exception handling.
9578 * Structured exception handling.
9579 * Structured exception handling.
9580 *
9581 */
9582#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9583
9584# define EH_NONCONTINUABLE KU32_C(0x00000001)
9585# define EH_UNWINDING KU32_C(0x00000002)
9586# define EH_EXIT_UNWIND KU32_C(0x00000004)
9587# define EH_STACK_INVALID KU32_C(0x00000008)
9588# define EH_NESTED_CALL KU32_C(0x00000010)
9589
9590typedef KU32 (__cdecl * volatile PFNXCPTHANDLER)(PEXCEPTION_RECORD, struct _EXCEPTION_REGISTRATION_RECORD*, PCONTEXT,
9591 struct _EXCEPTION_REGISTRATION_RECORD * volatile *);
9592typedef struct _EXCEPTION_REGISTRATION_RECORD
9593{
9594 struct _EXCEPTION_REGISTRATION_RECORD * volatile pPrevRegRec;
9595 PFNXCPTHANDLER pfnXcptHandler;
9596};
9597
9598
9599/**
9600 * Calls @a pfnHandler.
9601 */
9602static KU32 kwSandboxXcptCallHandler(PEXCEPTION_RECORD pXcptRec, struct _EXCEPTION_REGISTRATION_RECORD *pRegRec,
9603 PCONTEXT pXcptCtx, struct _EXCEPTION_REGISTRATION_RECORD * volatile * ppRegRec,
9604 PFNXCPTHANDLER pfnHandler)
9605{
9606# if 1
9607 /* This is a more robust version that isn't subject to calling
9608 convension cleanup disputes and such. */
9609 KU32 uSavedEdi;
9610 KU32 uSavedEsi;
9611 KU32 uSavedEbx;
9612 KU32 rcHandler;
9613
9614 __asm
9615 {
9616 mov [uSavedEdi], edi
9617 mov [uSavedEsi], esi
9618 mov [uSavedEbx], ebx
9619 mov esi, esp
9620 mov edi, esp
9621 mov edi, [pXcptRec]
9622 mov edx, [pRegRec]
9623 mov eax, [pXcptCtx]
9624 mov ebx, [ppRegRec]
9625 mov ecx, [pfnHandler]
9626 sub esp, 16
9627 and esp, 0fffffff0h
9628 mov [esp ], edi
9629 mov [esp + 4], edx
9630 mov [esp + 8], eax
9631 mov [esp + 12], ebx
9632 mov edi, esi
9633 call ecx
9634 mov esp, esi
9635 cmp esp, edi
9636 je stack_ok
9637 int 3
9638 stack_ok:
9639 mov edi, [uSavedEdi]
9640 mov esi, [uSavedEsi]
9641 mov ebx, [uSavedEbx]
9642 mov [rcHandler], eax
9643 }
9644 return rcHandler;
9645# else
9646 return pfnHandler(pXcptRec, pRegRec, pXctpCtx, ppRegRec);
9647# endif
9648}
9649
9650
9651/**
9652 * Vectored exception handler that emulates x86 chained exception handler.
9653 *
9654 * This is necessary because the RtlIsValidHandler check fails for self loaded
9655 * code and prevents cl.exe from working. (On AMD64 we can register function
9656 * tables, but on X86 cooking your own handling seems to be the only viabke
9657 * alternative.)
9658 *
9659 * @returns EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION.
9660 * @param pXcptPtrs The exception details.
9661 */
9662static LONG CALLBACK kwSandboxVecXcptEmulateChained(PEXCEPTION_POINTERS pXcptPtrs)
9663{
9664 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9665 KW_LOG(("kwSandboxVecXcptEmulateChained: %#x\n", pXcptPtrs->ExceptionRecord->ExceptionCode));
9666 if (g_Sandbox.fRunning)
9667 {
9668 HANDLE const hCurProc = GetCurrentProcess();
9669 PEXCEPTION_RECORD pXcptRec = pXcptPtrs->ExceptionRecord;
9670 PCONTEXT pXcptCtx = pXcptPtrs->ContextRecord;
9671 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9672 while (((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0 && pRegRec != NULL)
9673 {
9674 /* Read the exception record in a safe manner. */
9675 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9676 DWORD cbActuallyRead = 0;
9677 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9678 && cbActuallyRead == sizeof(RegRec))
9679 {
9680 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9681 KU32 rcHandler;
9682 KW_LOG(("kwSandboxVecXcptEmulateChained: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9683 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9684 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9685 KW_LOG(("kwSandboxVecXcptEmulateChained: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9686 if (rcHandler == ExceptionContinueExecution)
9687 {
9688 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE));
9689 KW_LOG(("kwSandboxVecXcptEmulateChained: returning EXCEPTION_CONTINUE_EXECUTION!\n"));
9690 return EXCEPTION_CONTINUE_EXECUTION;
9691 }
9692
9693 if (rcHandler == ExceptionContinueSearch)
9694 kHlpAssert(!(pXcptPtrs->ExceptionRecord->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9695 else if (rcHandler == ExceptionNestedException)
9696 kHlpAssertMsgFailed(("Nested exceptions.\n"));
9697 else
9698 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9699 }
9700 else
9701 {
9702 KW_LOG(("kwSandboxVecXcptEmulateChained: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9703 break;
9704 }
9705
9706 /*
9707 * Next.
9708 */
9709 pRegRec = RegRec.pPrevRegRec;
9710 }
9711 }
9712 return EXCEPTION_CONTINUE_SEARCH;
9713}
9714
9715
9716/** NtDll,Kernel32 - RtlUnwind */
9717static VOID WINAPI kwSandbox_ntdll_RtlUnwind(struct _EXCEPTION_REGISTRATION_RECORD *pStopXcptRec, PVOID pvTargetIp,
9718 PEXCEPTION_RECORD pXcptRec, PVOID pvReturnValue)
9719{
9720 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
9721 KW_LOG(("kwSandbox_ntdll_RtlUnwind: pStopXcptRec=%p pvTargetIp=%p pXctpRec=%p pvReturnValue=%p%s\n",
9722 pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue, g_Sandbox.fRunning ? "" : " [sandbox not running]"));
9723 if (g_Sandbox.fRunning)
9724 {
9725 HANDLE const hCurProc = GetCurrentProcess();
9726 PCONTEXT pXcptCtx = NULL;
9727 struct _EXCEPTION_REGISTRATION_RECORD * pRegRec = pTib->ExceptionList;
9728
9729 /*
9730 * Update / create an exception record.
9731 */
9732 if (pXcptRec)
9733 pXcptRec->ExceptionFlags |= EH_UNWINDING;
9734 else
9735 {
9736 pXcptRec = (PEXCEPTION_RECORD)alloca(sizeof(*pXcptRec));
9737 kHlpMemSet(pXcptRec, 0, sizeof(*pXcptRec));
9738 pXcptRec->ExceptionCode = STATUS_UNWIND;
9739 pXcptRec->ExceptionFlags = EH_UNWINDING;
9740 }
9741 if (!pStopXcptRec)
9742 pXcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
9743
9744 /*
9745 * Walk the chain till we find pStopXctpRec.
9746 */
9747 while ( ((KUPTR)pRegRec & (sizeof(void *) - 3)) == 0
9748 && pRegRec != NULL
9749 && pRegRec != pStopXcptRec)
9750 {
9751 /* Read the exception record in a safe manner. */
9752 struct _EXCEPTION_REGISTRATION_RECORD RegRec;
9753 DWORD cbActuallyRead = 0;
9754 if ( ReadProcessMemory(hCurProc, pRegRec, &RegRec, sizeof(RegRec), &cbActuallyRead)
9755 && cbActuallyRead == sizeof(RegRec))
9756 {
9757 struct _EXCEPTION_REGISTRATION_RECORD * volatile pDispRegRec = NULL;
9758 KU32 rcHandler;
9759 KW_LOG(("kwSandbox_ntdll_RtlUnwind: calling %p, pRegRec=%p, pPrevRegRec=%p\n",
9760 RegRec.pfnXcptHandler, pRegRec, RegRec.pPrevRegRec));
9761 rcHandler = kwSandboxXcptCallHandler(pXcptRec, pRegRec, pXcptCtx, &pDispRegRec, RegRec.pfnXcptHandler);
9762 KW_LOG(("kwSandbox_ntdll_RtlUnwind: rcHandler=%#x pDispRegRec=%p\n", rcHandler, pDispRegRec));
9763
9764 if (rcHandler == ExceptionContinueSearch)
9765 kHlpAssert(!(pXcptRec->ExceptionFlags & 8 /*EXCEPTION_STACK_INVALID*/));
9766 else if (rcHandler == ExceptionCollidedUnwind)
9767 kHlpAssertMsgFailed(("Implement collided unwind!\n"));
9768 else
9769 kHlpAssertMsgFailed(("Invalid return %#x (%d).\n", rcHandler, rcHandler));
9770 }
9771 else
9772 {
9773 KW_LOG(("kwSandbox_ntdll_RtlUnwind: Bad xcpt chain entry at %p! Stopping search.\n", pRegRec));
9774 break;
9775 }
9776
9777 /*
9778 * Pop next.
9779 */
9780 pTib->ExceptionList = RegRec.pPrevRegRec;
9781 pRegRec = RegRec.pPrevRegRec;
9782 }
9783 return;
9784 }
9785
9786 RtlUnwind(pStopXcptRec, pvTargetIp, pXcptRec, pvReturnValue);
9787}
9788
9789#endif /* WINDOWS + X86 */
9790
9791
9792/*
9793 *
9794 * Misc function only intercepted while debugging.
9795 * Misc function only intercepted while debugging.
9796 * Misc function only intercepted while debugging.
9797 *
9798 */
9799
9800#ifndef NDEBUG
9801
9802/** CRT - memcpy */
9803static void * __cdecl kwSandbox_msvcrt_memcpy(void *pvDst, void const *pvSrc, size_t cb)
9804{
9805 KU8 const *pbSrc = (KU8 const *)pvSrc;
9806 KU8 *pbDst = (KU8 *)pvDst;
9807 KSIZE cbLeft = cb;
9808 while (cbLeft-- > 0)
9809 *pbDst++ = *pbSrc++;
9810 return pvDst;
9811}
9812
9813
9814/** CRT - memset */
9815static void * __cdecl kwSandbox_msvcrt_memset(void *pvDst, int bFiller, size_t cb)
9816{
9817 KU8 *pbDst = (KU8 *)pvDst;
9818 KSIZE cbLeft = cb;
9819 while (cbLeft-- > 0)
9820 *pbDst++ = bFiller;
9821 return pvDst;
9822}
9823
9824#endif /* NDEBUG */
9825
9826
9827
9828/**
9829 * Functions that needs replacing for sandboxed execution.
9830 */
9831KWREPLACEMENTFUNCTION const g_aSandboxReplacements[] =
9832{
9833 /*
9834 * Kernel32.dll and friends.
9835 */
9836 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
9837 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
9838
9839 { TUPLE("LoadLibraryA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryA },
9840 { TUPLE("LoadLibraryW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryW },
9841 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExA },
9842 { TUPLE("LoadLibraryExW"), NULL, (KUPTR)kwSandbox_Kernel32_LoadLibraryExW },
9843 { TUPLE("FreeLibrary"), NULL, (KUPTR)kwSandbox_Kernel32_FreeLibrary },
9844 { TUPLE("GetModuleHandleA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleA },
9845 { TUPLE("GetModuleHandleW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleHandleW },
9846 { TUPLE("GetProcAddress"), NULL, (KUPTR)kwSandbox_Kernel32_GetProcAddress },
9847 { TUPLE("GetModuleFileNameA"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameA },
9848 { TUPLE("GetModuleFileNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetModuleFileNameW },
9849 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
9850
9851 { TUPLE("GetCommandLineA"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineA },
9852 { TUPLE("GetCommandLineW"), NULL, (KUPTR)kwSandbox_Kernel32_GetCommandLineW },
9853 { TUPLE("GetStartupInfoA"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoA },
9854 { TUPLE("GetStartupInfoW"), NULL, (KUPTR)kwSandbox_Kernel32_GetStartupInfoW },
9855
9856 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
9857
9858 { TUPLE("GetEnvironmentStrings"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStrings },
9859 { TUPLE("GetEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsA },
9860 { TUPLE("GetEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentStringsW },
9861 { TUPLE("FreeEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsA },
9862 { TUPLE("FreeEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_FreeEnvironmentStringsW },
9863 { TUPLE("GetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableA },
9864 { TUPLE("GetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_GetEnvironmentVariableW },
9865 { TUPLE("SetEnvironmentVariableA"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableA },
9866 { TUPLE("SetEnvironmentVariableW"), NULL, (KUPTR)kwSandbox_Kernel32_SetEnvironmentVariableW },
9867 { TUPLE("ExpandEnvironmentStringsA"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsA },
9868 { TUPLE("ExpandEnvironmentStringsW"), NULL, (KUPTR)kwSandbox_Kernel32_ExpandEnvironmentStringsW },
9869
9870 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
9871 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
9872 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
9873 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
9874#ifdef WITH_TEMP_MEMORY_FILES
9875 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
9876 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
9877 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
9878 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
9879 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
9880 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
9881 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
9882 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
9883 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
9884 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
9885#endif
9886 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
9887 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
9888 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
9889 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
9890 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
9891 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
9892 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
9893#ifdef WITH_TEMP_MEMORY_FILES
9894 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
9895#endif
9896
9897 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
9898 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
9899
9900 { TUPLE("VirtualAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualAlloc },
9901 { TUPLE("VirtualFree"), NULL, (KUPTR)kwSandbox_Kernel32_VirtualFree },
9902
9903 { TUPLE("HeapCreate"), NULL, (KUPTR)kwSandbox_Kernel32_HeapCreate, K_TRUE /*fOnlyExe*/ },
9904 { TUPLE("HeapDestroy"), NULL, (KUPTR)kwSandbox_Kernel32_HeapDestroy, K_TRUE /*fOnlyExe*/ },
9905
9906 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
9907 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
9908 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
9909 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
9910
9911 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
9912
9913#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
9914 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
9915#endif
9916
9917#ifdef WITH_HASH_MD5_CACHE
9918 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
9919 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
9920 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
9921 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
9922#endif
9923
9924#ifdef WITH_CRYPT_CTX_REUSE
9925 { TUPLE("CryptAcquireContextW"), NULL, (KUPTR)kwSandbox_Advapi32_CryptAcquireContextW },
9926 { TUPLE("CryptReleaseContext"), NULL, (KUPTR)kwSandbox_Advapi32_CryptReleaseContext },
9927 { TUPLE("CryptContextAddRef"), NULL, (KUPTR)kwSandbox_Advapi32_CryptContextAddRef },
9928#endif
9929
9930 /*
9931 * MS Visual C++ CRTs.
9932 */
9933 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
9934 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
9935 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
9936 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
9937 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
9938 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
9939
9940 { TUPLE("onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
9941 { TUPLE("_onexit"), NULL, (KUPTR)kwSandbox_msvcrt__onexit, K_TRUE /*fOnlyExe*/ },
9942 { TUPLE("atexit"), NULL, (KUPTR)kwSandbox_msvcrt_atexit, K_TRUE /*fOnlyExe*/ },
9943
9944 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
9945 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
9946 { TUPLE("_beginthreadex"), "msvcr120.dll", (KUPTR)kwSandbox_msvcr120__beginthreadex }, /* higher priority last */
9947
9948 { TUPLE("__argc"), NULL, (KUPTR)&g_Sandbox.cArgs },
9949 { TUPLE("__argv"), NULL, (KUPTR)&g_Sandbox.papszArgs },
9950 { TUPLE("__wargv"), NULL, (KUPTR)&g_Sandbox.papwszArgs },
9951 { TUPLE("__p___argc"), NULL, (KUPTR)kwSandbox_msvcrt___p___argc },
9952 { TUPLE("__p___argv"), NULL, (KUPTR)kwSandbox_msvcrt___p___argv },
9953 { TUPLE("__p___wargv"), NULL, (KUPTR)kwSandbox_msvcrt___p___wargv },
9954 { TUPLE("_acmdln"), NULL, (KUPTR)&g_Sandbox.pszCmdLine },
9955 { TUPLE("_wcmdln"), NULL, (KUPTR)&g_Sandbox.pwszCmdLine },
9956 { TUPLE("__p__acmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__acmdln },
9957 { TUPLE("__p__wcmdln"), NULL, (KUPTR)kwSandbox_msvcrt___p__wcmdln },
9958 { TUPLE("_pgmptr"), NULL, (KUPTR)&g_Sandbox.pgmptr },
9959 { TUPLE("_wpgmptr"), NULL, (KUPTR)&g_Sandbox.wpgmptr },
9960 { TUPLE("_get_pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_pgmptr },
9961 { TUPLE("_get_wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt__get_wpgmptr },
9962 { TUPLE("__p__pgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__pgmptr },
9963 { TUPLE("__p__wpgmptr"), NULL, (KUPTR)kwSandbox_msvcrt___p__wpgmptr },
9964 { TUPLE("_wincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wincmdln },
9965 { TUPLE("_wwincmdln"), NULL, (KUPTR)kwSandbox_msvcrt__wwincmdln },
9966 { TUPLE("__getmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___getmainargs},
9967 { TUPLE("__wgetmainargs"), NULL, (KUPTR)kwSandbox_msvcrt___wgetmainargs},
9968
9969 { TUPLE("_putenv"), NULL, (KUPTR)kwSandbox_msvcrt__putenv},
9970 { TUPLE("_wputenv"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv},
9971 { TUPLE("_putenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__putenv_s},
9972 { TUPLE("_wputenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wputenv_s},
9973 { TUPLE("__initenv"), NULL, (KUPTR)&g_Sandbox.initenv },
9974 { TUPLE("__winitenv"), NULL, (KUPTR)&g_Sandbox.winitenv },
9975 { TUPLE("__p___initenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___initenv},
9976 { TUPLE("__p___winitenv"), NULL, (KUPTR)kwSandbox_msvcrt___p___winitenv},
9977 { TUPLE("_environ"), NULL, (KUPTR)&g_Sandbox.environ },
9978 { TUPLE("_wenviron"), NULL, (KUPTR)&g_Sandbox.wenviron },
9979 { TUPLE("_get_environ"), NULL, (KUPTR)kwSandbox_msvcrt__get_environ },
9980 { TUPLE("_get_wenviron"), NULL, (KUPTR)kwSandbox_msvcrt__get_wenviron },
9981 { TUPLE("__p__environ"), NULL, (KUPTR)kwSandbox_msvcrt___p__environ },
9982 { TUPLE("__p__wenviron"), NULL, (KUPTR)kwSandbox_msvcrt___p__wenviron },
9983
9984#ifndef NDEBUG
9985 { TUPLE("memcpy"), NULL, (KUPTR)kwSandbox_msvcrt_memcpy },
9986 { TUPLE("memset"), NULL, (KUPTR)kwSandbox_msvcrt_memset },
9987#endif
9988};
9989/** Number of entries in g_aReplacements. */
9990KU32 const g_cSandboxReplacements = K_ELEMENTS(g_aSandboxReplacements);
9991
9992
9993/**
9994 * Functions that needs replacing in natively loaded DLLs when doing sandboxed
9995 * execution.
9996 */
9997KWREPLACEMENTFUNCTION const g_aSandboxNativeReplacements[] =
9998{
9999 /*
10000 * Kernel32.dll and friends.
10001 */
10002 { TUPLE("ExitProcess"), NULL, (KUPTR)kwSandbox_Kernel32_ExitProcess },
10003 { TUPLE("TerminateProcess"), NULL, (KUPTR)kwSandbox_Kernel32_TerminateProcess },
10004
10005#if 0
10006 { TUPLE("CreateThread"), NULL, (KUPTR)kwSandbox_Kernel32_CreateThread },
10007#endif
10008
10009 { TUPLE("CreateFileA"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileA },
10010 { TUPLE("CreateFileW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileW },
10011 { TUPLE("ReadFile"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFile },
10012 { TUPLE("ReadFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_ReadFileEx },
10013#ifdef WITH_TEMP_MEMORY_FILES
10014 { TUPLE("WriteFile"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFile },
10015 { TUPLE("WriteFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_WriteFileEx },
10016 { TUPLE("SetEndOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_SetEndOfFile },
10017 { TUPLE("GetFileType"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileType },
10018 { TUPLE("GetFileSize"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSize },
10019 { TUPLE("GetFileSizeEx"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileSizeEx },
10020 { TUPLE("CreateFileMappingW"), NULL, (KUPTR)kwSandbox_Kernel32_CreateFileMappingW },
10021 { TUPLE("MapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFile },
10022 { TUPLE("MapViewOfFileEx"), NULL, (KUPTR)kwSandbox_Kernel32_MapViewOfFileEx },
10023 { TUPLE("UnmapViewOfFile"), NULL, (KUPTR)kwSandbox_Kernel32_UnmapViewOfFile },
10024#endif
10025 { TUPLE("SetFilePointer"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointer },
10026 { TUPLE("SetFilePointerEx"), NULL, (KUPTR)kwSandbox_Kernel32_SetFilePointerEx },
10027 { TUPLE("DuplicateHandle"), NULL, (KUPTR)kwSandbox_Kernel32_DuplicateHandle },
10028 { TUPLE("CloseHandle"), NULL, (KUPTR)kwSandbox_Kernel32_CloseHandle },
10029 { TUPLE("GetFileAttributesA"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesA },
10030 { TUPLE("GetFileAttributesW"), NULL, (KUPTR)kwSandbox_Kernel32_GetFileAttributesW },
10031 { TUPLE("GetShortPathNameW"), NULL, (KUPTR)kwSandbox_Kernel32_GetShortPathNameW },
10032#ifdef WITH_TEMP_MEMORY_FILES
10033 { TUPLE("DeleteFileW"), NULL, (KUPTR)kwSandbox_Kernel32_DeleteFileW },
10034#endif
10035 { TUPLE("SetConsoleCtrlHandler"), NULL, (KUPTR)kwSandbox_Kernel32_SetConsoleCtrlHandler },
10036 { TUPLE("LoadLibraryExA"), NULL, (KUPTR)kwSandbox_Kernel32_Native_LoadLibraryExA },
10037
10038 { TUPLE("WriteConsoleA"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleA },
10039 { TUPLE("WriteConsoleW"), NULL, (KUPTR)kwSandbox_Kernel32_WriteConsoleW },
10040
10041#ifdef WITH_HASH_MD5_CACHE
10042 { TUPLE("CryptCreateHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptCreateHash },
10043 { TUPLE("CryptHashData"), NULL, (KUPTR)kwSandbox_Advapi32_CryptHashData },
10044 { TUPLE("CryptGetHashParam"), NULL, (KUPTR)kwSandbox_Advapi32_CryptGetHashParam },
10045 { TUPLE("CryptDestroyHash"), NULL, (KUPTR)kwSandbox_Advapi32_CryptDestroyHash },
10046#endif
10047
10048 { TUPLE("RtlPcToFileHeader"), NULL, (KUPTR)kwSandbox_ntdll_RtlPcToFileHeader },
10049
10050#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
10051 { TUPLE("RtlUnwind"), NULL, (KUPTR)kwSandbox_ntdll_RtlUnwind },
10052#endif
10053
10054 /*
10055 * MS Visual C++ CRTs.
10056 */
10057 { TUPLE("exit"), NULL, (KUPTR)kwSandbox_msvcrt_exit },
10058 { TUPLE("_exit"), NULL, (KUPTR)kwSandbox_msvcrt__exit },
10059 { TUPLE("_cexit"), NULL, (KUPTR)kwSandbox_msvcrt__cexit },
10060 { TUPLE("_c_exit"), NULL, (KUPTR)kwSandbox_msvcrt__c_exit },
10061 { TUPLE("_amsg_exit"), NULL, (KUPTR)kwSandbox_msvcrt__amsg_exit },
10062 { TUPLE("terminate"), NULL, (KUPTR)kwSandbox_msvcrt_terminate },
10063 { TUPLE("_wdupenv_s"), NULL, (KUPTR)kwSandbox_msvcrt__wdupenv_s, K_FALSE /*fOnlyExe*/, K_TRUE /*fCrtSlotArray*/ },
10064
10065#if 0 /* used by mspdbXXX.dll */
10066 { TUPLE("_beginthread"), NULL, (KUPTR)kwSandbox_msvcrt__beginthread },
10067 { TUPLE("_beginthreadex"), NULL, (KUPTR)kwSandbox_msvcrt__beginthreadex },
10068#endif
10069};
10070/** Number of entries in g_aSandboxNativeReplacements. */
10071KU32 const g_cSandboxNativeReplacements = K_ELEMENTS(g_aSandboxNativeReplacements);
10072
10073
10074/**
10075 * Functions that needs replacing when queried by GetProcAddress.
10076 */
10077KWREPLACEMENTFUNCTION const g_aSandboxGetProcReplacements[] =
10078{
10079 /*
10080 * Kernel32.dll and friends.
10081 */
10082 { TUPLE("FlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_FlsAlloc, K_TRUE /*fOnlyExe*/ },
10083 { TUPLE("FlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_FlsFree, K_TRUE /*fOnlyExe*/ },
10084 { TUPLE("TlsAlloc"), NULL, (KUPTR)kwSandbox_Kernel32_TlsAlloc, K_TRUE /*fOnlyExe*/ },
10085 { TUPLE("TlsFree"), NULL, (KUPTR)kwSandbox_Kernel32_TlsFree, K_TRUE /*fOnlyExe*/ },
10086};
10087/** Number of entries in g_aSandboxGetProcReplacements. */
10088KU32 const g_cSandboxGetProcReplacements = K_ELEMENTS(g_aSandboxGetProcReplacements);
10089
10090
10091/**
10092 * Control handler.
10093 *
10094 * @returns TRUE if handled, FALSE if not.
10095 * @param dwCtrlType The signal.
10096 */
10097static BOOL WINAPI kwSandboxCtrlHandler(DWORD dwCtrlType)
10098{
10099 DWORD cbIgn;
10100 int volatile rc; /* volatile for debugging */
10101 int volatile rcPrev;
10102 const char *pszMsg;
10103 switch (dwCtrlType)
10104 {
10105 case CTRL_C_EVENT:
10106 rc = 9;
10107 pszMsg = "kWorker: Ctrl-C\r\n";
10108 break;
10109
10110 case CTRL_BREAK_EVENT:
10111 rc = 10;
10112 pszMsg = "kWorker: Ctrl-Break\r\n";
10113 break;
10114
10115 case CTRL_CLOSE_EVENT:
10116 rc = 11;
10117 pszMsg = "kWorker: console closed\r\n";
10118 break;
10119
10120 case CTRL_LOGOFF_EVENT:
10121 rc = 11;
10122 pszMsg = "kWorker: logoff event\r\n";
10123 break;
10124
10125 case CTRL_SHUTDOWN_EVENT:
10126 rc = 11;
10127 pszMsg = "kWorker: shutdown event\r\n";
10128 break;
10129
10130 default:
10131 fprintf(stderr, "kwSandboxCtrlHandler: %#x\n", dwCtrlType);
10132 return TRUE;
10133 }
10134
10135 /*
10136 * Terminate the process after 5 seconds.
10137 * If we get here a second time we just terminate the process ourselves.
10138 *
10139 * Note! We do no try call exit() here as it turned out to deadlock a lot
10140 * flusing file descriptors (stderr back when we first wrote to it).
10141 */
10142 rcPrev = g_rcCtrlC;
10143 g_rcCtrlC = rc;
10144 WriteFile(GetStdHandle(STD_ERROR_HANDLE), pszMsg, (DWORD)strlen(pszMsg), &cbIgn, NULL);
10145 if (rcPrev == 0)
10146 {
10147 int i;
10148 for (i = 0; i < 10; i++)
10149 {
10150 CancelIoEx(g_hPipe, NULL); /* wake up idle main thread */
10151 Sleep(500);
10152 }
10153 }
10154 TerminateProcess(GetCurrentProcess(), rc);
10155 return TRUE;
10156}
10157
10158
10159/**
10160 * Resets the KWMODULE::fVisited flag for _all_ known modules.
10161 */
10162static void kwSandboxResetModuleVisited(void)
10163{
10164 PKWMODULE pMod = g_pModuleHead;
10165 while (pMod)
10166 {
10167 pMod->fVisited = K_FALSE;
10168 pMod = pMod->pNextList;
10169 }
10170}
10171
10172
10173/**
10174 * Used by kwSandboxExec to reset the state of the module tree.
10175 *
10176 * This is done recursively.
10177 *
10178 * @param pMod The root of the tree to consider.
10179 */
10180static void kwSandboxResetModuleState(PKWMODULE pMod)
10181{
10182 KWLDR_LOG(("kwSandboxResetModuleState: %d %d %s\n", pMod->fNative, pMod->fVisited, pMod->pszPath));
10183 if (!pMod->fNative)
10184 {
10185 pMod->u.Manual.enmState = KWMODSTATE_NEEDS_BITS;
10186 if (!pMod->fVisited) /* Avoid loops. */
10187 {
10188 KSIZE iImp;
10189 pMod->fVisited = K_TRUE;
10190 iImp = pMod->u.Manual.cImpMods;
10191 while (iImp-- > 0)
10192 kwSandboxResetModuleState(pMod->u.Manual.apImpMods[iImp]);
10193 }
10194 }
10195 /* Hack: Re-init mspdbXXX.dll when we want to use a different mspdbsrv.exe instance. */
10196 else if (pMod->fReInitOnMsPdbSrvEndpointChange)
10197 {
10198 const char *pszValue = kwSandboxDoGetEnvA(&g_Sandbox, TUPLE("_MSPDBSRV_ENDPOINT_"));
10199 if (pMod->fReInitOnMsPdbSrvEndpointChange == 1)
10200 {
10201 pMod->fReInitOnMsPdbSrvEndpointChange = 2;
10202 pMod->pszMsPdbSrvEndpoint = pszValue ? kHlpStrDup(pszValue) : NULL;
10203 KWLDR_LOG(("Not re-initing '%s': first time used (_MSPDBSRV_ENDPOINT_ is '%s')\n",
10204 pMod->pszPath, pszValue ? pszValue : "<null>"));
10205 }
10206 else if ( (pszValue == NULL && pMod->pszMsPdbSrvEndpoint == NULL)
10207 || (pszValue != NULL && pMod->pszMsPdbSrvEndpoint != NULL && kHlpStrComp(pszValue, pMod->pszMsPdbSrvEndpoint) == 0))
10208 KWLDR_LOG(("Not re-initing '%s': _MSPDBSRV_ENDPOINT_ unchanged ('%s')\n",
10209 pMod->pszPath, pszValue ? pszValue : "<null>"));
10210 else
10211 {
10212 KWLDR_LOG(("Re-initing '%s': _MSPDBSRV_ENDPOINT_ changed from '%s' to '%s'\n", pMod->pszPath,
10213 pMod->pszMsPdbSrvEndpoint ? pMod->pszMsPdbSrvEndpoint : "<null>", pszValue ? pszValue : "<null>"));
10214 kHlpFree(pMod->pszMsPdbSrvEndpoint);
10215 if (pszValue != NULL)
10216 pMod->pszMsPdbSrvEndpoint = kHlpStrDup(pszValue);
10217 else
10218 pMod->pszMsPdbSrvEndpoint = NULL;
10219 pMod->fNeedReInit = K_TRUE;
10220 }
10221 }
10222}
10223
10224static PPEB kwSandboxGetProcessEnvironmentBlock(void)
10225{
10226#if K_ARCH == K_ARCH_X86_32
10227 return (PPEB)__readfsdword(0x030 /* offset of ProcessEnvironmentBlock in TEB */);
10228#elif K_ARCH == K_ARCH_AMD64
10229 return (PPEB)__readgsqword(0x060 /* offset of ProcessEnvironmentBlock in TEB */);
10230#else
10231# error "Port me!"
10232#endif
10233}
10234
10235
10236/**
10237 * Enters the given handle into the handle table.
10238 *
10239 * @returns K_TRUE on success, K_FALSE on failure.
10240 * @param pSandbox The sandbox.
10241 * @param pHandle The handle.
10242 * @param hHandle The handle value to enter it under (for the
10243 * duplicate handle API).
10244 */
10245static KBOOL kwSandboxHandleTableEnter(PKWSANDBOX pSandbox, PKWHANDLE pHandle, HANDLE hHandle)
10246{
10247 KUPTR const idxHandle = KW_HANDLE_TO_INDEX(hHandle);
10248 kHlpAssertReturn(idxHandle < KW_HANDLE_MAX, K_FALSE);
10249
10250 /*
10251 * Grow handle table.
10252 */
10253 if (idxHandle >= pSandbox->cHandles)
10254 {
10255 void *pvNew;
10256 KU32 cHandles = pSandbox->cHandles ? pSandbox->cHandles * 2 : 32;
10257 while (cHandles <= idxHandle)
10258 cHandles *= 2;
10259 pvNew = kHlpRealloc(pSandbox->papHandles, cHandles * sizeof(pSandbox->papHandles[0]));
10260 if (!pvNew)
10261 {
10262 KW_LOG(("Out of memory growing handle table to %u handles\n", cHandles));
10263 return K_FALSE;
10264 }
10265 pSandbox->papHandles = (PKWHANDLE *)pvNew;
10266 kHlpMemSet(&pSandbox->papHandles[pSandbox->cHandles], 0,
10267 (cHandles - pSandbox->cHandles) * sizeof(pSandbox->papHandles[0]));
10268 pSandbox->cHandles = cHandles;
10269 }
10270
10271 /*
10272 * Check that the entry is unused then insert it.
10273 */
10274 kHlpAssertReturn(pSandbox->papHandles[idxHandle] == NULL, K_FALSE);
10275 pSandbox->papHandles[idxHandle] = pHandle;
10276 pSandbox->cActiveHandles++;
10277 return K_TRUE;
10278}
10279
10280
10281/**
10282 * Creates a correctly quoted ANSI command line string from the given argv.
10283 *
10284 * @returns Pointer to the command line.
10285 * @param cArgs Number of arguments.
10286 * @param papszArgs The argument vector.
10287 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
10288 * @param pcbCmdLine Where to return the command line length,
10289 * including one terminator.
10290 */
10291static char *kwSandboxInitCmdLineFromArgv(KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange, KSIZE *pcbCmdLine)
10292{
10293 KU32 i;
10294 KSIZE cbCmdLine;
10295 char *pszCmdLine;
10296
10297 /* Make a copy of the argument vector that we'll be quoting. */
10298 char **papszQuotedArgs = alloca(sizeof(papszArgs[0]) * (cArgs + 1));
10299 kHlpMemCopy(papszQuotedArgs, papszArgs, sizeof(papszArgs[0]) * (cArgs + 1));
10300
10301 /* Quote the arguments that need it. */
10302 quote_argv(cArgs, papszQuotedArgs, fWatcomBrainDamange, 0 /*leak*/);
10303
10304 /* figure out cmd line length. */
10305 cbCmdLine = 0;
10306 for (i = 0; i < cArgs; i++)
10307 cbCmdLine += kHlpStrLen(papszQuotedArgs[i]) + 1;
10308 *pcbCmdLine = cbCmdLine;
10309
10310 pszCmdLine = (char *)kHlpAlloc(cbCmdLine + 1);
10311 if (pszCmdLine)
10312 {
10313 char *psz = kHlpStrPCopy(pszCmdLine, papszQuotedArgs[0]);
10314 if (papszQuotedArgs[0] != papszArgs[0])
10315 free(papszQuotedArgs[0]);
10316
10317 for (i = 1; i < cArgs; i++)
10318 {
10319 *psz++ = ' ';
10320 psz = kHlpStrPCopy(psz, papszQuotedArgs[i]);
10321 if (papszQuotedArgs[i] != papszArgs[i])
10322 free(papszQuotedArgs[i]);
10323 }
10324 kHlpAssert((KSIZE)(&psz[1] - pszCmdLine) == cbCmdLine);
10325
10326 *psz++ = '\0';
10327 *psz++ = '\0';
10328 }
10329
10330 return pszCmdLine;
10331}
10332
10333
10334
10335static int kwSandboxInit(PKWSANDBOX pSandbox, PKWTOOL pTool,
10336 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10337 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10338{
10339 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10340 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10341 wchar_t *pwcPool;
10342 KSIZE cbStrings;
10343 KSIZE cwc;
10344 KSIZE cbCmdLine;
10345 KU32 i;
10346
10347 /* Simple stuff. */
10348 pSandbox->rcExitCode = 256;
10349 pSandbox->pTool = pTool;
10350 pSandbox->idMainThread = GetCurrentThreadId();
10351 pSandbox->pgmptr = (char *)pTool->pszPath;
10352 pSandbox->wpgmptr = (wchar_t *)pTool->pwszPath;
10353#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10354 if (pSandbox->StdOut.fIsConsole)
10355 pSandbox->StdOut.u.Con.cwcBuf = 0;
10356 else
10357 pSandbox->StdOut.u.Fully.cchBuf = 0;
10358 if (pSandbox->StdErr.fIsConsole)
10359 pSandbox->StdErr.u.Con.cwcBuf = 0;
10360 else
10361 pSandbox->StdErr.u.Fully.cchBuf = 0;
10362 pSandbox->Combined.cwcBuf = 0;
10363 pSandbox->Combined.cFlushes = 0;
10364#endif
10365 pSandbox->fNoPchCaching = fNoPchCaching;
10366 pSandbox->cArgs = cArgs;
10367 pSandbox->papszArgs = (char **)papszArgs;
10368 pSandbox->pszCmdLine = kwSandboxInitCmdLineFromArgv(cArgs, papszArgs, fWatcomBrainDamange, &cbCmdLine);
10369 if (!pSandbox->pszCmdLine)
10370 return KERR_NO_MEMORY;
10371
10372 /*
10373 * Convert command line and argv to UTF-16.
10374 * We assume each ANSI char requires a surrogate pair in the UTF-16 variant.
10375 */
10376 pSandbox->papwszArgs = (wchar_t **)kHlpAlloc(sizeof(wchar_t *) * (pSandbox->cArgs + 2) + cbCmdLine * 2 * sizeof(wchar_t));
10377 if (!pSandbox->papwszArgs)
10378 return KERR_NO_MEMORY;
10379 pwcPool = (wchar_t *)&pSandbox->papwszArgs[pSandbox->cArgs + 2];
10380 for (i = 0; i < cArgs; i++)
10381 {
10382 *pwcPool++ = pSandbox->papszArgs[i][-1]; /* flags */
10383 pSandbox->papwszArgs[i] = pwcPool;
10384 pwcPool += kwStrToUtf16(pSandbox->papszArgs[i], pwcPool, (kHlpStrLen(pSandbox->papszArgs[i]) + 1) * 2);
10385 pwcPool++;
10386 }
10387 pSandbox->papwszArgs[pSandbox->cArgs + 0] = NULL;
10388 pSandbox->papwszArgs[pSandbox->cArgs + 1] = NULL;
10389
10390 /*
10391 * Convert the commandline string to UTF-16, same pessimistic approach as above.
10392 */
10393 cbStrings = (cbCmdLine + 1) * 2 * sizeof(wchar_t);
10394 pSandbox->pwszCmdLine = kHlpAlloc(cbStrings);
10395 if (!pSandbox->pwszCmdLine)
10396 return KERR_NO_MEMORY;
10397 cwc = kwStrToUtf16(pSandbox->pszCmdLine, pSandbox->pwszCmdLine, cbStrings / sizeof(wchar_t));
10398
10399 pSandbox->SavedCommandLine = pProcParams->CommandLine;
10400 pProcParams->CommandLine.Buffer = pSandbox->pwszCmdLine;
10401 pProcParams->CommandLine.Length = (USHORT)cwc * sizeof(wchar_t);
10402
10403 /*
10404 * Setup the environment.
10405 */
10406 if ( cEnvVars + 2 <= pSandbox->cEnvVarsAllocated
10407 || kwSandboxGrowEnv(pSandbox, cEnvVars + 2) == 0)
10408 {
10409 KU32 iDst = 0;
10410 for (i = 0; i < cEnvVars; i++)
10411 {
10412 const char *pszVar = papszEnvVars[i];
10413 KSIZE cchVar = kHlpStrLen(pszVar);
10414 const char *pszEqual;
10415 if ( cchVar > 0
10416 && (pszEqual = kHlpMemChr(pszVar, '=', cchVar)) != NULL)
10417 {
10418 char *pszCopy = kHlpDup(pszVar, cchVar + 1);
10419 wchar_t *pwszCopy = kwStrToUtf16AllocN(pszVar, cchVar + 1);
10420 if (pszCopy && pwszCopy)
10421 {
10422 pSandbox->papszEnvVars[iDst] = pszCopy;
10423 pSandbox->environ[iDst] = pszCopy;
10424 pSandbox->papwszEnvVars[iDst] = pwszCopy;
10425 pSandbox->wenviron[iDst] = pwszCopy;
10426
10427 /* When we see the path, we must tell the system or native exec and module loading won't work . */
10428 if ( (pszEqual - pszVar) == 4
10429 && ( pszCopy[0] == 'P' || pszCopy[0] == 'p')
10430 && ( pszCopy[1] == 'A' || pszCopy[1] == 'a')
10431 && ( pszCopy[2] == 'T' || pszCopy[2] == 't')
10432 && ( pszCopy[3] == 'H' || pszCopy[3] == 'h'))
10433 if (!SetEnvironmentVariableW(L"Path", &pwszCopy[5]))
10434 kwErrPrintf("kwSandboxInit: SetEnvironmentVariableW(Path,) failed: %u\n", GetLastError());
10435
10436 iDst++;
10437 }
10438 else
10439 {
10440 kHlpFree(pszCopy);
10441 kHlpFree(pwszCopy);
10442 return kwErrPrintfRc(KERR_NO_MEMORY, "Out of memory setting up env vars!\n");
10443 }
10444 }
10445 else
10446 kwErrPrintf("kwSandboxInit: Skipping bad env var '%s'\n", pszVar);
10447 }
10448 pSandbox->papszEnvVars[iDst] = NULL;
10449 pSandbox->environ[iDst] = NULL;
10450 pSandbox->papwszEnvVars[iDst] = NULL;
10451 pSandbox->wenviron[iDst] = NULL;
10452 }
10453 else
10454 return kwErrPrintfRc(KERR_NO_MEMORY, "Error setting up environment variables: kwSandboxGrowEnv failed\n");
10455
10456 /*
10457 * Invalidate the volatile parts of cache (kBuild output directory,
10458 * temporary directory, whatever).
10459 */
10460 kFsCacheInvalidateCustomBoth(g_pFsCache);
10461
10462#ifdef WITH_HISTORY
10463 /*
10464 * Record command line in debug history.
10465 */
10466 kHlpFree(g_apszHistory[g_iHistoryNext]);
10467 g_apszHistory[g_iHistoryNext] = kHlpStrDup(pSandbox->pszCmdLine);
10468 g_iHistoryNext = (g_iHistoryNext + 1) % K_ELEMENTS(g_apszHistory);
10469#endif
10470
10471 return 0;
10472}
10473
10474
10475/**
10476 * Does sandbox cleanup between jobs.
10477 *
10478 * We postpone whatever isn't externally visible (i.e. files) and doesn't
10479 * influence the result, so that kmk can get on with things ASAP.
10480 *
10481 * @param pSandbox The sandbox.
10482 */
10483static void kwSandboxCleanupLate(PKWSANDBOX pSandbox)
10484{
10485 PROCESS_MEMORY_COUNTERS MemInfo;
10486 PKWVIRTALLOC pTracker;
10487 PKWHEAP pHeap;
10488 PKWLOCALSTORAGE pLocalStorage;
10489#ifdef WITH_HASH_MD5_CACHE
10490 PKWHASHMD5 pHash;
10491#endif
10492#ifdef WITH_TEMP_MEMORY_FILES
10493 PKWFSTEMPFILE pTempFile;
10494#endif
10495 PKWEXITCALLACK pExitCallback;
10496
10497 /*
10498 * First stuff that may cause code to run.
10499 */
10500
10501 /* Do exit callback first. */
10502 pExitCallback = g_Sandbox.pExitCallbackHead;
10503 g_Sandbox.pExitCallbackHead = NULL;
10504 while (pExitCallback)
10505 {
10506 PKWEXITCALLACK pNext = pExitCallback->pNext;
10507 KW_LOG(("kwSandboxCleanupLate: calling %p %sexit handler\n",
10508 pExitCallback->pfnCallback, pExitCallback->fAtExit ? "at" : "_on"));
10509 __try
10510 {
10511 pExitCallback->pfnCallback();
10512 }
10513 __except (EXCEPTION_EXECUTE_HANDLER)
10514 {
10515 KW_LOG(("kwSandboxCleanupLate: %sexit handler %p threw an exception!\n",
10516 pExitCallback->fAtExit ? "at" : "_on", pExitCallback->pfnCallback));
10517 kHlpAssertFailed();
10518 }
10519 kHlpFree(pExitCallback);
10520 pExitCallback = pNext;
10521 }
10522
10523 /* Free left behind FlsAlloc leaks. */
10524 pLocalStorage = g_Sandbox.pFlsAllocHead;
10525 g_Sandbox.pFlsAllocHead = NULL;
10526 while (pLocalStorage)
10527 {
10528 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10529 KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
10530 FlsFree(pLocalStorage->idx);
10531 kHlpFree(pLocalStorage);
10532 pLocalStorage = pNext;
10533 }
10534
10535 /* Free left behind TlsAlloc leaks. */
10536 pLocalStorage = g_Sandbox.pTlsAllocHead;
10537 g_Sandbox.pTlsAllocHead = NULL;
10538 while (pLocalStorage)
10539 {
10540 PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
10541 KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
10542 TlsFree(pLocalStorage->idx);
10543 kHlpFree(pLocalStorage);
10544 pLocalStorage = pNext;
10545 }
10546
10547
10548 /*
10549 * Then free resources associated with the sandbox run.
10550 */
10551
10552 /* Open handles, except fixed handles (stdout and stderr). */
10553 if (pSandbox->cActiveHandles > pSandbox->cFixedHandles)
10554 {
10555 KU32 idxHandle = pSandbox->cHandles;
10556 while (idxHandle-- > 0)
10557 if (pSandbox->papHandles[idxHandle] == NULL)
10558 { /* likely */ }
10559 else
10560 {
10561 PKWHANDLE pHandle = pSandbox->papHandles[idxHandle];
10562 if ( pHandle->enmType != KWHANDLETYPE_OUTPUT_BUF
10563 || idxHandle != KW_HANDLE_TO_INDEX(pHandle->hHandle) )
10564 {
10565 pSandbox->papHandles[idxHandle] = NULL;
10566 pSandbox->cLeakedHandles++;
10567
10568 switch (pHandle->enmType)
10569 {
10570 case KWHANDLETYPE_FSOBJ_READ_CACHE:
10571 KWFS_LOG(("Closing leaked read cache handle: %#x/%p cRefs=%d\n",
10572 idxHandle, pHandle->hHandle, pHandle->cRefs));
10573 break;
10574 case KWHANDLETYPE_FSOBJ_READ_CACHE_MAPPING:
10575 KWFS_LOG(("Closing leaked read mapping handle: %#x/%p cRefs=%d\n",
10576 idxHandle, pHandle->hHandle, pHandle->cRefs));
10577 break;
10578 case KWHANDLETYPE_OUTPUT_BUF:
10579 KWFS_LOG(("Closing leaked output buf handle: %#x/%p cRefs=%d\n",
10580 idxHandle, pHandle->hHandle, pHandle->cRefs));
10581 break;
10582 case KWHANDLETYPE_TEMP_FILE:
10583 KWFS_LOG(("Closing leaked temp file handle: %#x/%p cRefs=%d\n",
10584 idxHandle, pHandle->hHandle, pHandle->cRefs));
10585 pHandle->u.pTempFile->cActiveHandles--;
10586 break;
10587 case KWHANDLETYPE_TEMP_FILE_MAPPING:
10588 KWFS_LOG(("Closing leaked temp mapping handle: %#x/%p cRefs=%d\n",
10589 idxHandle, pHandle->hHandle, pHandle->cRefs));
10590 pHandle->u.pTempFile->cActiveHandles--;
10591 break;
10592 default:
10593 kHlpAssertFailed();
10594 }
10595 if (--pHandle->cRefs == 0)
10596 kHlpFree(pHandle);
10597 if (--pSandbox->cActiveHandles == pSandbox->cFixedHandles)
10598 break;
10599 }
10600 }
10601 kHlpAssert(pSandbox->cActiveHandles == pSandbox->cFixedHandles);
10602 }
10603
10604 /* Reset memory mappings - This assumes none of the DLLs keeps any of our mappings open! */
10605 g_Sandbox.cMemMappings = 0;
10606
10607#ifdef WITH_TEMP_MEMORY_FILES
10608 /* The temporary files aren't externally visible, they're all in memory. */
10609 pTempFile = pSandbox->pTempFileHead;
10610 pSandbox->pTempFileHead = NULL;
10611 while (pTempFile)
10612 {
10613 PKWFSTEMPFILE pNext = pTempFile->pNext;
10614 KU32 iSeg = pTempFile->cSegs;
10615 while (iSeg-- > 0)
10616 kHlpPageFree(pTempFile->paSegs[iSeg].pbData, pTempFile->paSegs[iSeg].cbDataAlloc);
10617 kHlpFree(pTempFile->paSegs);
10618 pTempFile->pNext = NULL;
10619 kHlpFree(pTempFile);
10620
10621 pTempFile = pNext;
10622 }
10623#endif
10624
10625 /* Free left behind HeapCreate leaks. */
10626 pHeap = g_Sandbox.pHeapHead;
10627 g_Sandbox.pHeapHead = NULL;
10628 while (pHeap != NULL)
10629 {
10630 PKWHEAP pNext = pHeap->pNext;
10631 KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
10632 HeapDestroy(pHeap->hHeap);
10633 pHeap = pNext;
10634 }
10635
10636 /* Free left behind VirtualAlloc leaks. */
10637 pTracker = g_Sandbox.pVirtualAllocHead;
10638 g_Sandbox.pVirtualAllocHead = NULL;
10639 while (pTracker)
10640 {
10641 PKWVIRTALLOC pNext = pTracker->pNext;
10642 KW_LOG(("Freeing VirtualFree leak %p LB %#x\n", pTracker->pvAlloc, pTracker->cbAlloc));
10643
10644#ifdef WITH_FIXED_VIRTUAL_ALLOCS
10645 if (pTracker->idxPreAllocated != KU32_MAX)
10646 kwSandboxResetFixedAllocation(pTracker->idxPreAllocated);
10647 else
10648#endif
10649 VirtualFree(pTracker->pvAlloc, 0, MEM_RELEASE);
10650 kHlpFree(pTracker);
10651 pTracker = pNext;
10652 }
10653
10654 /* Free the environment. */
10655 if (pSandbox->papszEnvVars)
10656 {
10657 KU32 i;
10658 for (i = 0; pSandbox->papszEnvVars[i]; i++)
10659 kHlpFree(pSandbox->papszEnvVars[i]);
10660 pSandbox->environ[0] = NULL;
10661 pSandbox->papszEnvVars[0] = NULL;
10662
10663 for (i = 0; pSandbox->papwszEnvVars[i]; i++)
10664 kHlpFree(pSandbox->papwszEnvVars[i]);
10665 pSandbox->wenviron[0] = NULL;
10666 pSandbox->papwszEnvVars[0] = NULL;
10667 }
10668
10669#ifdef WITH_HASH_MD5_CACHE
10670 /*
10671 * Hash handles.
10672 */
10673 pHash = pSandbox->pHashHead;
10674 pSandbox->pHashHead = NULL;
10675 while (pHash)
10676 {
10677 PKWHASHMD5 pNext = pHash->pNext;
10678 KWCRYPT_LOG(("Freeing leaked hash instance %#p\n", pHash));
10679 kHlpFree(pHash);
10680 pHash = pNext;
10681 }
10682#endif
10683
10684 /*
10685 * Check the memory usage. If it's getting high, trigger a respawn
10686 * after the next job.
10687 */
10688 MemInfo.WorkingSetSize = 0;
10689 if (GetProcessMemoryInfo(GetCurrentProcess(), &MemInfo, sizeof(MemInfo)))
10690 {
10691 /* The first time thru, we figure out approximately when to restart
10692 based on installed RAM and CPU threads. */
10693 static KU64 s_cbMaxWorkingSet = 0;
10694 if (s_cbMaxWorkingSet != 0)
10695 { /* likely */ }
10696 else
10697 {
10698 SYSTEM_INFO SysInfo;
10699 MEMORYSTATUSEX GlobalMemInfo;
10700 const char *pszValue;
10701
10702 /* Calculate a reasonable estimate. */
10703 kHlpMemSet(&SysInfo, 0, sizeof(SysInfo));
10704 GetNativeSystemInfo(&SysInfo);
10705
10706 kHlpMemSet(&GlobalMemInfo, 0, sizeof(GlobalMemInfo));
10707 GlobalMemInfo.dwLength = sizeof(GlobalMemInfo);
10708 if (!GlobalMemoryStatusEx(&GlobalMemInfo))
10709#if K_ARCH_BITS >= 64
10710 GlobalMemInfo.ullTotalPhys = KU64_C(0x000200000000); /* 8GB */
10711#else
10712 GlobalMemInfo.ullTotalPhys = KU64_C(0x000080000000); /* 2GB */
10713#endif
10714 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys / (K_MAX(SysInfo.dwNumberOfProcessors, 1) * 4);
10715 KW_LOG(("Raw estimate of s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10716
10717 /* User limit. */
10718 pszValue = getenv("KWORKER_MEMORY_LIMIT");
10719 if (pszValue != NULL)
10720 {
10721 char *pszNext;
10722 unsigned long ulValue = strtol(pszValue, &pszNext, 0);
10723 if (*pszNext == '\0' || *pszNext == 'M')
10724 s_cbMaxWorkingSet = ulValue * (KU64)1048576;
10725 else if (*pszNext == 'K')
10726 s_cbMaxWorkingSet = ulValue * (KU64)1024;
10727 else if (*pszNext == 'G')
10728 s_cbMaxWorkingSet = ulValue * (KU64)1073741824;
10729 else
10730 kwErrPrintf("Unable to grok KWORKER_MEMORY_LIMIT: %s\n", pszValue);
10731 KW_LOG(("User s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10732 }
10733
10734 /* Clamp it a little. */
10735 if (s_cbMaxWorkingSet < 168*1024*1024)
10736 s_cbMaxWorkingSet = 168*1024*1024;
10737#if K_ARCH_BITS < 64
10738 else
10739 s_cbMaxWorkingSet = K_MIN(s_cbMaxWorkingSet,
10740 SysInfo.dwProcessorType != PROCESSOR_ARCHITECTURE_AMD64
10741 ? 512*1024*1024 /* Only got 2 or 3 GB VA */
10742 : 1536*1024*1024 /* got 4GB VA */);
10743#endif
10744 if (s_cbMaxWorkingSet > GlobalMemInfo.ullTotalPhys)
10745 s_cbMaxWorkingSet = GlobalMemInfo.ullTotalPhys;
10746 KW_LOG(("Final s_cbMaxWorkingSet=%" KU64_PRI "\n", s_cbMaxWorkingSet));
10747 }
10748
10749 /* Finally the check. */
10750 if (MemInfo.WorkingSetSize >= s_cbMaxWorkingSet)
10751 {
10752 KW_LOG(("WorkingSetSize = %#x - > restart next time.\n", MemInfo.WorkingSetSize));
10753 g_fRestart = K_TRUE;
10754 }
10755 }
10756
10757 /*
10758 * The CRT has a max of 8192 handles, so we better restart after a while if
10759 * someone is leaking handles or we risk running out of descriptors.
10760 *
10761 * Note! We only detect leaks for handles we intercept. In the case of CL.EXE
10762 * doing _dup2(1, 2) (stderr ==> stdout), there isn't actually a leak.
10763 */
10764 if (pSandbox->cLeakedHandles > 6000)
10765 {
10766 KW_LOG(("LeakedHandles = %#x - > restart next time.\n", pSandbox->cLeakedHandles));
10767 g_fRestart = K_TRUE;
10768 }
10769}
10770
10771
10772/**
10773 * Does essential cleanups and restoring, anything externally visible.
10774 *
10775 * All cleanups that aren't externally visible are postponed till after we've
10776 * informed kmk of the result, so it can be done in the dead time between jobs.
10777 *
10778 * @param pSandbox The sandbox.
10779 */
10780static void kwSandboxCleanup(PKWSANDBOX pSandbox)
10781{
10782 /*
10783 * Restore the parent command line string.
10784 */
10785 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
10786 PMY_RTL_USER_PROCESS_PARAMETERS pProcParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
10787 pProcParams->CommandLine = pSandbox->SavedCommandLine;
10788 pProcParams->StandardOutput = pSandbox->StdOut.hOutput;
10789 pProcParams->StandardError = pSandbox->StdErr.hOutput; /* CL.EXE messes with this one. */
10790}
10791
10792
10793static int kwSandboxExec(PKWSANDBOX pSandbox, PKWTOOL pTool, KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
10794 KU32 cEnvVars, const char **papszEnvVars, KBOOL fNoPchCaching)
10795{
10796 int rcExit = 42;
10797 int rc;
10798
10799 /*
10800 * Initialize the sandbox environment.
10801 */
10802 rc = kwSandboxInit(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange, cEnvVars, papszEnvVars, fNoPchCaching);
10803 if (rc == 0)
10804 {
10805 /*
10806 * Do module initialization.
10807 */
10808 kwSandboxResetModuleVisited();
10809 kwSandboxResetModuleState(pTool->u.Sandboxed.pExe);
10810 rc = kwLdrModuleInitTree(pTool->u.Sandboxed.pExe);
10811 if (rc == 0)
10812 {
10813 /*
10814 * Call the main function.
10815 */
10816#if K_ARCH == K_ARCH_AMD64
10817 int (*pfnWin64Entrypoint)(void *pvPeb, void *, void *, void *);
10818#elif K_ARCH == K_ARCH_X86_32
10819 int (__cdecl *pfnWin32Entrypoint)(void *pvPeb);
10820#else
10821# error "Port me!"
10822#endif
10823
10824 /* Save the NT TIB first (should do that here, not in some other function). */
10825 PNT_TIB pTib = (PNT_TIB)NtCurrentTeb();
10826 pSandbox->TibMainThread = *pTib;
10827
10828 /* Make the call in a guarded fashion. */
10829#if K_ARCH == K_ARCH_AMD64
10830 /* AMD64 */
10831 *(KUPTR *)&pfnWin64Entrypoint = pTool->u.Sandboxed.uMainAddr;
10832 __try
10833 {
10834 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10835 if (setjmp(pSandbox->JmpBuf) == 0)
10836 {
10837 *(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10838 pSandbox->fRunning = K_TRUE;
10839 rcExit = pfnWin64Entrypoint(kwSandboxGetProcessEnvironmentBlock(), NULL, NULL, NULL);
10840 pSandbox->fRunning = K_FALSE;
10841 }
10842 else
10843 rcExit = pSandbox->rcExitCode;
10844 }
10845#elif K_ARCH == K_ARCH_X86_32
10846 /* x86 (see _tmainCRTStartup) */
10847 *(KUPTR *)&pfnWin32Entrypoint = pTool->u.Sandboxed.uMainAddr;
10848 __try
10849 {
10850 pSandbox->pOutXcptListHead = pTib->ExceptionList;
10851 if (setjmp(pSandbox->JmpBuf) == 0)
10852 {
10853 //*(KU64*)(pSandbox->JmpBuf) = 0; /** @todo find other way to prevent longjmp from doing unwind! */
10854 pSandbox->fRunning = K_TRUE;
10855 rcExit = pfnWin32Entrypoint(kwSandboxGetProcessEnvironmentBlock());
10856 pSandbox->fRunning = K_FALSE;
10857 }
10858 else
10859 rcExit = pSandbox->rcExitCode;
10860 }
10861#endif
10862 __except (EXCEPTION_EXECUTE_HANDLER)
10863 {
10864 kwErrPrintf("Caught exception %#x!\n", GetExceptionCode());
10865#ifdef WITH_HISTORY
10866 {
10867 KU32 cPrinted = 0;
10868 while (cPrinted++ < 5)
10869 {
10870 KU32 idx = (g_iHistoryNext + K_ELEMENTS(g_apszHistory) - cPrinted) % K_ELEMENTS(g_apszHistory);
10871 if (g_apszHistory[idx])
10872 kwErrPrintf("cmd[%d]: %s\n", 1 - cPrinted, g_apszHistory[idx]);
10873 }
10874 }
10875#endif
10876 rcExit = 512;
10877 }
10878 pSandbox->fRunning = K_FALSE;
10879
10880 /* Now, restore the NT TIB. */
10881 *pTib = pSandbox->TibMainThread;
10882 }
10883 else
10884 rcExit = 42 + 4;
10885
10886 /*
10887 * Flush and clean up the essential bits only, postpone whatever we
10888 * can till after we've replied to kmk.
10889 */
10890#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
10891 kwSandboxConsoleFlushAll(&g_Sandbox);
10892#endif
10893 kwSandboxCleanup(&g_Sandbox);
10894 /** @todo Flush sandboxed native CRTs too. */
10895 }
10896 else
10897 rcExit = 42 + 3;
10898
10899 return rcExit;
10900}
10901
10902
10903/**
10904 * Does the post command part of a job (optional).
10905 *
10906 * @returns The exit code of the job.
10907 * @param cPostCmdArgs Number of post command arguments (includes cmd).
10908 * @param papszPostCmdArgs The post command and its argument.
10909 */
10910static int kSubmitHandleJobPostCmd(KU32 cPostCmdArgs, const char **papszPostCmdArgs)
10911{
10912 const char *pszCmd = papszPostCmdArgs[0];
10913
10914 /* Allow the kmk builtin prefix. */
10915 static const char s_szKmkBuiltinPrefix[] = "kmk_builtin_";
10916 if (kHlpStrNComp(pszCmd, s_szKmkBuiltinPrefix, sizeof(s_szKmkBuiltinPrefix) - 1) == 0)
10917 pszCmd += sizeof(s_szKmkBuiltinPrefix) - 1;
10918
10919 /* Command switch. */
10920 if (kHlpStrComp(pszCmd, "kDepObj") == 0)
10921 {
10922 KMKBUILTINCTX Ctx = { papszPostCmdArgs[0], NULL };
10923 return kmk_builtin_kDepObj(cPostCmdArgs, (char **)papszPostCmdArgs, NULL, &Ctx);
10924 }
10925
10926 return kwErrPrintfRc(42 + 5, "Unknown post command: '%s'\n", pszCmd);
10927}
10928
10929
10930/**
10931 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
10932 */
10933static unsigned kwGetCurrentProcessorGroup(void)
10934{
10935 typedef BOOL (WINAPI *PFNGETTHREADGROUPAFFINITY)(HANDLE, GROUP_AFFINITY *);
10936 HMODULE hmodKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
10937 PFNGETTHREADGROUPAFFINITY pfnGetter = (PFNGETTHREADGROUPAFFINITY)GetProcAddress(hmodKernel32, "GetThreadGroupAffinity");
10938 if (pfnGetter)
10939 {
10940 GROUP_AFFINITY GroupAffinity;
10941 memset(&GroupAffinity, 0, sizeof(GroupAffinity));
10942 if (pfnGetter(GetCurrentThread(), &GroupAffinity))
10943 return GroupAffinity.Group;
10944 }
10945 return 0;
10946}
10947
10948
10949/**
10950 * Helper for kSubmitHandleSpecialEnvVar that gets the current process group.
10951 */
10952static KSIZE kwGetCurrentAuthenticationIdAsString(char *pszValue)
10953{
10954 KSIZE cchRet = 0;
10955 HANDLE hToken;
10956 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
10957 {
10958 DWORD cbRet;
10959 TOKEN_STATISTICS TokenStats;
10960 memset(&TokenStats, 0, sizeof(TokenStats));
10961 if (GetTokenInformation(hToken, TokenStatistics, &TokenStats, sizeof(TokenStats), &cbRet))
10962 cchRet = sprintf(pszValue, "%" KX64_PRI,
10963 ((KU64)TokenStats.AuthenticationId.HighPart << 32) | TokenStats.AuthenticationId.LowPart);
10964 else
10965 kwErrPrintf("GetTokenInformation/TokenStatistics failed: %u\n", GetLastError());
10966 CloseHandle(hToken);
10967 }
10968 else
10969 kwErrPrintf("OpenProcessToken failed: %u\n", GetLastError());
10970 return cchRet;
10971}
10972
10973
10974/**
10975 * Look for and expand the special environment variable.
10976 *
10977 * We the special variable contains elements like "@@VAR_NAME@@" that kmk
10978 * couldn't accuratly determine. Currently the following variables are
10979 * implemented:
10980 * - "@@PROCESSOR_GROUP@@" - The processor group number.
10981 * - "@@AUTHENTICATION_ID@@" - The authentication ID from the process token.
10982 * - "@@PID@@" - The kWorker process ID.
10983 * - "@@@@" - Escaped "@@".
10984 * - "@@DEBUG_COUNTER@@" - An ever increasing counter (starts at zero).
10985 */
10986static int kSubmitHandleSpecialEnvVar(KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv, char **ppszToFree)
10987{
10988 KSIZE const cchSpecialEnv = kHlpStrLen(pszSpecialEnv);
10989 KU32 i = cEnvVars;
10990 while (i-- > 0)
10991 if ( kHlpStrNComp(papszEnvVars[i], pszSpecialEnv, cchSpecialEnv) == 0
10992 && papszEnvVars[i][cchSpecialEnv] == '=')
10993 {
10994 /* We will expand stuff like @@NAME@@ */
10995 const char *pszValue = papszEnvVars[i];
10996 KSIZE offDst = 0;
10997 char szTmp[1024];
10998 for (;;)
10999 {
11000 const char *pszAt = kHlpStrChr(pszValue, '@');
11001 while (pszAt && pszAt[1] != '@')
11002 pszAt = kHlpStrChr(pszAt + 1, '@');
11003 if (pszAt)
11004 {
11005 KSIZE cchSrc = pszAt - pszValue;
11006 if (offDst + cchSrc < sizeof(szTmp))
11007 {
11008 char szSrc[64];
11009
11010 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11011 offDst += cchSrc;
11012 pszValue = pszAt + 2;
11013
11014 if (kHlpStrNComp(pszValue, "PROCESS_GROUP@@", 15) == 0)
11015 {
11016 pszValue += 15;
11017 if (g_iProcessGroup == -1)
11018 g_iProcessGroup = kwGetCurrentProcessorGroup();
11019 cchSrc = sprintf(szSrc, "%u", g_iProcessGroup);
11020 }
11021 else if (kHlpStrNComp(pszValue, "AUTHENTICATION_ID@@", 19) == 0)
11022 {
11023 pszValue += 19;
11024 cchSrc = kwGetCurrentAuthenticationIdAsString(szSrc);
11025 }
11026 else if (kHlpStrNComp(pszValue, "PID@@", 5) == 0)
11027 {
11028 pszValue += 5;
11029 cchSrc = sprintf(szSrc, "%d", getpid());
11030 }
11031 else if (kHlpStrNComp(pszValue, "@@", 2) == 0)
11032 {
11033 pszValue += 2;
11034 szSrc[0] = '@';
11035 szSrc[1] = '@';
11036 szSrc[2] = '\0';
11037 cchSrc = 2;
11038 }
11039 else if (kHlpStrNComp(pszValue, "DEBUG_COUNTER@@", 15) == 0)
11040 {
11041 static unsigned int s_iCounter = 0;
11042 pszValue += 15;
11043 cchSrc = sprintf(szSrc, "%u", s_iCounter++);
11044 }
11045 else
11046 return kwErrPrintfRc(42 + 6, "Special environment variable contains unknown reference: '%s'!\n",
11047 pszValue - 2);
11048 if (offDst + cchSrc < sizeof(szTmp))
11049 {
11050 kHlpMemCopy(&szTmp[offDst], szSrc, cchSrc);
11051 offDst += cchSrc;
11052 continue;
11053 }
11054 }
11055 }
11056 else
11057 {
11058 KSIZE cchSrc = kHlpStrLen(pszValue);
11059 if (offDst + cchSrc < sizeof(szTmp))
11060 {
11061 kHlpMemCopy(&szTmp[offDst], pszValue, cchSrc);
11062 offDst += cchSrc;
11063 break;
11064 }
11065 }
11066 return kwErrPrintfRc(42 + 6, "Special environment variable value too long!\n");
11067 }
11068 szTmp[offDst] = '\0';
11069
11070 /* Return a copy of it: */
11071 papszEnvVars[i] = *ppszToFree = kHlpDup(szTmp, offDst + 1);
11072 if (papszEnvVars[i])
11073 {
11074 SetEnvironmentVariableA(pszSpecialEnv, kHlpStrChr(papszEnvVars[i], '=') + 1); /* hack */
11075 return 0;
11076 }
11077 return kwErrPrintfRc(42 + 6, "Special environment variable: out of memory\n");
11078 }
11079
11080 return kwErrPrintfRc(42 + 6, "Special environment variable not found: '%s'\n", pszSpecialEnv);
11081}
11082
11083
11084/**
11085 * Part 2 of the "JOB" command handler.
11086 *
11087 * @returns The exit code of the job.
11088 * @param pszExecutable The executable to execute.
11089 * @param pszCwd The current working directory of the job.
11090 * @param cArgs The number of arguments.
11091 * @param papszArgs The argument vector.
11092 * @param fWatcomBrainDamange Whether to apply watcom rules while quoting.
11093 * @param cEnvVars The number of environment variables.
11094 * @param papszEnvVars The environment vector.
11095 * @param pszSpecialEnv Name of special environment variable that
11096 * requires selective expansion here.
11097 * @param fNoPchCaching Whether to disable precompiled header file
11098 * caching. Avoid trouble when creating them.
11099 * @param cPostCmdArgs Number of post command arguments (includes cmd).
11100 * @param papszPostCmdArgs The post command and its argument.
11101 */
11102static int kSubmitHandleJobUnpacked(const char *pszExecutable, const char *pszCwd,
11103 KU32 cArgs, const char **papszArgs, KBOOL fWatcomBrainDamange,
11104 KU32 cEnvVars, const char **papszEnvVars, const char *pszSpecialEnv,
11105 KBOOL fNoPchCaching, KU32 cPostCmdArgs, const char **papszPostCmdArgs)
11106{
11107 int rcExit;
11108 PKWTOOL pTool;
11109 char *pszSpecialEnvFree = NULL;
11110
11111 KW_LOG(("\n\nkSubmitHandleJobUnpacked: '%s' in '%s' cArgs=%u cEnvVars=%u cPostCmdArgs=%u\n",
11112 pszExecutable, pszCwd, cArgs, cEnvVars, cPostCmdArgs));
11113#ifdef KW_LOG_ENABLED
11114 {
11115 KU32 i;
11116 for (i = 0; i < cArgs; i++)
11117 KW_LOG((" papszArgs[%u]=%s\n", i, papszArgs[i]));
11118 for (i = 0; i < cPostCmdArgs; i++)
11119 KW_LOG((" papszPostCmdArgs[%u]=%s\n", i, papszPostCmdArgs[i]));
11120 }
11121#endif
11122 g_cJobs++;
11123
11124 /*
11125 * Expand pszSpecialEnv if present.
11126 */
11127 if (*pszSpecialEnv)
11128 {
11129 rcExit = kSubmitHandleSpecialEnvVar(cEnvVars, papszEnvVars, pszSpecialEnv, &pszSpecialEnvFree);
11130 if (!rcExit)
11131 { /* likely */ }
11132 else
11133 return rcExit;
11134 }
11135
11136 /*
11137 * Lookup the tool.
11138 */
11139 pTool = kwToolLookup(pszExecutable, cEnvVars, papszEnvVars);
11140 if (pTool)
11141 {
11142 /*
11143 * Change the directory if we're going to execute the job inside
11144 * this process. Then invoke the tool type specific handler.
11145 */
11146 switch (pTool->enmType)
11147 {
11148 case KWTOOLTYPE_SANDBOXED:
11149 case KWTOOLTYPE_WATCOM:
11150 {
11151 /* Change dir. */
11152 KFSLOOKUPERROR enmError;
11153 PKFSOBJ pNewCurDir = kFsCacheLookupA(g_pFsCache, pszCwd, &enmError);
11154 if ( pNewCurDir == g_pCurDirObj
11155 && pNewCurDir->bObjType == KFSOBJ_TYPE_DIR)
11156 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11157 else if (SetCurrentDirectoryA(pszCwd))
11158 {
11159 kFsCacheObjRelease(g_pFsCache, g_pCurDirObj);
11160 g_pCurDirObj = pNewCurDir;
11161 }
11162 else
11163 {
11164 kwErrPrintf("SetCurrentDirectory failed with %u on '%s'\n", GetLastError(), pszCwd);
11165 kFsCacheObjRelease(g_pFsCache, pNewCurDir);
11166 rcExit = 42 + 1;
11167 break;
11168 }
11169
11170 /* Call specific handler. */
11171 if (pTool->enmType == KWTOOLTYPE_SANDBOXED)
11172 {
11173 KW_LOG(("Sandboxing tool %s\n", pTool->pszPath));
11174 rcExit = kwSandboxExec(&g_Sandbox, pTool, cArgs, papszArgs, fWatcomBrainDamange,
11175 cEnvVars, papszEnvVars, fNoPchCaching);
11176 }
11177 else
11178 {
11179 kwErrPrintf("TODO: Watcom style tool %s\n", pTool->pszPath);
11180 rcExit = 42 + 2;
11181 }
11182 break;
11183 }
11184
11185 case KWTOOLTYPE_EXEC:
11186 kwErrPrintf("TODO: Direct exec tool %s\n", pTool->pszPath);
11187 rcExit = 42 + 2;
11188 break;
11189
11190 default:
11191 kHlpAssertFailed();
11192 kwErrPrintf("Internal tool type corruption!!\n");
11193 rcExit = 42 + 2;
11194 g_fRestart = K_TRUE;
11195 break;
11196 }
11197
11198 /*
11199 * Do the post command, if present.
11200 */
11201 if (cPostCmdArgs && rcExit == 0)
11202 rcExit = kSubmitHandleJobPostCmd(cPostCmdArgs, papszPostCmdArgs);
11203 }
11204 else
11205 rcExit = 42 + 1;
11206 if (pszSpecialEnvFree)
11207 {
11208 SetEnvironmentVariableA(pszSpecialEnv, NULL); /* hack */
11209 kHlpFree(pszSpecialEnvFree);
11210 }
11211 return rcExit;
11212}
11213
11214
11215/**
11216 * Handles a "JOB" command.
11217 *
11218 * @returns The exit code of the job.
11219 * @param pszMsg Points to the "JOB" command part of the message.
11220 * @param cbMsg Number of message bytes at @a pszMsg. There are
11221 * 4 more zero bytes after the message body to
11222 * simplify parsing.
11223 */
11224static int kSubmitHandleJob(const char *pszMsg, KSIZE cbMsg)
11225{
11226 int rcExit = 42;
11227
11228 /*
11229 * Unpack the message.
11230 */
11231 const char *pszExecutable;
11232 KSIZE cbTmp;
11233
11234 pszMsg += sizeof("JOB");
11235 cbMsg -= sizeof("JOB");
11236
11237 /* Executable name. */
11238 pszExecutable = pszMsg;
11239 cbTmp = kHlpStrLen(pszMsg) + 1;
11240 pszMsg += cbTmp;
11241 if ( cbTmp < cbMsg
11242 && cbTmp > 2)
11243 {
11244 const char *pszCwd;
11245 cbMsg -= cbTmp;
11246
11247 /* Current working directory. */
11248 pszCwd = pszMsg;
11249 cbTmp = kHlpStrLen(pszMsg) + 1;
11250 pszMsg += cbTmp;
11251 if ( cbTmp + sizeof(KU32) < cbMsg
11252 && cbTmp >= 2)
11253 {
11254 KU32 cArgs;
11255 cbMsg -= cbTmp;
11256
11257 /* Argument count. */
11258 kHlpMemCopy(&cArgs, pszMsg, sizeof(cArgs));
11259 pszMsg += sizeof(cArgs);
11260 cbMsg -= sizeof(cArgs);
11261
11262 if (cArgs > 0 && cArgs < 4096)
11263 {
11264 /* The argument vector. */
11265 char const **papszArgs = kHlpAlloc((cArgs + 1) * sizeof(papszArgs[0]));
11266 if (papszArgs)
11267 {
11268 KU32 i;
11269 for (i = 0; i < cArgs; i++)
11270 {
11271 papszArgs[i] = pszMsg + 1; /* First byte is expansion flags for MSC & EMX. */
11272 cbTmp = 1 + kHlpStrLen(pszMsg + 1) + 1;
11273 pszMsg += cbTmp;
11274 if (cbTmp < cbMsg)
11275 cbMsg -= cbTmp;
11276 else
11277 {
11278 cbMsg = 0;
11279 break;
11280 }
11281
11282 }
11283 papszArgs[cArgs] = 0;
11284
11285 /* Environment variable count. */
11286 if (cbMsg > sizeof(KU32))
11287 {
11288 KU32 cEnvVars;
11289 kHlpMemCopy(&cEnvVars, pszMsg, sizeof(cEnvVars));
11290 pszMsg += sizeof(cEnvVars);
11291 cbMsg -= sizeof(cEnvVars);
11292
11293 if (cEnvVars >= 0 && cEnvVars < 4096)
11294 {
11295 /* The argument vector. */
11296 char const **papszEnvVars = kHlpAlloc((cEnvVars + 1) * sizeof(papszEnvVars[0]));
11297 if (papszEnvVars)
11298 {
11299 for (i = 0; i < cEnvVars; i++)
11300 {
11301 papszEnvVars[i] = pszMsg;
11302 cbTmp = kHlpStrLen(pszMsg) + 1;
11303 pszMsg += cbTmp;
11304 if (cbTmp < cbMsg)
11305 cbMsg -= cbTmp;
11306 else
11307 {
11308 cbMsg = 0;
11309 break;
11310 }
11311 }
11312 papszEnvVars[cEnvVars] = 0;
11313
11314 /* Flags (currently just watcom argument brain damage and no precompiled header caching). */
11315 if (cbMsg >= sizeof(KU8) * 2)
11316 {
11317 KBOOL fWatcomBrainDamange = *pszMsg++;
11318 KBOOL fNoPchCaching = *pszMsg++;
11319 cbMsg -= 2;
11320
11321 /* Name of special enviornment variable requiring selective expansion. */
11322 if (cbMsg >= 1)
11323 {
11324 const char *pszSpecialEnv = pszMsg;
11325 cbTmp = kHlpStrLen(pszMsg);
11326 pszMsg += cbTmp + 1;
11327 cbMsg -= K_MIN(cbMsg, cbTmp + 1);
11328
11329 /* Post command argument count (can be zero). */
11330 if (cbMsg >= sizeof(KU32))
11331 {
11332 KU32 cPostCmdArgs;
11333 kHlpMemCopy(&cPostCmdArgs, pszMsg, sizeof(cPostCmdArgs));
11334 pszMsg += sizeof(cPostCmdArgs);
11335 cbMsg -= sizeof(cPostCmdArgs);
11336
11337 if (cPostCmdArgs >= 0 && cPostCmdArgs < 32)
11338 {
11339 char const *apszPostCmdArgs[32+1];
11340 for (i = 0; i < cPostCmdArgs; i++)
11341 {
11342 apszPostCmdArgs[i] = pszMsg;
11343 cbTmp = kHlpStrLen(pszMsg) + 1;
11344 pszMsg += cbTmp;
11345 if ( cbTmp < cbMsg
11346 || (cbTmp == cbMsg && i + 1 == cPostCmdArgs))
11347 cbMsg -= cbTmp;
11348 else
11349 {
11350 cbMsg = KSIZE_MAX;
11351 break;
11352 }
11353 }
11354 if (cbMsg == 0)
11355 {
11356 apszPostCmdArgs[cPostCmdArgs] = NULL;
11357
11358 /*
11359 * The next step.
11360 */
11361 rcExit = kSubmitHandleJobUnpacked(pszExecutable, pszCwd,
11362 cArgs, papszArgs, fWatcomBrainDamange,
11363 cEnvVars, papszEnvVars, pszSpecialEnv,
11364 fNoPchCaching,
11365 cPostCmdArgs, apszPostCmdArgs);
11366 }
11367 else if (cbMsg == KSIZE_MAX)
11368 kwErrPrintf("Detected bogus message unpacking post command and its arguments!\n");
11369 else
11370 kwErrPrintf("Message has %u bytes unknown trailing bytes\n", cbMsg);
11371 }
11372 else
11373 kwErrPrintf("Bogus post command argument count: %u %#x\n", cPostCmdArgs, cPostCmdArgs);
11374 }
11375 else
11376 kwErrPrintf("Detected bogus message looking for the post command argument count!\n");
11377 }
11378 else
11379 kwErrPrintf("Detected bogus message unpacking special environment variable!\n");
11380 }
11381 else
11382 kwErrPrintf("Detected bogus message unpacking flags!\n");
11383 kHlpFree((void *)papszEnvVars);
11384 }
11385 else
11386 kwErrPrintf("Error allocating papszEnvVars for %u variables\n", cEnvVars);
11387 }
11388 else
11389 kwErrPrintf("Bogus environment variable count: %u (%#x)\n", cEnvVars, cEnvVars);
11390 }
11391 else
11392 kwErrPrintf("Detected bogus message unpacking arguments and environment variable count!\n");
11393 kHlpFree((void *)papszArgs);
11394 }
11395 else
11396 kwErrPrintf("Error allocating argv for %u arguments\n", cArgs);
11397 }
11398 else
11399 kwErrPrintf("Bogus argument count: %u (%#x)\n", cArgs, cArgs);
11400 }
11401 else
11402 kwErrPrintf("Detected bogus message unpacking CWD path and argument count!\n");
11403 }
11404 else
11405 kwErrPrintf("Detected bogus message unpacking executable path!\n");
11406 return rcExit;
11407}
11408
11409
11410/**
11411 * Wrapper around WriteFile / write that writes the whole @a cbToWrite.
11412 *
11413 * @retval 0 on success.
11414 * @retval -1 on error (fully bitched).
11415 *
11416 * @param hPipe The pipe handle.
11417 * @param pvBuf The buffer to write out out.
11418 * @param cbToWrite The number of bytes to write.
11419 */
11420static int kSubmitWriteIt(HANDLE hPipe, const void *pvBuf, KU32 cbToWrite)
11421{
11422 KU8 const *pbBuf = (KU8 const *)pvBuf;
11423 KU32 cbLeft = cbToWrite;
11424 while (g_rcCtrlC == 0)
11425 {
11426 DWORD cbActuallyWritten = 0;
11427 if (WriteFile(hPipe, pbBuf, cbLeft, &cbActuallyWritten, NULL /*pOverlapped*/))
11428 {
11429 cbLeft -= cbActuallyWritten;
11430 if (!cbLeft)
11431 return 0;
11432 pbBuf += cbActuallyWritten;
11433 }
11434 else
11435 {
11436 DWORD dwErr = GetLastError();
11437 if (cbLeft == cbToWrite)
11438 kwErrPrintf("WriteFile failed: %u\n", dwErr);
11439 else
11440 kwErrPrintf("WriteFile failed %u byte(s) in: %u\n", cbToWrite - cbLeft, dwErr);
11441 return -1;
11442 }
11443 }
11444 return -1;
11445}
11446
11447
11448/**
11449 * Wrapper around ReadFile / read that reads the whole @a cbToRead.
11450 *
11451 * @retval 0 on success.
11452 * @retval 1 on shut down (fShutdownOkay must be K_TRUE).
11453 * @retval -1 on error (fully bitched).
11454 * @param hPipe The pipe handle.
11455 * @param pvBuf The buffer to read into.
11456 * @param cbToRead The number of bytes to read.
11457 * @param fShutdownOkay Whether connection shutdown while reading the
11458 * first byte is okay or not.
11459 */
11460static int kSubmitReadIt(HANDLE hPipe, void *pvBuf, KU32 cbToRead, KBOOL fMayShutdown)
11461{
11462 KU8 *pbBuf = (KU8 *)pvBuf;
11463 KU32 cbLeft = cbToRead;
11464 while (g_rcCtrlC == 0)
11465 {
11466 DWORD cbActuallyRead = 0;
11467 if (ReadFile(hPipe, pbBuf, cbLeft, &cbActuallyRead, NULL /*pOverlapped*/))
11468 {
11469 cbLeft -= cbActuallyRead;
11470 if (!cbLeft)
11471 return 0;
11472 pbBuf += cbActuallyRead;
11473 }
11474 else
11475 {
11476 DWORD dwErr = GetLastError();
11477 if (cbLeft == cbToRead)
11478 {
11479 if ( fMayShutdown
11480 && dwErr == ERROR_BROKEN_PIPE)
11481 return 1;
11482 kwErrPrintf("ReadFile failed: %u\n", dwErr);
11483 }
11484 else
11485 kwErrPrintf("ReadFile failed %u byte(s) in: %u\n", cbToRead - cbLeft, dwErr);
11486 return -1;
11487 }
11488 }
11489 return -1;
11490}
11491
11492
11493/**
11494 * Decimal formatting of a 64-bit unsigned value into a large enough buffer.
11495 *
11496 * @returns pszBuf
11497 * @param pszBuf The buffer (sufficiently large).
11498 * @param uValue The value.
11499 */
11500static const char *kwFmtU64(char *pszBuf, KU64 uValue)
11501{
11502 char szTmp[64];
11503 char *psz = &szTmp[63];
11504 int cch = 4;
11505
11506 *psz-- = '\0';
11507 do
11508 {
11509 if (--cch == 0)
11510 {
11511 *psz-- = ' ';
11512 cch = 3;
11513 }
11514 *psz-- = (uValue % 10) + '0';
11515 uValue /= 10;
11516 } while (uValue != 0);
11517
11518 return strcpy(pszBuf, psz + 1);
11519}
11520
11521
11522/**
11523 * Prints statistics.
11524 */
11525static void kwPrintStats(void)
11526{
11527 PROCESS_MEMORY_COUNTERS_EX MemInfo;
11528 MEMORYSTATUSEX MemStatus;
11529 IO_COUNTERS IoCounters;
11530 DWORD cHandles;
11531 KSIZE cMisses;
11532 char szBuf[16*1024];
11533 int off = 0;
11534 char szPrf[24];
11535 char sz1[64];
11536 char sz2[64];
11537 char sz3[64];
11538 char sz4[64];
11539
11540 sprintf(szPrf, "%5d/%u:", getpid(), K_ARCH_BITS);
11541
11542 szBuf[off++] = '\n';
11543
11544 off += sprintf(&szBuf[off], "%s %14s jobs, %s tools, %s modules, %s non-native ones\n", szPrf,
11545 kwFmtU64(sz1, g_cJobs), kwFmtU64(sz2, g_cTools), kwFmtU64(sz3, g_cModules), kwFmtU64(sz4, g_cNonNativeModules));
11546 off += sprintf(&szBuf[off], "%s %14s bytes in %s read-cached files, avg %s bytes\n", szPrf,
11547 kwFmtU64(sz1, g_cbReadCachedFiles), kwFmtU64(sz2, g_cReadCachedFiles),
11548 kwFmtU64(sz3, g_cbReadCachedFiles / K_MAX(g_cReadCachedFiles, 1)));
11549
11550 off += sprintf(&szBuf[off], "%s %14s bytes read in %s calls\n",
11551 szPrf, kwFmtU64(sz1, g_cbReadFileTotal), kwFmtU64(sz2, g_cReadFileCalls));
11552
11553 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from read cached files\n", szPrf,
11554 kwFmtU64(sz1, g_cbReadFileFromReadCached), (unsigned)(g_cbReadFileFromReadCached * (KU64)100 / g_cbReadFileTotal),
11555 kwFmtU64(sz2, g_cReadFileFromReadCached), (unsigned)(g_cReadFileFromReadCached * (KU64)100 / g_cReadFileCalls));
11556
11557 off += sprintf(&szBuf[off], "%s %14s bytes read (%u%%) in %s calls (%u%%) from in-memory temporary files\n", szPrf,
11558 kwFmtU64(sz1, g_cbReadFileFromInMemTemp), (unsigned)(g_cbReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cbReadFileTotal, 1)),
11559 kwFmtU64(sz2, g_cReadFileFromInMemTemp), (unsigned)(g_cReadFileFromInMemTemp * (KU64)100 / K_MAX(g_cReadFileCalls, 1)));
11560
11561 off += sprintf(&szBuf[off], "%s %14s bytes written in %s calls\n", szPrf,
11562 kwFmtU64(sz1, g_cbWriteFileTotal), kwFmtU64(sz2, g_cWriteFileCalls));
11563 off += sprintf(&szBuf[off], "%s %14s bytes written (%u%%) in %s calls (%u%%) to in-memory temporary files\n", szPrf,
11564 kwFmtU64(sz1, g_cbWriteFileToInMemTemp),
11565 (unsigned)(g_cbWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cbWriteFileTotal, 1)),
11566 kwFmtU64(sz2, g_cWriteFileToInMemTemp),
11567 (unsigned)(g_cWriteFileToInMemTemp * (KU64)100 / K_MAX(g_cWriteFileCalls, 1)));
11568
11569 off += sprintf(&szBuf[off], "%s %14s bytes for the cache\n", szPrf,
11570 kwFmtU64(sz1, g_pFsCache->cbObjects + g_pFsCache->cbAnsiPaths + g_pFsCache->cbUtf16Paths + sizeof(*g_pFsCache)));
11571 off += sprintf(&szBuf[off], "%s %14s objects, taking up %s bytes, avg %s bytes\n", szPrf,
11572 kwFmtU64(sz1, g_pFsCache->cObjects),
11573 kwFmtU64(sz2, g_pFsCache->cbObjects),
11574 kwFmtU64(sz3, g_pFsCache->cbObjects / g_pFsCache->cObjects));
11575 off += sprintf(&szBuf[off], "%s %14s A path hashes, taking up %s bytes, avg %s bytes, %s collision\n", szPrf,
11576 kwFmtU64(sz1, g_pFsCache->cAnsiPaths),
11577 kwFmtU64(sz2, g_pFsCache->cbAnsiPaths),
11578 kwFmtU64(sz3, g_pFsCache->cbAnsiPaths / K_MAX(g_pFsCache->cAnsiPaths, 1)),
11579 kwFmtU64(sz4, g_pFsCache->cAnsiPathCollisions));
11580#ifdef KFSCACHE_CFG_UTF16
11581 off += sprintf(&szBuf[off], "%s %14s W path hashes, taking up %s bytes, avg %s bytes, %s collisions\n", szPrf,
11582 kwFmtU64(sz1, g_pFsCache->cUtf16Paths),
11583 kwFmtU64(sz2, g_pFsCache->cbUtf16Paths),
11584 kwFmtU64(sz3, g_pFsCache->cbUtf16Paths / K_MAX(g_pFsCache->cUtf16Paths, 1)),
11585 kwFmtU64(sz4, g_pFsCache->cUtf16PathCollisions));
11586#endif
11587 off += sprintf(&szBuf[off], "%s %14s child hash tables, total of %s entries, %s children inserted, %s collisions\n", szPrf,
11588 kwFmtU64(sz1, g_pFsCache->cChildHashTabs),
11589 kwFmtU64(sz2, g_pFsCache->cChildHashEntriesTotal),
11590 kwFmtU64(sz3, g_pFsCache->cChildHashed),
11591 kwFmtU64(sz4, g_pFsCache->cChildHashCollisions));
11592
11593 cMisses = g_pFsCache->cLookups - g_pFsCache->cPathHashHits - g_pFsCache->cWalkHits;
11594 off += sprintf(&szBuf[off], "%s %14s lookups: %s (%u%%) path hash hits, %s (%u%%) walks hits, %s (%u%%) misses\n", szPrf,
11595 kwFmtU64(sz1, g_pFsCache->cLookups),
11596 kwFmtU64(sz2, g_pFsCache->cPathHashHits),
11597 (unsigned)(g_pFsCache->cPathHashHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11598 kwFmtU64(sz3, g_pFsCache->cWalkHits),
11599 (unsigned)(g_pFsCache->cWalkHits * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)),
11600 kwFmtU64(sz4, cMisses),
11601 (unsigned)(cMisses * (KU64)100 / K_MAX(g_pFsCache->cLookups, 1)));
11602
11603 off += sprintf(&szBuf[off], "%s %14s child searches, %s (%u%%) hash hits\n", szPrf,
11604 kwFmtU64(sz1, g_pFsCache->cChildSearches),
11605 kwFmtU64(sz2, g_pFsCache->cChildHashHits),
11606 (unsigned)(g_pFsCache->cChildHashHits * (KU64)100 / K_MAX(g_pFsCache->cChildSearches, 1)));
11607 off += sprintf(&szBuf[off], "%s %14s name changes, growing %s times (%u%%)\n", szPrf,
11608 kwFmtU64(sz1, g_pFsCache->cNameChanges),
11609 kwFmtU64(sz2, g_pFsCache->cNameGrowths),
11610 (unsigned)(g_pFsCache->cNameGrowths * 100 / K_MAX(g_pFsCache->cNameChanges, 1)) );
11611
11612
11613 /*
11614 * Process & Memory details.
11615 */
11616 if (!GetProcessHandleCount(GetCurrentProcess(), &cHandles))
11617 cHandles = 0;
11618 MemInfo.cb = sizeof(MemInfo);
11619 if (!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&MemInfo, sizeof(MemInfo)))
11620 memset(&MemInfo, 0, sizeof(MemInfo));
11621 off += sprintf(&szBuf[off], "%s %14s handles; %s page faults; %s bytes page file, peaking at %s bytes\n", szPrf,
11622 kwFmtU64(sz1, cHandles),
11623 kwFmtU64(sz2, MemInfo.PageFaultCount),
11624 kwFmtU64(sz3, MemInfo.PagefileUsage),
11625 kwFmtU64(sz4, MemInfo.PeakPagefileUsage));
11626 off += sprintf(&szBuf[off], "%s %14s bytes working set, peaking at %s bytes; %s byte private\n", szPrf,
11627 kwFmtU64(sz1, MemInfo.WorkingSetSize),
11628 kwFmtU64(sz2, MemInfo.PeakWorkingSetSize),
11629 kwFmtU64(sz3, MemInfo.PrivateUsage));
11630 off += sprintf(&szBuf[off], "%s %14s bytes non-paged pool, peaking at %s bytes; %s bytes paged pool, peaking at %s bytes\n",
11631 szPrf,
11632 kwFmtU64(sz1, MemInfo.QuotaNonPagedPoolUsage),
11633 kwFmtU64(sz2, MemInfo.QuotaPeakNonPagedPoolUsage),
11634 kwFmtU64(sz3, MemInfo.QuotaPagedPoolUsage),
11635 kwFmtU64(sz4, MemInfo.QuotaPeakPagedPoolUsage));
11636
11637 if (!GetProcessIoCounters(GetCurrentProcess(), &IoCounters))
11638 memset(&IoCounters, 0, sizeof(IoCounters));
11639 off += sprintf(&szBuf[off], "%s %14s bytes in %s reads [src: OS]\n", szPrf,
11640 kwFmtU64(sz1, IoCounters.ReadTransferCount),
11641 kwFmtU64(sz2, IoCounters.ReadOperationCount));
11642 off += sprintf(&szBuf[off], "%s %14s bytes in %s writes [src: OS]\n", szPrf,
11643 kwFmtU64(sz1, IoCounters.WriteTransferCount),
11644 kwFmtU64(sz2, IoCounters.WriteOperationCount));
11645 off += sprintf(&szBuf[off], "%s %14s bytes in %s other I/O operations [src: OS]\n", szPrf,
11646 kwFmtU64(sz1, IoCounters.OtherTransferCount),
11647 kwFmtU64(sz2, IoCounters.OtherOperationCount));
11648
11649 MemStatus.dwLength = sizeof(MemStatus);
11650 if (!GlobalMemoryStatusEx(&MemStatus))
11651 memset(&MemStatus, 0, sizeof(MemStatus));
11652 off += sprintf(&szBuf[off], "%s %14s bytes used VA, %#" KX64_PRI " bytes available\n", szPrf,
11653 kwFmtU64(sz1, MemStatus.ullTotalVirtual - MemStatus.ullAvailVirtual),
11654 MemStatus.ullAvailVirtual);
11655 off += sprintf(&szBuf[off], "%s %14u %% system memory load\n", szPrf, MemStatus.dwMemoryLoad);
11656
11657 maybe_con_fwrite(szBuf, off, 1, stdout);
11658 fflush(stdout);
11659}
11660
11661
11662/**
11663 * Handles what comes after --test.
11664 *
11665 * @returns Exit code.
11666 * @param argc Number of arguments after --test.
11667 * @param argv Arguments after --test.
11668 */
11669static int kwTestRun(int argc, char **argv)
11670{
11671 int i;
11672 int j;
11673 int rcExit;
11674 int cRepeats;
11675 char szCwd[MAX_PATH];
11676 const char *pszCwd = getcwd(szCwd, sizeof(szCwd));
11677 KU32 cEnvVars;
11678 char **papszEnvVars;
11679 const char *pszSpecialEnv = "";
11680 const char *pszSpecialEnvFull = NULL;
11681 KBOOL fWatcomBrainDamange = K_FALSE;
11682 KBOOL fNoPchCaching = K_FALSE;
11683
11684 /*
11685 * Parse arguments.
11686 */
11687 /* Repeat count. */
11688 i = 0;
11689 if (i >= argc)
11690 return kwErrPrintfRc(2, "--test takes an repeat count argument or '--'!\n");
11691 if (strcmp(argv[i], "--") != 0)
11692 {
11693 cRepeats = atoi(argv[i]);
11694 if (cRepeats <= 0)
11695 return kwErrPrintfRc(2, "The repeat count '%s' is zero, negative or invalid!\n", argv[i]);
11696 i++;
11697
11698 /* Optional directory change. */
11699 if ( i < argc
11700 && ( strcmp(argv[i], "--chdir") == 0
11701 || strcmp(argv[i], "-C") == 0 ) )
11702 {
11703 i++;
11704 if (i >= argc)
11705 return kwErrPrintfRc(2, "--chdir takes an argument!\n");
11706 pszCwd = argv[i++];
11707 }
11708
11709 /* Optional watcom flag directory change. */
11710 if ( i < argc
11711 && ( strcmp(argv[i], "--wcc-brain-damage") == 0
11712 || strcmp(argv[i], "--watcom-brain-damage") == 0) )
11713 {
11714 fWatcomBrainDamange = K_TRUE;
11715 i++;
11716 }
11717
11718 /* Optional watcom flag directory change. */
11719 if ( i < argc
11720 && strcmp(argv[i], "--no-pch-caching") == 0)
11721 {
11722 fNoPchCaching = K_TRUE;
11723 i++;
11724 }
11725
11726 /* Optional directory change. */
11727 if ( i < argc
11728 && ( strcmp(argv[i], "--set-special") == 0
11729 || strcmp(argv[i], "-s") == 0 ) )
11730 {
11731 i++;
11732 if (i >= argc)
11733 return kwErrPrintfRc(2, "--set-special takes an argument!\n");
11734 pszSpecialEnvFull = argv[i++];
11735 putenv(pszSpecialEnvFull);
11736 pszSpecialEnv = strdup(pszSpecialEnvFull);
11737 *strchr(pszSpecialEnv, '=') = '\0';
11738 }
11739
11740 /* Trigger breakpoint */
11741 if ( i < argc
11742 && strcmp(argv[i], "--breakpoint") == 0)
11743 {
11744 __debugbreak();
11745 i++;
11746 }
11747
11748 /* Check for '--'. */
11749 if (i >= argc)
11750 return kwErrPrintfRc(2, "Missing '--'\n");
11751 if (strcmp(argv[i], "--") != 0)
11752 return kwErrPrintfRc(2, "Expected '--' found '%s'\n", argv[i]);
11753 i++;
11754 }
11755 else
11756 {
11757 cRepeats = 1;
11758 i++;
11759 }
11760 if (i >= argc)
11761 return kwErrPrintfRc(2, "Nothing to execute after '--'!\n");
11762
11763 /*
11764 * Duplicate the environment.
11765 */
11766 cEnvVars = 0;
11767 while (environ[cEnvVars] != NULL)
11768 cEnvVars++;
11769 papszEnvVars = (char **)kHlpAllocZ(sizeof(papszEnvVars[0]) * (cEnvVars + 2));
11770
11771 /*
11772 * Do the job.
11773 */
11774 for (j = 0; j < cRepeats; j++)
11775 {
11776 memcpy(papszEnvVars, environ, sizeof(papszEnvVars[0]) * cEnvVars);
11777 rcExit = kSubmitHandleJobUnpacked(argv[i], pszCwd,
11778 argc - i, &argv[i], fWatcomBrainDamange,
11779 cEnvVars, papszEnvVars, pszSpecialEnv, fNoPchCaching,
11780 0, NULL);
11781 KW_LOG(("rcExit=%d\n", rcExit));
11782 kwSandboxCleanupLate(&g_Sandbox);
11783 }
11784
11785 if (getenv("KWORKER_STATS") != NULL)
11786 kwPrintStats();
11787
11788# ifdef WITH_LOG_FILE
11789 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
11790 CloseHandle(g_hLogFile);
11791# endif
11792 return rcExit;
11793}
11794
11795
11796/**
11797 * Reads @a pszFile into memory and chops it up into an argument vector.
11798 *
11799 * @returns Pointer to the argument vector on success, NULL on failure.
11800 * @param pszFile The file to load.
11801 * @param pcArgs Where to return the number of arguments.
11802 * @param ppszFileContent Where to return the allocation.
11803 */
11804static char **kwFullTestLoadArgvFile(const char *pszFile, int *pcArgs, char **ppszFileContent)
11805{
11806 char **papszArgs = NULL;
11807 FILE *pFile = fopen(pszFile, "r");
11808 if (pFile)
11809 {
11810 long cbFile;
11811 if ( fseek(pFile, 0, SEEK_END) == 0
11812 && (cbFile = ftell(pFile)) >= 0
11813 && fseek(pFile, 0, SEEK_SET) == 0)
11814 {
11815 char *pszFile = kHlpAllocZ(cbFile + 3);
11816 if (pszFile)
11817 {
11818 size_t cbRead = fread(pszFile, 1, cbFile + 1, pFile);
11819 if ( feof(pFile)
11820 && !ferror(pFile))
11821 {
11822 size_t off = 0;
11823 int cArgs = 0;
11824 int cAllocated = 0;
11825 char ch;
11826
11827 pszFile[cbRead] = '\0';
11828 pszFile[cbRead + 1] = '\0';
11829 pszFile[cbRead + 2] = '\0';
11830
11831 while ((ch = pszFile[off]) != '\0')
11832 {
11833 char *pszArg;
11834 switch (ch)
11835 {
11836 case ' ':
11837 case '\t':
11838 case '\n':
11839 case '\r':
11840 off++;
11841 continue;
11842
11843 case '\\':
11844 if (pszFile[off + 1] == '\n' || pszFile[off + 1] == '\r')
11845 {
11846 off += 2;
11847 continue;
11848 }
11849 /* fall thru */
11850 default:
11851 pszArg = &pszFile[off];
11852 do
11853 ch = pszFile[++off];
11854 while (ch != '\0' && ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r');
11855 pszFile[off++] = '\0';
11856 break;
11857
11858 case '\'':
11859 pszArg = &pszFile[++off];
11860 while ((ch = pszFile[off]) != '\0' && ch != '\'')
11861 off++;
11862 pszFile[off++] = '\0';
11863 break;
11864
11865 case '\"': /** @todo escape sequences */
11866 pszArg = &pszFile[++off];
11867 while ((ch = pszFile[off]) != '\0' && ch != '"')
11868 off++;
11869 pszFile[off++] = '\0';
11870 break;
11871 }
11872 if (cArgs + 1 >= cAllocated)
11873 {
11874 void *pvNew;
11875 cAllocated = cAllocated ? cAllocated * 2 : 16;
11876 pvNew = kHlpRealloc(papszArgs, cAllocated * sizeof(papszArgs[0]));
11877 if (pvNew)
11878 papszArgs = (char **)pvNew;
11879 else
11880 {
11881 kHlpFree(papszArgs);
11882 papszArgs = NULL;
11883 break;
11884 }
11885 }
11886 papszArgs[cArgs] = pszArg;
11887 papszArgs[++cArgs] = NULL;
11888 }
11889 *pcArgs = cArgs;
11890 }
11891 else
11892 kwErrPrintf("Error reading '%s'!\n", pszFile);
11893 }
11894 else
11895 kwErrPrintf("Error allocating %lu bytes!\n", cbFile + 2);
11896 }
11897 else
11898 kwErrPrintf("Error seeking '%s'!\n", pszFile);
11899 fclose(pFile);
11900 }
11901 else
11902 kwErrPrintf("Error opening '%s'!\n", pszFile);
11903 return papszArgs;
11904}
11905
11906/**
11907 * Appends a string to an string vector (arguments or enviornment).
11908 *
11909 * @returns 0 on success, non-zero on failure (exit code).
11910 * @param ppapszVector Pointer to the string pointer array.
11911 * @param pcEntries Pointer to the array size.
11912 * @param pszAppend The string to append.
11913 */
11914static int kwFullTestVectorAppend(const char ***ppapszVector, int *pcEntries, char const *pszAppend)
11915{
11916 unsigned cEntries = *pcEntries;
11917 if (!(cEntries & 15))
11918 {
11919 void *pvNew = kHlpRealloc((void *)*ppapszVector, sizeof(char *) * (cEntries + 16 + 1));
11920 if (pvNew)
11921 *ppapszVector = (const char **)pvNew;
11922 else
11923 return kwErrPrintfRc(2, "Out of memory!\n");
11924 }
11925 (*ppapszVector)[cEntries] = pszAppend;
11926 (*ppapszVector)[++cEntries] = NULL;
11927 *pcEntries = cEntries;
11928 return 0;
11929}
11930
11931
11932/**
11933 * Parses arguments for --full-test.
11934 *
11935 * @returns 0 on success, non-zero on failure (exit code).
11936 */
11937static int kwFullTestRunParseArgs(PKWONETEST *ppHead, int *piState, int argc, char **argv,
11938 const char *pszDefaultCwd, int cRecursions, const char *pszJobSrc)
11939{
11940 PKWONETEST pCur = *ppHead;
11941 int i;
11942 for (i = 0; i < argc; i++)
11943 {
11944 int rc = 0;
11945 const char *pszArg = argv[i];
11946 if (*pszArg == 'k' && kHlpStrComp(pszArg, "kSubmit") == 0)
11947 {
11948 if (*piState != 0)
11949 {
11950 pCur = (PKWONETEST)kHlpAllocZ(sizeof(*pCur));
11951 if (!pCur)
11952 return kwErrPrintfRc(2, "Out of memory!\n");
11953 pCur->fVirgin = K_TRUE;
11954 pCur->pszCwd = pszDefaultCwd;
11955 pCur->cRuns = 1;
11956 pCur->pNext = *ppHead;
11957 *ppHead = pCur;
11958 *piState = 0;
11959 }
11960 else if (!pCur->fVirgin)
11961 return kwErrPrintfRc(2, "Unexpected 'kSubmit' as argument #%u\n", i);
11962 pCur->pszJobSrc = pszJobSrc;
11963 pCur->iJobSrc = i;
11964 continue; /* (to stay virgin) */
11965 }
11966 else if (*pszArg == '-' && *piState == 0)
11967 {
11968 const char *pszValue = NULL;
11969 char ch = *++pszArg;
11970 pszArg++;
11971 if (ch == '-')
11972 {
11973 ch = '\0';
11974 if (*pszArg == '\0') /* -- */
11975 *piState = 2;
11976 /* Translate or handle long options: */
11977 else if (kHlpStrComp(pszArg, "putenv") == 0 || kHlpStrComp(pszArg, "set") == 0)
11978 ch = 'E';
11979 else if (kHlpStrComp(pszArg, "special-env") == 0)
11980 ch = 's';
11981 else if (kHlpStrComp(pszArg, "default-env") == 0)
11982 {
11983 unsigned i;
11984 pCur->cEnvVars = 0;
11985 for (i = 0; environ[i] && rc == 0; i++)
11986 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, kHlpStrDup(environ[i])); /* leaks; unchecked */
11987 }
11988 else if (kHlpStrComp(pszArg, "chdir") == 0)
11989 ch = 'C';
11990 else if (kHlpStrComp(pszArg, "post-cmd") == 0)
11991 ch = 'P';
11992 else if (kHlpStrComp(pszArg, "response-file") == 0)
11993 ch = '@';
11994 else if (kHlpStrComp(pszArg, "runs") == 0)
11995 ch = 'R';
11996 else if (kHlpStrComp(pszArg, "watcom-brain-damage") == 0)
11997 pCur->fWatcomBrainDamange = K_TRUE;
11998 else if (kHlpStrComp(pszArg, "no-pch-caching") == 0)
11999 pCur->fNoPchCaching = K_TRUE;
12000 else if (kHlpStrComp(pszArg, "executable") == 0)
12001 ch = 'e';
12002 else if (kHlpStrComp(pszArg, "breakpoint") == 0)
12003 {
12004 __debugbreak();
12005 continue; /* (to stay virgin) */
12006 }
12007 else
12008 return kwErrPrintfRc(2, "Unknown option: --%s\n", pszArg);
12009 pszArg = "";
12010 }
12011
12012 while (ch != '\0' && rc == 0)
12013 {
12014 /* Fetch value if needed: */
12015 switch (ch)
12016 {
12017 case '@':
12018 case 'e':
12019 case 'E':
12020 case 's':
12021 case 'C':
12022 case 'R':
12023 if (*pszArg == ':' || *pszArg == '=')
12024 pszValue = &pszArg[1];
12025 else if (*pszArg)
12026 pszValue = pszArg;
12027 else if (i + 1 < argc)
12028 pszValue = argv[++i];
12029 else
12030 return kwErrPrintfRc(2, "Option -%c takes a value\n", ch);
12031 pszArg = "";
12032 break;
12033 }
12034
12035 /* Handle the option: */
12036 switch (ch)
12037 {
12038 case 'E':
12039 rc = kwFullTestVectorAppend(&pCur->papszEnvVars, &pCur->cEnvVars, pszValue);
12040 break;
12041 case 'C':
12042 pCur->pszCwd = pszValue;
12043 break;
12044 case 's':
12045 pCur->pszSpecialEnv = pszValue;
12046 break;
12047 case 'e':
12048 pCur->pszExecutable = pszValue;
12049 break;
12050 case 'P':
12051 *piState = 1;
12052 if (*pszArg)
12053 return kwErrPrintfRc(2, "Option -P cannot be followed by other options!\n");
12054 break;
12055 case 'R':
12056 pCur->cRuns = atoi(pszValue);
12057 if ((int)pCur->cRuns < 0)
12058 return kwErrPrintfRc(2, "Option -R takes a positive (or zero) integer as value: %s\n", pszValue);
12059 break;
12060 case '@':
12061 if (cRecursions < 5)
12062 {
12063 char *pszLeaked = NULL;
12064 int cArgs = 0;
12065 char **papszArgsLeaked = kwFullTestLoadArgvFile(pszValue, &cArgs, &pszLeaked);
12066 if (papszArgsLeaked)
12067 {
12068 rc = kwFullTestRunParseArgs(ppHead, piState, cArgs, papszArgsLeaked, pszDefaultCwd,
12069 cRecursions + 1, pszValue);
12070 pCur = *ppHead;
12071 }
12072 else
12073 return 2;
12074 }
12075 else
12076 return kwErrPrintfRc(2, "Too deep response file nesting!\n");
12077 break;
12078 }
12079
12080 /* next */
12081 ch = *pszArg++;
12082 }
12083 }
12084 else if (*piState == 2)
12085 rc = kwFullTestVectorAppend(&pCur->papszArgs, &pCur->cArgs, pszArg);
12086 else if (*piState == 1)
12087 {
12088 if (pszArg[0] != '-' || pszArg[1] != '-' || pszArg[2] != '\0')
12089 rc = kwFullTestVectorAppend(&pCur->papszPostCmdArgs, &pCur->cPostCmdArgs, pszArg);
12090 else
12091 *piState = 2;
12092 }
12093 else
12094 return kwErrPrintfRc(2, "Unexpected argument: %s\n", pszArg);
12095 if (rc)
12096 return rc;
12097 pCur->fVirgin = K_FALSE;
12098 }
12099 return 0;
12100}
12101
12102
12103/**
12104 * Handles what comes after --full-test.
12105 *
12106 * @returns Exit code.
12107 * @param argc Number of arguments after --full-test.
12108 * @param argv Arguments after --full-test.
12109 */
12110static int kwFullTestRun(int argc, char **argv)
12111{
12112 char szDefaultCwd[MAX_PATH];
12113 const char *pszDefaultCwd = getcwd(szDefaultCwd, sizeof(szDefaultCwd));
12114 KWONETEST FirstTest;
12115 PKWONETEST pHead = &FirstTest;
12116 PKWONETEST pCur;
12117 int iState = 0;
12118 int rcExit;
12119
12120 /*
12121 * Parse arguments.
12122 */
12123 kHlpMemSet(&FirstTest, 0, sizeof(FirstTest));
12124 FirstTest.pszJobSrc = "command-line";
12125 FirstTest.iJobSrc = 1;
12126 FirstTest.fVirgin = K_TRUE;
12127 FirstTest.pszCwd = pszDefaultCwd;
12128 FirstTest.cRuns = 1;
12129
12130 rcExit = kwFullTestRunParseArgs(&pHead, &iState, argc, argv, pszDefaultCwd, 0, "command-line");
12131 if (rcExit)
12132 return rcExit;
12133
12134 /*
12135 * Do the job. LIFO ordering (see kSubmit).
12136 */
12137 for (pCur = pHead; pCur; pCur = pCur->pNext)
12138 {
12139 if (!pCur->pszExecutable && pCur->papszArgs)
12140 pCur->pszExecutable = pCur->papszArgs[0];
12141 if ( pCur->pszExecutable
12142 && pCur->cArgs > 0
12143 && pCur->cEnvVars > 0)
12144 {
12145 size_t const cbEnvVarCopy = sizeof(pCur->papszEnvVars[0]) * (pCur->cEnvVars + 1);
12146 char ** const papszEnvVarsCopy = (char **)kHlpDup(pCur->papszEnvVars, cbEnvVarCopy);
12147 unsigned iRun;
12148
12149 for (iRun = 0; iRun < pCur->cRuns; iRun++)
12150 {
12151 rcExit = kSubmitHandleJobUnpacked(pCur->pszExecutable, pCur->pszCwd,
12152 pCur->cArgs, pCur->papszArgs, pCur->fWatcomBrainDamange,
12153 pCur->cEnvVars, pCur->papszEnvVars, pCur->pszSpecialEnv,
12154 pCur->fNoPchCaching, pCur->cPostCmdArgs, pCur->papszPostCmdArgs);
12155
12156 KW_LOG(("rcExit=%d\n", rcExit));
12157 kwSandboxCleanupLate(&g_Sandbox);
12158
12159 memcpy((void *)pCur->papszEnvVars, papszEnvVarsCopy, cbEnvVarCopy);
12160 }
12161 kHlpFree(papszEnvVarsCopy);
12162 }
12163 else
12164 rcExit = kwErrPrintfRc(2, "Job is underspecified! %s%s%s (Job started with argument #%u, %s)\n",
12165 pCur->pszExecutable ? "" : " No executable!",
12166 pCur->cArgs < 1 ? " No arguments!" : "",
12167 pCur->cEnvVars < 1 ? " No environment!" : "",
12168 pCur->iJobSrc, pCur->pszJobSrc);
12169 }
12170
12171 if (getenv("KWORKER_STATS") != NULL)
12172 kwPrintStats();
12173
12174# ifdef WITH_LOG_FILE
12175 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12176 CloseHandle(g_hLogFile);
12177# endif
12178 return rcExit;
12179}
12180
12181
12182int main(int argc, char **argv)
12183{
12184 KSIZE cbMsgBuf = 0;
12185 KU8 *pbMsgBuf = NULL;
12186 int i;
12187 HANDLE hPipe = INVALID_HANDLE_VALUE;
12188 const char *pszTmp;
12189 KFSLOOKUPERROR enmIgnored;
12190#if defined(KBUILD_OS_WINDOWS) && defined(KBUILD_ARCH_X86)
12191 PVOID pvVecXcptHandler = AddVectoredExceptionHandler(0 /*called last*/,
12192 kwSandboxVecXcptEmulateChained);
12193#endif
12194#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12195 HANDLE hCurProc = GetCurrentProcess();
12196 PPEB pPeb = kwSandboxGetProcessEnvironmentBlock();
12197 PMY_RTL_USER_PROCESS_PARAMETERS pProcessParams = (PMY_RTL_USER_PROCESS_PARAMETERS)pPeb->ProcessParameters;
12198 DWORD dwType;
12199#endif
12200
12201
12202#ifdef WITH_FIXED_VIRTUAL_ALLOCS
12203 /*
12204 * Reserve memory for cl.exe
12205 */
12206 for (i = 0; i < K_ELEMENTS(g_aFixedVirtualAllocs); i++)
12207 {
12208 g_aFixedVirtualAllocs[i].fInUse = K_FALSE;
12209 g_aFixedVirtualAllocs[i].pvReserved = VirtualAlloc((void *)g_aFixedVirtualAllocs[i].uFixed,
12210 g_aFixedVirtualAllocs[i].cbFixed,
12211 MEM_RESERVE, PAGE_READWRITE);
12212 if ( !g_aFixedVirtualAllocs[i].pvReserved
12213 || g_aFixedVirtualAllocs[i].pvReserved != (void *)g_aFixedVirtualAllocs[i].uFixed)
12214 {
12215 kwErrPrintf("Failed to reserve %p LB %#x: %u\n", g_aFixedVirtualAllocs[i].uFixed, g_aFixedVirtualAllocs[i].cbFixed,
12216 GetLastError());
12217 if (g_aFixedVirtualAllocs[i].pvReserved)
12218 {
12219 VirtualFree(g_aFixedVirtualAllocs[i].pvReserved, g_aFixedVirtualAllocs[i].cbFixed, MEM_RELEASE);
12220 g_aFixedVirtualAllocs[i].pvReserved = NULL;
12221 }
12222 }
12223 }
12224#endif
12225
12226 /*
12227 * Register our Control-C and Control-Break handlers.
12228 */
12229 if (!SetConsoleCtrlHandler(kwSandboxCtrlHandler, TRUE /*fAdd*/))
12230 return kwErrPrintfRc(3, "SetConsoleCtrlHandler failed: %u\n", GetLastError());
12231
12232 /*
12233 * Create the cache and mark the temporary directory as using the custom revision.
12234 */
12235 g_pFsCache = kFsCacheCreate(KFSCACHE_F_MISSING_OBJECTS | KFSCACHE_F_MISSING_PATHS);
12236 if (!g_pFsCache)
12237 return kwErrPrintfRc(3, "kFsCacheCreate failed!\n");
12238
12239 pszTmp = getenv("TEMP");
12240 if (pszTmp && *pszTmp != '\0')
12241 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12242 pszTmp = getenv("TMP");
12243 if (pszTmp && *pszTmp != '\0')
12244 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12245 pszTmp = getenv("TMPDIR");
12246 if (pszTmp && *pszTmp != '\0')
12247 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, pszTmp, &enmIgnored));
12248
12249 /*
12250 * Make g_abDefLdBuf executable.
12251 */
12252 if (!VirtualProtect(g_abDefLdBuf, sizeof(g_abDefLdBuf), PAGE_EXECUTE_READWRITE, &dwType))
12253 return kwErrPrintfRc(3, "VirtualProtect(%p, %#x, PAGE_EXECUTE_READWRITE,NULL) failed: %u\n",
12254 g_abDefLdBuf, sizeof(g_abDefLdBuf), GetLastError());
12255
12256#ifdef WITH_CONSOLE_OUTPUT_BUFFERING
12257 /*
12258 * Get and duplicate the console handles.
12259 */
12260 /* Standard output. */
12261 g_Sandbox.StdOut.hOutput = pProcessParams->StandardOutput;
12262 if (!DuplicateHandle(hCurProc, pProcessParams->StandardOutput, hCurProc, &g_Sandbox.StdOut.hBackup,
12263 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12264 kHlpAssertFailedStmt(g_Sandbox.StdOut.hBackup = pProcessParams->StandardOutput);
12265 dwType = GetFileType(g_Sandbox.StdOut.hOutput);
12266 g_Sandbox.StdOut.fIsConsole = dwType == FILE_TYPE_CHAR;
12267 g_Sandbox.StdOut.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12268 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12269 g_Sandbox.HandleStdOut.enmType = KWHANDLETYPE_OUTPUT_BUF;
12270 g_Sandbox.HandleStdOut.cRefs = 0x10001;
12271 g_Sandbox.HandleStdOut.dwDesiredAccess = GENERIC_WRITE;
12272 g_Sandbox.HandleStdOut.u.pOutBuf = &g_Sandbox.StdOut;
12273 g_Sandbox.HandleStdOut.hHandle = g_Sandbox.StdOut.hOutput;
12274 if (g_Sandbox.StdOut.hOutput != INVALID_HANDLE_VALUE)
12275 {
12276 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdOut, g_Sandbox.StdOut.hOutput))
12277 g_Sandbox.cFixedHandles++;
12278 else
12279 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdOut (%p)!\n", g_Sandbox.StdOut.hOutput);
12280 }
12281 KWOUT_LOG(("StdOut: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12282 g_Sandbox.StdOut.hOutput, g_Sandbox.StdOut.hBackup, g_Sandbox.StdOut.fIsConsole, dwType));
12283
12284 /* Standard error. */
12285 g_Sandbox.StdErr.hOutput = pProcessParams->StandardError;
12286 if (!DuplicateHandle(hCurProc, pProcessParams->StandardError, hCurProc, &g_Sandbox.StdErr.hBackup,
12287 GENERIC_WRITE, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
12288 kHlpAssertFailedStmt(g_Sandbox.StdErr.hBackup = pProcessParams->StandardError);
12289 dwType = GetFileType(g_Sandbox.StdErr.hOutput);
12290 g_Sandbox.StdErr.fIsConsole = dwType == FILE_TYPE_CHAR;
12291 g_Sandbox.StdErr.fFileType = (dwType & ~FILE_TYPE_REMOTE) < 0xf
12292 ? (KU8)((dwType & ~FILE_TYPE_REMOTE) | (dwType >> 8)) : KU8_MAX;
12293 g_Sandbox.HandleStdErr.enmType = KWHANDLETYPE_OUTPUT_BUF;
12294 g_Sandbox.HandleStdErr.cRefs = 0x10001;
12295 g_Sandbox.HandleStdErr.dwDesiredAccess = GENERIC_WRITE;
12296 g_Sandbox.HandleStdErr.u.pOutBuf = &g_Sandbox.StdErr;
12297 g_Sandbox.HandleStdErr.hHandle = g_Sandbox.StdErr.hOutput;
12298 if ( g_Sandbox.StdErr.hOutput != INVALID_HANDLE_VALUE
12299 && g_Sandbox.StdErr.hOutput != g_Sandbox.StdOut.hOutput)
12300 {
12301 if (kwSandboxHandleTableEnter(&g_Sandbox, &g_Sandbox.HandleStdErr, g_Sandbox.StdErr.hOutput))
12302 g_Sandbox.cFixedHandles++;
12303 else
12304 return kwErrPrintfRc(3, "kwSandboxHandleTableEnter failed for StdErr (%p)!\n", g_Sandbox.StdErr.hOutput);
12305 }
12306 KWOUT_LOG(("StdErr: hOutput=%p (%p) fIsConsole=%d dwType=%#x\n",
12307 g_Sandbox.StdErr.hOutput, g_Sandbox.StdErr.hBackup, g_Sandbox.StdErr.fIsConsole, dwType));
12308
12309 /* Combined console buffer. */
12310 if (g_Sandbox.StdErr.fIsConsole)
12311 {
12312 g_Sandbox.Combined.hOutput = g_Sandbox.StdErr.hBackup;
12313 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12314 }
12315 else if (g_Sandbox.StdOut.fIsConsole)
12316 {
12317 g_Sandbox.Combined.hOutput = g_Sandbox.StdOut.hBackup;
12318 g_Sandbox.Combined.uCodepage = GetConsoleCP();
12319 }
12320 else
12321 {
12322 g_Sandbox.Combined.hOutput = INVALID_HANDLE_VALUE;
12323 g_Sandbox.Combined.uCodepage = CP_ACP;
12324 }
12325 KWOUT_LOG(("Combined: hOutput=%p uCodepage=%d\n", g_Sandbox.Combined.hOutput, g_Sandbox.Combined.uCodepage));
12326#endif /* WITH_CONSOLE_OUTPUT_BUFFERING */
12327
12328
12329 /*
12330 * Parse arguments.
12331 */
12332 for (i = 1; i < argc; i++)
12333 {
12334 if (strcmp(argv[i], "--pipe") == 0)
12335 {
12336 i++;
12337 if (i < argc)
12338 {
12339 char *pszEnd = NULL;
12340 unsigned __int64 u64Value = _strtoui64(argv[i], &pszEnd, 16);
12341 if ( *argv[i]
12342 && pszEnd != NULL
12343 && *pszEnd == '\0'
12344 && u64Value != 0
12345 && u64Value != (uintptr_t)INVALID_HANDLE_VALUE
12346 && (uintptr_t)u64Value == u64Value)
12347 hPipe = (HANDLE)(uintptr_t)u64Value;
12348 else
12349 return kwErrPrintfRc(2, "Invalid --pipe argument: %s\n", argv[i]);
12350 }
12351 else
12352 return kwErrPrintfRc(2, "--pipe takes an argument!\n");
12353 }
12354 else if (strcmp(argv[i], "--volatile") == 0)
12355 {
12356 i++;
12357 if (i < argc)
12358 kFsCacheSetupCustomRevisionForTree(g_pFsCache, kFsCacheLookupA(g_pFsCache, argv[i], &enmIgnored));
12359 else
12360 return kwErrPrintfRc(2, "--volatile takes an argument!\n");
12361 }
12362 else if (strcmp(argv[i], "--test") == 0)
12363 return kwTestRun(argc - i - 1, &argv[i + 1]);
12364 else if (strcmp(argv[i], "--full-test") == 0)
12365 return kwFullTestRun(argc - i - 1, &argv[i + 1]);
12366 else if (strcmp(argv[i], "--priority") == 0)
12367 {
12368 i++;
12369 if (i < argc)
12370 {
12371 char *pszEnd = NULL;
12372 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12373 if ( *argv[i]
12374 && pszEnd != NULL
12375 && *pszEnd == '\0'
12376 && uValue >= 1
12377 && uValue <= 5)
12378 {
12379 DWORD dwClass, dwPriority;
12380 switch (uValue)
12381 {
12382 case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break;
12383 case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break;
12384 case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break;
12385 case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12386 case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = 0xffffffff; break;
12387 }
12388 SetPriorityClass(GetCurrentProcess(), dwClass);
12389 if (dwPriority != 0xffffffff)
12390 SetThreadPriority(GetCurrentThread(), dwPriority);
12391 }
12392 else
12393 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12394 }
12395 else
12396 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12397 }
12398 else if (strcmp(argv[i], "--group") == 0)
12399 {
12400 i++;
12401 if (i < argc)
12402 {
12403 char *pszEnd = NULL;
12404 unsigned long uValue = strtoul(argv[i], &pszEnd, 16);
12405 if ( *argv[i]
12406 && pszEnd != NULL
12407 && *pszEnd == '\0'
12408 && uValue == (WORD)uValue)
12409 {
12410 typedef BOOL (WINAPI *PFNSETTHREADGROUPAFFINITY)(HANDLE, const GROUP_AFFINITY*, GROUP_AFFINITY *);
12411 PFNSETTHREADGROUPAFFINITY pfnSetThreadGroupAffinity;
12412 pfnSetThreadGroupAffinity = (PFNSETTHREADGROUPAFFINITY)GetProcAddress(GetModuleHandleW(L"KERNEL32.DLL"),
12413 "SetThreadGroupAffinity");
12414 if (pfnSetThreadGroupAffinity)
12415 {
12416 GROUP_AFFINITY NewAff = { ~(uintptr_t)0, (WORD)uValue, 0, 0, 0 };
12417 GROUP_AFFINITY OldAff = { 0, 0, 0, 0, 0 };
12418 if (!pfnSetThreadGroupAffinity(GetCurrentThread(), &NewAff, &OldAff))
12419 kwErrPrintf("Failed to set processor group to %lu: %u\n", uValue, GetLastError());
12420 }
12421 else
12422 kwErrPrintf("Cannot set processor group to %lu because SetThreadGroupAffinity was not found\n", uValue);
12423 }
12424 else
12425 return kwErrPrintfRc(2, "Invalid --priority argument: %s\n", argv[i]);
12426 }
12427 else
12428 return kwErrPrintfRc(2, "--priority takes an argument!\n");
12429 }
12430 else if ( strcmp(argv[i], "--help") == 0
12431 || strcmp(argv[i], "-h") == 0
12432 || strcmp(argv[i], "-?") == 0)
12433 {
12434 printf("usage: kWorker [--volatile dir] [--priority <1-5>] [--group <processor-grp>\n"
12435 "usage: kWorker <--help|-h>\n"
12436 "usage: kWorker <--version|-V>\n"
12437 "usage: kWorker [--volatile dir] --test [<times> [--chdir <dir>] [--breakpoint] -- args\n"
12438 "\n"
12439 "This is an internal kmk program that is used via the builtin_kSubmit.\n");
12440 return 0;
12441 }
12442 else if ( strcmp(argv[i], "--version") == 0
12443 || strcmp(argv[i], "-V") == 0)
12444 return kbuild_version(argv[0]);
12445 else
12446 return kwErrPrintfRc(2, "Unknown argument '%s'\n", argv[i]);
12447 }
12448
12449 /*
12450 * If no --pipe argument, then assume its standard input.
12451 * We need to carefully replace the CRT stdin with a handle to "nul".
12452 */
12453 if (hPipe == INVALID_HANDLE_VALUE)
12454 {
12455 hPipe = GetStdHandle(STD_INPUT_HANDLE);
12456 if (GetFileType(hPipe) == FILE_TYPE_PIPE)
12457 {
12458 HANDLE hDuplicate = INVALID_HANDLE_VALUE;
12459 if (DuplicateHandle(GetCurrentProcess(), hPipe, GetCurrentProcess(), &hDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS))
12460 {
12461 int fdNul = _wopen(L"nul", O_RDWR | O_BINARY);
12462 if (fdNul >= 0)
12463 {
12464 if (_dup2(fdNul, 0) >= 0)
12465 {
12466 close(fdNul);
12467 hPipe = hDuplicate;
12468 }
12469 else
12470 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12471 }
12472 else
12473 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12474 }
12475 else
12476 return kwErrPrintfRc(2, "DuplicateHandle pipe failed: %u\n", GetLastError());
12477 }
12478 else
12479 return kwErrPrintfRc(2, "No --pipe <pipe-handle> argument and standard input is not a valid pipe handle (%#x, %u)\n",
12480 GetFileType(hPipe), GetLastError());
12481 }
12482 else if (GetFileType(hPipe) != FILE_TYPE_PIPE)
12483 return kwErrPrintfRc(2, "The specified --pipe %p is not a pipe handle: type %#x (last err %u)!\n",
12484 GetFileType(hPipe), GetLastError());
12485 g_hPipe = hPipe;
12486
12487 /*
12488 * Serve the pipe.
12489 */
12490 for (;;)
12491 {
12492 KU32 cbMsg = 0;
12493 int rc = kSubmitReadIt(hPipe, &cbMsg, sizeof(cbMsg), K_TRUE /*fShutdownOkay*/);
12494 if (rc == 0)
12495 {
12496 /* Make sure the message length is within sane bounds. */
12497 if ( cbMsg > 4
12498 && cbMsg <= 256*1024*1024)
12499 {
12500 /* Reallocate the message buffer if necessary. We add 4 zero bytes. */
12501 if (cbMsg + 4 <= cbMsgBuf)
12502 { /* likely */ }
12503 else
12504 {
12505 cbMsgBuf = K_ALIGN_Z(cbMsg + 4, 2048);
12506 pbMsgBuf = kHlpRealloc(pbMsgBuf, cbMsgBuf);
12507 if (!pbMsgBuf)
12508 return kwErrPrintfRc(1, "Failed to allocate %u bytes for a message buffer!\n", cbMsgBuf);
12509 }
12510
12511 /* Read the whole message into the buffer, making sure there is are a 4 zero bytes following it. */
12512 *(KU32 *)pbMsgBuf = cbMsg;
12513 rc = kSubmitReadIt(hPipe, &pbMsgBuf[sizeof(cbMsg)], cbMsg - sizeof(cbMsg), K_FALSE /*fShutdownOkay*/);
12514 if (rc == 0)
12515 {
12516 const char *psz;
12517
12518 pbMsgBuf[cbMsg] = '\0';
12519 pbMsgBuf[cbMsg + 1] = '\0';
12520 pbMsgBuf[cbMsg + 2] = '\0';
12521 pbMsgBuf[cbMsg + 3] = '\0';
12522
12523 /* The first string after the header is the command. */
12524 psz = (const char *)&pbMsgBuf[sizeof(cbMsg)];
12525 if ( strcmp(psz, "JOB") == 0
12526 && g_rcCtrlC == 0)
12527 {
12528 struct
12529 {
12530 KI32 rcExitCode;
12531 KU8 bExiting;
12532 KU8 abZero[3];
12533 } Reply;
12534 Reply.rcExitCode = kSubmitHandleJob(psz, cbMsg - sizeof(cbMsg));
12535 Reply.bExiting = g_fRestart;
12536 Reply.abZero[0] = 0;
12537 Reply.abZero[1] = 0;
12538 Reply.abZero[2] = 0;
12539 rc = kSubmitWriteIt(hPipe, &Reply, sizeof(Reply));
12540 if ( rc == 0
12541 && !g_fRestart)
12542 {
12543 kwSandboxCleanupLate(&g_Sandbox);
12544 if (g_rcCtrlC == 0)
12545 continue;
12546 }
12547 }
12548 else
12549 rc = kwErrPrintfRc(-1, "Unknown command: '%s'\n", psz);
12550 }
12551 }
12552 else
12553 rc = kwErrPrintfRc(-1, "Bogus message length: %u (%#x)\n", cbMsg, cbMsg);
12554 }
12555
12556 /*
12557 * If we're exitting because we're restarting, we need to delay till
12558 * kmk/kSubmit has read the result. Windows documentation says it
12559 * immediately discards pipe buffers once the pipe is broken by the
12560 * server (us). So, We flush the buffer and queues a 1 byte read
12561 * waiting for kSubmit to close the pipe when it receives the
12562 * bExiting = K_TRUE result.
12563 */
12564 if (g_fRestart)
12565 {
12566 KU8 b;
12567 FlushFileBuffers(hPipe);
12568 ReadFile(hPipe, &b, 1, &cbMsg, NULL);
12569 }
12570
12571 CloseHandle(hPipe);
12572#ifdef WITH_LOG_FILE
12573 if (g_hLogFile != INVALID_HANDLE_VALUE && g_hLogFile != NULL)
12574 CloseHandle(g_hLogFile);
12575#endif
12576 if (getenv("KWORKER_STATS") != NULL)
12577 kwPrintStats();
12578 return g_rcCtrlC != 0 ? g_rcCtrlC : rc > 0 ? 0 : 1;
12579 }
12580}
12581
12582
12583/** @page pg_kWorker kSubmit / kWorker
12584 *
12585 * @section sec_kWorker_Motivation Motivation / Inspiration
12586 *
12587 * The kSubmit / kWorker combo was conceived as a way to speed up VirtualBox
12588 * builds on machines "infested" by Anti Virus protection and disk encryption
12589 * software. Build times jumping from 35-40 min to 77-82 min after the machine
12590 * got "infected".
12591 *
12592 * Speeing up builting of Boot Sector Kit \#3 was also hightly desirable. It is
12593 * mainly a bunch of tiny assembly and C files being compiler a million times.
12594 * As some of us OS/2 users maybe recalls, the Watcom make program can run its
12595 * own toolchain from within the same process, saving a lot of process creation
12596 * and teardown overhead.
12597 *
12598 *
12599 * @section sec_kWorker_kSubmit About kSubmit
12600 *
12601 * When wanting to execute a job in a kWorker instance, it must be submitted
12602 * using the kmk_builtin_kSubmit command in kmk. As the name suggest, this is
12603 * built into kmk and does not exist as an external program. The reason for
12604 * this is that it keep track of the kWorker instances.
12605 *
12606 * The kSubmit command has the --32-bit and --64-bit options for selecting
12607 * between 32-bit and 64-bit worker instance. We generally assume the user of
12608 * the command knows which bit count the executable has, so kSubmit is spared
12609 * the extra work of finding out.
12610 *
12611 * The kSubmit command shares a environment and current directory manipulation
12612 * with the kRedirect command, but not the file redirection. So long no file
12613 * operation is involed, kSubmit is a drop in kRedirect replacement. This is
12614 * hand for tools like OpenWatcom, NASM and YASM which all require environment
12615 * and/or current directory changes to work.
12616 *
12617 * Unlike the kRedirect command, the kSubmit command can also specify an
12618 * internall post command to be executed after the main command succeeds.
12619 * Currently only kmk_builtin_kDepObj is supported. kDepObj gathers dependency
12620 * information from Microsoft COFF object files and Watcom OMF object files and
12621 * is scheduled to replace kDepIDB.
12622 *
12623 *
12624 * @section sec_kWorker_Interaction kSubmit / kWorker interaction
12625 *
12626 * The kmk_builtin_kSubmit communicates with the kWorker instances over pipes.
12627 * A job request is written by kSubmit and kWorker read, unpacks it and executes
12628 * it. When the job is completed, kWorker writes a short reply with the exit
12629 * code and an internal status indicating whether it is going to restart.
12630 *
12631 * The kWorker intance will reply to kSubmit before completing all the internal
12632 * cleanup work, so as not to delay the next job execution unnecessarily. This
12633 * includes checking its own memory consumption and checking whether it needs
12634 * restarting. So, a decision to restart unfortunately have to wait till after
12635 * the next job has completed. This is a little bit unfortunate if the next job
12636 * requires a lot of memory and kWorker has already leaked/used a lot.
12637 *
12638 *
12639 * @section sec_kWorker_How_Works How kWorker Works
12640 *
12641 * kWorker will load the executable specified by kSubmit into memory and call
12642 * it's entrypoint in a lightly sandbox'ed environment.
12643 *
12644 *
12645 * @subsection ssec_kWorker_Loaing Image loading
12646 *
12647 * kWorker will manually load all the executable images into memory, fix them
12648 * up, and make a copy of the virgin image so it can be restored using memcpy
12649 * the next time it is used.
12650 *
12651 * Imported functions are monitored and replacements used for a few of them.
12652 * These replacements are serve the following purposes:
12653 * - Provide a different command line.
12654 * - Provide a different environment.
12655 * - Intercept process termination.
12656 * - Intercept thread creation (only linker is allowed to create threads).
12657 * - Intercept file reading for caching (header files, ++) as file system
12658 * access is made even slower by anti-virus software.
12659 * - Intercept crypto hash APIs to cache MD5 digests of header files
12660 * (c1.dll / c1xx.dll spends a noticable bit of time doing MD5).
12661 * - Intercept temporary files (%TEMP%/_CL_XXXXXXyy) to keep the entirely
12662 * in memory as writing files grows expensive with encryption and
12663 * anti-virus software active.
12664 * - Intercept some file system queries to use the kFsCache instead of
12665 * going to the kernel and slowly worm thru the AV filter driver.
12666 * - Intercept standard output/error and console writes to aggressivly
12667 * buffer the output. The MS CRT does not buffer either when it goes to
12668 * the console, resulting in terrible performance and mixing up output
12669 * with other compile jobs.
12670 * This also allows us to filter out the annoying source file announcements
12671 * by cl.exe.
12672 * - Intercept VirtualAlloc and VirtualFree to prevent
12673 * CL.EXE/C1.DLL/C1XX.DLL from leaking some 72MB internal allocat area.
12674 * - Intercept FlsAlloc/FlsFree to make sure the allocations are freed and
12675 * the callbacks run after each job.
12676 * - Intercept HeapCreate/HeapFree to reduce leaks from statically linked
12677 * executables and tools using custom heaps (like the microsoft linker).
12678 * [exectuable images only]
12679 * - Intercept atexit and _onexit registration to be able run them after
12680 * each job instead of crashing as kWorker exits. This also helps avoid
12681 * some leaks. [executable image only]
12682 *
12683 * DLLs falls into two categories, system DLLs which we always load using the
12684 * native loader, and tool DLLs which can be handled like the executable or
12685 * optionally using the native loader. We maintain a hardcoded white listing of
12686 * tool DLLs we trust to load using the native loader.
12687 *
12688 * Imports of natively loaded DLLs are processed too, but we only replace a
12689 * subset of the functions compared to natively loaded excutable and DLL images.
12690 *
12691 * DLLs are never unloaded and we cache LoadLibrary requests (hash the input).
12692 * This is to speed up job execution.
12693 *
12694 * It was thought that we needed to restore (memcpy) natively loaded tool DLLs
12695 * for each job run, but so far this hasn't been necessary.
12696 *
12697 *
12698 * @subsection ssec_kWorker_Optimizing Optimizing the Compiler
12699 *
12700 * The Visual Studio 2010 C/C++ compiler does a poor job at processing header
12701 * files and uses a whole bunch of temporary files (in %TEMP%) for passing
12702 * intermediate representation between the first (c1/c1xx.dll) and second pass
12703 * (c2.dll).
12704 *
12705 * kWorker helps the compiler as best as it can. Given a little knowledge about
12706 * stable and volatile file system areas, it can do a lot of caching that a
12707 * normal compiler driver cannot easily do when given a single file.
12708 *
12709 *
12710 * @subsubsection sssec_kWorker_Headers Cache Headers Files and Searches
12711 *
12712 * The preprocessor part will open and process header files exactly as they are
12713 * encountered in the source files. If string.h is included by the main source
12714 * and five other header files, it will be searched for (include path), opened,
12715 * read, MD5-summed, and pre-processed six times. The last five times is just a
12716 * waste of time because of the guards or \#pragma once. A smart compiler would
12717 * make a little extra effort and realize this.
12718 *
12719 * kWorker will cache help the preprocessor by remembering places where the
12720 * header was not found with help of kFsCache, and cache the file in memory when
12721 * found. The first part is taken care of by intercepting GetFileAttributesW,
12722 * and the latter by intercepting CreateFileW, ReadFile and CloseFile. Once
12723 * cached, the file is kept open and the CreateFileW call returns a duplicate of
12724 * that handle. An internal handle table is used by ReadFile and CloseFile to
12725 * keep track of intercepted handles (also used for temporary file, temporary
12726 * file mappings, console buffering, and standard out/err buffering).
12727 *
12728 * PS. The header search optimization also comes in handy when cl.exe goes on
12729 * thru the whole PATH looking for c1/c1xx.exe and c2.exe after finding
12730 * c1/c1xx.dll and c2.dll. My guess is that the compiler team can
12731 * optionally compile the three pass DLLs as executables during development
12732 * and problem analysis.
12733 *
12734 *
12735 * @subsubsection sssec_kWorker_Temp_Files Temporary Files In Memory
12736 *
12737 * The issues of the temporary files is pretty severe on the Dell machine used
12738 * for benchmarking with full AV and encryption. The synthetic benchmark
12739 * improved by 30% when kWorker implemented measures to keep them entirely in
12740 * memory.
12741 *
12742 * kWorker implement these by recognizing the filename pattern in CreateFileW
12743 * and creating/opening the given file as needed. The handle returned is a
12744 * duplicate of the current process, thus giving us a good chance of catching
12745 * API calls we're not intercepting.
12746 *
12747 * In addition to CreateFileW, we also need to intercept GetFileType, ReadFile,
12748 * WriteFile, SetFilePointer+Ex, SetEndOfFile, and CloseFile. The 2nd pass
12749 * additionally requires GetFileSize+Ex, CreateFileMappingW, MapViewOfFile and
12750 * UnmapViewOfFile.
12751 *
12752 *
12753 * @section sec_kWorker_Numbers Some measurements.
12754 *
12755 * - r2881 building src/VBox/Runtime:
12756 * - without: 2m01.016388s = 120.016388 s
12757 * - with: 1m15.165069s = 75.165069 s => 120.016388s - 75.165069s = 44.851319s => 44.85/120.02 = 37% speed up.
12758 * - r2884 building vbox/debug (r110512):
12759 * - without: 11m14.446609s = 674.446609 s
12760 * - with: 9m01.017344s = 541.017344 s => 674.446609s - 541.017344s = 133.429265 => 133.43/674.45 = 19% speed up
12761 * - r2896 building vbox/debug (r110577):
12762 * - with: 8m31.182384s = 511.182384 s => 674.446609s - 511.182384s = 163.264225 = 163.26/674.45 = 24% speed up
12763 * - r2920 building vbox/debug (r110702) on Skylake (W10/amd64, only standard
12764 * MS Defender as AV):
12765 * - without: 10m24.990389s = 624.990389s
12766 * - with: 08m04.738184s = 484.738184s
12767 * - delta: 624.99s - 484.74s = 140.25s
12768 * - saved: 140.25/624.99 = 22% faster
12769 *
12770 *
12771 * @subsection subsec_kWorker_Early_Numbers Early Experiments
12772 *
12773 * These are some early experiments doing 1024 compilations of
12774 * VBoxBS2Linker.cpp using a hard coded command line and looping in kWorker's
12775 * main function:
12776 *
12777 * Skylake (W10/amd64, only stdandard MS defender):
12778 * - cmd 1: 48 /1024 = 0x0 (0.046875) [for /l %i in (1,1,1024) do ...]
12779 * - kmk 1: 44 /1024 = 0x0 (0.04296875) [all: ; 1024 x cl.exe]
12780 * - run 1: 37 /1024 = 0x0 (0.0361328125) [just process creation gain]
12781 * - run 2: 34 /1024 = 0x0 (0.033203125) [get file attribs]
12782 * - run 3: 32.77 /1024 = 0x0 (0.032001953125) [read caching of headers]
12783 * - run 4: 32.67 /1024 = 0x0 (0.031904296875) [loader tweaking]
12784 * - run 5: 29.144/1024 = 0x0 (0.0284609375) [with temp files in memory]
12785 *
12786 * Dell (W7/amd64, infected by mcafee):
12787 * - kmk 1: 285.278/1024 = 0x0 (0.278591796875)
12788 * - run 1: 134.503/1024 = 0x0 (0.1313505859375) [w/o temp files in memory]
12789 * - run 2: 78.161/1024 = 0x0 (0.0763291015625) [with temp files in memory]
12790 *
12791 * The command line:
12792 * @code{.cpp}
12793 "\"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"
12794 * @endcode
12795 */
12796
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